feat: add fee workspace and polish AI presentation

This commit is contained in:
2026-07-09 17:05:19 -07:00
parent 3aabdf0c4a
commit ef4dd21d32
19 changed files with 1589 additions and 15 deletions

View File

@@ -0,0 +1,75 @@
import { describe, expect, it } from "vitest";
import { createInitialBillingStatements } from "@/modules/billing/lib/presentation-data";
import {
addBillingCharge,
addBillingPayment,
calculateBillingSummary,
getBillingStatus,
getStatementOutstandingCents,
getStatementTotalCents,
} from "@/modules/billing/types";
const referenceDate = new Date("2026-07-09T12:00:00.000Z");
describe("billing calculations", () => {
it("derives paid, partial, pending, and overdue states from ledger activity", () => {
const statements = createInitialBillingStatements(referenceDate);
expect(statements.map((statement) => getBillingStatus(statement, referenceDate))).toEqual([
"paid",
"partial",
"pending",
"overdue",
"overdue",
"paid",
]);
});
it("calculates statement and workspace totals from charges and payments", () => {
const statements = createInitialBillingStatements(referenceDate);
const summary = calculateBillingSummary(statements, referenceDate);
const firstStatement = statements[0];
expect(firstStatement).toBeDefined();
if (!firstStatement) {
return;
}
expect(getStatementTotalCents(firstStatement)).toBe(960000);
expect(summary.receivableCents).toBe(5995000);
expect(summary.collectedCents).toBe(2810000);
expect(summary.outstandingCents).toBe(3185000);
expect(summary.overdueCount).toBe(2);
});
it("updates outstanding amounts when payments and charges are appended", () => {
const statement = createInitialBillingStatements(referenceDate)[2];
expect(statement).toBeDefined();
if (!statement) {
return;
}
const paidStatement = addBillingPayment(statement, {
id: "payment-new",
amountCents: 200000,
method: "wechat",
paidAt: referenceDate.toISOString(),
reference: "WX-NEW",
operator: "收费员",
note: "",
});
expect(getStatementOutstandingCents(paidStatement)).toBe(640000);
expect(getBillingStatus(paidStatement, referenceDate)).toBe("partial");
const chargedStatement = addBillingCharge(paidStatement, {
id: "charge-new",
category: "生活用品",
description: "护理耗材",
quantity: 2,
serviceDate: referenceDate.toISOString(),
unitPriceCents: 5000,
});
expect(getStatementOutstandingCents(chargedStatement)).toBe(650000);
expect(chargedStatement.invoiceStatus).toBe("not_requested");
});
});