const { test } = require('node:test') const assert = require('node:assert/strict') const fs = require('node:fs') const vm = require('node:vm') const ts = require('typescript') const path = require('node:path') function loadModule(file) { const filename = path.resolve(__dirname, file) const module = { exports: {} } const localRequire = id => id.startsWith('.') ? loadModule(path.relative(__dirname, path.resolve(path.dirname(filename), id + '.ts'))) : require(id) vm.runInNewContext(ts.transpile(fs.readFileSync(filename, 'utf8'), { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, esModuleInterop: true }), { module, exports: module.exports, require: localRequire }) return module.exports } const { getBankAllocationAmounts, getBankAllocationTaxBreakdown } = loadModule('../utils/bankAllocationTax.ts') const { getStatementAccountExpenseTotal, getStatementAllocationDepreciationAmount } = loadModule('../composables/useDepreciation.ts') const allocation = (amount, datevTaxKey, extra = {}) => ({ bankstatement: { id: 42 }, account: 13, amount, datevTaxKey, ...extra }) for (const [gross, key, net, tax, side, rate] of [ [-119, '9', -100, -19, 'input', 19], [-107, '8', -100, -7, 'input', 7], [119, '3', 100, 19, 'output', 19], [107, '2', 100, 7, 'output', 7], [59.5, '9', 50, 9.5, 'input', 19], [-59.5, '3', -50, -9.5, 'output', 19], ]) { test(`gross ${gross} and key ${key} produce correct net, VAT and refund signs`, () => { const item = allocation(gross, key) const amounts = getBankAllocationAmounts(item) assert.equal(amounts.amountGross, gross) assert.equal(amounts.amountNet, net) assert.equal(amounts.amountTax, tax) const result = getBankAllocationTaxBreakdown(item) assert.equal(result[side][`net${rate}`], net * (side === 'input' ? -1 : 1)) assert.equal(result[side][`tax${rate}`], tax * (side === 'input' ? -1 : 1)) }) } test('rounding preserves gross, and tax-free and legacy allocations retain their amounts', () => { const amounts = getBankAllocationAmounts(allocation(-10, '9')) assert.equal(amounts.amountNet, -8.4) assert.equal(amounts.amountTax, -1.6) for (const key of ['40', null]) { const value = getBankAllocationAmounts(allocation(-100, key)) assert.equal(value.amountNet, -100) assert.equal(value.amountTax, 0) } }) test('invoice settlements, archived bookings and manual bookings are not counted again', () => { for (const extra of [{ incominginvoice: 1 }, { createddocument: 1 }, { archived: true }, { bankstatement: null }]) { const result = getBankAllocationTaxBreakdown(allocation(-119, '9', extra)) assert.ok([...Object.values(result.input), ...Object.values(result.output)].every(value => value === 0)) } }) test('additional accounts carry the same tax breakdown', () => { assert.equal(getBankAllocationAmounts(allocation(-119, '9', { account: null, ownaccount: 'own' })).amountTax, -19) }) test('BWA separates net expenses from gross cash flows including partial refunds', () => { const items = [allocation(-119, '9'), allocation(59.5, '9')] assert.equal(getStatementAccountExpenseTotal(items), 50) assert.equal(getStatementAccountExpenseTotal(items, true), 59.5) }) test('depreciation uses the net acquisition amount when input VAT is selected', () => { assert.equal(getStatementAllocationDepreciationAmount(allocation(-1428, '9', { bookingMode: 'depreciation_single', depreciationMonths: 12, depreciationStartDate: '2026-01-01', }), '2026-01-01', '2026-01-31'), 100) })