UNPKG

@accounter/server

Version:
161 lines • 7.25 kB
import { z } from 'zod'; import { ALLOWED_BALANCE_SHEET_CODES, BALANCE_SHEET_CODE_NAMES, NEGATIVE_ALLOWED_BALANCE_SHEET_CODES, SECTION_1_21, SECTION_1_27, SECTION_1_28, SECTION_1_30, SECTION_1_32, SECTION_1_33, SECTION_1_36, SECTION_1_37, SECTION_1_38, SECTION_1_39, SECTION_1_40, SECTION_1_41, SECTION_1_43, SECTION_1_44, SECTION_1_45, SECTION_1_46, SECTION_1_48, SECTION_1_50, SECTION_1_51, } from '../types/index.js'; // Create a set for faster lookup of allowed codes const allowedCodesSet = new Set(ALLOWED_BALANCE_SHEET_CODES); const negativeAllowedCodesSet = new Set(NEGATIVE_ALLOWED_BALANCE_SHEET_CODES); function codeToName(code) { return `${BALANCE_SHEET_CODE_NAMES[code] ?? 'Unknown code'} (${code})`; } // Zod schema for a single balance sheet record const balanceSheetRecordSchema = z .object({ code: z .number() .int() .superRefine((value, ctx) => { if (!allowedCodesSet.has(value)) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: `Invalid balance sheet code: ${value}`, path: ['code'], }); } }), amount: z.number().int(), }) .superRefine((record, ctx) => { // Validate that non-negative codes don't have negative amounts if (!negativeAllowedCodesSet.has(record.code) && record.amount < 0) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: `Code ${codeToName(record.code)} does not allow negative amounts`, path: ['amount'], }); } }); function cumulativeCodeValidationFormula(code, sectionCodes, subtractSectionCodes) { return (map) => { const sectionAmount = sectionCodes .filter(sectionCode => sectionCode !== code) .map(sectionCode => map.get(sectionCode) || 0) .reduce((a, b) => a + b, 0); const subtractAmount = subtractSectionCodes ? subtractSectionCodes .filter(sectionCode => sectionCode !== code) .map(sectionCode => map.get(sectionCode) || 0) .reduce((a, b) => a + b, 0) : 0; return sectionAmount - subtractAmount; }; } function commonCumulativeCodeValidation(code, sectionCodes, subtractSectionCodes) { return { formula: cumulativeCodeValidationFormula(code, sectionCodes, subtractSectionCodes), description: 'Sum of all subsections', }; } // Define the validation logic for each summary code const summaryValidations = { 7000: commonCumulativeCodeValidation(7000, [7100, 7200, 7300, 7400, 7600, 7700, 7800]), 7100: commonCumulativeCodeValidation(7100, SECTION_1_27), 7200: commonCumulativeCodeValidation(7200, SECTION_1_28), 7300: { formula: cumulativeCodeValidationFormula(7300, [7310, 7320, 7330, 7350, 7360, 7390], [7380]), description: '7310+7320+7330+7350+7360-7380+7390', }, 7400: commonCumulativeCodeValidation(7400, SECTION_1_30), 7600: commonCumulativeCodeValidation(7600, SECTION_1_21), 7700: commonCumulativeCodeValidation(7700, SECTION_1_32), 7800: commonCumulativeCodeValidation(7800, SECTION_1_33), 8000: { formula: cumulativeCodeValidationFormula(8000, [8010, 8020, 8025, 8030, 8040, 8050, 8060, 8080, 8090, 8095, 8100, 8105, 8170], [8110, 8120, 8130, 8140, 8150, 8160, 8180, 8190]), description: '8010+8020+8025+8030+8040+8050+8060+8080+8090+8095+8100+8105-8110-8120-8130-8140-8150-8160+8170-8180-8190', }, 8200: commonCumulativeCodeValidation(8200, SECTION_1_37), 8300: commonCumulativeCodeValidation(8300, SECTION_1_38), 8400: commonCumulativeCodeValidation(8400, SECTION_1_39), 8500: commonCumulativeCodeValidation(8500, SECTION_1_40), 8600: commonCumulativeCodeValidation(8600, SECTION_1_41), 8700: commonCumulativeCodeValidation(8700, SECTION_1_36), 8888: { formula: cumulativeCodeValidationFormula(8888, [7000, 8000, 8700, 8200, 8300, 8400, 8500, 8600]), description: '7000+8000+8700+8200+8300+8400+8500+8600', }, 9000: { formula: cumulativeCodeValidationFormula(9000, [9100, 9200, 9400, 9500]), description: '9100+9200+9400+9500', }, 9100: commonCumulativeCodeValidation(9100, SECTION_1_43), 9200: commonCumulativeCodeValidation(9200, SECTION_1_44), 9400: commonCumulativeCodeValidation(9400, SECTION_1_45), 9500: commonCumulativeCodeValidation(9500, SECTION_1_46), 9600: commonCumulativeCodeValidation(9600, SECTION_1_48), 9700: { formula: cumulativeCodeValidationFormula(9700, [9710, 9790], [9720]), description: '9710-9720+9790', }, 9800: commonCumulativeCodeValidation(9800, SECTION_1_50), 9900: commonCumulativeCodeValidation(9900, SECTION_1_51), 9999: { formula: cumulativeCodeValidationFormula(9999, [9000, 9600, 9700, 9800, 9900]), description: '9000+9600+9700+9800+9900', }, }; // Zod schema for the array of balance sheet records export const balanceSheetArraySchema = z .array(balanceSheetRecordSchema) .max(150) .superRefine((records, ctx) => { // Check for unique code values & create a map for faster lookups const codeToAmountMap = new Map(); records.map((record, i) => { const code = record.code; if (codeToAmountMap.has(code)) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: `Duplicate code: ${codeToName(code)}`, path: [i, 'code'], }); } else { codeToAmountMap.set(code, record.amount); } }); // Validate each summary field with its formula for (const [codeStr, validation] of Object.entries(summaryValidations)) { const code = parseInt(codeStr); if (codeToAmountMap.has(code)) { const actualAmount = codeToAmountMap.get(code); const expectedAmount = validation.formula(codeToAmountMap); if (actualAmount !== expectedAmount) { const index = records.findIndex(r => r.code === code); ctx.addIssue({ code: z.ZodIssueCode.custom, message: `Value for code ${codeToName(code)} should equal the sum: ${validation.description}`, path: [index, 'amount'], }); } } } // Check total assets equals total liabilities + equity const totalAssets = codeToAmountMap.get(8888); const totalLiabilitiesEquity = codeToAmountMap.get(9999); if (totalAssets !== totalLiabilitiesEquity) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: `Total assets (8888) must equal total liabilities + equity (9999). Got: ${totalAssets} vs ${totalLiabilitiesEquity}`, }); } // Check if all required summary codes are present const requiredSummaryCodes = [7000, 8888, 9000, 9999]; for (const code of requiredSummaryCodes) { if (!codeToAmountMap.has(code)) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: `Missing required summary code: ${code}`, }); } } }); //# sourceMappingURL=balance-sheet.js.map