feat: add repayment discounts and interactive net worth trends
Support positive repayment discounts and negative surcharges with exact decimal conversion. Add independent trend toggles, pointer and keyboard date inspection, tests and Chinese documentation.
This commit is contained in:
1 parent
096f1577c2
commit
b2c22ca050
16 files changed
+505
-84
No files matched your search
@@ -5,7 +5,7 @@
|
||||
"dev": "node scripts/dev.cjs",
|
||||
"build": "tsc",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "tsx --test --test-concurrency=1 test/calculation.test.ts test/update.test.ts test/zip.test.ts test/metals.test.ts ../web/test/i18n.test.ts test/account-deletion.test.ts test/mcp-hosts.test.ts test/mcp-http.test.ts test/network.test.ts ../web/test/quick-entry.test.ts",
|
||||
"test": "tsx --test --test-concurrency=1 test/calculation.test.ts test/update.test.ts test/zip.test.ts test/metals.test.ts ../web/test/i18n.test.ts test/account-deletion.test.ts test/mcp-hosts.test.ts test/mcp-http.test.ts test/network.test.ts ../web/test/quick-entry.test.ts ../web/test/net-worth-chart.test.ts",
|
||||
"db:generate": "prisma generate",
|
||||
"db:migrate": "node scripts/database.cjs deploy",
|
||||
"db:status": "node scripts/database.cjs status",
|
||||
|
||||
@@ -168,6 +168,58 @@ test('quick entries undo paired repayment and balance, persist monthly marks, pr
|
||||
assert.equal((await call(a, '/transfers/' + payment.data.id, 'DELETE')).status, 200);
|
||||
assert.equal((await call(a, '/positions/' + cash)).data.amount, '1000');
|
||||
assert.equal((await call(a, '/positions/' + card)).data.amount, '200');
|
||||
|
||||
for (const [fee, expected] of [
|
||||
['-5.87654321', '955.87654321'],
|
||||
['1.86420975', '948.13579025'],
|
||||
] as const) {
|
||||
const movement = await call(a, '/transfers', 'POST', {
|
||||
sourceId: cash,
|
||||
targetId: card,
|
||||
amount: '50',
|
||||
received: '50',
|
||||
fee,
|
||||
date: '2026-09-03T11:00',
|
||||
requestId: randomUUID(),
|
||||
});
|
||||
assert.equal(movement.status, 201);
|
||||
assert.equal((await call(a, '/positions/' + cash)).data.amount, expected);
|
||||
assert.equal((await call(a, '/positions/' + card)).data.amount, '150');
|
||||
assert.equal((await call(a, '/transfers/' + movement.data.id, 'DELETE')).status, 200);
|
||||
assert.equal((await call(a, '/positions/' + cash)).data.amount, '1000');
|
||||
assert.equal((await call(a, '/positions/' + card)).data.amount, '200');
|
||||
}
|
||||
const invalidDiscount = await call(a, '/transfers', 'POST', {
|
||||
sourceId: cash,
|
||||
targetId: card,
|
||||
amount: '30',
|
||||
received: '30',
|
||||
fee: '-31',
|
||||
date: '2026-09-03T11:00',
|
||||
requestId: randomUUID(),
|
||||
});
|
||||
assert.equal(invalidDiscount.status, 400);
|
||||
assert.equal((await call(a, '/positions/' + cash)).data.amount, '1000');
|
||||
const loan = await create('测试独立欠款', 'debt', 'liability', 'personal', '200');
|
||||
for (const [fee, expected] of [
|
||||
['-5', '975'],
|
||||
['2', '968'],
|
||||
] as const) {
|
||||
const movement = await call(a, '/transfers', 'POST', {
|
||||
operation: 'repay',
|
||||
sourceId: cash,
|
||||
targetId: loan,
|
||||
amount: '30',
|
||||
received: '30',
|
||||
fee,
|
||||
date: '2026-09-03T12:00',
|
||||
requestId: randomUUID(),
|
||||
});
|
||||
assert.equal(movement.status, 201);
|
||||
assert.equal((await call(a, '/positions/' + cash)).data.amount, expected);
|
||||
assert.equal((await call(a, '/positions/' + loan)).data.amount, '170');
|
||||
assert.equal((await call(a, '/transfers/' + movement.data.id, 'DELETE')).status, 200);
|
||||
}
|
||||
const month = today().slice(0, 7),
|
||||
path = '/positions/' + card + '/repayment-mark';
|
||||
assert.equal((await call(b, path, 'PATCH', { month, expectedMonth: null })).status, 404);
|
||||
|
||||
Reference in new issue
Block a user