实现报销材料合并预览与差旅概要(07-27-yang-jun-reimbursement-readiness)
这个提交包含在:
@@ -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({
|
||||
|
||||
在新工单中引用
屏蔽一个用户