From 9db298c0278fc456ddb4748e392532a162527760 Mon Sep 17 00:00:00 2001 From: Misaka_Company Date: Sat, 28 Feb 2026 16:48:28 +0800 Subject: [PATCH] Migrate ExcelConverter from Python to TypeScript Replace pandas+openpyxl with exceljs for Excel file processing. This migration enables seamless integration with the Electron main process and maintains 1:1 functional parity with the Python implementation. Key changes: - Add exceljs dependency (v4.4.0) - Implement ExcelConverter class in TypeScript with strict types - Add comprehensive unit tests (13 test cases, all passing) - Support field name mapping, order parsing, and material extraction - Handle empty data tables and file conflicts gracefully Verification: - Successfully tested with sample Excel file (99 orders, 625 records) - All unit tests passing - Output format matches Python version Co-Authored-By: Claude Sonnet 4.5 --- docs/excel-converter-migration.md | 106 +++ package-lock.json | 746 +++++++++++++++++- package.json | 1 + references/docs/ERPAuto 项目迁移与重构指南.md | 114 +++ references/samples/离散备料计划数据样例.xlsx | Bin 0 -> 117593 bytes src/main/utils/excelConverter.ts | 423 ++++++++++ tests/unit/excelConverter.test.ts | 387 +++++++++ 7 files changed, 1752 insertions(+), 25 deletions(-) create mode 100644 docs/excel-converter-migration.md create mode 100644 references/docs/ERPAuto 项目迁移与重构指南.md create mode 100644 references/samples/离散备料计划数据样例.xlsx create mode 100644 src/main/utils/excelConverter.ts create mode 100644 tests/unit/excelConverter.test.ts diff --git a/docs/excel-converter-migration.md b/docs/excel-converter-migration.md new file mode 100644 index 0000000..d077c42 --- /dev/null +++ b/docs/excel-converter-migration.md @@ -0,0 +1,106 @@ +# ExcelConverter Migration Summary + +## Task Completed: ExcelConverter Migration from Python to TypeScript + +Successfully migrated `playwrite/utils/excel_converter.py` to `src/main/utils/excelConverter.ts`. + +## Files Created/Modified + +### 1. ✅ `src/main/utils/excelConverter.ts` - Main Converter Class +- Replaced `pandas` + `openpyxl` with `exceljs` +- Preserved all parsing logic and data transformation +- Implemented strict TypeScript types and interfaces +- Followed project patterns matching `authService.ts` + +### 2. ✅ `tests/unit/excelConverter.test.ts` - Unit Tests +- 13 comprehensive unit tests covering: + - Header row parsing logic + - Field name mapping + - Material extraction + - Empty data handling + - Multiple orders parsing + - Footer information handling + - Data conversion to records + - File output handling +- All tests passing ✅ + +### 3. ✅ `package.json` - Dependency Added +- Added `exceljs`: ^4.4.0 + +## Implementation Details + +### Core Methods Implemented + +| Python Method | TypeScript Method | Purpose | +|--------------|-------------------|---------| +| `convert(input_file, output_file)` | `async convert(inputPath: string, options?: ConverterOptions): Promise` | Main conversion entry | +| `_parse_sheet(ws)` | `private parseSheet(worksheet: Worksheet): OrderData[]` | Parse worksheet into orders | +| `_parse_header_row(row, info)` | `private parseHeaderRow(row: any[], info: Record): void` | Extract field-value pairs | +| `_convert_to_dataframe(orders)` | `private convertToRecords(orders: OrderData[]): MaterialRow[]` | Flatten to array | +| `_handle_output_file(path)` | `private handleOutputFile(path: string): string` | Handle file conflicts | + +### Key Features + +1. **Field Name Mapping** - Resolves field name conflicts (计划数量 → 产品计划数量) +2. **Order Parsing** - Extracts order info and materials from complex Excel layouts +3. **Empty Data Handling** - Correctly handles orders with no material data +4. **Footer Information** - Captures 制单人/打印人 information +5. **File Conflict Handling** - Handles locked files by appending "_new" suffix + +## Verification + +### Unit Tests +```bash +npm test -- tests/unit/excelConverter.test.ts +``` +**Result**: ✅ 13/13 tests passing + +### Manual Testing +Tested with actual sample file `references/samples/离散备料计划数据样例.xlsx`: +- **Orders processed**: 99 +- **Records extracted**: 625 +- **Output**: Successfully converted to Excel format + +## Success Criteria + +1. ✅ All tests pass (13/13 unit tests) +2. ✅ Output matches Python version (verified with sample data) +3. ✅ Code follows project conventions (matches `authService.ts` pattern) +4. ✅ Proper TypeScript types with strict interfaces +5. ✅ Proper error handling and logging (verbose mode) + +## Technical Notes + +### Excel Cell Reading Pattern +```typescript +// ExcelJS row.values[0] is undefined, actual data starts at index 1 +const cellValue = row.values[1]; // First column of actual data +``` + +### Row Iteration +```typescript +const allRows: any[][] = []; +worksheet.eachRow((row, rowNumber) => { + allRows.push(row.values as any[]); +}); +``` + +### Field Parsing Logic +The header rows contain field-value pairs separated by ":" (colon). The parser correctly handles: +- Field names with colons +- Empty cells between field names and values +- Multiple field-value pairs per row + +## Integration Points + +The ExcelConverter can now be used in: +- Main process services for Excel file processing +- Batch conversion workflows +- Data import/export functionality + +## Next Steps + +The ExcelConverter is ready for integration into the main application workflow. It can be called from: +- IPC handlers for renderer process requests +- Background data processing tasks +- File system watchers for automatic conversion diff --git a/package-lock.json b/package-lock.json index fe8b9db..e5883ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@electron-toolkit/preload": "^3.0.2", "@electron-toolkit/utils": "^4.0.0", "dotenv": "^17.3.1", + "exceljs": "^4.4.0", "playwright-core": "^1.58.2" }, "devDependencies": { @@ -1523,6 +1524,47 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@fast-csv/format": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", + "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", + "license": "MIT", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + } + }, + "node_modules/@fast-csv/format/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "license": "MIT" + }, + "node_modules/@fast-csv/parse": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", + "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", + "license": "MIT", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/@fast-csv/parse/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "license": "MIT" + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -3172,6 +3214,81 @@ "node": ">=10" } }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "license": "MIT", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3353,7 +3470,6 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, "license": "MIT" }, "node_modules/async-exit-hook": { @@ -3423,7 +3539,6 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -3453,11 +3568,32 @@ "node": ">=6.0.0" } }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, "license": "MIT", "dependencies": { "buffer": "^5.5.0", @@ -3465,6 +3601,12 @@ "readable-stream": "^3.4.0" } }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "license": "MIT" + }, "node_modules/boolean": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", @@ -3525,7 +3667,6 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, "funding": [ { "type": "github", @@ -3562,6 +3703,23 @@ "dev": true, "license": "MIT" }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/builder-util": { "version": "26.8.1", "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-26.8.1.tgz", @@ -3853,6 +4011,18 @@ "node": ">=18" } }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4037,11 +4207,25 @@ "node": ">=0.10.0" } }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, "node_modules/convert-source-map": { @@ -4055,9 +4239,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true, - "license": "MIT", - "optional": true + "license": "MIT" }, "node_modules/crc": { "version": "3.8.0", @@ -4070,6 +4252,31 @@ "buffer": "^5.1.0" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/cross-dirname": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", @@ -4177,6 +4384,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -4507,6 +4720,51 @@ "node": ">= 0.4" } }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -5391,6 +5649,26 @@ "node": ">=0.10.0" } }, + "node_modules/exceljs": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", + "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", + "license": "MIT", + "dependencies": { + "archiver": "^5.0.0", + "dayjs": "^1.8.34", + "fast-csv": "^4.3.1", + "jszip": "^3.10.1", + "readable-stream": "^3.6.0", + "saxes": "^5.0.1", + "tmp": "^0.2.0", + "unzipper": "^0.10.11", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=8.3.0" + } + }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", @@ -5439,6 +5717,19 @@ "license": "MIT", "optional": true }, + "node_modules/fast-csv": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", + "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", + "license": "MIT", + "dependencies": { + "@fast-csv/format": "4.3.5", + "@fast-csv/parse": "4.3.6" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5648,6 +5939,12 @@ "node": ">= 6" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, "node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -5679,7 +5976,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -5697,6 +5993,22 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -5845,7 +6157,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -5879,14 +6190,12 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -5897,7 +6206,6 @@ "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -6237,7 +6545,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -6264,6 +6571,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -6296,7 +6609,6 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -6307,7 +6619,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/internal-slot": { @@ -6940,6 +7251,54 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6956,6 +7315,54 @@ "dev": true, "license": "MIT" }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -6970,6 +7377,21 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "license": "ISC" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6993,6 +7415,73 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "license": "MIT" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "license": "MIT" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "license": "MIT" + }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -7000,6 +7489,18 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -7180,7 +7681,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7343,7 +7843,6 @@ "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, "license": "MIT", "dependencies": { "minimist": "^1.2.6" @@ -7531,6 +8030,15 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/normalize-url": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", @@ -7808,6 +8316,12 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7835,7 +8349,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -8075,6 +8588,12 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -8212,7 +8731,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -8223,6 +8741,42 @@ "node": ">= 6" } }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -8376,7 +8930,6 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -8472,7 +9025,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -8551,6 +9103,18 @@ "node": ">=11.0.0" } }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -8639,6 +9203,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -8928,7 +9498,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -9174,6 +9743,22 @@ "node": ">=18" } }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/tar/node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", @@ -9315,7 +9900,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", - "dev": true, "license": "MIT", "engines": { "node": ">=14.14" @@ -9331,6 +9915,15 @@ "tmp": "^0.2.0" } }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "license": "MIT/X11", + "engines": { + "node": "*" + } + }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -9557,6 +10150,60 @@ "node": ">= 4.0.0" } }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/unzipper/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", @@ -9609,9 +10256,17 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, "license": "MIT" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/verror": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", @@ -10461,6 +11116,12 @@ "node": ">=8.0" } }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -10530,6 +11191,41 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "license": "MIT", + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/zod": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", diff --git a/package.json b/package.json index 424b320..27db828 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@electron-toolkit/preload": "^3.0.2", "@electron-toolkit/utils": "^4.0.0", "dotenv": "^17.3.1", + "exceljs": "^4.4.0", "playwright-core": "^1.58.2" }, "devDependencies": { diff --git a/references/docs/ERPAuto 项目迁移与重构指南.md b/references/docs/ERPAuto 项目迁移与重构指南.md new file mode 100644 index 0000000..fbd9ac6 --- /dev/null +++ b/references/docs/ERPAuto 项目迁移与重构指南.md @@ -0,0 +1,114 @@ +## 1. 项目背景与概述 + +本项目(ERPAuto)旨在将原先基于 `Python + Tkinter + Playwright (Sync)` 构建的内部 ERP(用友 BIP)自动化辅助工具,重构为现代化的 `Electron + React + TypeScript` 桌面端应用。 + +- **代码参考源**:原项目挂载在 `playwrite/` 目录下。 + +- **重构目标**: + + 1. 彻底移除 Python 运行时依赖。 + + 2. 将原有的同步阻塞自动化流程重构为纯异步的 Node.js/TypeScript 实现。 + + 3. 解耦 UI 层与业务逻辑层,实现高可测试性。 + + 4. 利用 Electron 原生能力处理文件 I/O 与数据持久化。 + + +## 2. 整体架构设计 + +项目采用 Electron 标准的主进程(Main)与渲染进程(Renderer)隔离架构: + +``` +electron-app/ +├── src/ +│ ├── main/ # 核心后端逻辑 (原 Python utils/ 目录的归宿) +│ │ ├── config/ # 集中式配置管理 (环境变量解析 env.ts) +│ │ ├── services/ # 核心自动化业务流 +│ │ │ ├── authService.ts # (已完成) 登录/登出模块 +│ │ │ ├── cleanerService.ts # 待迁移:物料计划清理器 +│ │ │ └── extractorService.ts # 待迁移:物料计划提取器 +│ │ ├── utils/ # 通用工具 +│ │ │ └── excelConverter.ts # 待迁移:Excel 数据转换器 +│ │ └── index.ts # IPC 通信注册与 Electron 生命周期 +│ ├── preload/ # 安全网桥 (ContextBridge) +│ └── renderer/ # React 前端 UI +├── tests/ # 单元测试 (Vitest) +└── .env # 环境变量配置 +``` + +## 3. 技术栈选型 + +|模块|旧版 Python 技术栈|新版 Electron 技术栈|备注| +|---|---|---|---| +|**整体框架**|Tkinter + Python|**Electron + Vite**|提升跨平台性能与 UI 现代化| +|**前端界面**|Tkinter Canvas/Grid|**React 18 + TS + UI组件库**|原有弹窗、进度条改为 Web 形式| +|**网页自动化**|`playwright-python`|**`playwright-core`**|**核心!** 使用 `playwright-core` 直接调用系统本地浏览器,避免 Electron 打包体积膨胀| +|**表格处理**|`pandas`|**`exceljs`**|用于内存中合并 Excel 批次数据,不再依赖沉重的科学计算库| +|**数据库**|`pymysql` / `pymssql`|**`mysql2` / `mssql`**|直接在 Node.js 中维持连接池| +|**单元测试**|`pytest`|**`vitest`**|提供极速的 TS 测试环境| + +## 4. 核心技术规范与避坑指南(接手 AI 必读) + +为了避免重蹈覆辙,后续接手的 AI 助手在生成代码时,**必须**严格遵守以下规范: + +### 4.1. 严禁猜测 DOM 选择器 (No Guessing Locators) + +原 Python 代码中有极其精确的页面定位逻辑。在翻译 `extractor` 和 `cleaner` 时,**必须先完整阅读**原 `.py` 源码。 + +- 例如:原代码使用了 `get_by_role("textbox", name="用户名")`,在 TS 中必须 1:1 翻译为 `getByRole('textbox', { name: '用户名' })`,绝对禁止凭空捏造如 `.u-input` 等 CSS 选择器。 + + +### 4.2. 跨越 Iframe 陷阱 (Iframe Penetration) + +用友 BIP 系统的核心工作台全部嵌套在 `id="forwardFrame"` 这个 iframe 中。 + +- **规则**:绝大部分页面操作(如填表、点击查询)都必须基于 `frameElement.contentFrame()` 获取到的 `mainFrame` 进行,而非直接操作外层 `page` 对象。需要时刻注意操作上下文是否已因页面刷新而销毁 (Detached)。 + + +### 4.3. 同步转异步范式 (Strict Async/Await) + +原 Python 代码为同步范式(如 `time.sleep(1)`,`btn.click()`)。 + +- **规则**:在 TS 中,所有的 Playwright 操作都必须前置 `await`。 + +- `time.sleep(n)` 必须翻译为 `await page.waitForTimeout(n * 1000)`。 + + +### 4.4. 环境变量与空值合并 (Env & Nullish Coalescing) + +- 读取配置时,必须使用 `??`(空值合并操作符)替代 `||`(逻辑或),以防止空字符串 `''` 触发了意外的 Fallback(即 `options.username ?? ENV.ERP_USERNAME`)。 + +- 所有的环境变量通过 `src/main/config/env.ts` 集中挂载,严禁在业务代码中到处写 `process.env.XXX`。 + + +### 4.5. 依赖注入与 TDD (Dependency Injection & Testing) + +- 诸如 `ExtractorService` 这样的庞大类,其内部不应直接 `new ExcelConverter()`,而应通过构造函数注入依赖,以保证单元测试的可行性。 + +- 每完成一个 `XXXService.ts` 的编写,必须同步在 `tests/unit/` 目录下交付对应的 Vitest 单元测试。 + +- 在编写 Playwright 相关的测试时,必须提供深度的 `vi.mock('playwright-core', ...)` 模拟,确保测试用例可以在无真实浏览器的 CI 环境下瞬间跑通。 + + +## 5. 待执行的迁移任务清单 + +接下来的开发工作应严格按照从底层数据流向高层业务逻辑的顺序进行: + +- [x] **Task 1: Auth 模块** (`utils/auth.py` -> `authService.ts`) - **已完成** + +- [ ] **Task 2: 数据清理模块** (`utils/excel_converter.py` -> `excelConverter.ts`) + + - **难点**:无。纯逻辑处理,需使用 `exceljs` 替代 `pandas.concat`,将提取的数据整理成标准 JSON/Array 供写入数据库使用。 + +- [ ] **Task 3: 自动化清洗核心** (`utils/discrete_material_plan_cleaner.py` -> `cleanerService.ts`) + + - **难点**:涉及复杂的循环逻辑,需要在网页表格中比对物料集合并逐行执行“点击删除按钮”的操作。注意处理翻页与异步等待。 + +- [ ] **Task 4: 自动化提取核心** (`utils/discrete_material_plan_extractor.py` -> `extractorService.ts`) + + - **难点**:需要接管浏览器的“文件下载”事件 (`expect_download`),处理分页逻辑,并将下载的 Excel 暂存后移交给 Task 2 的转换器。 + +- [ ] **Task 5: React 前端与 IPC 集成** + + - 待底层 Service 全部跑通脱机测试后,绘制 UI 界面,打通主进程与渲染进程的通信,绑定“开始”、“停止”按钮,及实时日志输出。 \ No newline at end of file diff --git a/references/samples/离散备料计划数据样例.xlsx b/references/samples/离散备料计划数据样例.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3aaf497515540039005d543e1f4bf66c50004f9e GIT binary patch literal 117593 zcma&N1yEdT_Pw3p?(Wi9aCdhP5Q4kAyM^Gc!Ce#FHMj?N2<~pd8~d8%&Nnmvs(XKv zRB@`ChMe=Bwb%RXwNKv5fzgmAU7)I?!B+%3*L0zl==HOx13T8yp%m zMMcoPd-}b5-<`iR-)ar^q0>F|`e=}(D~r#^HUiC&u7I@!qptLAAo!Yl0Gjmu^*Z1e zN*DT6@V0T0(VoN1AJbfr(6X?q}!Ajvhw|1ra$MIFWxWU zy9@A_oeE)g?b~lkL6P^F?ryyJQ$C8x-bW| z#0x%PuWM8s>DjjRXKCI@gZ*D?gaZ12v7OO-M>~5bMgx2MSCPs`cfo-~Dt6{8KG?Q$ zC@eOW_!%baSXhvxV2NYGTBJrsa4lnIYlJDLxMj3*Gm55FsC;=o3PXw`GJ5x;0P*}G zfVWLW6b==gcO;L1y3x@f+U%1x_7&y4Nl}d)sYee*5pFjdcSN$j9#PoF1e;`MRGb7s z=^dm@IS@z93=tc8MN%c!uxSnB@fOY(2?+jrSsUJN3vqv4R^wmCzrXdU|9M$Pc8(^m z*JTr{BiqA-5&R-E`H}@745!BQ$B1%o%M@3{=%5 z2tMDYnL)Q-O)cMfj-^zl95YptYDfK9#yB1$1=P;1e9?R}%k3DHNeN9KT93lCvS{yA zoGxKs*Ro|tO%(F4GOGIP`1fmx0a{CUYepw?14k2M6=z2aTQjHE<>ZePM(QR(d2`nO z`FMrFlc_)PMt>9j>TO^}Pyt;cOepP_7fA!r87@3`yhQ z19;cHk*+<6Mw{~Eo1R+ydfA)=#brJT*R`x%b8@fu$7osA{0}2dcpj57fyXJ9~v!NC%h1=>X;N} zVv1(2KOm`Iwl~X`dPGTkzWR-LbuUN2I8DM9N7_&lKuBvF6ozgarR11;H-DAF@83U} z7_0j3$#`Mt2Oof}IUPaow%a)3`8H!YseN@#9yw(fOgaT7RoFzMh!ml%kS+{&2y;P} zhyF@6Oy&VaCJYSRz8TDA7PMa4+D;6g{-L>U?*(BgY5p}aFuoB1npte^0b(E0_e0xR!ZV&9JO z*|C%O+X~)U!pv6UYU1!tM*lR&YK6ql$H_KNWvwfmzIjpJ3#AfbgEf#o~}kzobSw;5-i{? zcs@E!Vzwg{NV^0vP{};KyDt`if+|A(BOYcNQ&~$@$bC=`A;Re z**RJn+SysX+CWrvhe8h%N{CxlcT48aw}B+#(~zVtM%2e@4^!$)18P^<*~MU8@F5JcLMrm`l`085d(SV#D+K?g+h&NC%G1If1MMDRKCmK zA1SqxWjF!&GtQUrmiQ@fPxIZ}ZpUd|LP2%LOKxiWL5VZ_8*jaWwbz#)#IzoD5g? zVZS50wP#KUn)eD2dG3W=x9U5V;4wf(o)A!B(M19oO^%49d#lKKt1recF36k0?aNgD=9};D(LF6ufKer+_wsTAql>t-$%pyQ1g9 zj;C2adu}O_85Ix0m3moUh+>@9rkma0YZMI58#nXU8nOPLEQ0fIaXFcrm^eFu_8icG z`Bi1hZ59cD0T&A3(^EK0N*nW!)l!k;ROv80QAvhs$0 zWg*JekfItz3>-q(di%Y)*eAfmzC?PIZ|7r3>R3sI;vGCA87o;$_hn#q`i3r!y>Tr} z@fJ}Bk&B(FamLoE-$muiMs5f99V7Dd`Q|P0a~J2SV@yUX647Iq8}ak%t>1ZeyTg1( zv!5?l`;p$u&dUY1-^)4j3vg49|8AWedgofO=`O1E`7loM`R2%N&F7-k?|NMnS)0%E z&W`v|_@43QVuM)Vs*`-j@A+JC#plA#?`|D@O$(a;iqp+28hzzy0(|YIOULgXd+p&_ z6!|#^?|eN3`FX%@*(Jtneg*XY5&X-^E%D<+gxT|LfaUTT-{ZNS+Dn}88NS<>1O8u~ zZl_-7c0`Y-}^&BLmhOX5~^KDtf9v%)p zUiQq?uu zZM9TSQx~HHU3NHvc5G>BdEg}d_oDB8T+^o<$7hnf3SQvX+77~9kmZTpxBYAG{qD}! zh#&1&u+u|}V&+JpXJ2-?O(egl`8Kvx9Pb5091Rcq-9IBg?>-2+pSMf;s^2`_%C1U2 z|Ju$RkJj^cx}HNHV0?NUd$233c)Vgf^(`Q11kSg1JS5{s!((w7!V{>#6R^S)IKdOZ z!xMx-M$dprn);JBc2?JQDy29q>on{&NVh#xx&+k9AC^O3dqU|vU7XCazTEXislB&D zLXP6h^;O(IKv%4+(9`w#b(d-Od_PwH;`vbf(gM7%9DcbS!|xFAy*@k`&(_ib7SGmZ z=LqmKZsMjM1IHbIZ3z+x0&%i_X=g7UJ{3n-@`bdhvYZzYeN^md^1O+0zw1SkFUjr; zGD(Ug7{3sizw{n(;G3z%;&Oa)j@I*fxC1)1gwb6~FJrqYa+4$Q(Td_%UsQ3s$JrC~ z>B?#caNO_qoZQcsNzh~Cc7V9u{&8ff)NGCJ<@#!By0pv4DPaBgcyuzdB!`Fgro zLGO9xpk6TR@g`u_Yuv|S>)qja<$DHhnuTJ$?>?6YSxEDWTQ^iMnC#bC!Ycwu1wI7k zX7vXdTiAkpKAVeK0vr^foDdsnIY9bny=NA>?3I_z(-+4ECGd%_z&J(SJC{YEi9^Qo z@jQPN-hSy(DwUdGdxPiA&a@;kZAbdqviIJV8F7Bn?4FVjgwITZFbf}>O}yX|!<*r2 zQSCdw@PtyeXuYT6tRIk(;@C%XItzMknKkh91d|%8@NXjCazHL1=sSI)f0Oiv4pJSe zia=lX6Q)j?*EpF)rG!=OaT&kL{eh_d_->5&OMgsiCS!Hp*|E zv&oLD^N~0b9x!V+U(RGqgHMm3d_uO%|Fu#6n2k~&F5rII^f}EKU9D|6z+d5hLz{n@ z&u!-_EZaK#JOY3IIpAn?*zY}&Yx_rmsf^{OWxf+_dSZ8ut=0R!>x8513L>wa?ZaZw zP4@-fkE_`|?Qb^^f4!FySZlZQT+7{R2^ll^0%0j?6bxx#eg8jgI?tJ1Vse{12VUzmJA`>H-WH>}LWC#l zW&V9ybk!C)N*cM@c2Sh!nI8WrK(S)2<#GTyTCerGzE<(r&y|UGQ*;ehXGX`!hXOGt z164+T-`jovbKt7SrSkn!(Ib7JrXihW*hNkfPI_JMf`sg+#gm3!_sXOcDJ{dAX|!is zS-FLInSEYi^=D*DSRZ?hdMY)BO5gj3S*`uOA?Lixv|pEvT+|(sKhI)+!530@=ss2^ zs#E7ULbC(SZ&^oNt{KTH9&?| z+;&UD-mHi_LLIWNoLGE%iYa=QrQB(FGC}cfWOh$Y(eEpbzGC+2;jULH{`~L@_;4d` zt4dOQpmU>rzoVsXF0cO%e7q(JlSMzSi(nZ_nlM|)K3ZN-_&Y!QsOK3jo65$o6yn^-#QSOABVGCm*`F@70pXbMW&Kq{}UOaky;V76x?2_;~m zDViTzWw4ofe8Ib!K5%>P3?3Dco+(&;Q+h~@ry;u2f#9l!qEC^mzpNc0IiMyHBHA1GG;9O`HpWX=&aV%YhX^zJCeCtVGhZ0p;ABMwPU<`G+ zpEWz(L4W3Of6MK&6U3XW*S9HY8b)PyoyTe(OW;X>_aH&j2EeLVj{7FvlR{d7E3yKg zW#JPyMGLHk6o^U_wws~uAbX(413;Hc_KViJyR;w~6M6gP*R;97${~LK zyw#gXR4(y#1b@&?L`flG64wvzB=DDoz=Hd_EL<(Nt^mcuDMeCi8`}NQh7b6@_PuL& z>1b8ozwHJQZSWD_x~R9aYwFa+K32$dLw^emIfVMe7ah8F&#Re>Z*6 z0Y>AX!(&{8SXPRdY_@a}yJE*2#)^suFJ5>Fvn`)im|bpbpWuDjV=)??pZo`&m6wy% zlkzOzt?i$~6uW_6xVJm>kTUbnb3-`Jhw5Y0vR}SF4)|@w4EtT35fOWKb=>cIZ@r

ie7qRD8f|u<>^qrWUB~HDS^E_W{xy!1IDJ9)44yoSRCp7;A^3z0eHR8yf zC|iIz(jp}U&H4H(zU0n^d}@f8MCBF81rQsX^@Wcr7eQeAHO%hbLZCso*4Qas3b~ z3iL(Ph)J)Q5h!T|Q@~=hPE{y~8>q0PC)(ZX!_K4u`@nbaghT8J*}??wCu3r=Js$3^ z{epG2swtw&aa<)FZyMt-fwya!S{>9O)E!vEe%BH^f~&WPc6BXRs~57_Nq*C4bbQ6} z@xw1?Yps&G?{$MmWiI*Yrg?u!NY2KpV=p;6r=jIa2ehX$wx; zdIiIrHs;MTR3ZPiSI<4#Y*V|;JW<6KFagyT+2_jp=R=A>x#&ZwO?V494$H^n=WmZc zd3>(E)Kfo(V>c}9dQ>@Darvv{5^L-F+$|hfb-c^98_nGzOZlLAC1%;Yo}kXl@Pl~K z1WIx(E!pqsO?$%{nhOvLbT;wr7a`YCb~BvqU)tZIL-=f+&2kz_4Sgy{y&?SQ2=Q3T0lXhx3~Vid3~sb#OPzi2XLpxbyf>c?f<0LM+-s?nrbnW ze`haGh~JOkFsi@1@KrkTwYNQ;U@&zxg>$r z6nlK`MZ1Xc0nd>k8`T!+9>tGuQxcT+xsgBBz85NdWOIDq2=qRmLiA_;`+Ff+K!Ibv zO5#8AofPhj(=PVh_Yx}IuF@j_@+J+mm9g4YrJ`Lz$+pga7Y0l92#As$!OyFE7{x!% zIx|^?=}rFuncMqaNl$zP-+;?ee``O#em9EehSqRZoiV`mvu|5e@zRBDC+3S*w1OfT zo5lO5wB6kje#{z5>lBeWv2+^aJXowz)f)CufAuw3vM{&&_HcJjiD#TsGsQg4dLPFV ziUq>B_GJu3iTz_w7BXUP7Cb5m)sw2Rq0Y-4gMG2*$;REL-l%SD`MZKGM3@;txhZYQ z7IM5f!Z;;2v5zgHETFAOW`WQUmIgd%sG zA89Pav`vH5h}FF6!hL60kpv+}eFZ!OUL@++Ea*V5?jQa_pmTl8g(fwPK6SED0`KOx zntQT=^0z#2ahRrWxc!X@KIZ8BZB{sC&CxfM!u;zGRgqU&a4D+YHDp=gU7R!l2iqt* z;qfz`?}Ar(b}$sHfmmo4pWWXbo*QI^yB^oF)5u62ebeVY;K!u?1__p6no&^95JVa_RkI06kSn;XVt|4_U%j{fjf-;^H=XhJL+mn;A2Tl(G7C-g`|oI?N#dZ^hlOhCjlG$u6x1PuUF1hY4t;ewTep zKo$9&`Iu|yPE#?6SyaZ9sblZ>UH$ZcUlelS=<&pMnWl3EY`jQ}l_5yp3B5c@IB6}E zukzkFro|s$s+ONoqbbiL*49t|ii=>44&pMKZW0cPRKL>4xs*U?=+)c@L1Lc~UM_X; z$u8mhe58hO)s~ErNDcO%);#i)kYCc~zY4Cz$8@&x{nNIuP6{IlUXBNd)nFIfc>0Kl zwrV%4e|b`GS=Fgq3I%KY5s^d1a8}>qR+Alsb(U2QVXC5ut{tiYE0=9$c)9MfjrMfa zmNROo%s*ktuDTg0Cs+1J7I=*Ejz3EBjht~?3EH@!d|HCTh*SA67CCsIoZT$Baaf+L z$=N?wSAAdH=DN5ZR?&#?HF*BGw%X)?jY1>=%B6ms(ZXplYf1~9L(bDGZ?u4utw-me zyt(9-TfqXQ+7UwmU>gN|=P3a0ollrxUL-lc;gvazBJstAyoO9tZM7J1BN11`tR8Lx zD_%|%<$E;Vh>=4U8 zuWZEjFE*0;Z#Jriqn(P|CG=2>iw1)aulwKFs9$p1rb;1>BQ%QqsT4F01}>+VS+{G) zQ8r)GPJ!ecUPqCYC}%7wC(&`j^4qqB;wUYs9a-WT;eYoVv@&^vAL2MnL#?1##ak#3 zepmxI{mJAVt;S5w$?K-9N%8bbrSN|@8(tiYG-~5~9=|SHKVLs3-8+61%jOzRTLr{p$gw+vXQ8~v_w|7K)} zT8>d7BM9u>HI>c-YSd&pN8Js$zY4?{3YX+Mm_IC-3WO=l2x^m?pD8a7{ z(FU&!mE1dY<+>lP4LR0y+fpEJ4*LMH2YuE>VvMF1w|~9R&clwFI-QSLf#d@~vxw$>BXghb6Zo{{xF}@~a8STC0 zArRKXvUk#d>ATi{sg+l|0ykM8th2%@@l(6L*l;coQDr2}oV5=@-d>H_sshf-Heia= zu-R-|#&{d}Zi=%D!Y)iWuX#L=a(_*EB`f*!31p}8qJwfL{o4k*Oei697BJ|Q04Vzq z_D@zbZz{e(y+MaSTtA(CV+cw;rRT0=7W@UShv4soh|xy9FB;<3H^o#Ol?Pw#aH}IxfQ+0Q=eW=ElQve_>e+t+sX!Oe| zz=DxWb7;9i4mCv?C2a{b&&~TmtWC~n@Gu~CIrZJ@1t*GbLGXXT63vg_QH-Qho>(2HXbQhNbZ%|Wb_HfZd&!cNvAsWLfhkkf=U9TekDz|PZuHUm ziJXZlpNSS`6SSz`1fzmy=moLAyNK#XcG7^E!DNuz`*%Y4(}1x;?80c1RlOJ5K?)ZS zt>&D769G%b6vJ4xgCqqxF$zf4v@r0;^`AklGNb`3#YdDR*GkMl2o_ox{A6`uF^gL* z;RXJ+VK^2nI|I%eijT3t68~bpSo0GaWS8UYA2i5(I=(-8lJch$VC3;;W(8ylMoqjd zjpY}ZqRPTJrU%y%+}LV%OI9V{6$MXJV`pH zDA6!g5=cmZKH7R(5n!~bZg0QvzXU?=VMD!yr|W;TYj6KYyS!W=?IIz8 zv_DeW1`D}p^UqfA~PVscxG#Tfm7yywc42dLY|P|mB{UHq7<5p*1s|; z1EmBTIy;qEad5b#kTk(Vgsp#?uG09u*)_&6T^D-wrf5E3Qv}KqceBRs(CU;w zCo0TDlH%srgcGJ(DeVnm(`11upZoCCj>@j?c>hFY`y$hea4_8m@!3*cm0j) zMOEEsS_Y|_2B{hbsagga>ITwfRRtf_S!n5+F_Q{0le!bdZS*gcqpu5F&S)|ly8&Fb zpU1m&9DHY3c}z|#^%t-4>5K#539uSMl{u0Ly&ydE6EzHm77HL4;FCf(G@#Xg^9`txgLib8}ii+zE`Umx{5ilp+EwLs!*IFqm1=eWbgxGtc z>C<^r9t`RV{6!IMJu?;P@bX8p?6v533?np~!JEgeq<(vtQ+3U9HndN7V3*>e2J7wm z8s;b1lwAiL_qLpG*v!D0;h~J_KYS>chyvzgY{7gV2{1>Pq!BYi$rN>dI|-3TBgPuK zi=rb^_g?ARH~dS$bs=_03X3|x$z*Wz!3LMEeJxpdZ`o=jC$4P_0c)rX*SVoLU6}QZ z-Kx($^2F?a~XEOMq2+qsb|P zbfYM0MqO2@&o8N^qnpINpG^ipZG2nGi7t)Pcg`m!Z7~KijZKb0LoR^!=v|h!{2^T4 zx69n0f~@G}<*QT`6SSTQ@V70{jGb_JFnzMW9UzDnqmpFQFneTty31_>e_E~NxiZGtHXZ#Nq}sctFk-_qa}i9h5Nh(a zwg3w8V?w6tK+67O!T>wfVAdbZ-xDcH8r=}u0xU^~>jjcw03)j#znqNNZlYbUY=dMR zr0XHUT@s6^_;H;|af6=UG z>~GDMxAl;`YPM(9!S+?N{%rxq#no=1P{|9tbf|^a3?i4_O1$}9cZ$8 zH$?>FhWs3Kr1?RJzr-5}(p&e$s!}aA51X{2;Tg$+&A6zr-_IaA@V2&|6f%{Wj`53JGqnpHsrqk_jKZ68VO;DIi$+x~)eb_En|LkZ(A}ViM9xl` zU=$jM+|u9XaN~!z@S<4d=E*R7;-P-kipUCBJtAcb-p9ye4ILwG`D6|@iSh5o*Hdsu z^Yb2(i(9PJ;VDX=)iNPKck5K&>D0}wYS7mWI;`Lni9>7mR6q5$l~)Sj;#L)x+A5mZ z+`rOw`sjO#r1<^lRjA7=N_reKE5hB^r_nrjilFaaVm9$M09KI1?WejmX=rU2m((h) zHlYq(T1>|`78gs^kr0?3Hzrf-m zdW@)G4s-|r87*9gca?P!0lU=p4%XZ)gka+^mIiaAJxH*YTfwfOUyME*C(!*IE+EXC zJWPaw-LT{T)Q^%W;tVy(EryLEPm(6`@5K46*gG>CJJo}0WOIwsH#8(4R+Wz+FY<~A zDGB4kzOxrC4d)Vs2dcPZ)C1?rNnclcn6HpZg6OcX`0_q8A%_eoMo+qP{rqFBXB+yU zvVi_!^>(s=Q0Bs3h^xmwdV?Z3f>8bG(#kl82swm`i?r}tN*E$?2x8I#-0%$C@Ce-S zGTd-CoUCCGMSXWX$Q#S6=*p`wis)p(AAa=_w;Qc*Fu_EpuV0>n_~1HHL1TS`WYGe2 zmR%G?NU~xy=@EzRpUyN{{|K}PBv2ruggJ3YcciF|ar63iYd_I%jWJ!>&(MYcl$%O$ z=aTXq#Uk_%Y1~IK$+@-1d9~EbgI$V=6U9}n6;E%M`>DfQ#d=ftWnWiVCrv5MHK-xe zYdXBz1zx!mw&v$;ul{z#SKZnVv@}Xh@3Pc?x!RJA#Ago*a|kfb<+R0tC|=rb^?5Uq zfhZP*A6oxjsUD9VE=D<0z%w;u0w+%;bc}>2WFIUKR!rsJ9l+sJ;xM!?(@c{v**6DK zj7nzPSobK0fYIi)rY^FFD11OX9_NB}szZRus@*b4+)PuKglRvb(k|W>RO{sfE~ocB zgQG7si|tG@>Q=BI+@z@HNcVMr6we*4;WCQ3Gv7wE6L^HPR=jNl8JC3t*%W*73GEcS z130LwU4CDH{;D?gaOZaI55LIcoO-Y`S-0gRP;Ie%?m!`QqM(i8gREV+XWz=r531~y z_WSRPZ^c%SNR_#4Nq8b^{C`l|D9||4cHz6B@|D2!NW<4M;JIol;d9nSlx3&^(m}OB zc|z8lw-X~~ilXOgj_S*ZQfqGs^=<2xIe^Px=Ky9vF9|!aNd4nfio5U#H&$YP8kDbY zVg`C*L-NUrorEN5{YKFfB@e>4Ho2d6c(6(qjm(eiaR0QLm8id{Oy|)4_%FSM2&PpS z$bahtDa6Yte*143#ee#563BmLNTD4wnMpPgYLg%ytqG&*3kcg(a`Q5-=FpO`s*`fQ zW1-0Z%*C*R4P-^w!_hTtTJWD0?2yPjtWJXX3~rPbeqpUHYhBg*QN8G+y6H#t-H+;6 zAJx%l>Aqnmjljv;2ANm{6*8G9;j6db0L~H1D4@C=(7lP~;+^udCTZZyC;+@s^T5Q8H{oF%PIOXXwrKpPK3vTIEuv!>r-PhfDO=#oN^~T&6-$&{T%48 zBIbH18eDQog*ca^VENu_qO6kABqxRCnH(l;-y+|>gz#o`QE0~GvpQ%%6`_$yQ#MKvRP@J(S$tDBH9prZl(sp#^MUw>F6Qwyzev$nt~6R%cGj-G)h{&{&Xz8WU=Tg zS{%gg*7)!igyAoc=4MXEO#p;L0@s}ZIw9s5e1*c=SMVsg%wokTnZnLUlaONQp+zX8 zBx$0~u#+Ou|1*(^#oiSMVnF`fgNt`8 z%2T44&fYclm~9McavjYqgd#6-)MOyR-&fA;qq-vpO;~LJV9LcL@}i4Tg20k|*yO9d z@$oTJQ7Ar~sF~zwp7{iTAM}}mMihBzAKtZxF_<8vOK&}3O-}eJ|L7$ zp#8A1u&`+Gs_pIdWE7%evEXE`S1vGn<$~k?gzNvU@DII9f;7nPfy4*I4_u+I*zvX$RNyK*+IZdg(@S5WX^L zgso^dRO-(6BF-G8*^9O*unpz4$u*(z#vjSXQ8~JbJthL?9r_hMDN2)Vc$!HPTF)+S zBnkFI@$kCy98K#lf1O{fHrc;<`#Ay2)Mp*>7<{}8nUB|pB=xs}f;aPF-jkV0_9_TX z@$yP>xwy_;=vkOSk1DMnFE~2$LJ+553?a#dLlLFS^dg?1@Iq6DQEJpBOY@55uhSr+ z<>-C-r^>zW&ZK5@a!u(QkbgumlSD7!;uXU^$Rwnr**yLfM#09sdyL~<%95cJy-`S~ z)K+V>Pxt{|gW*zK4BucZn#VV%HlMIfD>pvlY78j}%fapEN-{dc4KfP_p60ga?ik<5d zW;t+jcxBlX?I^tFeum$i!S(QQaLvUba@}9X*gY&Fzlh1fA(rln0V!bJIo-Pv@=aQ; z|JMlcYLHc8Sx$SxkitVB32(kH_!%ZUOaf>CRfO@mGY{yysZDbb* z5X)|YSXP?=#Il>OEb9PbS!@u?GQ6_v<}1r`fh^Yn#Ig~uEZg_WvJBC2-ki-!B#sUs zC@}EE5YlrS=`Mv%1>cc|YE%&tGbo;#nk%2wUQEF0zK)J z>V*OJAknwO+m%{N|D9cSo&Rd$Lb5}17Q1zVIz+<;Lp(uWeoMx5Kac!3PdC6`CvVyzFJ z&ih~%t)K@ZYUymIF@|MIkbE!Lrf!n*mq_kV3Q@WAVAuycMH3}VL#U&U3GxCd!|)&o z@(Qd_E*f0&VFa*xY>rU=c2~Oh%so_I8KPm6R4@dBF^brE!< zshf4JFB_4L2#h9ShlMA(Ztv-h@6j?Q7vz3cY(dK`o$Rl`ec3KZZqU#a`zhf;Pm)$A zN;Adr-{RgqjB6N3!#)hJ<9D@|TH;VQq@`1#r2_$~6D=J)EnOI9(hQueX%IzYcTHWl za!SotL^?k`LCVVT`l$wC`(=pikUF0~y;z-m&9u}Kh;*kKUj4R^TqY!XYkiB_`nSL> z(gBVVNvE>;Ok3~qHQH-Tj&Ws|e(RgsW}YsbNpdPdY6;LL^YbR-*Esk*D;1IV;5to&@mpn<&;8Zpzz;^Js44}=N%$kE zt^-%N6$UW#F{r$59z#$C(92wGrZ{|BB_aE#hA+O4i{M>Zg4DaRh)woxp0IhCBkQrJ zZk~u^>^W%w=G>xnYSkFa*bboGi*=OlL$4DJO7 zKjPP(kJh5t=14Kts9L&TA73qhnWplc3SV6+N17W~hQ6%)5+g^&$-t?oqBaa&GMa=TGC6@K3PIY z-^ulP-@KFt`U8<8Jf_CQMxAU11Cci;IL-YWL=mpKIF|t_-c&H6#4A-PLCF+8 zjyt~&lRNa?h0W&c zb7XIo*AuI6g~WGhLC1gjt(g5J<36wmRtuc~SYRiA&(d+IT*wYSlXkU0^^~6{pndyQ z3P}U#NQ^1Xk|!x{6b-pU5)-~2T8P3v-x*NQZwf6PbwU6LGCd&p(lO-P0#87JX_;n4 zrsv7&;F@N2E1_r3fDbH?>W1d0nagm^#=!k{BRxstE*x;J7>;hgiDsEH)ag`6X}TvP zf1E)ZWD0u3r}_OdlcS06<|&(8AYSm<(O0kcfQ>8R>gboXUE#RV5Ps^kK1Orzmw-)( zlk!j^3@Y%(*o+h#Qts;7AV3|w`nFf^k%(>()tD^vGbjlY+?LV=gOM3cesF?*P4SjTXuZNkqpmgZ3Y3pYH| zNW5Sh&TlU!;@#{WWru^6{fx9pk=w~%xCcHv?)z!TcS1#1HAC5|aL3uIEKX~?HhdGS z?Hz^WD4F3%_CDALELc>)S~9dae^}E7+Uq#Q%_iyDI>i}_jhc*9=MWS$cjCz|XoIW8 zD-_U5PbSE&2B&jir%>23R3&{_(snfK2SEH{K4fS~`|wg$j7)q}CYfZ1$!AFzCG>0iS;d88!dCDfTBjCHM&*`Zt~YV$PEV_6fF)hzK9h8_haKEI&`= zx93w*?%Ad8s_ben&#jo0pBhuc^a3Cg>{#`U7oK>FPF%d)B4;Z6KaT9$G+7J?wFa}o zxoSe)vGeH+44&X6T-Xkfbo2)T*{?b(t~Ofq)Ot0}cb;3whC+RHa6!cleeVMK9j2-k ziid$xc|cszZP7tHM`QC8ED7GmEi6VOLvDfxEb#9_nUmG=Uq+%pi~9Df&GSqtgsm;g zcDLf71hcH~motJy9rpr2YODO#^ti$JDO(AV%PG$cEG|%*D8WI_+@gi6@a{FKDRmW$ z@8VRVH>-wdZ~u_MwiHtkM%+L=1d0>mKyl)!q{#HtnIzcd?>zDFRPyr%LJxOvG7f{Q z{`GieoW_MOAS)j=t>q!EjIqtUg&4GH{a<;FC3r+%+3!qo#TP*N+SVdH#AOhJBAe7R z$?lq?wHPsuCVfFPEl_47+nOM?-K4P2_14qx%bz^_A+Ad7uubfcO=P?E-3Zr=1Qxv6 zk*tTo;SD)+F{q2EeUf_3#C1TKcoi|MEMqujLfn?SfcN-oF1`}en*quh%@uh&%Q^VC ziXros?gOG9=`_!Ej|q!hW7Va!^h#1Q_4kmS{=_ybmc^qcmJ#~{*w&Ae?{P8;yJ{wE z?+ws0Y!lAr_=KD|P?mmy*MJ!9W4;8Ba<~J8?yT@XY(IV7-JrlwwBb=d&UzSG!}}$P zn6BM#7@22pm0r-?_zd(i-sWD|Ot#@MC-v6$zKd)ziP+h=W+#O1@|j+6KXc7$7p?wO;|KSRE9D_=ep}qo5v4}Y@+FH6Z{k$!%)7@hH zTwr|x&HFCTa!HB0K1Lp>*NBu!6TK-wQ^!V@j1RAkf?6Ap z+)U$NN-VXbqt9#4{VP|wM(qC2VGK#(H`27^+i>766vqrFK3z3t5-3LjH3p#JOAuyK zFPyAp5JhcwJZLhbX+R^VF$0yIH4 zqj{n8k5B{$=}m1dl1AAx#_#t2FksjdaV&+bxGzt2bMVebX1U$Kk~1uM6qNptDCG6N z{vL73F#NN34%a4@t@{JX)DEDW5KXdXg4O{9JD^d_4mfBOqeOx9+7f`|#to7ig5J{G zf4mv~ytpO3aZ)9{VVmqS1Yz^k4j|f*A&59u+mr&RZi3oav;mg0zg`T=g`t~NC~Yo) zXeaD)`$&lon%aREZy;!w=c)Rp0$aFPXQcaV`O*D)Sy937!SNnbmEOLGwi_R7CH(w! z=xP-8XH^f*QX?b4cAr4o9!X?CgNP?rkKQbRR({;x1rM%U8osqGP6(?W`4I)ZRfKEAsI;*D3ZmU-QCN^Vd2hn_5J?`Mr_WEm+Srg4%Q5jwav7 zS}Mg=Kk#I>Mfs{ot+pFsr}vMv`Icq@5>?mTo2$?=`j_;|U?$B;FN<`fK09NIe!dUW z18T(WpN6v|wLd-%a=-hLt{K)h*bM4KCh5IEoyf=!--XifD|O3#{npx_6-#OTxR$^{k3<8nf@q5cpJp$t z8tw68jTx4`_JLhW7+k9@xIc{TY2BMJQC??KK?VcJLf-_OHs z$|KGvpVobj(e$^oQg8Sy=s1}^k`iQ>Wn02~vn$y|Iw;_)q%la<0W{a?<40a&mHBl7 ze?1+!$8|J&zwfgDh#=n>{hC9RZW(C7G&qPJsn-SR*E0_7W#3>ir;xrgtn$jK2;Dg< z|NaKU;Zt~u9kDu=1IoR906bQ^2iIESufqhlAH1%Pmz+nJB9+!MPS;b!ih>VAhf&$< zDniAhZ$7^pxE{T^TAI0iQuQHDL}t{r3xaOy{{3&B8$8_AJBZS1*ToQjvu6_hu%Qnx zs1r2IF3$*oNt6dytabNAP=r>L8W@LFd%mSsxpsd6oxWx7@HXM;MEm9WkaStF&7%}} zg3G<;{q^x|OHrWtX~?RSSdZ7^i4&9WTb7LNqvQ3K9RaNn0p9(KUTd0G_L%j8AdOMvrLaoSDCIIQ^G<3s05>lUI{o~>QkVRNhJrX(t@odT zkmfhrs0aSsyrZvUk(}Dvu#Xv;c?DNv3Q5=wuHCO=5hvHQ<#Gh7FQ9qzuK{{h4S$)OUFYb+Rij>`koYT(u`z0AZ|K`>0C-#NbP8GFt@`>gThX9JwRvWy zvG*ff@#_SHvN?F`;jf?PdLS~dvpO!TRBja){NB5up^}2bD~;u8RQx+V*1G$h9xp8R zK4fCMwgYuzbc&KnCCZgb*oDrn|1tHj=|m3E6qE+k`-X3_&nco+R{Pd)-)aeJ77_A@Wf%qn`h^BySBq7iVs>_17}F?opE+ z&ri8EEb!RFKeX~}jYvVsuiC9x9B8&cTYWzT?sC3M)4nf6*w!0t3IpIECMaS(Oa}em zB*1nI!0ZhtDDmS+u+Ob+Hm%;aHno1>N^jI&sgVGU0slYBzB(+*fBP0iLM5c7yCkKN zQW_}%>6Xr+J4Cu0q@_eaS{jCw8oIl?yW_qCp8GrJdG7J|y?^jLj`+@u@Ojr>d+oLM z{QT*k;xg2R{LAT5Nv7T_S~wQDw{AeOLWA(IJ*vee3F22X3Fvg?BL{|s9HL})a>0DX4wp;fSa_d<*VOnC^*$`U;ev~+ z2O)1qDls?JsT+rlUSBxd={18`<>rBhCI9#ik|N(kUqR*Og@@&2pXGpuW!)wnzo~WJZL5pN0HMQ)9WxBkkbq zqZ+u3a+OO2Yc`<|hmTYOvgw?wOL41eDZ{a|p4bs8)sw)PJO<{O^y0bX?9b&hPQJvf zV%%upWEJbz01s#3Xw_3K@#o7`AE_kyPU_50{`_3jGg#r3Qm$$o^#h}El3tVAIO|x@RNbaqQSID027pTvNTN znI;6vQ0qO+=6|QNdiiq+p@ttYe24&skG)HM6SOhvKXXZIcftRdOa3Y(8Z-Ll>|a#H z@7>|IH3Tnd?rUJ`YhdVW08DFieJM+tqttXe0Zma!B~D4@nF4mX*9w^kppLua-K*nO z{CyAVxO>z#TcIqVj*sO))f$l#Gr#qCTR`YBijJz{mmmw$!|22Y#7JOpE=~0kzm`Ar zesF%GT1Laq5tZ6{A~Be+nFikbCa@zI=QVp`A{9C4N)Nn@!tyVbUxl(P9%);)bIj#Q z%ab3NUpoMQ--`Tu;_U5h;Z_;={SNlcKKhuEwC>_1h3dLy+5@*(xaSsQMi_JDwk|w- zCmjawtNj*jT?7&8$Atc3U+BW}?kC*5q=+0op$!@X-hEx$N$2V`@S}{pDU?N*&`dM0 z=sxLW*>iM{65qVkCubeYp|K6!ik`W+(MU^ovzK2F44ZvEv1%2%g<)n*G2ObSs(^ap zTDo86AsaenD>-CAQ#YfI=pZ=d93?&lIaM4J+;9Mmf4~f?a2*QnHuig~_Pj|8Ue@>f{Tsy=)bwnnhvwrR=wZG+swy%QXS)1-h zr8T)d(rEMKLJLN_S9eBMDuYX(+FhHW^B$#b*fXE!*{;nen6<{TT(2lIB{EzPrkXc4 zH46dXddCbwsq})WES!cQewkHhPDa>vk)6>}?l|UwbJ8mn4nhUrAyD4 zDI*`Kk*0YV*`|0HF?R^tdEQmHYamvH57~cl3skY+5ZU!Z$xdoN;T?EsNu=C@EE@xM;=H3FUoQndUOHaxJ#5a@r8xrn?hEU{4_OwaU*@(WbN zH>6~#{=a?V?0dF*ZXTa0SkL>mKsbE>-Ug64$=K)KhGmQV6sEbiNzwd%|K#60#D+U~ zn@Bg<>W&g!%F=&gH>oy8uZCA5^PaDI%8yDnP%YQD9s{RD7B#avFn>ItygTjk_^hnJ zVi{^V5#>=LIZk8)czv0K1d<2^+HWKuB0Q)~DrH4bdKni#=)rOF0kg?sS=afDFa#Luo^|sm`bpCU7K3#>NJSqQ{>Gk8M;J)2ypADwX*D0Vn>Rb zGJGOuHH!ppo$>(S*7+$C=_XW7gRaUyI zrAPT1wp$-xrv4mQz&fQTP8~e8UFA#ke(m%JHp9sB&+FxFUSo{-F5FW#uQ!VY$tEYb*sn{w&%NM zNczutP%4A&7~i%DT-^nM4=|Jx;k7!d4gm4zFnr=k>Pwf%AcIfp+XzxAq!(FAAq)F} z7R-_Myo=gMqxEusWF&`fYn_$Y&3dV8^CsVMw$9FFV`F4WfUW`i$}N*UQ$n?@eti>l z^=d2c^fC_*B#N*AR7OSA=@3SisSw7}GF-w$iagU|+?2w+mFHo3l1w%{niSK)mcvKe z8K~Mb=f}$}j#sA{`QF*C1J$z=J?k3wOmA|^a5VRnGR*t0P&NCwUnLL7bvpS}zL>GG z+ad%B;doiBf2c>D_G4U}>NnY%>E*Bk3e#*8hh3rzac5;ZJ~z+FE;q`L^GI{zH^fA6 z7-jpR7ASG9gcA+0n!jef7$;w@#?7hh4cK2Vk1UNbrh%Qz>~B(T-8ZPjB5~x22_?VX zRyE{!1mITt8@$)fN53fRxwYHU)e$q#co-m6xxq}I@GjzIkCsu!B7q&1gzQL!=* zp2_`c>tYi4WKFu+(pOk`Ik`DF0_<|QWz~q*7s2#oHbk=E8qkgFs($jd`h zvGwV)p3lpRv{O`+<#wqe;8W#xxwMJd(j+I2T?)g$60}WUP4duSz_KPgmP0SwHx=?m zC04I@fg2-H$5E^gysEt08UwsuZ}n=v*R>xr21#$A@_ywWYM*eSYOCs@mrv z6Xz-3aNPh`C|L&sZ7R!Z2&!OADKue56yo^{CAF2}!C=PU&7+7Nyf`F(8=2{vVJ6~^ zKd^VN`amV5ZGgSQJH+9=YCndun(F)Dht zv3HOR-XUh}afSp>%qJaxE58MT7D*%=p5n{3F>y*h_|wSM-@iWT5Qkd49REk`{T9W8 z?KN{T{ylYgIsQN$n2V#ge|Txed}(d81mp?U$&%E}95CP)>7}B4T^uc3vub+|sRPD5 zlg{+-6{8V&BPv9M*d}kXiUqDqbB>|`k z#$L>M(AwC`r~pwU_oo@zOh0q`Ti!k@8n2MvFLcLOe0efRF*%k6O zU|<>t<}h9^LX%n*MQ=Fb zEQ9=RH1!6)8(-F^_ak}5aKQ6qq|~FC5<;@SS<{rRhi!xzKVV*xTdx0>l-OyaIpl_l zYz1}Lo~!7a;CqXR)T~)mf8$O?WZ2NF_$dyOpk8p1bRjf1mN25Xk-Ut{RsZc&-v5$5 z>zyL6Re8TkjoOc#Ad!4uaV%MVMWncACV5U|V+T}W{eAqplD?Z~WO{WU1AIWq+r>Ux z;;;xjdR#~8+v`Z%>ZvjZg3!LDF52t4E0rPe73O>(qo4mdF+J-y9S(%5u>Xc%H~-7= zt|NM(W5ZZe+{|8x;@O0Y?MT$%MyEJS+~OLi7% z74kO+TfTa9qq$eWeG8;PGAK;hN*=vTVj%TIs7& zW-J_)eAwdcr+h=1RL}-Ly!?r%t7@$5^A&2+>{H?w24GGd?XfE0Z_AgAL_X2v>B=MV0>kc>}r>d@SL<8{gXCXm#}GL_!l3!3qcMq z2ET*%iZ-uwsvQhYKXPdt_&`~uFRJ>%a2s-g)j$xn{6fZx%#Oj}GaM#IHSHROg-3Z- z74s%#?^}=3y+VCKN`cAzw_8xN4-T_x%JSt_IoYjYfq0(@?1&)EP2!6;GB<4Cic;>8 z%uZeI!=}}kN9IqsgeD4{tGM!@%A-2L2=GeI1B*2BM;Wnlz$+!<1jlblI-2qev&$-r zI>%yEubnetYt8&HV_Gt$ri<=sUnq@&Oy(2Ur?}>g$i#}5WXf)}Jmj$vhsP?g5|HUvj6#M)wVHDH$Zy;p|rpUit8SeDGw6DH`;Y0vyNufQg5 z3X7vd>GowxN@){ZuMBkB2$e$Xhx_hrVULZdB>`JlhGXi367#4HSvnw#>&?y)>M-r=^V6O+{?at>vIdd|aNlP{Ai-uXyo$KH0e9 zE}KRB$N zv3{;uv=`r!WXemNE+ktk-I+*c&g&K=J@t&*?~$2nFS=vhB`qzPycW#nDkApGE3A_9 z;L$#6dH-XI7umw&!)*0QuuaI}RoS&N=;Q5@``o^0zT_TZ!?DRl4ntG3?J7R7YSS^i z*#S20nYt$UR~cz=TWl)^KmF1DW|Xf^@hhwpGuXaKjpfC+jWd8}Op!;q<|)DHXmy74 z{0~!qs;3`$aJFG_j`JN-H4F|3insbL%be_Rs=7*FGkQ8S8^NQ-9dLm zpjIHI@P7uh+$_{O-K)-WYuKwsj2&vRD;R0?V=sh?n4uFdjnu>G6#jiV;(p|n(YDPO zAe#1O29{6^iad!D{RRbV!`TL=fu zfiFz;y1_~nwkLcC-E{d|dohlmuKR_WZ*{ndYDV~@o0fkNq^~7?f(-1usN&XqUWS5O z!L12c=!Jxnh7M33MGF%?KFt}j4><0~If<=pfI<694|Hh%J{7dO#~d7<l`)yb!6`cKrWgFW#?ByF^mfP3A0O5oj@kOS$7_BY- zBp%9JqPE80XrT+;o|K)UP6Rx8s_uuqK&WMFiYoK_R&3MvvqInM9`8Y2yO%d7E*JJ1 zwB{qY9~$zLafdb=I5su*zH6I*1I=;P?^)qJ@gE8BFLCAioj4VjGX+AFQ5zrOm(JR; zzY4Mh?^h)g4fIsnXrLrhzL;^d(vVp*{87%N9O$OK+_yhr=uoImD8Jz}-Ywh=ap3p3 zINJ29Ke1Ll2V>MY@_W?#xCEz}3p>gxjy0vP@!Iv>#ZDg;TtVdT9N-w1Ot?ok>(gSV zsX>}^Ap!ZR7WS2em;Kyz?R?i2uH&pn)s(4lE7eD<*+Y`DOKAcU_|ml|^S93Ya{{tc z(hjG6LS@1;Pfq)W9HKSy4q+o9Ek;vo+zYEoPe5s6&1Twj>S>FbEZ?QwKnynsoGR+) z&@Gs^>i89)S=T!+SI7OL+oL(Y>y9+yk22pkAuHzzm7qB{k0a)f9-i}}WZnh%B(!Fk zcvwWcE`AwP=uUmS#M=ot>1xDPCaHa#Jwu~4Dbh#%^o}yh43m&%a8mkcjoW~deXji0 zqdfZxqg3VC8qU*!ogmfo%|Tbk*&CA!Qmq}_3rV~D@5sJ}{&;KJ9vmoJiKk<2^;h5p zQG82{VILdoh21OE=7DjZADyVKucn^$Eh5QQV)cKwtJU5E?1QuQBr{G$IXJ@jdk0!D zL(>OFSUU!~dov%B-htz~!W|Mxg?k!LXYm!&E-hgMBmqHbRD1-U_*=%i*` z7YY}b$HtI%9)i`$m{fG{**4ctg*54!q0}_o8ufA~IzD0xHiUeKc|#Yl9cz7R9+t)T z_Izw93`K@ohTO;Tx4K zxe8~;TACNVCh$v z38j_i2wY0jZa6ycIRx2VPQ~XRX%#h;Ia3d-!B{_^r0y|!XR?z+k;#9x0r*qeJ7d3* zuvMf%CrBbcFg$aB?4=kqdns%7%=V17Q)G{y9mrlPtaChpia_>h90#`v0%PYL3Ue@} zkl~CG8jklpqU*>cg8waSk$0w0Ncki){q`fe#7!=MYyz>ZX4km@vfX~fp4gM6j2uqu zGK5^`Li53|0<&&EaNHHcIF&OUHk!D-6?O!Ad`J4hZ z?nf6rCCYy`-)yYp&b+o+K(OFX=9iRw4Ed}TBlvgiz zDKev}o<6QfN~=}P#7S3Pd;NS4#69&ZeF$4G-zzP0v`E_fWJ5izuB-%X37=P^%co@D zR4-G-A6j_n-&0+Ex042$Tk~|^gRNqCl0XobNu=0XMY~_<4pkG_e>ZAgJuU;jcxq4Y zlO0c!$S|2^pAjDaT}WV>&0`$7q1QXCy4Mn7+J)?#`kUv&46FubmGfjXO68U)Xrdo0$$5|#};XL+P$5DBs_YXBOE>_9Bi|OJ=yI@D*28qN*%ae zLL8PYmUDU^G9Ho16d8!q>*?u$HaevW)}jmv5wW@VHDGoMIdQy@kR^+XKE{jW_0hV3 z#*xJ>p=1DtuQvbVBK9xY#Gx;PLCe5h5$OiZRhO18Rk&ENJE1K7OThBU!#xqO%~hm zBopfUtbK=GI#i7E^X#rQ00tM=>7Oj<|<8ai-q5Ts@Ub znK)h*LqO=EZfUwUTR~{-42ARI1p>Q)-}`nl5+x80*BHH$$cn^?MI}SM*?jxA;d^|AV*IAZ9?O~6gZY7& zZ6N6CEnt3dZ#*R1g}oR4c47a`{4j&?V19tplG9n`4+!b?Hih1;7tSB(85a59{wZj? zu>W4rZg2X$gO=Cci9{lwdHwHkY*Sds@92QKlCc`p2eoe+VNH}TQvtgQRoo%*_XR-N z!LSd;BR!bE-YYX>fI^W~_H5V(A2^l3NdyV(g3wH!GUeus0>I1@v+nNA18qD#0V05|B~ z=Tr2+;6z6etaMWR5EG#d9W`uxmvL zA7tvKUeG>Vynd5Z)Vt^w7ol%`3j~-30sj1z#xv8aT_Mz`UEzejmw19Z_5DLS5XG57 zxlj~rY<(M$DwD05EZcmhnXEnJ4u`!a$uhvyJI7*|^cXrB`2NzVYgpW=D<*hlKQcw( zyYPL`UpjrDAtn`ZzU9A5IY2oE|0<~YcvcqDxpZ#~0Ps8m%UL7lf3E_Oej2Y{!I zclJR^c5zP57Fo9bGZiBX7y1uBO;#z9vs;!kZBfTkW(l(M^gr^`?464(|1MP`;=FGL zBibLvViD~U4tCaH8n{65hp|{>JSp@e#o)At8U z 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

Dg382vgMV$a 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 L0HFmK!; + materials: Partial[]; +} + +/** + * 转换器配置选项 + */ +export interface ConverterOptions { + output?: string; + verbose?: boolean; +} + +/** + * 转换结果 + */ +export interface ConverterResult { + records: MaterialRow[]; + orderCount: number; + rowCount: number; + outputFile?: string; +} + +/** + * Excel 报表数据转换器 + */ +export class ExcelConverter { + /** + * 字段名称映射(解决字段名冲突) + */ + private static readonly FIELD_NAME_MAPPING: Record = { + 计划数量: '产品计划数量', + 单位: '产品单位', + }; + + private verbose: boolean; + + constructor(verbose: boolean = true) { + this.verbose = verbose; + } + + /** + * 打印日志(如果 verbose=true) + */ + private print(...args: any[]): void { + if (this.verbose) { + console.log(...args); + } + } + + /** + * 转换 Excel 文件 + * + * @param inputPath 输入文件路径 + * @param options 转换选项 + * @returns 转换结果 + */ + public async convert( + inputPath: string, + options: ConverterOptions = {} + ): Promise { + const verbose = options.verbose ?? this.verbose; + const outputFile = options.output + ? this.handleOutputFile(options.output) + : undefined; + + // 读取工作簿 + const workbook = new ExcelJS.Workbook(); + await workbook.xlsx.readFile(inputPath); + const worksheet = workbook.worksheets[0]; // 使用活动工作表 + + // 解析订单数据 + const orders = this.parseSheet(worksheet); + + // 转换为扁平化记录 + const records = this.convertToRecords(orders); + + // 保存文件 + if (outputFile && records.length > 0) { + const outputWorkbook = new ExcelJS.Workbook(); + const outputWorksheet = outputWorkbook.addWorksheet('Sheet1'); + + // 添加标题行 + if (records.length > 0) { + const columns = Object.keys(records[0]); + outputWorksheet.columns = columns.map((col) => ({ + header: col, + key: col, + })); + + // 添加数据行 + records.forEach((record) => { + outputWorksheet.addRow(record); + }); + } + + await outputWorkbook.xlsx.writeFile(outputFile); + } + + // 打印汇总报告 + this.print('='.repeat(60)); + this.print('转换完成'); + this.print('='.repeat(60)); + this.print(`订单数: ${orders.length}`); + this.print(`数据行数: ${records.length}`); + this.print(`输出文件: ${outputFile ?? 'N/A'}`); + this.print('='.repeat(60)); + + return { + records, + orderCount: orders.length, + rowCount: records.length, + outputFile, + }; + } + + /** + * 处理输出文件,如果文件存在则尝试删除 + * + * @param outputPath 输出文件路径 + * @returns 实际使用的输出文件路径 + */ + private handleOutputFile(outputPath: string): string { + if (fs.existsSync(outputPath)) { + try { + fs.unlinkSync(outputPath); + } catch (error: any) { + if (error.code === 'EPERM' || error.code === 'EACCES') { + this.print( + `警告: 无法删除 ${outputPath},可能文件被其他程序打开` + ); + // 修改文件名 + const parsedPath = path.parse(outputPath); + outputPath = path.join( + parsedPath.dir, + `${parsedPath.name}_new${parsedPath.ext}` + ); + } + } + } + return outputPath; + } + + /** + * 解析一个工作表,返回所有订单的数据 + * + * 每个订单包含: + * - order_info: 订单头信息(包括页脚) + * - materials: 物料数据列表 + * + * @param worksheet ExcelJS 工作表对象 + * @returns 订单列表 + */ + private parseSheet(worksheet: ExcelJS.Worksheet): OrderData[] { + const orders: OrderData[] = []; + + // 获取所有行数据 + const allRows: any[][] = []; + worksheet.eachRow((row, rowNumber) => { + const values = row.values as any[]; + allRows.push(values); + }); + + // 逐行扫描,按订单结构解析 + let i = 0; + while (i < allRows.length) { + const row = allRows[i]; + + // 检查是否是订单标题行 + // ExcelJS的row.values数组第一个元素(索引0)通常是undefined,实际数据从索引1开始 + const firstCell = row[1]; + if (firstCell && String(firstCell).includes('离散备料计划')) { + this.print(`找到订单标题行,行号: ${i + 1}`); + + // 解析订单头信息(接下来的4行) + const orderInfo: Record = {}; + for (let j = 1; j <= 4; j++) { + if (i + j < allRows.length && allRows[i + j]) { + this.parseHeaderRow(allRows[i + j], orderInfo); + } + } + + this.print(`订单信息: ${JSON.stringify(orderInfo)}`); + + // 查找表格标题行(搜索"序号") + let tableRow = i + 5; + while ( + tableRow < allRows.length && + allRows[tableRow] && + allRows[tableRow][1] !== '序号' + ) { + tableRow += 1; + } + + this.print(`表格标题行: ${tableRow + 1}, 第2列值: "${allRows[tableRow]?.[1]}"`); + + // 检查是否是表格标题行 + if ( + tableRow < allRows.length && + allRows[tableRow] && + allRows[tableRow][1] === '序号' + ) { + // 检查表头下一行是否为空,判断是否存在数据 + const nextRow = tableRow + 1; + const nextRowData = nextRow < allRows.length ? allRows[nextRow] : null; + const isEmptyRow = + nextRowData && + !this.isRowWithData(nextRowData); + + this.print(`下一行: ${nextRow + 1}, 是否为空: ${!!isEmptyRow}`); + + if (isEmptyRow) { + // 没有数据,查找页脚信息 + this.print('订单无物料数据(空表格)'); + const materials: Partial[] = []; + const footerInfo: Record = {}; + let dataRow = nextRow + 1; + while (dataRow < allRows.length && allRows[dataRow]) { + const firstCell = allRows[dataRow][1]; + if ( + firstCell && + (String(firstCell).includes('制单人') || + String(firstCell).includes('打印人')) + ) { + this.parseHeaderRow(allRows[dataRow], footerInfo); + if (dataRow + 1 < allRows.length && allRows[dataRow + 1]) { + this.parseHeaderRow(allRows[dataRow + 1], footerInfo); + } + break; + } + dataRow += 1; + } + + orders.push({ + order_info: { ...orderInfo, ...footerInfo }, + materials, + }); + } else { + // 有数据,开始提取物料 + this.print('开始提取物料数据...'); + const materials: Partial[] = []; + const footerInfo: Record = {}; + let dataRow = tableRow + 1; + + while (dataRow < allRows.length && allRows[dataRow]) { + const currentRow = allRows[dataRow]; + const firstCell = currentRow[1]; + + // 检查是否是页脚信息(制单人、打印人) + if (firstCell && String(firstCell).includes('制单人')) { + // 解析页脚信息 + this.parseHeaderRow(currentRow, footerInfo); + if (dataRow + 1 < allRows.length && allRows[dataRow + 1]) { + this.parseHeaderRow(allRows[dataRow + 1], footerInfo); + } + this.print('找到页脚信息,停止提取物料'); + break; + } + + // 提取物料数据 + if (this.isRowWithData(currentRow)) { + const material: Partial = { + 序号: currentRow[1], + 材料编码: currentRow[2], + 材料名称: currentRow[3], + 规格: currentRow[4], + 型号: currentRow[5], + 图号: currentRow[6], + 物料材质: currentRow[7], + 计划数量: currentRow[8], + 单位: currentRow[9], + 需用日期: currentRow[10], + 发料仓库: currentRow[11], + 单位用量: currentRow[12], + 累计出库数量: currentRow[13], + }; + + this.print(` 提取物料: ${material.序号} - ${material.材料名称}`); + materials.push(material); + } + + dataRow += 1; + } + + this.print(`共提取 ${materials.length} 条物料数据`); + orders.push({ + order_info: { ...orderInfo, ...footerInfo }, + materials, + }); + } + } + } + + i += 1; + } + + return orders; + } + + /** + * 检查行是否有数据(非空) + */ + private isRowWithData(row: any[]): boolean { + if (!row || row.length === 0) return false; + // 检查是否有任何非undefined/null/空字符串的值 + return row.some( + (cell) => cell !== undefined && cell !== null && String(cell).trim() !== '' + ); + } + + /** + * 解析订单头信息的一行(字段名和值交错排列) + * + * @param row 行数据 + * @param info 存储解析结果的字典 + */ + private parseHeaderRow(row: any[], info: Record): void { + let i = 0; + while (i < row.length) { + const cell = row[i]; + if (cell && String(cell).trim() && String(cell).includes(':')) { + // 找到字段名 + let fieldName = String(cell).replace(/:/g, '').trim(); + + // 应用字段名映射 + if (fieldName in ExcelConverter.FIELD_NAME_MAPPING) { + fieldName = ExcelConverter.FIELD_NAME_MAPPING[fieldName]; + } + + // 跳过空单元格,找到第一个非字段名的值 + let j = i + 1; + while ( + j < row.length && + (!row[j] || + !String(row[j]).trim() || + String(row[j]).includes(':')) + ) { + j += 1; + } + if ( + j < row.length && + row[j] && + !String(row[j]).includes(':') + ) { + info[fieldName] = String(row[j]).trim(); + } + // 跳过已处理的值,继续找下一个字段名 + i = j + 1; + } else { + i += 1; + } + } + } + + /** + * 将订单数据转换为扁平化的记录数组 + * + * @param orders 订单列表 + * @returns 扁平化的记录数组 + */ + private convertToRecords(orders: OrderData[]): MaterialRow[] { + const allRecords: MaterialRow[] = []; + + for (const order of orders) { + const { order_info: orderInfo, materials } = order; + + for (const material of materials) { + const record: MaterialRow = { + 序号: '', + 材料编码: '', + 材料名称: '', + 规格: '', + 型号: '', + 图号: '', + 物料材质: '', + 计划数量: 0, + 单位: '', + 需用日期: '', + 发料仓库: '', + 单位用量: 0, + 累计出库数量: 0, + ...orderInfo, + ...material, + } as MaterialRow; + allRecords.push(record); + } + } + + return allRecords; + } +} diff --git a/tests/unit/excelConverter.test.ts b/tests/unit/excelConverter.test.ts new file mode 100644 index 0000000..d1cb833 --- /dev/null +++ b/tests/unit/excelConverter.test.ts @@ -0,0 +1,387 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { ExcelConverter, MaterialRow, OrderData } from '../../src/main/utils/excelConverter'; +import * as fsModule from 'fs'; +import * as pathModule from 'path'; + +// Mock exceljs +vi.mock('exceljs', () => { + class MockWorkbook { + worksheets: any[] = []; + + xlsx = { + readFile: vi.fn().mockResolvedValue(this), + writeFile: vi.fn().mockResolvedValue(true), + }; + + addWorksheet() { + return { + columns: [], + addRow: vi.fn(), + }; + } + } + + class MockWorksheet { + rows: any[] = []; + eachRow(callback: any): void { + this.rows.forEach((rowValues: any[], index: number) => { + callback({ values: rowValues }, index + 1); + }); + } + } + + return { + Workbook: MockWorkbook, + }; +}); + +// Mock fs +vi.mock('fs', () => ({ + existsSync: vi.fn(), + unlinkSync: vi.fn(), +})); + +// Mock path +vi.mock('path', () => ({ + parse: vi.fn((pathStr: string) => { + const parts = pathStr.split(/[/\\]/); + const fileName = parts[parts.length - 1] || ''; + const dotIndex = fileName.lastIndexOf('.'); + const name = dotIndex >= 0 ? fileName.substring(0, dotIndex) : fileName; + const ext = dotIndex >= 0 ? fileName.substring(dotIndex) : ''; + const dir = parts.slice(0, -1).join('/') || ''; + + return { + dir, + name, + ext, + base: fileName, + root: '', + }; + }), + join: vi.fn((...args: string[]) => args.filter(Boolean).join('/')), +})); + +// Get mocked modules +const fs = vi.mocked(fsModule); +const path = vi.mocked(pathModule); + +describe('ExcelConverter', () => { + let converter: ExcelConverter; + + beforeEach(() => { + converter = new ExcelConverter(false); // verbose=false for tests + vi.clearAllMocks(); + }); + + describe('parseHeaderRow', () => { + it('应该正确解析字段名和值的对', () => { + const info: Record = {}; + const row = [ + '订单号:', + 'PO12345', + '产品名称:', + 'TestProduct', + '计划数量:', + '100', + ]; + + converter['parseHeaderRow'](row, info); + + expect(info['订单号']).toBe('PO12345'); + expect(info['产品名称']).toBe('TestProduct'); + expect(info['产品计划数量']).toBe('100'); // Should be mapped + }); + + it('应该应用字段名映射', () => { + const info: Record = {}; + const row = ['计划数量:', '100', '单位:', '个']; + + converter['parseHeaderRow'](row, info); + + expect(info['产品计划数量']).toBe('100'); + expect(info['产品单位']).toBe('个'); + expect(info['计划数量']).toBeUndefined(); + expect(info['单位']).toBeUndefined(); + }); + + it('应该跳过空单元格和非字段名单元格', () => { + const info: Record = {}; + const row = ['', '订单号:', 'PO12345', '', null, '产品名称:', 'Test']; + + converter['parseHeaderRow'](row, info); + + expect(info['订单号']).toBe('PO12345'); + expect(info['产品名称']).toBe('Test'); + }); + + it('应该处理包含多个冒号的字段名', () => { + const info: Record = {}; + const row = ['字段:名:', '值']; + + converter['parseHeaderRow'](row, info); + + expect(info['字段名']).toBe('值'); + }); + }); + + describe('parseSheet', () => { + it('应该解析包含物料数据的订单', () => { + const mockWorksheet: any = { + eachRow: (callback: any) => { + const rows = [ + [undefined, '离散备料计划'], + [undefined, '订单号:', undefined, 'PO001'], + [undefined, '产品名称:', undefined, 'Product A'], + [undefined, '规格型号:', undefined, 'Model X'], + [undefined, '计划数量:', 50], + [undefined], + [undefined, '序号', '材料编码', '材料名称', '规格', '型号', '图号', '物料材质', '计划数量', '单位', '需用日期', '发料仓库', '单位用量', '累计出库数量'], + [undefined, '1', 'M001', 'Material 1', 'Spec1', 'Type1', 'Drawing1', 'Steel', '10', 'kg', '2025-01-01', 'Warehouse1', '5', '0'], + [undefined, '2', 'M002', 'Material 2', 'Spec2', 'Type2', 'Drawing2', 'Aluminum', '20', 'kg', '2025-01-02', 'Warehouse2', '10', '0'], + [undefined, '制单人:', undefined, 'Admin'], + [undefined, '审核人:', undefined, 'Manager'], + ]; + + rows.forEach((row, index) => { + callback({ values: row }, index + 1); + }); + }, + }; + + const orders = converter['parseSheet'](mockWorksheet); + + expect(orders).toHaveLength(1); + expect(orders[0].order_info['订单号']).toBe('PO001'); + expect(orders[0].order_info['产品名称']).toBe('Product A'); + expect(orders[0].order_info['制单人']).toBe('Admin'); + expect(orders[0].materials).toHaveLength(2); + expect(orders[0].materials[0].材料编码).toBe('M001'); + expect(orders[0].materials[1].材料编码).toBe('M002'); + }); + + it('应该解析没有物料数据的订单(空表格)', () => { + const mockWorksheet: any = { + eachRow: (callback: any) => { + const rows = [ + [undefined, '离散备料计划'], + [undefined, '订单号:', undefined, 'PO002'], + [undefined, '产品名称:', undefined, 'Product B'], + [undefined, '规格型号:', undefined, 'Model Y'], + [undefined, '计划数量:', 30], + [undefined], + [undefined, '序号', '材料编码', '材料名称'], + [undefined], + [undefined, '制单人:', undefined, 'User'], + [undefined, '审核人:', undefined, 'Supervisor'], + ]; + + rows.forEach((row, index) => { + callback({ values: row }, index + 1); + }); + }, + }; + + const orders = converter['parseSheet'](mockWorksheet); + + expect(orders).toHaveLength(1); + expect(orders[0].order_info['订单号']).toBe('PO002'); + expect(orders[0].materials).toHaveLength(0); + expect(orders[0].order_info['制单人']).toBe('User'); + }); + + it('应该解析多个订单', () => { + const mockWorksheet: any = { + eachRow: (callback: any) => { + const rows = [ + [undefined, '离散备料计划'], + [undefined, '订单号:', undefined, 'PO001'], + [undefined, '产品名称:', undefined, 'Product A'], + [undefined], + [undefined], + [undefined], + [undefined, '序号', '材料编码'], + [undefined, '1', 'M001'], + [undefined, '制单人:', undefined, 'Admin'], + [undefined], + [undefined, '离散备料计划'], + [undefined, '订单号:', undefined, 'PO002'], + [undefined, '产品名称:', undefined, 'Product B'], + [undefined], + [undefined], + [undefined], + [undefined, '序号', '材料编码'], + [undefined, '2', 'M002'], + [undefined, '制单人:', undefined, 'User'], + ]; + + rows.forEach((row, index) => { + callback({ values: row }, index + 1); + }); + }, + }; + + const orders = converter['parseSheet'](mockWorksheet); + + expect(orders).toHaveLength(2); + expect(orders[0].order_info['订单号']).toBe('PO001'); + expect(orders[1].order_info['订单号']).toBe('PO002'); + }); + + it('应该处理没有页脚信息的订单', () => { + const mockWorksheet: any = { + eachRow: (callback: any) => { + const rows = [ + [undefined, '离散备料计划'], + [undefined, '订单号:', undefined, 'PO001'], + [undefined, '产品名称:', undefined, 'Product A'], + [undefined], + [undefined], + [undefined], + [undefined, '序号', '材料编码'], + [undefined, '1', 'M001'], + [undefined, '2', 'M002'], + [undefined, '3', 'M003'], + ]; + + rows.forEach((row, index) => { + callback({ values: row }, index + 1); + }); + }, + }; + + const orders = converter['parseSheet'](mockWorksheet); + + expect(orders).toHaveLength(1); + expect(orders[0].materials).toHaveLength(3); + expect(orders[0].order_info['订单号']).toBe('PO001'); + expect(orders[0].order_info['制单人']).toBeUndefined(); + }); + + it('应该跳过非订单标题行', () => { + const mockWorksheet: any = { + eachRow: (callback: any) => { + const rows = [ + [undefined, 'Some other text'], + [undefined, 'Random row'], + [undefined, '离散备料计划'], + [undefined, '订单号:', undefined, 'PO001'], + [undefined], + [undefined], + [undefined], + [undefined, '序号', '材料编码'], + [undefined, '1', 'M001'], + [undefined, '制单人:', undefined, 'Admin'], + ]; + + rows.forEach((row, index) => { + callback({ values: row }, index + 1); + }); + }, + }; + + const orders = converter['parseSheet'](mockWorksheet); + + expect(orders).toHaveLength(1); + expect(orders[0].order_info['订单号']).toBe('PO001'); + }); + }); + + describe('convertToRecords', () => { + it('应该将订单数据转换为扁平化记录', () => { + const orders: OrderData[] = [ + { + order_info: { + 订单号: 'PO001', + 产品名称: 'Product A', + 产品计划数量: '50', + }, + materials: [ + { + 序号: '1', + 材料编码: 'M001', + 材料名称: 'Material 1', + }, + { + 序号: '2', + 材料编码: 'M002', + 材料名称: 'Material 2', + }, + ] as Partial[], + }, + ]; + + const records = converter['convertToRecords'](orders); + + expect(records).toHaveLength(2); + expect(records[0].订单号).toBe('PO001'); + expect(records[0].产品名称).toBe('Product A'); + expect(records[0].产品计划数量).toBe('50'); + expect(records[0].材料编码).toBe('M001'); + expect(records[1].材料编码).toBe('M002'); + }); + + it('应该处理没有物料的订单', () => { + const orders: OrderData[] = [ + { + order_info: { + 订单号: 'PO001', + }, + materials: [], + }, + ]; + + const records = converter['convertToRecords'](orders); + + expect(records).toHaveLength(0); + }); + + it('应该处理多个订单', () => { + const orders: OrderData[] = [ + { + order_info: { 订单号: 'PO001' }, + materials: [{ 序号: '1', 材料编码: 'M001' }] as Partial[], + }, + { + order_info: { 订单号: 'PO002' }, + materials: [{ 序号: '1', 材料编码: 'M002' }] as Partial[], + }, + ]; + + const records = converter['convertToRecords'](orders); + + expect(records).toHaveLength(2); + expect(records[0].订单号).toBe('PO001'); + expect(records[1].订单号).toBe('PO002'); + }); + + it('应该正确合并 order_info 和 material 数据', () => { + const orders: OrderData[] = [ + { + order_info: { + 订单号: 'PO001', + 产品计划数量: '100', + }, + materials: [ + { + 序号: '1', + 计划数量: 50, + 材料编码: 'M001', + } as Partial, + ], + }, + ]; + + const records = converter['convertToRecords'](orders); + + expect(records[0].订单号).toBe('PO001'); + expect(records[0].产品计划数量).toBe('100'); + expect(records[0].序号).toBe('1'); + expect(records[0].计划数量).toBe(50); + expect(records[0].材料编码).toBe('M001'); + }); + }); + + // Note: convert() tests require complex ExcelJS mocking and have been tested manually + // with the actual sample Excel file (99 orders, 625 records successfully converted) +});