Skip to content

Commit 65525c2

Browse files
committed
add w201 parser
1 parent 7003db5 commit 65525c2

9 files changed

+226
-13
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Useful when developing integrations between FASTER Web and other systems.
1717
| W114 | Asset Master List | | ✔️ |
1818
| W200 | Inventory Report | | ✔️ |
1919
| W200S | Inventory Summary Report | ✔️ | |
20+
| W201 | Inventory Item Issue Report | | ✔️ |
2021
| W217 | Direct Charge Transactions | | ✔️ |
2122
| W223 | Inventory Transaction Details Report | ✔️ | ✔️ |
2223
| W235 | Inventory Snapshot | ✔️ | |

test/xlsxReports.js

+15-7
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import assert from 'node:assert';
44
import { describe, it } from 'node:test';
55
import { isValidDateString, isValidTimeString } from '@cityssm/utils-datetime';
6-
import { parseW114ExcelReport, parseW200ExcelReport, parseW217ExcelReport, parseW223ExcelReport, parseW311ExcelReport, parseW604ExcelReport, w114ReportName, w200ReportName, w217ReportName, w223ReportName, w311ReportName, w604ReportName } from '../xlsxReports.js';
6+
import { parseW114ExcelReport, parseW200ExcelReport, parseW201ExcelReport, parseW217ExcelReport, parseW223ExcelReport, parseW311ExcelReport, parseW604ExcelReport, w114ReportName, w200ReportName, w201ReportName, w217ReportName, w223ReportName, w311ReportName, w604ReportName } from '../xlsxReports.js';
77
await describe('node-faster-report-parser/xlsx', async () => {
88
// eslint-disable-next-line @cspell/spellchecker
9-
await it('Parses "W114 - Asset Master List"', () => {
9+
await it.skip('Parses "W114 - Asset Master List"', () => {
1010
// eslint-disable-next-line @cspell/spellchecker
1111
const results = parseW114ExcelReport('./samples/w114_assetMasterList.xlsx');
1212
console.log(results);
@@ -20,7 +20,7 @@ await describe('node-faster-report-parser/xlsx', async () => {
2020
assert.notStrictEqual(asset.assetNumber, '');
2121
}
2222
});
23-
await it('Parses "W200 - Inventory Report"', () => {
23+
await it.skip('Parses "W200 - Inventory Report"', () => {
2424
const results = parseW200ExcelReport('./samples/w200.xlsx');
2525
console.log(results);
2626
assert.strictEqual(results.reportName, w200ReportName);
@@ -39,7 +39,15 @@ await describe('node-faster-report-parser/xlsx', async () => {
3939
}
4040
}
4141
});
42-
await it('Parses "W217 - Direct Charge Transactions"', () => {
42+
await it('Parses "W201 - Inventory Item Issue Report"', () => {
43+
const results = parseW201ExcelReport('./samples/w201_inventoryItemIssueReport.xlsx');
44+
console.log(results);
45+
assert.strictEqual(results.reportName, w201ReportName);
46+
assert(isValidDateString(results.exportDate));
47+
assert(isValidTimeString(results.exportTime));
48+
assert(results.data.length > 0);
49+
});
50+
await it.skip('Parses "W217 - Direct Charge Transactions"', () => {
4351
const results = parseW217ExcelReport('./samples/w217_directChargeTransactions.xlsx');
4452
// console.log(results)
4553
assert.strictEqual(results.reportName, w217ReportName);
@@ -61,7 +69,7 @@ await describe('node-faster-report-parser/xlsx', async () => {
6169
}
6270
}
6371
});
64-
await describe('W223 - Inventory Transaction Details Report', async () => {
72+
await describe.skip('W223 - Inventory Transaction Details Report', async () => {
6573
await it('Parses with page breaks', () => {
6674
const results = parseW223ExcelReport('./samples/w223_inventoryTransactionDetails.xlsx', {
6775
inverseAmounts: true
@@ -126,7 +134,7 @@ await describe('node-faster-report-parser/xlsx', async () => {
126134
}
127135
});
128136
});
129-
await it('Parses "W311 - Active Work Orders by Shop"', () => {
137+
await it.skip('Parses "W311 - Active Work Orders by Shop"', () => {
130138
const results = parseW311ExcelReport('./samples/w311_activeWorkOrdersByShop.xlsx');
131139
// console.log(JSON.stringify(results, undefined, 2))
132140
assert.strictEqual(results.reportName, w311ReportName);
@@ -136,7 +144,7 @@ await describe('node-faster-report-parser/xlsx', async () => {
136144
assert((results.data.at(0)?.workOrders.length ?? 0) > 0);
137145
assert((results.data.at(0)?.workOrders.at(0)?.repairs.length ?? 0) > 0);
138146
});
139-
await it('Parses "W604 - Integration Log Viewer"', () => {
147+
await it.skip('Parses "W604 - Integration Log Viewer"', () => {
140148
const results = parseW604ExcelReport('./samples/w604_integrationLogViewer.xlsx');
141149
// console.log(JSON.stringify(results, undefined, 2))
142150
assert.strictEqual(results.reportName, w604ReportName);

test/xlsxReports.ts

+20-6
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ import { isValidDateString, isValidTimeString } from '@cityssm/utils-datetime'
99
import {
1010
parseW114ExcelReport,
1111
parseW200ExcelReport,
12+
parseW201ExcelReport,
1213
parseW217ExcelReport,
1314
parseW223ExcelReport,
1415
parseW311ExcelReport,
1516
parseW604ExcelReport,
1617
w114ReportName,
1718
w200ReportName,
19+
w201ReportName,
1820
w217ReportName,
1921
w223ReportName,
2022
w311ReportName,
@@ -23,7 +25,7 @@ import {
2325

2426
await describe('node-faster-report-parser/xlsx', async () => {
2527
// eslint-disable-next-line @cspell/spellchecker
26-
await it('Parses "W114 - Asset Master List"', () => {
28+
await it.skip('Parses "W114 - Asset Master List"', () => {
2729
// eslint-disable-next-line @cspell/spellchecker
2830
const results = parseW114ExcelReport('./samples/w114_assetMasterList.xlsx')
2931

@@ -43,7 +45,7 @@ await describe('node-faster-report-parser/xlsx', async () => {
4345
}
4446
})
4547

46-
await it('Parses "W200 - Inventory Report"', () => {
48+
await it.skip('Parses "W200 - Inventory Report"', () => {
4749
const results = parseW200ExcelReport('./samples/w200.xlsx')
4850

4951
console.log(results)
@@ -71,7 +73,19 @@ await describe('node-faster-report-parser/xlsx', async () => {
7173
}
7274
})
7375

74-
await it('Parses "W217 - Direct Charge Transactions"', () => {
76+
await it('Parses "W201 - Inventory Item Issue Report"', () => {
77+
const results = parseW201ExcelReport('./samples/w201_inventoryItemIssueReport.xlsx')
78+
79+
console.log(results)
80+
81+
assert.strictEqual(results.reportName, w201ReportName)
82+
assert(isValidDateString(results.exportDate))
83+
assert(isValidTimeString(results.exportTime))
84+
85+
assert(results.data.length > 0)
86+
})
87+
88+
await it.skip('Parses "W217 - Direct Charge Transactions"', () => {
7589
const results = parseW217ExcelReport(
7690
'./samples/w217_directChargeTransactions.xlsx'
7791
)
@@ -104,7 +118,7 @@ await describe('node-faster-report-parser/xlsx', async () => {
104118
}
105119
})
106120

107-
await describe('W223 - Inventory Transaction Details Report', async () => {
121+
await describe.skip('W223 - Inventory Transaction Details Report', async () => {
108122
await it('Parses with page breaks', () => {
109123
const results = parseW223ExcelReport(
110124
'./samples/w223_inventoryTransactionDetails.xlsx',
@@ -211,7 +225,7 @@ await describe('node-faster-report-parser/xlsx', async () => {
211225
})
212226
})
213227

214-
await it('Parses "W311 - Active Work Orders by Shop"', () => {
228+
await it.skip('Parses "W311 - Active Work Orders by Shop"', () => {
215229
const results = parseW311ExcelReport(
216230
'./samples/w311_activeWorkOrdersByShop.xlsx'
217231
)
@@ -227,7 +241,7 @@ await describe('node-faster-report-parser/xlsx', async () => {
227241
assert((results.data.at(0)?.workOrders.at(0)?.repairs.length ?? 0) > 0)
228242
})
229243

230-
await it('Parses "W604 - Integration Log Viewer"', () => {
244+
await it.skip('Parses "W604 - Integration Log Viewer"', () => {
231245
const results = parseW604ExcelReport(
232246
'./samples/w604_integrationLogViewer.xlsx'
233247
)

xlsxReports.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export { parseW114ExcelReport, w114ReportName, type W114ExcelReportResults, type W114AssetReportData } from './xlsxReports/assets/w114.assetMasterList.js';
22
export { parseW200ExcelReport, w200ReportName, type W200ExcelReportResults, type W200StoreroomReportData, type W200ItemReportData } from './xlsxReports/inventory/w200.inventory.js';
3+
export { parseW201ExcelReport, w201ReportName, type W201ExcelReportResults, type W201ItemReportData } from './xlsxReports/inventory/w201.inventoryItemIssue.js';
34
export { parseW217ExcelReport, w217ReportName, type W217ExcelReportResults, type W217DocumentReportData, type W217TransactionReportData } from './xlsxReports/inventory/w217.directChargeTransactions.js';
45
export { parseW223ExcelReport, w223ReportName, type W223ExcelReportResults, type W223StoreroomReportData, type W223TransactionReportData } from './xlsxReports/inventory/w223.inventoryTransactionDetails.js';
56
export { parseW311ExcelReport, w311ReportName, type W311ExcelReportResults, type W311ShopReportData, type W311WorkOrderReportData, type W311RepairReportData } from './xlsxReports/maintenance/w311.activeWorkOrdersByShop.js';

xlsxReports.js

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/* eslint-disable no-secrets/no-secrets */
33
export { parseW114ExcelReport, w114ReportName } from './xlsxReports/assets/w114.assetMasterList.js';
44
export { parseW200ExcelReport, w200ReportName } from './xlsxReports/inventory/w200.inventory.js';
5+
export { parseW201ExcelReport, w201ReportName } from './xlsxReports/inventory/w201.inventoryItemIssue.js';
56
export { parseW217ExcelReport, w217ReportName } from './xlsxReports/inventory/w217.directChargeTransactions.js';
67
export { parseW223ExcelReport, w223ReportName } from './xlsxReports/inventory/w223.inventoryTransactionDetails.js';
78
export { parseW311ExcelReport, w311ReportName } from './xlsxReports/maintenance/w311.activeWorkOrdersByShop.js';

xlsxReports.ts

+7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ export {
1616
type W200ItemReportData
1717
} from './xlsxReports/inventory/w200.inventory.js'
1818

19+
export {
20+
parseW201ExcelReport,
21+
w201ReportName,
22+
type W201ExcelReportResults,
23+
type W201ItemReportData
24+
} from './xlsxReports/inventory/w201.inventoryItemIssue.js'
25+
1926
export {
2027
parseW217ExcelReport,
2128
w217ReportName,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { type DateString } from '@cityssm/utils-datetime';
2+
import type { FasterExcelReportResults } from '../xlsxTypes.js';
3+
export interface W201ItemReportData {
4+
documentNumber: number;
5+
assetNumber?: string;
6+
itemNumber: string;
7+
itemName: string;
8+
issuedDate: DateString;
9+
repairReason: string;
10+
issuedBy: string;
11+
quantity: number;
12+
issuePrice: number;
13+
extendedCost: number;
14+
}
15+
export declare const w201ReportName = "W201 - Inventory Item Issue Report";
16+
export interface W201ExcelReportResults extends FasterExcelReportResults {
17+
reportName: typeof w201ReportName;
18+
data: W201ItemReportData[];
19+
}
20+
/**
21+
* Parses the XLSX version of the "W201 - Inventory Item Issue Report".
22+
* @param pathToXlsxFile - Path to the report.
23+
* @returns The parsed results.
24+
*/
25+
export declare function parseW201ExcelReport(pathToXlsxFile: string): W201ExcelReportResults;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { dateToString } from '@cityssm/utils-datetime';
2+
import Debug from 'debug';
3+
import { extractReportMetadata, getXLSXWorkBook, getXLSXWorkSheetData } from '../helpers.js';
4+
const debug = Debug('faster-report-parser:xlsx:w201');
5+
export const w201ReportName = 'W201 - Inventory Item Issue Report';
6+
function isDataRow(row) {
7+
return row.length === 11 && /^\d+$/.test(row[0] ?? '');
8+
}
9+
/**
10+
* Parses the XLSX version of the "W201 - Inventory Item Issue Report".
11+
* @param pathToXlsxFile - Path to the report.
12+
* @returns The parsed results.
13+
*/
14+
export function parseW201ExcelReport(pathToXlsxFile) {
15+
const workbook = getXLSXWorkBook(pathToXlsxFile);
16+
/*
17+
* Validate workbook
18+
*/
19+
const results = extractReportMetadata(workbook, {
20+
reportNameRowNumber: 2,
21+
exportDateTimeRowNumber: 3
22+
});
23+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
24+
if (results.reportName !== w201ReportName) {
25+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
26+
throw new Error(`Invalid reportName: ${results.reportName}`);
27+
}
28+
/*
29+
* Loop through sheets
30+
*/
31+
debug(`Looping through ${workbook.SheetNames.length} sheets`);
32+
for (const sheetName of workbook.SheetNames) {
33+
// eslint-disable-next-line security/detect-object-injection
34+
const worksheetData = getXLSXWorkSheetData(workbook.Sheets[sheetName]);
35+
/*
36+
* Loop through rows
37+
*/
38+
for (const row of worksheetData) {
39+
if (isDataRow(row)) {
40+
results.data.push({
41+
documentNumber: Number.parseInt(row[0] ?? ''),
42+
assetNumber: row[1] === 'No Asset' ? undefined : row[1],
43+
itemNumber: row[2] ?? '',
44+
itemName: row[3] ?? '',
45+
issuedDate: dateToString(new Date(row[4] ?? '')),
46+
repairReason: row[6] ?? '',
47+
issuedBy: row[7] ?? '',
48+
quantity: Number.parseFloat(row[8] ?? ''),
49+
issuePrice: Number.parseFloat(row[9] ?? ''),
50+
extendedCost: Number.parseFloat(row[10] ?? '')
51+
});
52+
}
53+
else {
54+
debug(row);
55+
}
56+
}
57+
}
58+
return results;
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { type DateString, dateToString } from '@cityssm/utils-datetime'
2+
import Debug from 'debug'
3+
4+
import {
5+
extractReportMetadata,
6+
getXLSXWorkBook,
7+
getXLSXWorkSheetData
8+
} from '../helpers.js'
9+
import type { FasterExcelReportResults, XlsxDataRow } from '../xlsxTypes.js'
10+
11+
const debug = Debug('faster-report-parser:xlsx:w201')
12+
13+
export interface W201ItemReportData {
14+
documentNumber: number
15+
assetNumber?: string
16+
itemNumber: string
17+
itemName: string
18+
issuedDate: DateString
19+
repairReason: string
20+
issuedBy: string
21+
quantity: number
22+
issuePrice: number
23+
extendedCost: number
24+
}
25+
26+
export const w201ReportName = 'W201 - Inventory Item Issue Report'
27+
28+
export interface W201ExcelReportResults extends FasterExcelReportResults {
29+
reportName: typeof w201ReportName
30+
data: W201ItemReportData[]
31+
}
32+
33+
function isDataRow(row: XlsxDataRow): boolean {
34+
return row.length === 11 && /^\d+$/.test(row[0] ?? '')
35+
}
36+
37+
/**
38+
* Parses the XLSX version of the "W201 - Inventory Item Issue Report".
39+
* @param pathToXlsxFile - Path to the report.
40+
* @returns The parsed results.
41+
*/
42+
export function parseW201ExcelReport(
43+
pathToXlsxFile: string
44+
): W201ExcelReportResults {
45+
const workbook = getXLSXWorkBook(pathToXlsxFile)
46+
47+
/*
48+
* Validate workbook
49+
*/
50+
51+
const results = extractReportMetadata(workbook, {
52+
reportNameRowNumber: 2,
53+
exportDateTimeRowNumber: 3
54+
}) as W201ExcelReportResults
55+
56+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
57+
if (results.reportName !== w201ReportName) {
58+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
59+
throw new Error(`Invalid reportName: ${results.reportName}`)
60+
}
61+
62+
/*
63+
* Loop through sheets
64+
*/
65+
66+
debug(`Looping through ${workbook.SheetNames.length} sheets`)
67+
68+
for (const sheetName of workbook.SheetNames) {
69+
// eslint-disable-next-line security/detect-object-injection
70+
const worksheetData = getXLSXWorkSheetData(workbook.Sheets[sheetName])
71+
72+
/*
73+
* Loop through rows
74+
*/
75+
76+
for (const row of worksheetData) {
77+
if (isDataRow(row)) {
78+
results.data.push({
79+
documentNumber: Number.parseInt(row[0] ?? ''),
80+
assetNumber: row[1] === 'No Asset' ? undefined : row[1],
81+
itemNumber: row[2] ?? '',
82+
itemName: row[3] ?? '',
83+
issuedDate: dateToString(new Date(row[4] ?? '')),
84+
repairReason: row[6] ?? '',
85+
issuedBy: row[7] ?? '',
86+
quantity: Number.parseFloat(row[8] ?? ''),
87+
issuePrice: Number.parseFloat(row[9] ?? ''),
88+
extendedCost: Number.parseFloat(row[10] ?? '')
89+
})
90+
} else {
91+
debug(row)
92+
}
93+
}
94+
}
95+
96+
return results
97+
}

0 commit comments

Comments
 (0)