feat(core): improve in-memory validation, FatturaPA detection coverage, and published type compatibility
This commit is contained in:
@@ -185,7 +185,8 @@ export class ConformanceTestHarness {
|
||||
r.source === 'Schematron'
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn(`Schematron not available for ${sample.format}: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`Schematron not available for ${sample.format}: ${errorMessage}`);
|
||||
}
|
||||
|
||||
// Aggregate results
|
||||
@@ -202,12 +203,13 @@ export class ConformanceTestHarness {
|
||||
result.passed = result.errors.length === 0 === sample.expectedValid;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error testing ${sample.name}: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error(`Error testing ${sample.name}: ${errorMessage}`);
|
||||
result.errors.push({
|
||||
ruleId: 'TEST-ERROR',
|
||||
source: 'TestHarness',
|
||||
severity: 'error',
|
||||
message: `Test execution failed: ${error.message}`
|
||||
message: `Test execution failed: ${errorMessage}`
|
||||
});
|
||||
}
|
||||
|
||||
@@ -588,4 +590,4 @@ export async function runConformanceTests(
|
||||
// Generate HTML report
|
||||
await harness.generateHTMLReport();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ export class EN16931Validator {
|
||||
];
|
||||
|
||||
/**
|
||||
* Validates that an invoice object contains all mandatory EN16931 fields
|
||||
* Collects mandatory EN16931 field errors without throwing.
|
||||
* @param invoice The invoice object to validate
|
||||
* @throws Error if mandatory fields are missing
|
||||
* @returns List of validation error messages
|
||||
*/
|
||||
public static validateMandatoryFields(invoice: any): void {
|
||||
public static collectMandatoryFieldErrors(invoice: any): string[] {
|
||||
const errors: string[] = [];
|
||||
|
||||
// BR-01: Invoice number is mandatory
|
||||
@@ -49,7 +49,7 @@ export class EN16931Validator {
|
||||
errors.push('BR-06: Seller name is mandatory');
|
||||
}
|
||||
|
||||
// BR-07: Buyer name is mandatory
|
||||
// BR-07: Buyer name is mandatory
|
||||
if (!invoice.to?.name) {
|
||||
errors.push('BR-07: Buyer name is mandatory');
|
||||
}
|
||||
@@ -67,11 +67,10 @@ export class EN16931Validator {
|
||||
// BR-05: Invoice currency code is mandatory
|
||||
if (!invoice.currency) {
|
||||
errors.push('BR-05: Invoice currency code is mandatory');
|
||||
} else {
|
||||
// Validate currency format
|
||||
if (!this.VALID_CURRENCIES.includes(invoice.currency.toUpperCase())) {
|
||||
errors.push(`Invalid currency code: ${invoice.currency}. Must be a valid ISO 4217 currency code`);
|
||||
}
|
||||
} else if (!this.VALID_CURRENCIES.includes(invoice.currency.toUpperCase())) {
|
||||
errors.push(
|
||||
`BR-05: Invalid currency code: ${invoice.currency}. Must be a valid ISO 4217 currency code`
|
||||
);
|
||||
}
|
||||
|
||||
// BR-08: Seller postal address is mandatory
|
||||
@@ -84,6 +83,17 @@ export class EN16931Validator {
|
||||
errors.push('BR-10: Buyer postal address (city, postal code, country) is mandatory');
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that an invoice object contains all mandatory EN16931 fields
|
||||
* @param invoice The invoice object to validate
|
||||
* @throws Error if mandatory fields are missing
|
||||
*/
|
||||
public static validateMandatoryFields(invoice: any): void {
|
||||
const errors = this.collectMandatoryFieldErrors(invoice);
|
||||
|
||||
if (errors.length > 0) {
|
||||
throw new Error(`EN16931 validation failed:\n${errors.join('\n')}`);
|
||||
}
|
||||
@@ -132,4 +142,4 @@ export class EN16931Validator {
|
||||
throw new Error(`EN16931 XML validation failed:\n${errors.join('\n')}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,8 @@ export class MainValidator {
|
||||
this.schematronEnabled = true;
|
||||
console.log(`Schematron validation enabled for ${standard} ${format}`);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to initialize Schematron: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`Failed to initialize Schematron: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +122,8 @@ export class MainValidator {
|
||||
);
|
||||
results.push(...schematronResults);
|
||||
} catch (error) {
|
||||
console.warn(`Schematron validation error: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`Schematron validation error: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,4 +404,4 @@ export async function createValidator(
|
||||
}
|
||||
|
||||
// Export for convenience
|
||||
export type { ValidationReport, ValidationResult, ValidationOptions } from './validation.types.js';
|
||||
export type { ValidationReport, ValidationResult, ValidationOptions } from './validation.types.js';
|
||||
|
||||
@@ -139,7 +139,8 @@ export class SchematronDownloader {
|
||||
console.log(`Successfully downloaded: ${fileName}`);
|
||||
return filePath;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to download ${source.name}: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(`Failed to download ${source.name}: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +161,8 @@ export class SchematronDownloader {
|
||||
const path = await this.download(source);
|
||||
paths.push(path);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to download ${source.name}: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`Failed to download ${source.name}: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +211,8 @@ export class SchematronDownloader {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Failed to list cached files: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`Failed to list cached files: ${errorMessage}`);
|
||||
}
|
||||
|
||||
return files;
|
||||
@@ -230,7 +233,8 @@ export class SchematronDownloader {
|
||||
|
||||
console.log('Schematron cache cleared');
|
||||
} catch (error) {
|
||||
console.warn(`Failed to clear cache: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`Failed to clear cache: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,9 +300,10 @@ export async function downloadISOSkeletons(targetDir: string = 'assets_downloade
|
||||
|
||||
console.log(`Downloaded: ${name}`);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to download ${name}: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`Failed to download ${name}: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('ISO Schematron skeleton download complete');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,8 @@ export class IntegratedValidator {
|
||||
});
|
||||
results.push(...schematronResults);
|
||||
} catch (error) {
|
||||
console.warn(`Schematron validation failed: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`Schematron validation failed: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +225,8 @@ export class IntegratedValidator {
|
||||
try {
|
||||
await this.loadSchematron('EN16931', format);
|
||||
} catch (error) {
|
||||
console.warn(`Could not load Schematron: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`Could not load Schematron: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,8 +280,9 @@ export async function createStandardValidator(
|
||||
try {
|
||||
await validator.loadSchematron(standard, format);
|
||||
} catch (error) {
|
||||
console.warn(`Schematron not available for ${standard} ${format}: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`Schematron not available for ${standard} ${format}: ${errorMessage}`);
|
||||
}
|
||||
|
||||
return validator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
import * as SaxonJS from 'saxon-js';
|
||||
import type { ValidationResult } from './validation.types.js';
|
||||
|
||||
/**
|
||||
@@ -30,9 +29,7 @@ export class SchematronValidator {
|
||||
*/
|
||||
public async loadSchematron(source: string, isFilePath: boolean = true): Promise<void> {
|
||||
if (isFilePath) {
|
||||
// Load from file
|
||||
const smartfile = await import('@push.rocks/smartfile');
|
||||
this.schematronRules = await smartfile.SmartFile.fromFilePath(source).then(f => f.contentBuffer.toString());
|
||||
this.schematronRules = await plugins.fs.readFile(source, 'utf-8');
|
||||
} else {
|
||||
// Use provided string
|
||||
this.schematronRules = source;
|
||||
@@ -58,14 +55,15 @@ export class SchematronValidator {
|
||||
const xslt = this.generateXSLTFromSchematron(this.schematronRules);
|
||||
|
||||
// Compile the XSLT with Saxon-JS
|
||||
this.compiledStylesheet = await SaxonJS.compile({
|
||||
this.compiledStylesheet = await plugins.SaxonJS.compile({
|
||||
stylesheetText: xslt,
|
||||
warnings: 'silent'
|
||||
});
|
||||
|
||||
this.isCompiled = true;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to compile Schematron: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(`Failed to compile Schematron: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +85,7 @@ export class SchematronValidator {
|
||||
|
||||
try {
|
||||
// Transform the XML with the compiled Schematron XSLT
|
||||
const transformResult = await SaxonJS.transform({
|
||||
const transformResult = await plugins.SaxonJS.transform({
|
||||
stylesheetInternal: this.compiledStylesheet,
|
||||
sourceText: xmlContent,
|
||||
destination: 'serialized',
|
||||
@@ -108,11 +106,12 @@ export class SchematronValidator {
|
||||
|
||||
return results;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
results.push({
|
||||
ruleId: 'SCHEMATRON-ERROR',
|
||||
source: 'SCHEMATRON',
|
||||
severity: 'error',
|
||||
message: `Schematron validation failed: ${error.message}`,
|
||||
message: `Schematron validation failed: ${errorMessage}`,
|
||||
btReference: undefined,
|
||||
bgReference: undefined
|
||||
});
|
||||
@@ -323,7 +322,8 @@ export class HybridValidator {
|
||||
try {
|
||||
results.push(...validator.validate(xmlContent));
|
||||
} catch (error) {
|
||||
console.warn(`TS validator failed: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`TS validator failed: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +333,8 @@ export class HybridValidator {
|
||||
const schematronResults = await this.schematronValidator.validate(xmlContent, options);
|
||||
results.push(...schematronResults);
|
||||
} catch (error) {
|
||||
console.warn(`Schematron validation failed: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn(`Schematron validation failed: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,4 +346,4 @@ export class HybridValidator {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user