Move embedded payment rows into Transaction documents with due dates so remaining tuition can be tracked separately from paid amounts.
86 lines
2.9 KiB
JavaScript
86 lines
2.9 KiB
JavaScript
'use strict';
|
|
|
|
const { describe, it } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const {
|
|
getPayableAmount,
|
|
normalizeDiscount,
|
|
sanitizeNotes,
|
|
NOTES_MAX_LENGTH,
|
|
rialsToToman,
|
|
isPaidTransaction,
|
|
sumPaidTransactions,
|
|
remainingPayable
|
|
} = require('./paymentAmount');
|
|
|
|
describe('payment amount helpers', () => {
|
|
it('returns the original amount when there is no discount', () => {
|
|
assert.equal(getPayableAmount({ amount: 1_000_000 }), 1_000_000);
|
|
assert.equal(getPayableAmount({ amount: 1_000_000, discount: 0 }), 1_000_000);
|
|
});
|
|
|
|
it('subtracts a discount from the total', () => {
|
|
assert.equal(getPayableAmount({ amount: 1_000_000, discount: 150_000 }), 850_000);
|
|
});
|
|
|
|
it('never returns a negative payable amount', () => {
|
|
assert.equal(getPayableAmount({ amount: 100, discount: 250 }), 0);
|
|
});
|
|
|
|
it('treats missing or invalid values as zero', () => {
|
|
assert.equal(getPayableAmount({}), 0);
|
|
assert.equal(getPayableAmount({ amount: 'abc', discount: 'x' }), 0);
|
|
assert.equal(normalizeDiscount(-50, 1000), 0);
|
|
assert.equal(normalizeDiscount('not-a-number', 1000), 0);
|
|
});
|
|
|
|
it('clamps discount so it cannot exceed the total', () => {
|
|
assert.equal(normalizeDiscount(2_000, 1_000), 1_000);
|
|
assert.equal(normalizeDiscount(200, 1_000), 200);
|
|
});
|
|
});
|
|
|
|
describe('rialsToToman', () => {
|
|
it('drops one zero so spreadsheet Rials become website Toman', () => {
|
|
assert.equal(rialsToToman(110_000_000), 11_000_000);
|
|
assert.equal(rialsToToman(5_000_000), 500_000);
|
|
assert.equal(rialsToToman(0), 0);
|
|
});
|
|
});
|
|
|
|
describe('paid vs pending transactions', () => {
|
|
it('counts only paid transactions toward the paid total', () => {
|
|
const transactions = [
|
|
{ amount: 8_000_000, status: 'paid', date: '2026-08-03' },
|
|
{ amount: 3_000_000, status: 'pending', dueDate: '2026-08-23' }
|
|
];
|
|
assert.equal(sumPaidTransactions(transactions), 8_000_000);
|
|
assert.equal(
|
|
remainingPayable({ amount: 11_000_000, discount: 0 }, transactions),
|
|
3_000_000
|
|
);
|
|
});
|
|
|
|
it('treats a 10m billed course as 1m off an 11m tuition', () => {
|
|
assert.equal(getPayableAmount({ amount: 11_000_000, discount: 1_000_000 }), 10_000_000);
|
|
});
|
|
|
|
it('treats an 8m billed course as 3m off an 11m tuition', () => {
|
|
assert.equal(getPayableAmount({ amount: 11_000_000, discount: 3_000_000 }), 8_000_000);
|
|
});
|
|
|
|
it('does not treat pending remainder rows as paid', () => {
|
|
assert.equal(isPaidTransaction({ status: 'pending', amount: 2_000_000 }), false);
|
|
assert.equal(isPaidTransaction({ status: 'paid', amount: 8_000_000 }), true);
|
|
});
|
|
});
|
|
|
|
describe('sanitizeNotes', () => {
|
|
it('trims notes and caps length', () => {
|
|
assert.equal(sanitizeNotes(' hello '), 'hello');
|
|
assert.equal(sanitizeNotes(null), '');
|
|
assert.equal(sanitizeNotes(undefined), '');
|
|
assert.equal(sanitizeNotes('a'.repeat(NOTES_MAX_LENGTH + 10)).length, NOTES_MAX_LENGTH);
|
|
});
|
|
});
|