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.7 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