@accounter/server 0.0.9-alpha-20251231123714-6cdd9de71b4672d74ece5d34c438d162987b2c93 → 0.0.9-alpha-20251231171312-67d2850c6eacafb65d42d987f3d49dca3c0f8bdd
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -5
- package/README.md +66 -3
- package/dist/green-invoice-graphql/src/mesh-artifacts/index.d.ts +1 -1
- package/dist/green-invoice-graphql/src/mesh-artifacts/index.js +2 -2
- package/dist/green-invoice-graphql/src/mesh-artifacts/index.js.map +1 -1
- package/dist/server/scripts/seed-admin-context.js +20 -25
- package/dist/server/scripts/seed-admin-context.js.map +1 -1
- package/dist/server/src/__tests__/db-bootstrap.test.js +7 -2
- package/dist/server/src/__tests__/db-bootstrap.test.js.map +1 -1
- package/dist/server/src/__tests__/factories/business.d.ts +1 -1
- package/dist/server/src/__tests__/factories/financial-account.d.ts +1 -1
- package/dist/server/src/__tests__/factories/index.test.js +1 -0
- package/dist/server/src/__tests__/factories/index.test.js.map +1 -1
- package/dist/server/src/__tests__/factories/tax-category.d.ts +1 -1
- package/dist/server/src/__tests__/factories/tax-category.js +1 -1
- package/dist/server/src/__tests__/factories/tax-category.js.map +1 -1
- package/dist/server/src/__tests__/factories/tax-category.test.js +8 -6
- package/dist/server/src/__tests__/factories/tax-category.test.js.map +1 -1
- package/dist/server/src/__tests__/helpers/fixture-loader.d.ts +1 -1
- package/dist/server/src/__tests__/helpers/fixture-loader.js +25 -52
- package/dist/server/src/__tests__/helpers/fixture-loader.js.map +1 -1
- package/dist/server/src/__tests__/helpers/migration-verification.d.ts +1 -1
- package/dist/server/src/__tests__/helpers/migration-verification.js.map +1 -1
- package/dist/server/src/__tests__/helpers/seed-helpers.business.test.js +4 -4
- package/dist/server/src/__tests__/helpers/seed-helpers.business.test.js.map +1 -1
- package/dist/server/src/__tests__/helpers/seed-helpers.d.ts +9 -9
- package/dist/server/src/__tests__/helpers/seed-helpers.js +57 -54
- package/dist/server/src/__tests__/helpers/seed-helpers.js.map +1 -1
- package/dist/server/src/__tests__/seed-admin-context.integration.test.js +2 -1
- package/dist/server/src/__tests__/seed-admin-context.integration.test.js.map +1 -1
- package/dist/server/src/demo-fixtures/__tests__/deterministic-uuid.test.js +3 -2
- package/dist/server/src/demo-fixtures/__tests__/deterministic-uuid.test.js.map +1 -1
- package/dist/server/src/demo-fixtures/__tests__/seed-and-validate.test.d.ts +1 -0
- package/dist/server/src/demo-fixtures/__tests__/seed-and-validate.test.js +69 -0
- package/dist/server/src/demo-fixtures/__tests__/seed-and-validate.test.js.map +1 -0
- package/dist/server/src/demo-fixtures/__tests__/use-case-registry.test.d.ts +1 -0
- package/dist/server/src/demo-fixtures/__tests__/use-case-registry.test.js +26 -0
- package/dist/server/src/demo-fixtures/__tests__/use-case-registry.test.js.map +1 -0
- package/dist/server/src/demo-fixtures/helpers/admin-context.d.ts +10 -0
- package/dist/server/src/demo-fixtures/helpers/admin-context.js +40 -0
- package/dist/server/src/demo-fixtures/helpers/admin-context.js.map +1 -0
- package/dist/server/src/demo-fixtures/helpers/placeholder.d.ts +45 -0
- package/dist/server/src/demo-fixtures/helpers/placeholder.js +50 -0
- package/dist/server/src/demo-fixtures/helpers/placeholder.js.map +1 -0
- package/dist/server/src/demo-fixtures/helpers/seed-exchange-rates.d.ts +21 -0
- package/dist/server/src/demo-fixtures/helpers/seed-exchange-rates.js +26 -0
- package/dist/server/src/demo-fixtures/helpers/seed-exchange-rates.js.map +1 -0
- package/dist/server/src/demo-fixtures/helpers/seed-vat.d.ts +20 -0
- package/dist/server/src/demo-fixtures/helpers/seed-vat.js +30 -0
- package/dist/server/src/demo-fixtures/helpers/seed-vat.js.map +1 -0
- package/dist/server/src/demo-fixtures/use-cases/equity/shareholder-dividend.d.ts +9 -0
- package/dist/server/src/demo-fixtures/use-cases/equity/shareholder-dividend.js +86 -0
- package/dist/server/src/demo-fixtures/use-cases/equity/shareholder-dividend.js.map +1 -0
- package/dist/server/src/demo-fixtures/use-cases/expenses/monthly-expense-foreign-currency.d.ts +12 -0
- package/dist/server/src/demo-fixtures/use-cases/expenses/monthly-expense-foreign-currency.js +375 -0
- package/dist/server/src/demo-fixtures/use-cases/expenses/monthly-expense-foreign-currency.js.map +1 -0
- package/dist/server/src/demo-fixtures/use-cases/income/client-payment-with-refund.d.ts +10 -0
- package/dist/server/src/demo-fixtures/use-cases/income/client-payment-with-refund.js +113 -0
- package/dist/server/src/demo-fixtures/use-cases/income/client-payment-with-refund.js.map +1 -0
- package/dist/server/src/demo-fixtures/use-cases/index.d.ts +41 -0
- package/dist/server/src/demo-fixtures/use-cases/index.js +50 -0
- package/dist/server/src/demo-fixtures/use-cases/index.js.map +1 -0
- package/dist/server/src/demo-fixtures/validate-demo-data.d.ts +1 -0
- package/dist/server/src/demo-fixtures/validate-demo-data.js +117 -0
- package/dist/server/src/demo-fixtures/validate-demo-data.js.map +1 -0
- package/dist/server/src/demo-fixtures/validators/ledger-validators.d.ts +349 -0
- package/dist/server/src/demo-fixtures/validators/ledger-validators.js +602 -0
- package/dist/server/src/demo-fixtures/validators/ledger-validators.js.map +1 -0
- package/dist/server/src/demo-fixtures/validators/ledger-validators.test.d.ts +1 -0
- package/dist/server/src/demo-fixtures/validators/ledger-validators.test.js +247 -0
- package/dist/server/src/demo-fixtures/validators/ledger-validators.test.js.map +1 -0
- package/dist/server/src/demo-fixtures/validators/types.d.ts +69 -0
- package/dist/server/src/demo-fixtures/validators/types.js +8 -0
- package/dist/server/src/demo-fixtures/validators/types.js.map +1 -0
- package/dist/server/src/fixtures/fixture-spec.d.ts +146 -0
- package/dist/server/src/fixtures/fixture-spec.js +2 -0
- package/dist/server/src/fixtures/fixture-spec.js.map +1 -0
- package/dist/server/src/modules/charges-matcher/__tests__/single-match-integration.test.js +4 -0
- package/dist/server/src/modules/charges-matcher/__tests__/single-match-integration.test.js.map +1 -1
- package/dist/server/src/modules/ledger/__tests__/ledger-scenario-a.integration.test.js +4 -3
- package/dist/server/src/modules/ledger/__tests__/ledger-scenario-a.integration.test.js.map +1 -1
- package/dist/server/src/modules/ledger/__tests__/ledger-scenario-b.integration.test.js +5 -3
- package/dist/server/src/modules/ledger/__tests__/ledger-scenario-b.integration.test.js.map +1 -1
- package/dist/server/src/shared/constants.d.ts +1 -0
- package/dist/server/src/shared/constants.js +1 -0
- package/dist/server/src/shared/constants.js.map +1 -1
- package/dist/server/src/shared/helpers/misc.js +2 -2
- package/dist/server/src/shared/helpers/misc.js.map +1 -1
- package/docs/demo-staging-guide.md +611 -0
- package/package.json +5 -2
- package/scripts/seed-admin-context.ts +22 -33
- package/src/__tests__/db-bootstrap.test.ts +9 -2
- package/src/__tests__/factories/business.ts +1 -1
- package/src/__tests__/factories/financial-account.ts +1 -1
- package/src/__tests__/factories/index.test.ts +1 -0
- package/src/__tests__/factories/tax-category.test.ts +8 -6
- package/src/__tests__/factories/tax-category.ts +2 -2
- package/src/__tests__/helpers/fixture-loader.ts +26 -61
- package/src/__tests__/helpers/migration-verification.ts +2 -2
- package/src/__tests__/helpers/seed-helpers.business.test.ts +4 -4
- package/src/__tests__/helpers/seed-helpers.ts +66 -75
- package/src/__tests__/seed-admin-context.integration.test.ts +2 -1
- package/src/demo-fixtures/__tests__/deterministic-uuid.test.ts +3 -2
- package/src/demo-fixtures/__tests__/seed-and-validate.test.ts +96 -0
- package/src/demo-fixtures/__tests__/use-case-registry.test.ts +27 -0
- package/src/demo-fixtures/helpers/admin-context.ts +59 -0
- package/src/demo-fixtures/helpers/placeholder.ts +50 -0
- package/src/demo-fixtures/helpers/seed-exchange-rates.ts +29 -0
- package/src/demo-fixtures/helpers/seed-vat.ts +35 -0
- package/src/demo-fixtures/use-cases/equity/shareholder-dividend.ts +88 -0
- package/src/demo-fixtures/use-cases/expenses/monthly-expense-foreign-currency.ts +377 -0
- package/src/demo-fixtures/use-cases/income/client-payment-with-refund.ts +115 -0
- package/src/demo-fixtures/use-cases/index.ts +52 -0
- package/src/demo-fixtures/validate-demo-data.ts +153 -0
- package/src/demo-fixtures/validators/README.md +190 -0
- package/src/demo-fixtures/validators/ledger-validators.test.ts +298 -0
- package/src/demo-fixtures/validators/ledger-validators.ts +711 -0
- package/src/demo-fixtures/validators/types.ts +83 -0
- package/src/fixtures/fixture-spec.ts +158 -0
- package/src/modules/charges-matcher/__tests__/single-match-integration.test.ts +6 -0
- package/src/modules/ledger/__tests__/ledger-scenario-a.integration.test.ts +4 -3
- package/src/modules/ledger/__tests__/ledger-scenario-b.integration.test.ts +6 -3
- package/src/shared/constants.ts +2 -0
- package/src/shared/helpers/misc.ts +2 -3
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demo Data Validation Script
|
|
3
|
+
*
|
|
4
|
+
* Validates the integrity of seeded demo data with comprehensive ledger validation.
|
|
5
|
+
*
|
|
6
|
+
* Validation Checks:
|
|
7
|
+
* - Admin business entity exists
|
|
8
|
+
* - Charge count matches expected
|
|
9
|
+
* - Ledger records for each use-case:
|
|
10
|
+
* - Per-record internal balance (FR1)
|
|
11
|
+
* - Aggregate balance (FR2)
|
|
12
|
+
* - Entity-level balance (FR3)
|
|
13
|
+
* - No orphaned amounts (FR4)
|
|
14
|
+
* - Positive amounts only (FR5)
|
|
15
|
+
* - Foreign currency consistency (FR6)
|
|
16
|
+
* - Valid dates (FR7)
|
|
17
|
+
* - Record count matches (FR8)
|
|
18
|
+
*
|
|
19
|
+
* Exit codes:
|
|
20
|
+
* - 0: All validations passed
|
|
21
|
+
* - 1: Validation errors found or database connection failed
|
|
22
|
+
*/
|
|
23
|
+
import { config } from 'dotenv';
|
|
24
|
+
import pg from 'pg';
|
|
25
|
+
import { getAllUseCases } from './use-cases/index.js';
|
|
26
|
+
import { validateLedgerRecords } from './validators/ledger-validators.js';
|
|
27
|
+
config({
|
|
28
|
+
path: '../../.env',
|
|
29
|
+
});
|
|
30
|
+
const DEFAULT_CURRENCY = 'ILS';
|
|
31
|
+
const BALANCE_TOLERANCE = 0.005;
|
|
32
|
+
/**
|
|
33
|
+
* Main validation function - connects to DB and runs all checks
|
|
34
|
+
*/
|
|
35
|
+
async function validateDemoData() {
|
|
36
|
+
const client = new pg.Client({
|
|
37
|
+
user: process.env.POSTGRES_USER,
|
|
38
|
+
password: process.env.POSTGRES_PASSWORD,
|
|
39
|
+
host: process.env.POSTGRES_HOST,
|
|
40
|
+
port: parseInt(process.env.POSTGRES_PORT || '5432'),
|
|
41
|
+
database: process.env.POSTGRES_DB,
|
|
42
|
+
ssl: process.env.POSTGRES_SSL === '1',
|
|
43
|
+
});
|
|
44
|
+
const errors = [];
|
|
45
|
+
try {
|
|
46
|
+
await client.connect();
|
|
47
|
+
// 1. Admin business exists
|
|
48
|
+
const adminCheck = await client.query(`SELECT id FROM accounter_schema.financial_entities WHERE type = 'business' AND name = 'Accounter Admin Business'`);
|
|
49
|
+
if (adminCheck.rows.length === 0) {
|
|
50
|
+
errors.push('Admin business entity missing');
|
|
51
|
+
}
|
|
52
|
+
// 2. Use-case charge count reconciliation
|
|
53
|
+
const useCases = getAllUseCases();
|
|
54
|
+
const expectedChargeCount = useCases.reduce((sum, uc) => sum + uc.fixtures.charges.length, 0);
|
|
55
|
+
const actualChargeCount = await client.query(`SELECT COUNT(*) FROM accounter_schema.charges`);
|
|
56
|
+
if (parseInt(actualChargeCount.rows[0].count) !== expectedChargeCount) {
|
|
57
|
+
errors.push(`Charge count mismatch: expected ${expectedChargeCount}, got ${actualChargeCount.rows[0].count}`);
|
|
58
|
+
}
|
|
59
|
+
// 3. Comprehensive ledger validation for all use-cases with expectations (FR9)
|
|
60
|
+
const useCasesWithExpectations = useCases.filter(uc => uc.expectations);
|
|
61
|
+
console.log(`\nValidating ledger records for ${useCasesWithExpectations.length} use-case(s)...`);
|
|
62
|
+
let ledgerRecordsExist = false;
|
|
63
|
+
for (const useCase of useCasesWithExpectations) {
|
|
64
|
+
// Get all charges for this use-case
|
|
65
|
+
const chargeIds = useCase.fixtures.charges.map(c => c.id);
|
|
66
|
+
// Fetch all ledger records for these charges
|
|
67
|
+
const ledgerRecords = await client.query(`SELECT * FROM accounter_schema.ledger_records WHERE charge_id = ANY($1) ORDER BY created_at`, [chargeIds]);
|
|
68
|
+
if (ledgerRecords.rows.length === 0) {
|
|
69
|
+
// Ledger generation is separate from seeding - this is expected
|
|
70
|
+
console.log(` ⚠ ${useCase.id}: no ledger records (ledger generation not run)`);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
ledgerRecordsExist = true;
|
|
74
|
+
// Create validation context
|
|
75
|
+
const context = {
|
|
76
|
+
useCaseId: useCase.id,
|
|
77
|
+
defaultCurrency: DEFAULT_CURRENCY,
|
|
78
|
+
tolerance: BALANCE_TOLERANCE,
|
|
79
|
+
};
|
|
80
|
+
// Run comprehensive validation
|
|
81
|
+
const validationErrors = validateLedgerRecords(ledgerRecords.rows, useCase.expectations.ledgerRecordCount, context);
|
|
82
|
+
errors.push(...validationErrors);
|
|
83
|
+
// Log progress
|
|
84
|
+
if (validationErrors.length === 0) {
|
|
85
|
+
console.log(` ✓ ${useCase.id} (${ledgerRecords.rows.length} records)`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
console.log(` ✗ ${useCase.id} (${validationErrors.length} error(s))`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!ledgerRecordsExist) {
|
|
92
|
+
console.log(' ℹ️ Ledger records not found. Run ledger generation to create and validate ledger entries.');
|
|
93
|
+
}
|
|
94
|
+
// 4. VAT row present (percentage stored as decimal 0.17 for 17%)
|
|
95
|
+
const vatCheck = await client.query(`SELECT 1 FROM accounter_schema.vat_value WHERE percentage = 0.17`);
|
|
96
|
+
if (vatCheck.rows.length === 0) {
|
|
97
|
+
errors.push('VAT default (17%) missing');
|
|
98
|
+
}
|
|
99
|
+
// Report errors or success
|
|
100
|
+
if (errors.length > 0) {
|
|
101
|
+
console.error('❌ Validation failed:');
|
|
102
|
+
for (const err of errors)
|
|
103
|
+
console.error(` - ${err}`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
console.log('✅ Demo data validation passed');
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
console.error('❌ Validation error:', error);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
await client.end();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
validateDemoData();
|
|
117
|
+
//# sourceMappingURL=validate-demo-data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-demo-data.js","sourceRoot":"","sources":["../../../../src/demo-fixtures/validate-demo-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAG1E,MAAM,CAAC;IACL,IAAI,EAAE,YAAY;CACnB,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAC/B,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEhC;;GAEG;AACH,KAAK,UAAU,gBAAgB;IAC7B,MAAM,MAAM,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC;QAC3B,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa;QAC/B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;QACvC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa;QAC/B,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,MAAM,CAAC;QACnD,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW;QACjC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,GAAG;KACtC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QAEvB,2BAA2B;QAC3B,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,KAAK,CACnC,kHAAkH,CACnH,CAAC;QACF,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,MAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC/C,CAAC;QAED,0CAA0C;QAC1C,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;QAClC,MAAM,mBAAmB,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC9F,MAAM,iBAAiB,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAC9F,IAAI,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,mBAAmB,EAAE,CAAC;YACtE,MAAM,CAAC,IAAI,CACT,mCAAmC,mBAAmB,SAAS,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CACjG,CAAC;QACJ,CAAC;QAED,+EAA+E;QAC/E,MAAM,wBAAwB,GAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC;QAExE,OAAO,CAAC,GAAG,CACT,mCAAmC,wBAAwB,CAAC,MAAM,iBAAiB,CACpF,CAAC;QAEF,IAAI,kBAAkB,GAAG,KAAK,CAAC;QAE/B,KAAK,MAAM,OAAO,IAAI,wBAAwB,EAAE,CAAC;YAC/C,oCAAoC;YACpC,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAE1D,6CAA6C;YAC7C,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,KAAK,CACtC,6FAA6F,EAC7F,CAAC,SAAS,CAAC,CACZ,CAAC;YAEF,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACpC,gEAAgE;gBAChE,OAAO,CAAC,GAAG,CAAC,OAAO,OAAO,CAAC,EAAE,iDAAiD,CAAC,CAAC;gBAChF,SAAS;YACX,CAAC;YAED,kBAAkB,GAAG,IAAI,CAAC;YAE1B,4BAA4B;YAC5B,MAAM,OAAO,GAAsB;gBACjC,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,eAAe,EAAE,gBAAgB;gBACjC,SAAS,EAAE,iBAAiB;aAC7B,CAAC;YAEF,+BAA+B;YAC/B,MAAM,gBAAgB,GAAG,qBAAqB,CAC5C,aAAa,CAAC,IAAI,EAClB,OAAO,CAAC,YAAa,CAAC,iBAAiB,EACvC,OAAO,CACR,CAAC;YAEF,MAAM,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,CAAC;YAEjC,eAAe;YACf,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAClC,OAAO,CAAC,GAAG,CAAC,OAAO,OAAO,CAAC,EAAE,KAAK,aAAa,CAAC,IAAI,CAAC,MAAM,WAAW,CAAC,CAAC;YAC1E,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,OAAO,OAAO,CAAC,EAAE,KAAK,gBAAgB,CAAC,MAAM,YAAY,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QAED,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CACT,8FAA8F,CAC/F,CAAC;QACJ,CAAC;QAED,iEAAiE;QACjE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CACjC,kEAAkE,CACnE,CAAC;QACF,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC3C,CAAC;QAED,2BAA2B;QAC3B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;YACtC,KAAK,MAAM,GAAG,IAAI,MAAM;gBAAE,OAAO,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;YACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;QAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;YAAS,CAAC;QACT,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC;IACrB,CAAC;AACH,CAAC;AAED,gBAAgB,EAAE,CAAC"}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import type { LedgerRecord, ValidationContext } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parse a numeric amount from a string, handling null/undefined values
|
|
4
|
+
*
|
|
5
|
+
* PostgreSQL returns numeric values as strings when queried via pg driver.
|
|
6
|
+
* This utility safely converts them to numbers for arithmetic operations.
|
|
7
|
+
*
|
|
8
|
+
* @param value - The string value to parse (may be null or undefined)
|
|
9
|
+
* @returns The parsed number, or 0 if value is null/undefined/invalid
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* parseAmount('100.50') // 100.50
|
|
13
|
+
* parseAmount(null) // 0
|
|
14
|
+
* parseAmount('invalid') // 0
|
|
15
|
+
*/
|
|
16
|
+
export declare function parseAmount(value: string | null | undefined): number;
|
|
17
|
+
/**
|
|
18
|
+
* Check if two numeric values are balanced within a tolerance
|
|
19
|
+
*
|
|
20
|
+
* Uses tolerance-based comparison to handle floating-point arithmetic
|
|
21
|
+
* imprecision. Two values are considered balanced if their absolute
|
|
22
|
+
* difference is less than or equal to the tolerance.
|
|
23
|
+
*
|
|
24
|
+
* @param a - First value to compare
|
|
25
|
+
* @param b - Second value to compare
|
|
26
|
+
* @param tolerance - Maximum acceptable difference (default: 0.005)
|
|
27
|
+
* @returns True if values are balanced within tolerance, false otherwise
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* isBalanced(100, 100) // true
|
|
31
|
+
* isBalanced(100, 100.004, 0.005) // true
|
|
32
|
+
* isBalanced(100, 102, 0.005) // false
|
|
33
|
+
*/
|
|
34
|
+
export declare function isBalanced(a: number, b: number, tolerance?: number): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Validate per-record internal balance (FR1)
|
|
37
|
+
*
|
|
38
|
+
* Ensures each ledger record is internally balanced according to double-entry
|
|
39
|
+
* bookkeeping principles: total debits must equal total credits within tolerance.
|
|
40
|
+
* Also detects empty records where all amounts are zero.
|
|
41
|
+
*
|
|
42
|
+
* This is a fundamental validation that must pass for every record individually
|
|
43
|
+
* before aggregate-level validations can be meaningful.
|
|
44
|
+
*
|
|
45
|
+
* @param records - Array of ledger records to validate
|
|
46
|
+
* @param context - Validation context containing use-case ID and tolerance
|
|
47
|
+
* @returns Array of error messages (empty if all records are valid)
|
|
48
|
+
*
|
|
49
|
+
* Functional Requirement: FR1 - Per-Record Internal Balance
|
|
50
|
+
* - Rule: (debit_local_amount1 + debit_local_amount2) == (credit_local_amount1 + credit_local_amount2)
|
|
51
|
+
* - Tolerance: Specified in context (typically ±0.005 for accounting rounding)
|
|
52
|
+
* - Also implements FR10: Empty Ledger Detection
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* const errors = validateRecordInternalBalance(records, {
|
|
56
|
+
* useCaseId: 'monthly-expense',
|
|
57
|
+
* defaultCurrency: 'ILS',
|
|
58
|
+
* tolerance: 0.005
|
|
59
|
+
* });
|
|
60
|
+
* // Returns: [] if valid, or error messages like:
|
|
61
|
+
* // ["monthly-expense - Record 0 (uuid-123): internal imbalance (debit=100.00, credit=99.98)"]
|
|
62
|
+
*/
|
|
63
|
+
export declare function validateRecordInternalBalance(records: LedgerRecord[], context: ValidationContext): string[];
|
|
64
|
+
/**
|
|
65
|
+
* Validate aggregate balance across all records (FR2)
|
|
66
|
+
*
|
|
67
|
+
* Validates that the sum of all debits equals the sum of all credits across
|
|
68
|
+
* all ledger records for a use-case. This is the second level of validation
|
|
69
|
+
* (after per-record balance) and ensures the entire ledger set balances.
|
|
70
|
+
*
|
|
71
|
+
* This refactors and enhances the existing aggregate balance validation logic
|
|
72
|
+
* that was previously only applied to a single use-case. The new implementation
|
|
73
|
+
* applies to all use-cases with expectations.
|
|
74
|
+
*
|
|
75
|
+
* @param records - Array of ledger records to validate
|
|
76
|
+
* @param context - Validation context containing use-case ID and tolerance
|
|
77
|
+
* @returns Array of error messages (empty if aggregate is balanced)
|
|
78
|
+
*
|
|
79
|
+
* Functional Requirement: FR2 - Aggregate Balance Validation
|
|
80
|
+
* - Rule: Σ(all debits) == Σ(all credits)
|
|
81
|
+
* - Tolerance: Specified in context (typically ±0.005)
|
|
82
|
+
* - Enhancement: Now applies to ALL use-cases, not just first one
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* const errors = validateAggregateBalance(records, {
|
|
86
|
+
* useCaseId: 'monthly-expense',
|
|
87
|
+
* defaultCurrency: 'ILS',
|
|
88
|
+
* tolerance: 0.005
|
|
89
|
+
* });
|
|
90
|
+
* // Returns: [] if valid, or error like:
|
|
91
|
+
* // ["monthly-expense: aggregate ledger not balanced (debit 1000.00, credit 999.50)"]
|
|
92
|
+
*/
|
|
93
|
+
export declare function validateAggregateBalance(records: LedgerRecord[], context: ValidationContext): string[];
|
|
94
|
+
/**
|
|
95
|
+
* Validate entity-level balance (FR3)
|
|
96
|
+
*
|
|
97
|
+
* Validates that each financial entity's net position across all ledger records
|
|
98
|
+
* balances to zero (or within tolerance). This is the third level of validation
|
|
99
|
+
* in the hierarchy:
|
|
100
|
+
* 1. Per-record balance (FR1) - each record internally balanced
|
|
101
|
+
* 2. Aggregate balance (FR2) - all records collectively balanced
|
|
102
|
+
* 3. Entity balance (FR3) - each entity's position balanced across records
|
|
103
|
+
*
|
|
104
|
+
* In double-entry bookkeeping, every entity that appears in the ledger should
|
|
105
|
+
* have a net zero position when considering all transactions. If an entity has
|
|
106
|
+
* debits totaling $500 across various records, it should also have credits
|
|
107
|
+
* totaling $500 across those same or other records.
|
|
108
|
+
*
|
|
109
|
+
* @param records - Array of ledger records to validate
|
|
110
|
+
* @param context - Validation context containing use-case ID and tolerance
|
|
111
|
+
* @returns Array of error messages (empty if all entities are balanced)
|
|
112
|
+
*
|
|
113
|
+
* Functional Requirement: FR3 - Entity-Level Balance Validation
|
|
114
|
+
* - Rule: For each entity, Σ(debits) - Σ(credits) ≈ 0
|
|
115
|
+
* - Tolerance: Specified in context (typically ±0.005)
|
|
116
|
+
* - Tracks: debit/credit amounts across all 4 entity fields per record
|
|
117
|
+
*
|
|
118
|
+
* Implementation:
|
|
119
|
+
* - Accumulates debits and credits per entity across all records
|
|
120
|
+
* - Calculates net balance (totalDebit - totalCredit) for each entity
|
|
121
|
+
* - Validates net balance is within tolerance of zero
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* const errors = validateEntityBalance(records, {
|
|
125
|
+
* useCaseId: 'monthly-expense',
|
|
126
|
+
* defaultCurrency: 'ILS',
|
|
127
|
+
* tolerance: 0.005
|
|
128
|
+
* });
|
|
129
|
+
* // Returns: [] if valid, or errors like:
|
|
130
|
+
* // ["monthly-expense: Entity entity-123 unbalanced (net=50.00, debit=150.00, credit=100.00, records=3)"]
|
|
131
|
+
*/
|
|
132
|
+
export declare function validateEntityBalance(records: LedgerRecord[], context: ValidationContext): string[];
|
|
133
|
+
/**
|
|
134
|
+
* Validate ledger record count (FR8)
|
|
135
|
+
*
|
|
136
|
+
* Validates that the actual number of ledger records matches the expected count
|
|
137
|
+
* specified in the use-case expectations. This ensures data completeness and
|
|
138
|
+
* detects cases where records may be missing or extra records were created.
|
|
139
|
+
*
|
|
140
|
+
* This enhances the existing record count validation by applying it to all
|
|
141
|
+
* use-cases systematically rather than ad-hoc checks.
|
|
142
|
+
*
|
|
143
|
+
* @param records - Array of ledger records to validate
|
|
144
|
+
* @param expectedCount - Expected number of ledger records for this use-case
|
|
145
|
+
* @param context - Validation context containing use-case ID
|
|
146
|
+
* @returns Array of error messages (empty if count matches)
|
|
147
|
+
*
|
|
148
|
+
* Functional Requirement: FR8 - Record Count Validation
|
|
149
|
+
* - Rule: Actual record count must match expected count exactly
|
|
150
|
+
* - Enhancement: Future support for minimum count validation for cases
|
|
151
|
+
* where ledger generation may create additional balancing entries
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* const errors = validateRecordCount(records, 24, {
|
|
155
|
+
* useCaseId: 'monthly-expense',
|
|
156
|
+
* defaultCurrency: 'ILS',
|
|
157
|
+
* tolerance: 0.005
|
|
158
|
+
* });
|
|
159
|
+
* // Returns: [] if count matches, or error like:
|
|
160
|
+
* // ["monthly-expense: ledger record count mismatch (expected 24, got 23)"]
|
|
161
|
+
*/
|
|
162
|
+
export declare function validateRecordCount(records: LedgerRecord[], expectedCount: number, context: ValidationContext): string[];
|
|
163
|
+
/**
|
|
164
|
+
* Validate all amounts are positive (FR5)
|
|
165
|
+
*
|
|
166
|
+
* Ensures data integrity by validating that all amount fields contain
|
|
167
|
+
* non-negative values. Negative amounts are not allowed in the ledger
|
|
168
|
+
* system as they violate accounting principles where debits and credits
|
|
169
|
+
* must always be positive or zero.
|
|
170
|
+
*
|
|
171
|
+
* This validation checks all 8 amount fields per record:
|
|
172
|
+
* - Local amounts: debit_local_amount1/2, credit_local_amount1/2
|
|
173
|
+
* - Foreign amounts: debit_foreign_amount1/2, credit_foreign_amount1/2
|
|
174
|
+
*
|
|
175
|
+
* @param records - Array of ledger records to validate
|
|
176
|
+
* @param context - Validation context containing use-case ID
|
|
177
|
+
* @returns Array of error messages (empty if all amounts are non-negative)
|
|
178
|
+
*
|
|
179
|
+
* Functional Requirement: FR5 - Positive Amount Validation
|
|
180
|
+
* - Checks all amount fields for negative values
|
|
181
|
+
* - Reports specific field and value for any negative amounts found
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* const errors = validatePositiveAmounts(records, {
|
|
185
|
+
* useCaseId: 'monthly-expense',
|
|
186
|
+
* defaultCurrency: 'ILS',
|
|
187
|
+
* tolerance: 0.005
|
|
188
|
+
* });
|
|
189
|
+
* // Returns: [] if valid, or errors like:
|
|
190
|
+
* // ["monthly-expense - Record 0 (uuid-123): negative amount in debit_local_amount1 (-100.00)"]
|
|
191
|
+
*/
|
|
192
|
+
export declare function validatePositiveAmounts(records: LedgerRecord[], context: ValidationContext): string[];
|
|
193
|
+
/**
|
|
194
|
+
* Validate dates (FR7)
|
|
195
|
+
*
|
|
196
|
+
* Ensures all ledger records have valid invoice_date and value_date fields
|
|
197
|
+
* within acceptable ranges. Proper date validation is critical for:
|
|
198
|
+
* - Financial reporting accuracy
|
|
199
|
+
* - Tax compliance and audit trails
|
|
200
|
+
* - Chronological transaction ordering
|
|
201
|
+
* - Preventing data entry errors
|
|
202
|
+
*
|
|
203
|
+
* Validation rules:
|
|
204
|
+
* - Both invoice_date and value_date must be present (not null)
|
|
205
|
+
* - Both dates must be valid Date objects (not NaN)
|
|
206
|
+
* - Both dates must fall within the range 2020-01-01 to 2030-12-31
|
|
207
|
+
*
|
|
208
|
+
* @param records - Array of ledger records to validate
|
|
209
|
+
* @param context - Validation context containing use-case ID
|
|
210
|
+
* @returns Array of error messages (empty if all dates are valid)
|
|
211
|
+
*
|
|
212
|
+
* Functional Requirement: FR7 - Date Validation
|
|
213
|
+
* - Checks for missing dates (null values)
|
|
214
|
+
* - Checks for invalid dates (parse errors)
|
|
215
|
+
* - Checks for dates outside reasonable business range
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* const errors = validateDates(records, {
|
|
219
|
+
* useCaseId: 'monthly-expense',
|
|
220
|
+
* defaultCurrency: 'ILS',
|
|
221
|
+
* tolerance: 0.005
|
|
222
|
+
* });
|
|
223
|
+
* // Returns: [] if valid, or errors like:
|
|
224
|
+
* // ["monthly-expense - Record 0 (uuid-123): missing invoice_date"]
|
|
225
|
+
* // ["monthly-expense - Record 1 (uuid-456): invoice_date out of range (1999-01-01T00:00:00.000Z)"]
|
|
226
|
+
*/
|
|
227
|
+
export declare function validateDates(records: LedgerRecord[], context: ValidationContext): string[];
|
|
228
|
+
/**
|
|
229
|
+
* Validate foreign currency handling (FR6)
|
|
230
|
+
*
|
|
231
|
+
* Ensures proper handling of foreign currency transactions by validating:
|
|
232
|
+
* 1. Currency field consistency with foreign amount fields
|
|
233
|
+
* 2. Presence of foreign amounts when currency is not the default (ILS)
|
|
234
|
+
* 3. Absence of foreign amounts when currency is the default (ILS)
|
|
235
|
+
* 4. Reasonableness of implied exchange rates between local and foreign amounts
|
|
236
|
+
*
|
|
237
|
+
* Foreign currency validation is critical for:
|
|
238
|
+
* - Accurate financial reporting in multi-currency environments
|
|
239
|
+
* - Compliance with international accounting standards
|
|
240
|
+
* - Detection of data entry errors in currency conversion
|
|
241
|
+
* - Prevention of fraudulent or suspicious exchange rate manipulation
|
|
242
|
+
*
|
|
243
|
+
* Exchange rate validation:
|
|
244
|
+
* - Implied rate = local_amount / foreign_amount
|
|
245
|
+
* - Rate must be between 0.1 and 10.0 to be considered reasonable
|
|
246
|
+
* - Rates outside this range likely indicate data entry errors
|
|
247
|
+
*
|
|
248
|
+
* @param records - Array of ledger records to validate
|
|
249
|
+
* @param context - Validation context containing use-case ID and default currency
|
|
250
|
+
* @returns Array of error messages (empty if all currency handling is valid)
|
|
251
|
+
*
|
|
252
|
+
* Functional Requirement: FR6 - Foreign Currency Validation
|
|
253
|
+
* - Validates currency field matches foreign amount presence/absence
|
|
254
|
+
* - Checks exchange rate consistency within reasonable bounds
|
|
255
|
+
* - Applies to all 4 amount pairs (debit1, debit2, credit1, credit2)
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* const errors = validateForeignCurrency(records, {
|
|
259
|
+
* useCaseId: 'monthly-expense',
|
|
260
|
+
* defaultCurrency: 'ILS',
|
|
261
|
+
* tolerance: 0.005
|
|
262
|
+
* });
|
|
263
|
+
* // Returns: [] if valid, or errors like:
|
|
264
|
+
* // ["monthly-expense - Record 0 (uuid-123): foreign currency (USD) but no foreign amounts"]
|
|
265
|
+
* // ["monthly-expense - Record 1 (uuid-456): local currency (ILS) but has foreign amounts"]
|
|
266
|
+
* // ["monthly-expense - Record 2 (uuid-789): suspicious exchange rate in debit1 (rate=15.2000)"]
|
|
267
|
+
*/
|
|
268
|
+
export declare function validateForeignCurrency(records: LedgerRecord[], context: ValidationContext): string[];
|
|
269
|
+
/**
|
|
270
|
+
* Validate no orphaned amounts (FR4)
|
|
271
|
+
*
|
|
272
|
+
* Ensures data integrity by validating that every non-zero amount field has
|
|
273
|
+
* a corresponding entity reference. This prevents "orphaned" amounts that
|
|
274
|
+
* cannot be attributed to any financial entity.
|
|
275
|
+
*
|
|
276
|
+
* An "orphaned amount" is a ledger entry where an amount value exists but
|
|
277
|
+
* its corresponding entity field is null. This violates double-entry
|
|
278
|
+
* bookkeeping principles where every amount must be associated with an entity.
|
|
279
|
+
*
|
|
280
|
+
* Rules enforced:
|
|
281
|
+
* - Primary fields (entity1): If amount > 0, entity must be present
|
|
282
|
+
* - Secondary fields (entity2): If entity is null, amount must also be null
|
|
283
|
+
*
|
|
284
|
+
* @param records - Array of ledger records to validate
|
|
285
|
+
* @param context - Validation context containing use-case ID
|
|
286
|
+
* @returns Array of error messages (empty if no orphaned amounts found)
|
|
287
|
+
*
|
|
288
|
+
* Functional Requirement: FR4 - Orphaned Amount Detection
|
|
289
|
+
* - Checks all 4 amount/entity pairs per record
|
|
290
|
+
* - Detects amounts without entities
|
|
291
|
+
* - Detects secondary fields that should be null
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* const errors = validateNoOrphanedAmounts(records, {
|
|
295
|
+
* useCaseId: 'monthly-expense',
|
|
296
|
+
* defaultCurrency: 'ILS',
|
|
297
|
+
* tolerance: 0.005
|
|
298
|
+
* });
|
|
299
|
+
* // Returns: [] if valid, or errors like:
|
|
300
|
+
* // ["monthly-expense - Record 0 (uuid-123): orphaned amount in debit_local_amount1/debit_entity1 (100.00 without entity)"]
|
|
301
|
+
*/
|
|
302
|
+
export declare function validateNoOrphanedAmounts(records: LedgerRecord[], context: ValidationContext): string[];
|
|
303
|
+
/**
|
|
304
|
+
* Master validation function - runs all validators (FR1-FR10)
|
|
305
|
+
*
|
|
306
|
+
* Orchestrates comprehensive ledger validation by executing all individual
|
|
307
|
+
* validation functions in a logical sequence. This is the main entry point
|
|
308
|
+
* for validating a complete set of ledger records for a use-case.
|
|
309
|
+
*
|
|
310
|
+
* Validation hierarchy:
|
|
311
|
+
* 1. Per-record validation (FR1, FR10) - Each record is internally balanced
|
|
312
|
+
* 2. Aggregate validation (FR2) - Total debits equal total credits
|
|
313
|
+
* 3. Entity validation (FR3) - Each entity's position balances
|
|
314
|
+
* 4. Data integrity (FR4, FR5) - No orphaned amounts, all amounts positive
|
|
315
|
+
* 5. Business rules (FR6, FR7) - Foreign currency and date validation
|
|
316
|
+
* 6. Structural validation (FR8) - Record count matches expectations
|
|
317
|
+
*
|
|
318
|
+
* This function implements NFR2 (Error Reporting): Collects ALL errors before
|
|
319
|
+
* failing (no fail-fast), allowing comprehensive error discovery in a single run.
|
|
320
|
+
*
|
|
321
|
+
* @param records - Array of ledger records to validate
|
|
322
|
+
* @param expectedRecordCount - Expected number of ledger records for this use-case
|
|
323
|
+
* @param context - Validation context containing use-case ID, currency, and tolerance
|
|
324
|
+
* @returns Array of all error messages from all validators (empty if fully valid)
|
|
325
|
+
*
|
|
326
|
+
* Functional Requirements Implemented:
|
|
327
|
+
* - FR1: Per-Record Internal Balance
|
|
328
|
+
* - FR2: Aggregate Balance Validation
|
|
329
|
+
* - FR3: Entity-Level Balance Validation
|
|
330
|
+
* - FR4: Orphaned Amount Detection
|
|
331
|
+
* - FR5: Positive Amount Validation
|
|
332
|
+
* - FR6: Foreign Currency Validation
|
|
333
|
+
* - FR7: Date Validation
|
|
334
|
+
* - FR8: Record Count Validation
|
|
335
|
+
* - FR10: Empty Ledger Detection (within FR1)
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* const errors = validateLedgerRecords(records, 24, {
|
|
339
|
+
* useCaseId: 'monthly-expense',
|
|
340
|
+
* defaultCurrency: 'ILS',
|
|
341
|
+
* tolerance: 0.005
|
|
342
|
+
* });
|
|
343
|
+
* if (errors.length > 0) {
|
|
344
|
+
* console.error('Validation failed:', errors);
|
|
345
|
+
* } else {
|
|
346
|
+
* console.log('All validations passed');
|
|
347
|
+
* }
|
|
348
|
+
*/
|
|
349
|
+
export declare function validateLedgerRecords(records: LedgerRecord[], expectedRecordCount: number, context: ValidationContext): string[];
|