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

273 lines (272 loc) 12.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.D110Schema = void 0; exports.encodeD110 = encodeD110; exports.parseD110 = parseD110; const zod_1 = require("zod"); const index_js_1 = require("../../format/index.js"); const index_js_2 = require("../../types/index.js"); const index_js_3 = require("../index.js"); /** * D110 Record Schema - Document Line record * Fields 1250-1275 based on SHAAM 1.31 specification table */ exports.D110Schema = zod_1.z.object({ // Field 1250: Record code (4) - Required - Alphanumeric code: zod_1.z.literal('D110').describe('Record type code - always "D110"'), // Field 1251: Record number in file (9) - Required - Numeric recordNumber: zod_1.z.string().min(1).max(9).regex(/^\d+$/).describe('Sequential record number'), // Field 1252: Tax ID (9) - Required - Numeric vatId: zod_1.z.string().min(1).max(9).regex(/^\d+$/).describe('VAT identification number'), // Field 1253: Document type (3) - Required - Numeric documentType: index_js_2.DocumentTypeEnum.describe('Document type - see Appendix 1'), // Field 1254: Document number (20) - Required - Alphanumeric documentNumber: zod_1.z.string().min(1).max(20).describe('Document number'), // Field 1255: Line number in document (4) - Required - Numeric lineNumber: zod_1.z.string().min(1).max(4).regex(/^\d+$/).describe('Line number in document'), // Field 1256: Base document type (3) - Conditional - Numeric baseDocumentType: zod_1.z .string() .max(3) .regex(/^(\d{1,3}|)$/) .default('') .transform(val => (val === '' ? val : val)) .describe('Base document type - required if based on another document'), // Field 1257: Base document number (20) - Conditional - Alphanumeric baseDocumentNumber: zod_1.z .string() .max(20) .default('') .describe('Base document number - required if based on another document'), // Field 1258: Transaction type (1) - Optional - Numeric transactionType: zod_1.z .string() .max(1) .regex(/^([123]|)$/) .default('') .describe('Transaction type: 1-Service; 2-Goods; 3-Service & Goods'), // Field 1259: Internal catalog code (20) - Optional - Alphanumeric internalCatalogCode: zod_1.z.string().max(20).default('').describe('Internal catalog code'), // Field 1260: Goods/Service description (30) - Required - Alphanumeric goodsServiceDescription: zod_1.z.string().min(1).max(30).describe('Goods or service description'), // Field 1261: Manufacturer name (50) - Optional - Alphanumeric manufacturerName: zod_1.z .string() .max(50) .default('') .describe('Manufacturer name - relevant for goods listed in Appendix G'), // Field 1262: Serial number (30) - Optional - Alphanumeric serialNumber: zod_1.z .string() .max(30) .default('') .describe('Serial number - relevant for goods listed in Appendix G'), // Field 1263: Unit of measure description (20) - Optional - Alphanumeric unitOfMeasureDescription: zod_1.z .string() .max(20) .default('') .describe('Unit of measure description - use descriptive name or יחידה'), // Field 1264: Quantity (17) - Required - Alphanumeric with decimal format X9(12)V9999 quantity: zod_1.z.string().min(1).max(17).describe('Quantity'), // Field 1265: Unit price excluding VAT (15) - Optional - Alphanumeric with decimal format X9(12)V99 unitPriceExcludingVat: zod_1.z.string().max(15).default('').describe('Unit price excluding VAT in NIS'), // Field 1266: Line discount (15) - Optional - Alphanumeric with decimal format X9(12)V99 lineDiscount: zod_1.z.string().max(15).default('').describe('Line discount in NIS'), // Field 1267: Line total (15) - Optional - Alphanumeric with decimal format X9(12)V99 lineTotal: zod_1.z .string() .max(15) .default('') .describe('Line total: quantity * unit price - discount'), // Field 1268: VAT rate percentage (4) - Optional - Numeric with decimal format V99(2) vatRatePercent: zod_1.z .string() .max(4) .regex(/^(\d{1,4}|)$/) .default('') .describe('VAT rate percentage'), // Field 1269: Reserved field (0) - Deprecated - Alphanumeric reserved1: zod_1.z.string().max(0).default('').describe('Reserved field - deprecated'), // Field 1270: Branch ID (7) - Required - Alphanumeric branchId: zod_1.z.string().min(1).max(7).describe('Branch ID - required if field 1034 = 1'), // Field 1271: Reserved field (0) - Deprecated - Alphanumeric reserved2: zod_1.z.string().max(0).default('').describe('Reserved field - deprecated'), // Field 1272: Document date (8) - Required - Numeric YYYYMMDD format documentDate: zod_1.z .string() .min(1) .max(8) .regex(/^\d{8}$/) .describe('Document date in YYYYMMDD format'), // Field 1273: Header link field (7) - Optional - Numeric headerLinkField: zod_1.z .string() .max(7) .regex(/^(\d{1,7}|)$/) .default('') .describe('Header link field - link to C100'), // Field 1274: Base document branch ID (7) - Optional - Alphanumeric baseDocumentBranchId: zod_1.z .string() .max(7) .default('') .describe('Base document branch ID - if field 1034 = 1'), // Field 1275: Reserved field (21) - Optional - Alphanumeric reserved3: zod_1.z.string().max(21).default('').describe('Reserved field'), }); /** * Encodes a D110 record to fixed-width string format * Total line width: 339 characters + CRLF */ function encodeD110(input) { const fields = [ (0, index_js_3.formatField)(input.code, 4, 'left'), // Field 1250: Record code (4) - Alphanumeric (0, index_js_3.formatNumericField)(input.recordNumber, 9), // Field 1251: Record number (9) - Numeric (0, index_js_3.formatNumericField)(input.vatId, 9), // Field 1252: VAT ID (9) - Numeric (0, index_js_3.formatNumericField)(input.documentType, 3), // Field 1253: Document type (3) - Numeric (0, index_js_3.formatField)(input.documentNumber, 20, 'left'), // Field 1254: Document number (20) - Alphanumeric (0, index_js_3.formatNumericField)(input.lineNumber, 4), // Field 1255: Line number (4) - Numeric (0, index_js_3.formatNumericField)(input.baseDocumentType, 3), // Field 1256: Base document type (3) - Numeric (0, index_js_3.formatField)(input.baseDocumentNumber, 20, 'left'), // Field 1257: Base document number (20) - Alphanumeric (0, index_js_3.formatNumericField)(input.transactionType, 1), // Field 1258: Transaction type (1) - Numeric (0, index_js_3.formatField)(input.internalCatalogCode, 20, 'left'), // Field 1259: Internal catalog code (20) - Alphanumeric (0, index_js_3.formatField)(input.goodsServiceDescription, 30, 'left'), // Field 1260: Goods/Service description (30) - Alphanumeric (0, index_js_3.formatField)(input.manufacturerName, 50, 'left'), // Field 1261: Manufacturer name (50) - Alphanumeric (0, index_js_3.formatField)(input.serialNumber, 30, 'left'), // Field 1262: Serial number (30) - Alphanumeric (0, index_js_3.formatField)(input.unitOfMeasureDescription, 20, 'left'), // Field 1263: Unit of measure description (20) - Alphanumeric (0, index_js_3.formatField)(input.quantity, 17, 'left'), // Field 1264: Quantity (17) - Alphanumeric but monetary format (0, index_js_3.formatField)(input.unitPriceExcludingVat, 15, 'left'), // Field 1265: Unit price excluding VAT (15) - Alphanumeric but monetary format (0, index_js_3.formatField)(input.lineDiscount, 15, 'left'), // Field 1266: Line discount (15) - Alphanumeric but monetary format (0, index_js_3.formatField)(input.lineTotal, 15, 'left'), // Field 1267: Line total (15) - Alphanumeric but monetary format (0, index_js_3.formatNumericField)(input.vatRatePercent, 4), // Field 1268: VAT rate % (4) - Numeric with decimal (0, index_js_3.formatField)(input.reserved1, 0, 'left'), // Field 1269: Reserved (0) - Deprecated (0, index_js_3.formatField)(input.branchId, 7, 'left'), // Field 1270: Branch ID (7) - Alphanumeric (0, index_js_3.formatField)(input.reserved2, 0, 'left'), // Field 1271: Reserved (0) - Deprecated (0, index_js_3.formatNumericField)(input.documentDate, 8), // Field 1272: Document date (8) - Numeric YYYYMMDD (0, index_js_3.formatNumericField)(input.headerLinkField, 7), // Field 1273: Header link field (7) - Numeric (0, index_js_3.formatField)(input.baseDocumentBranchId, 7, 'left'), // Field 1274: Base document branch ID (7) - Alphanumeric (0, index_js_3.formatField)(input.reserved3, 21, 'left'), // Field 1275: Reserved (21) - Alphanumeric ]; return fields.join('') + index_js_1.CRLF; } /** * Parses a fixed-width D110 record line back to object * Expected line length: 339 characters (excluding CRLF) */ function parseD110(line) { // Remove CRLF if present const cleanLine = line.replace(/\r?\n$/, ''); if (cleanLine.length !== 339) { throw new Error(`Invalid D110 record length: expected 339 characters, got ${cleanLine.length}`); } // Extract fields at their fixed positions let pos = 0; const code = cleanLine.slice(pos, pos + 4).trim(); pos += 4; const recordNumber = cleanLine .slice(pos, pos + 9) .trim() .replace(/^0+/, '') || '0'; pos += 9; const vatId = cleanLine .slice(pos, pos + 9) .trim() .replace(/^0+/, '') || '0'; pos += 9; const documentType = (cleanLine .slice(pos, pos + 3) .trim() .replace(/^0+/, '') || '0'); pos += 3; const documentNumber = cleanLine.slice(pos, pos + 20).trim(); pos += 20; const lineNumber = cleanLine .slice(pos, pos + 4) .trim() .replace(/^0+/, '') || '0'; pos += 4; const baseDocumentType = cleanLine.slice(pos, pos + 3).trim(); pos += 3; const baseDocumentNumber = cleanLine.slice(pos, pos + 20).trim(); pos += 20; const transactionType = cleanLine.slice(pos, pos + 1).trim(); const processedTransactionType = transactionType === '0' ? '' : transactionType; pos += 1; const internalCatalogCode = cleanLine.slice(pos, pos + 20).trim(); pos += 20; const goodsServiceDescription = cleanLine.slice(pos, pos + 30).trim(); pos += 30; const manufacturerName = cleanLine.slice(pos, pos + 50).trim(); pos += 50; const serialNumber = cleanLine.slice(pos, pos + 30).trim(); pos += 30; const unitOfMeasureDescription = cleanLine.slice(pos, pos + 20).trim(); pos += 20; const quantity = cleanLine.slice(pos, pos + 17).trim(); pos += 17; const unitPriceExcludingVat = cleanLine.slice(pos, pos + 15).trim(); pos += 15; const lineDiscount = cleanLine.slice(pos, pos + 15).trim(); pos += 15; const lineTotal = cleanLine.slice(pos, pos + 15).trim(); pos += 15; const vatRatePercent = cleanLine .slice(pos, pos + 4) .trim() .replace(/^0+/, '') || '0'; pos += 4; const reserved1 = cleanLine.slice(pos, pos + 0).trim(); pos += 0; // 0 width const branchId = cleanLine.slice(pos, pos + 7).trim(); pos += 7; const reserved2 = cleanLine.slice(pos, pos + 0).trim(); pos += 0; // 0 width const documentDate = cleanLine .slice(pos, pos + 8) .trim() .replace(/^0+/, '') || '0'; pos += 8; const headerLinkField = cleanLine .slice(pos, pos + 7) .trim() .replace(/^0+/, '') || '0'; pos += 7; const baseDocumentBranchId = cleanLine.slice(pos, pos + 7).trim(); pos += 7; const reserved3 = cleanLine.slice(pos, pos + 21).trim(); // 21 width // Validate the code field if (code !== 'D110') { throw new Error(`Invalid D110 record code: expected "D110", got "${code}"`); } const parsed = { code, recordNumber, vatId, documentType, documentNumber, lineNumber, baseDocumentType, baseDocumentNumber, transactionType: processedTransactionType, internalCatalogCode, goodsServiceDescription, manufacturerName, serialNumber, unitOfMeasureDescription, quantity, unitPriceExcludingVat, lineDiscount, lineTotal, vatRatePercent, reserved1, branchId, reserved2, documentDate, headerLinkField, baseDocumentBranchId, reserved3, }; // Validate against schema return exports.D110Schema.parse(parsed); }