Add professor share calculation and financial reporting feature
Introduces per-class payout configuration (percentage or hourly rate, plus an optional per-session expense allowance), pure calculation utilities for professor payout/net profit/receivables, and a new financial-reports API surface with per-class, per-session, and date-range/monthly reports. Also adds full CRUD for institutional expenses used in the range report, and wires up the new permissions and roles. Fixes a pre-existing ReferenceError (missing parseImportDate import) in paymentService that blocked creating payments with dated transactions.
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { resolveDateRange, isWithinRange } = require('./financialRange');
|
||||
|
||||
describe('resolveDateRange', () => {
|
||||
it('builds a range from explicit start/end dates', () => {
|
||||
const { start, end } = resolveDateRange({ startDate: '2026-01-01', endDate: '2026-01-31' });
|
||||
assert.equal(start.getFullYear(), 2026);
|
||||
assert.equal(start.getMonth(), 0);
|
||||
assert.equal(start.getDate(), 1);
|
||||
assert.equal(end.getDate(), 31);
|
||||
assert.equal(end.getHours(), 23);
|
||||
});
|
||||
|
||||
it('builds a range from month + year shorthand', () => {
|
||||
const { start, end } = resolveDateRange({ month: 2, year: 2026 });
|
||||
assert.equal(start.getMonth(), 1);
|
||||
assert.equal(start.getDate(), 1);
|
||||
assert.equal(end.getMonth(), 1);
|
||||
assert.equal(end.getDate(), 28); // 2026 is not a leap year
|
||||
});
|
||||
|
||||
it('falls back to all-time when nothing is provided', () => {
|
||||
const { start, end } = resolveDateRange({});
|
||||
assert.ok(start.getFullYear() <= 1970);
|
||||
assert.ok(end.getTime() <= Date.now());
|
||||
});
|
||||
|
||||
it('swaps start/end when given out of order', () => {
|
||||
const { start, end } = resolveDateRange({ startDate: '2026-03-01', endDate: '2026-01-01' });
|
||||
assert.ok(start.getTime() < end.getTime());
|
||||
});
|
||||
});
|
||||
|
||||
describe('isWithinRange', () => {
|
||||
it('detects dates inside and outside the range', () => {
|
||||
const { start, end } = resolveDateRange({ startDate: '2026-01-01', endDate: '2026-01-31' });
|
||||
assert.equal(isWithinRange('2026-01-15', start, end), true);
|
||||
assert.equal(isWithinRange('2026-02-01', start, end), false);
|
||||
});
|
||||
|
||||
it('returns false for missing or invalid values', () => {
|
||||
const { start, end } = resolveDateRange({ startDate: '2026-01-01', endDate: '2026-01-31' });
|
||||
assert.equal(isWithinRange(null, start, end), false);
|
||||
assert.equal(isWithinRange('not-a-date', start, end), false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user