@accounter/shaam-uniform-format-generator
Version:
Fully typed application that generates, parses, and validates SHAAM uniform format tax reports (INI.TXT and BKMVDATA.TXT).
144 lines (130 loc) • 3.59 kB
text/typescript
/**
* BKMVDATA.TXT file parsing utilities
*/
import {
parseA100,
parseB100,
parseB110,
parseC100,
parseD110,
parseD120,
parseM100,
parseZ900,
} from '../generator/records/index.js';
export interface ParseError {
lineNumber: number;
recordType: string;
message: string;
line: string;
}
export interface UnknownRecord {
lineNumber: number;
recordType: string;
line: string;
}
export interface ParsedDataRecords {
a100: ReturnType<typeof parseA100> | null;
b100: ReturnType<typeof parseB100>[];
b110: ReturnType<typeof parseB110>[];
c100: ReturnType<typeof parseC100>[];
d110: ReturnType<typeof parseD110>[];
d120: ReturnType<typeof parseD120>[];
m100: ReturnType<typeof parseM100>[];
z900: ReturnType<typeof parseZ900> | null;
}
export interface ParseDataFileResult {
records: ParsedDataRecords;
summary: {
totalLines: number;
parsedRecords: number;
failedRecords: number;
unknownRecords: number;
perType: Record<string, number>;
};
errors: ParseError[];
unknownRecords: UnknownRecord[];
}
/**
* Parses BKMVDATA.TXT file content
*
* @param content - Raw BKMVDATA.TXT file content
* @returns Parsed data structure
*/
export function parseDataFile(content: string): ParseDataFileResult {
const lines = content.split(/\r?\n/).map(line => line.replace(/\r$/, ''));
const records = {
a100: null as ReturnType<typeof parseA100> | null,
b100: [] as ReturnType<typeof parseB100>[],
b110: [] as ReturnType<typeof parseB110>[],
c100: [] as ReturnType<typeof parseC100>[],
d110: [] as ReturnType<typeof parseD110>[],
d120: [] as ReturnType<typeof parseD120>[],
m100: [] as ReturnType<typeof parseM100>[],
z900: null as ReturnType<typeof parseZ900> | null,
};
const recordCounts: Record<string, number> = {};
const errors: ParseError[] = [];
const unknownRecords: UnknownRecord[] = [];
for (let index = 0; index < lines.length; index++) {
const line = lines[index];
if (line.trim().length === 0) continue;
if (line.length < 4) continue;
const lineNumber = index + 1;
const recordType = line.slice(0, 4);
try {
switch (recordType) {
case 'A100':
records.a100 = parseA100(line);
break;
case 'B100':
records.b100.push(parseB100(line));
break;
case 'B110':
records.b110.push(parseB110(line));
break;
case 'C100':
records.c100.push(parseC100(line));
break;
case 'D110':
records.d110.push(parseD110(line));
break;
case 'D120':
records.d120.push(parseD120(line));
break;
case 'M100':
records.m100.push(parseM100(line));
break;
case 'Z900':
records.z900 = parseZ900(line);
break;
default:
unknownRecords.push({
lineNumber,
recordType,
line,
});
continue;
}
recordCounts[recordType] = (recordCounts[recordType] || 0) + 1;
} catch (error) {
errors.push({
lineNumber,
recordType,
message: error instanceof Error ? error.message : 'Unknown parse error',
line,
});
}
}
return {
records,
summary: {
totalLines: lines.length,
parsedRecords: Object.values(recordCounts).reduce((sum, count) => sum + count, 0),
failedRecords: errors.length,
unknownRecords: unknownRecords.length,
perType: recordCounts,
},
errors,
unknownRecords,
};
}