feat(reimbursement-small-team-form-validation): 完善付款批次核对与回单预览

这个提交包含在:
2026-08-05 01:02:51 +08:00
父节点 9db8bbc338
当前提交 d4400116c5
共修改 24 个文件,包含 979 行新增和 245 行删除
@@ -688,6 +688,58 @@ describe("GET /api/reimbursements/:id detail enhancements", () => {
await app.close();
});
it("attaches the payment-batch link to exactly one status-owning stage", async () => {
const cases = [
{ status: "draft", reviewedAt: null, targetIndex: 3 },
{ status: "reviewed", reviewedAt: new Date("2026-04-11T08:00:00.000Z"), targetIndex: 4 },
{ status: "executed", reviewedAt: new Date("2026-04-11T08:00:00.000Z"), targetIndex: 4 },
{ status: "failed", reviewedAt: new Date("2026-04-11T08:00:00.000Z"), targetIndex: 4 },
{ status: "cancelled", reviewedAt: null, targetIndex: 3 },
{ status: "cancelled", reviewedAt: new Date("2026-04-11T08:00:00.000Z"), targetIndex: 4 },
];
for (const item of cases) {
const { app } = await buildApp({
permissionCodes: ["expense.view_own", "payment.batch.review"],
paymentBatchItem: {
id: "batch-item-1",
payment_batch: {
id: "batch-1",
status: item.status,
entity_type: "reimbursement",
default_payment_channel: "bank",
payment_order_export_count: 0,
created_at: new Date("2026-04-11T07:00:00.000Z"),
created_by: "maker-1",
reviewed_at: item.reviewedAt,
reviewed_by: item.reviewedAt ? "reviewer-1" : null,
executed_at: ["executed", "failed"].includes(item.status) ? new Date("2026-04-12T08:00:00.000Z") : null,
executed_by: ["executed", "failed"].includes(item.status) ? "executor-1" : null,
cancelled_at: item.status === "cancelled" ? new Date("2026-04-11T09:00:00.000Z") : null,
cancelled_by: item.status === "cancelled" ? "reviewer-1" : null,
cancelled_reason: item.status === "cancelled" ? "批次资料需调整" : null,
items: [{ status: item.status === "executed" ? "executed" : "pending", payment_channel_override: null }],
_count: { receipt_attachments: item.status === "executed" ? 1 : 0 },
},
},
});
const response = await app.inject({
method: "GET",
url: `/api/reimbursements/${REIMBURSEMENT_ID}`,
headers: { authorization: "Bearer test" },
});
expect(response.statusCode).toBe(200);
const stages = response.json().data.processingProgress.stages;
const linkedStageIndexes = stages
.map((stage, index) => stage.targetHref ? index : -1)
.filter((index) => index >= 0);
expect(linkedStageIndexes).toEqual([item.targetIndex]);
expect(stages[item.targetIndex].targetHref).toBe("/payment-batches?id=batch-1");
await app.close();
}
});
it("marks finance batch creation as the current responsibility after workflow approval", async () => {
const { app } = await buildApp({
reimbursement: buildReimbursementRecord({ status: "approved", workflow_instances: [] }),