UNPKG

@accounter/shaam-uniform-format-generator

Version:

Fully typed application that generates, parses, and validates SHAAM uniform format tax reports (INI.TXT and BKMVDATA.TXT).

664 lines (557 loc) 16.8 kB
# @accounter/shaam-uniform-format-generator Fully typed application that generates, parses, and validates SHAAM uniform format tax reports (INI.TXT and BKMVDATA.TXT). ## 🧩 Overview This package provides a comprehensive solution for working with SHAAM (Israeli tax authority) uniform format files. It allows you to: 1. **Generate** `INI.TXT` and `BKMVDATA.TXT` files from a high-level JSON object 2. **Parse** those files back into structured, validated JSON with comprehensive validation 3. **Validate** data against SHAAM 1.31 specifications with multiple validation modes 4. **Format** output with spec-compliant field widths, padding, and CRLF line endings 5. **Round-trip** data preservation ensuring high-fidelity parsing and re-generation ## 🚀 Features - **Type Safety**: Full TypeScript support with strict typing - **Validation**: Built-in Zod schemas for data validation with multiple modes - **Format Compliance**: Generates files that meet SHAAM 1.31 specifications - **Developer Experience**: Excellent autocompletion and helpful error messages - **File System Agnostic**: Returns content in memory without writing to disk - **Comprehensive Testing**: Full test coverage with Vitest including integration tests - **Dual Module Support**: Ships with both CommonJS and ESM builds - **Round-trip Fidelity**: Preserves original data through parse-generate cycles ## 📁 Supported Record Types ### `INI.TXT` - `A000` — Header - `A000Sum` — Count summary records for each record type ### `BKMVDATA.TXT` - `A100` — Business opening record - `C100` — Document header - `D110` — Document line - `D120` — Payment/receipt - `B100` — Journal entry line - `B110` — Account - `M100` — Inventory item - `Z900` — Closing record ## 🛠️ Installation ```bash npm install @accounter/shaam-uniform-format-generator # or yarn add @accounter/shaam-uniform-format-generator ``` ## 📖 API Documentation ### Main Functions #### `generateUniformFormatReport(input, options?)` Generates SHAAM uniform format report files from a high-level JSON input object. ```typescript import { generateUniformFormatReport, type ReportInput, } from '@accounter/shaam-uniform-format-generator'; const reportInput: ReportInput = { business: { businessId: '12345', name: 'Example Business Ltd', taxId: '123456789', reportingPeriod: { startDate: '2023-01-01', endDate: '2023-12-31', }, }, documents: [ { id: 'INV001', type: '320', // Invoice date: '2023-06-15', amount: 1000.0, description: 'Service invoice', }, ], journalEntries: [ { id: 'JE001', date: '2023-06-15', amount: 1000.0, accountId: '1100', description: 'Revenue entry', }, ], accounts: [ { id: '1100', name: 'Revenue Account', type: '4', // Revenue balance: 1000.0, }, ], inventory: [ { id: 'ITEM001', name: 'Service Item', quantity: 1, unitPrice: 1000.0, }, ], }; const result = generateUniformFormatReport(reportInput, { validationMode: 'fail-fast', // or 'collect-all' fileNameBase: 'my-report', }); console.log(result.iniText); // INI.TXT content console.log(result.dataText); // BKMVDATA.TXT content console.log(result.summary); // Generation summary ``` #### `parseUniformFormatFiles(iniContent, dataContent, options?)` Parses SHAAM uniform format files back into structured JSON with comprehensive validation. ```typescript import { parseUniformFormatFiles } from '@accounter/shaam-uniform-format-generator'; const parseResult = parseUniformFormatFiles(iniFileContent, dataFileContent, { validationMode: 'lenient', // 'strict' | 'lenient' | 'none' skipUnknownRecords: true, allowPartialData: true, }); console.log(parseResult.data.business); // Parsed business metadata console.log(parseResult.data.documents); // Parsed documents console.log(parseResult.summary.totalRecords); // Parse summary console.log(parseResult.summary.errors); // Validation errors console.log(parseResult.summary.crossValidationPassed); // Cross-validation result ``` ### Types and Interfaces #### `ReportInput` ```typescript interface ReportInput { business: BusinessMetadata; documents: Document[]; journalEntries: JournalEntry[]; accounts: Account[]; inventory: InventoryItem[]; } ``` #### `BusinessMetadata` ```typescript interface BusinessMetadata { businessId: string; name: string; taxId: string; reportingPeriod: { startDate: string; // YYYY-MM-DD format endDate: string; // YYYY-MM-DD format }; } ``` #### `Document` ```typescript interface Document { id: string; type: DocumentType; // e.g., "320" for invoice, "330" for credit note date: string; // YYYY-MM-DD format amount: number; description?: string; } ``` #### `JournalEntry` ```typescript interface JournalEntry { id: string; date: string; // YYYY-MM-DD format amount: number; accountId: string; description?: string; transactionNumber?: number; transactionLineNumber?: number; batchNumber?: number; transactionType?: string; referenceDocument?: string; referenceDocumentType?: DocumentType; referenceDocument2?: string; referenceDocumentType2?: DocumentType; valueDate?: string; counterAccountKey?: string; debitCreditIndicator?: '1' | '2'; // 1=Debit, 2=Credit currencyCode?: CurrencyCode; transactionAmount?: number; // Preserve original B100 transaction amount foreignCurrencyAmount?: number; quantityField?: number; matchingField1?: string; matchingField2?: string; branchId?: string; entryDate?: string; operatorUsername?: string; reserved?: string; } ``` #### `Account` ```typescript interface Account { id: string; name?: string; // Optional for round-trip compatibility sortCode: { key: string; // Required - Account sort code key name?: string; // Optional - Sort code description }; address?: { street?: string; houseNumber?: string; city?: string; zip?: string; country?: string; }; countryCode?: string; // ISO country code parentAccountKey?: string; // Parent account identifier vatId?: string; // Supplier/Customer VAT ID accountOpeningBalance: number; // Required - Opening balance amount totalDebits?: number; // Total debit transactions totalCredits?: number; // Total credit transactions accountingClassificationCode?: string; // Classification code (max 4 digits) branchId?: string; // Branch identifier openingBalanceForeignCurrency?: number; // Opening balance in foreign currency foreignCurrencyCode?: string; // Foreign currency code (e.g., "USD", "EUR") } ``` #### `InventoryItem` ```typescript interface InventoryItem { id: string; name: string; quantity: number; unitPrice: number; } ``` #### `ValidationError` ```typescript interface ValidationError { recordType: string; recordIndex: number; field: string; message: string; severity?: 'error' | 'warning'; // Only in parse results } ``` ```` #### `ReportOutput` ```typescript interface ReportOutput { iniText: string // INI.TXT file content dataText: string // BKMVDATA.TXT file content iniFile: File // Virtual File object for INI.TXT dataFile: File // Virtual File object for BKMVDATA.TXT summary: { totalRecords: number perType: Record<string, number> errors?: ValidationError[] // Only present if validation fails in collect-all mode } } ```` #### `ParseResult` ```typescript interface ParseResult { data: ReportInput; // Parsed structured data summary: { totalRecords: number; perType: Record<string, number>; errors: ValidationError[]; crossValidationPassed: boolean; }; } ``` ### Options #### `GenerationOptions` ```typescript interface GenerationOptions { validationMode?: 'fail-fast' | 'collect-all'; // Default: 'fail-fast' fileNameBase?: string; // Default: 'report' } ``` - **`validationMode`**: - `'fail-fast'`: Stop validation on first error and throw immediately - `'collect-all'`: Collect all validation errors before throwing - **`fileNameBase`**: Base name for generated files (without extension) ### Enums and Constants The package exports comprehensive enums for SHAAM code tables: ```typescript import { CountryCodeEnum, CurrencyCodeEnum, DebitCreditIndicatorEnum, DocumentTypeEnum, PaymentMethodEnum, RecordTypeEnum, // ... and many more } from '@accounter/shaam-uniform-format-generator'; // Document types const invoiceType = DocumentTypeEnum.enum['320']; // Invoice const creditNoteType = DocumentTypeEnum.enum['330']; // Credit Tax Invoice // Currency codes const ils = CurrencyCodeEnum.enum.ILS; // Israeli Shekel const usd = CurrencyCodeEnum.enum.USD; // US Dollar // Payment methods const cash = PaymentMethodEnum.enum['1']; // Cash const check = PaymentMethodEnum.enum['2']; // Check // Debit/Credit indicators const debit = DebitCreditIndicatorEnum.enum['1']; // Debit const credit = DebitCreditIndicatorEnum.enum['2']; // Credit // Record types const b110 = RecordTypeEnum.enum.B110; // Account record const c100 = RecordTypeEnum.enum.C100; // Document header record ``` ## 🎯 Complete Example ```typescript import { generateUniformFormatReport, parseUniformFormatFiles, type ReportInput, } from '@accounter/shaam-uniform-format-generator'; // 1. Prepare your data const reportData: ReportInput = { business: { businessId: 'COMP001', name: 'Acme Corp Ltd', taxId: '123456789', reportingPeriod: { startDate: '2023-01-01', endDate: '2023-12-31', }, }, documents: [ { id: 'INV-2023-001', type: '320', // Invoice date: '2023-03-15', amount: 2340.0, description: 'Consulting services', }, { id: 'CN-2023-001', type: '330', // Credit note date: '2023-04-10', amount: -340.0, description: 'Service adjustment', }, ], journalEntries: [ { id: 'JE-2023-001', date: '2023-03-15', amount: 2340.0, accountId: '4000', description: 'Consulting revenue', batchNumber: 'BATCH-Q1-2023', transactionType: 'SALE', referenceDocument: 'INV-2023-001', }, { id: 'JE-2023-002', date: '2023-04-10', amount: -340.0, accountId: '4000', description: 'Revenue adjustment', currencyCode: 'USD', foreignCurrencyAmount: -290.0, }, ], accounts: [ { id: '4000', name: 'Consulting Revenue', sortCode: { key: 'Revenue', name: 'Revenue Accounts', }, accountOpeningBalance: 0.0, totalDebits: 500.0, totalCredits: 2500.0, accountingClassificationCode: '0001', }, { id: '1200', name: 'Accounts Receivable', sortCode: { key: 'Asset', name: 'Asset Accounts', }, accountOpeningBalance: 1500.0, countryCode: 'IL', branchId: 'MAIN', foreignCurrencyCode: 'USD', openingBalanceForeignCurrency: 1250.0, }, ], inventory: [ { id: 'SERV-001', name: 'Consulting Hour', quantity: 20, unitPrice: 117.0, }, ], }; // 2. Generate the files try { const result = generateUniformFormatReport(reportData, { validationMode: 'fail-fast', fileNameBase: 'quarterly-report-2023-q1', }); // 3. Access the generated content console.log('Generated INI.TXT:'); console.log(result.iniText); console.log('\nGenerated BKMVDATA.TXT:'); console.log(result.dataText); console.log('\nSummary:'); console.log(`Total records: ${result.summary.totalRecords}`); console.log('Records per type:', result.summary.perType); // 4. Save files (example using Node.js fs) // import { writeFileSync } from 'fs' // writeFileSync('report.INI.TXT', result.iniText, 'utf8') // writeFileSync('report.BKMVDATA.TXT', result.dataText, 'utf8') // 5. Parse files back (round-trip test) const parsedData = parseUniformFormatFiles(result.iniText, result.dataText); console.log('\nParsed business data:', parsedData.business); } catch (error) { console.error('Generation failed:', error.message); if (error.errors) { console.error('Validation errors:', error.errors); } } ``` ## 🔧 Advanced Usage ### Custom Validation ```typescript import { ReportInputSchema } from '@accounter/shaam-uniform-format-generator'; // Validate data before generation const validationResult = ReportInputSchema.safeParse(yourData); if (!validationResult.success) { console.error('Validation errors:', validationResult.error.issues); } ``` ### Parse with Different Validation Modes ```typescript import { parseUniformFormatFiles } from '@accounter/shaam-uniform-format-generator'; // Strict validation - throws on any error try { const strictResult = parseUniformFormatFiles(iniContent, dataContent, { validationMode: 'strict', allowPartialData: false, }); console.log('Strict parsing succeeded:', strictResult.data); } catch (error) { console.error('Strict parsing failed:', error.message); } // Lenient validation - reports issues but continues const lenientResult = parseUniformFormatFiles(iniContent, dataContent, { validationMode: 'lenient', }); console.log('Parsed data:', lenientResult.data); console.log('Validation issues:', lenientResult.summary.errors); // No validation - fastest parsing const fastResult = parseUniformFormatFiles(iniContent, dataContent, { validationMode: 'none', }); console.log('Fast parsing result:', fastResult.data); ``` ### Working with Individual Records ```typescript import { encodeC100, parseC100, type C100 } from '@accounter/shaam-uniform-format-generator'; // Encode a single document record const documentRecord: C100 = { code: 'C100', recordNumber: 1, vatId: '123456789', documentType: '320', documentId: 'INV001', documentIssueDate: '20230315', documentIssueTime: '', customerName: '', customerStreet: '', customerHouseNumber: '', customerCity: '', customerPostCode: '', customerCountry: '', customerCountryCode: '', customerPhone: '', customerVatId: '', documentValueDate: '', foreignCurrencyAmount: '', currencyCode: '', amountBeforeDiscount: '', documentDiscount: '', amountAfterDiscountExcludingVat: '', vatAmount: '', amountIncludingVat: '1000.00', withholdingTaxAmount: '', customerKey: '', matchingField: '', cancelledAttribute1: '', cancelledDocument: '', cancelledAttribute2: '', documentDate: '', branchKey: '', cancelledAttribute3: '', actionExecutor: '', lineConnectingField: '', reserved: '', }; const encodedLine = encodeC100(documentRecord); console.log(encodedLine); // Fixed-width formatted line // Parse it back const parsedRecord = parseC100(encodedLine); console.log(parsedRecord); // Structured object ``` ## 🏗️ Development This project uses: - **TypeScript** in strict mode for type safety - **Zod** for runtime validation and schema definitions - **Vitest** for testing with comprehensive integration tests - **Bob the Bundler** for building dual CJS/ESM packages - **ESLint** for code linting and formatting ### Commands ```bash # Development yarn dev # Testing yarn test yarn test:watch # Building yarn build # Linting yarn lint ``` ## 📋 Requirements - Node.js ^20.0.0 || >= 22 - TypeScript support ## 🚨 Error Handling The library provides detailed error information: ```typescript import { ShaamFormatError } from '@accounter/shaam-uniform-format-generator'; try { const result = generateUniformFormatReport(invalidData); } catch (error) { if (error instanceof ShaamFormatError) { console.error('SHAAM format error:', error.message); console.error('Validation errors:', error.errors); } } // Parse errors include context and severity try { const parseResult = parseUniformFormatFiles(iniContent, dataContent, { validationMode: 'strict', }); } catch (error) { console.error('Parse failed:', error.message); } // Access detailed validation information const parseResult = parseUniformFormatFiles(iniContent, dataContent, { validationMode: 'lenient', }); for (const error of parseResult.summary.errors) { console.log(`${error.severity}: ${error.message}`); console.log(`Record: ${error.recordType}[${error.recordIndex}], Field: ${error.field}`); } ``` ## �📄 License MIT ## 🤝 Contributing Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository. ## 📚 Documentation For detailed documentation about SHAAM format specifications, see the `documentation/` folder. ## 🔗 Related - [SHAAM Specification 1.31](documentation/) - Official specification documents - [Israeli Tax Authority](https://www.gov.il/he/departments/taxes) - Official tax authority website