实现报销材料合并预览与差旅概要(07-27-yang-jun-reimbursement-readiness)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-08-01 09:25:44 +08:00
父节点 3d9ec4c5a7
当前提交 da35591bd5
共修改 29 个文件,包含 907 行新增和 527 行删除
@@ -99,6 +99,9 @@ async function buildApp(options = {}) {
invoicePool: {
count: vi.fn(async () => 1),
},
reimbursementItem: {
findFirst: vi.fn(async () => (options.travelItem ? { id: "travel-item" } : null)),
},
budgetLedgerEntry: {
create: vi.fn(async () => ({ id: "budget-ledger-1" })),
},
@@ -230,6 +233,18 @@ describe("reimbursements routes expense standard integration", () => {
resolveRecipientsForAssigneeMock.mockResolvedValue([]);
});
it("blocks travel reimbursement submit until the structured summary is complete", async () => {
const { app, tx } = await buildApp({ travelItem: true });
const response = await app.inject({ method: "POST", url: `/api/reimbursements/${REIMBURSEMENT_ID}/submit` });
expect(response.statusCode).toBe(400);
expect(response.json().error.details).toEqual(expect.objectContaining({
travelSummary: "incomplete",
missingFields: ["travelPurpose", "travelLocation", "travelStartDate", "travelEndDate"],
}));
expect(tx.workflowInstance.create).not.toHaveBeenCalled();
await app.close();
});
it("returns standardChecks from reimbursement precheck", async () => {
resolveEntityPrecheckMock.mockResolvedValue(
buildPrecheck({