@accounter/shaam6111-generator
Version:
Fully typed application that generates, parses, and validates SHAAM 6111 tax reports.
103 lines (102 loc) • 3.96 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.taxAdjustmentArraySchema = exports.negativeAllowedCodesSet = exports.allowedCodesSet = void 0;
const zod_1 = require("zod");
const index_js_1 = require("../types/index.js");
/**
* Tax Adjustment Record Schema
* Based on the SHAAM 6111 specification (IncomeTax_software-houses-6111-2023.pdf)
*/
// Create a set for faster lookup of allowed codes
exports.allowedCodesSet = new Set(index_js_1.ALLOWED_TAX_ADJUSTMENT_CODES);
exports.negativeAllowedCodesSet = new Set(index_js_1.NEGATIVE_ALLOWED_TAX_ADJUSTMENTS_CODES);
function codeToName(code) {
return `${index_js_1.TAX_ADJUSTMENT_CODE_NAMES[code] ?? 'Unknown code'} (${code})`;
}
// Create the Tax Adjustment record schema
const taxAdjustmentRecordSchema = zod_1.z
.object({
// קוד שדה - Field code
code: zod_1.z
.number()
.int()
.refine(code => exports.allowedCodesSet.has(code), {
message: 'Invalid code for tax adjustment section',
}),
// סכום - Amount
amount: zod_1.z.number().int(),
})
.superRefine((record, ctx) => {
// Validate that non-negative codes don't have negative amounts
if (!exports.negativeAllowedCodesSet.has(record.code) && record.amount < 0) {
ctx.addIssue({
code: zod_1.z.ZodIssueCode.custom,
message: `Code ${codeToName(record.code)} does not allow negative amounts`,
path: ['amount'],
});
}
});
// Define the validation logic for each summary code
const summaryValidations = {
// 100:
// 103:
// 104: validated on the report level
370: {
formula: (map) => {
return ([110, 120, 140, 150, 160, 181, 182, 183, 184, 190, 310, 330, 350, 360]
.map(code => map.get(code) || 0)
.reduce((a, b) => a + b, 0) -
[130, 135, 170, 180, 200, 300, 320]
.map(code => map.get(code) || 0)
.reduce((a, b) => a + b, 0));
},
description: '110+120-130-135+140+150+160-170-180+181+182+183+184+190-200-300+310-320+330+350+360',
},
// 383:
// 400: validated on the report level
500: {
formula: (map) => {
return (map.get(400) || 0) - (map.get(430) || 0) - (map.get(480) || 0) + (map.get(490) || 0);
},
description: '400-430-480+490',
},
// 600:
};
// Schema for an array of Tax Adjustment records
exports.taxAdjustmentArraySchema = zod_1.z
.array(taxAdjustmentRecordSchema)
.max(150)
.superRefine((records, ctx) => {
// Check for unique code values & create a map for faster lookups
const codeToAmountMap = checkForUniqueCodes(records, ctx);
// 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: zod_1.z.ZodIssueCode.custom,
message: `Value for code ${codeToName(code)} should equal the sum: ${validation.description}`,
path: [index, 'amount'],
});
}
}
}
});
function checkForUniqueCodes(records, ctx) {
const codeMap = new Map();
records.map((record, index) => {
if (codeMap.has(record.code)) {
ctx.addIssue({
code: zod_1.z.ZodIssueCode.custom,
message: `Duplicate code ${codeToName(record.code)} found`,
path: [index, 'code'],
});
}
codeMap.set(record.code, record.amount);
});
return codeMap;
}