修复报销票据门禁提示与 OCR 降级(08-30-b2-submit-error-ocr-fallback)

这个提交包含在:
2026-08-31 20:20:20 +08:00
父节点 8b19538584
当前提交 f57e2236f2
共修改 21 个文件,包含 1829 行新增和 49 行删除
@@ -41,6 +41,7 @@ function buildReimbursementRecord(overrides = {}) {
name: "打车费",
category: "travel",
enabled: true,
requires_invoice: true,
},
},
],
@@ -56,6 +57,10 @@ function buildReimbursementRecord(overrides = {}) {
total_amount: "188.80",
original_name: "receipt.pdf",
linked_item_id: "item-1",
ocr_attempts: 1,
ocr_last_requested_at: new Date("2026-04-09T01:00:00.000Z"),
ocr_last_error: null,
ocr_completed_at: new Date("2026-04-09T01:00:01.000Z"),
},
],
workflow_instances: [
@@ -358,8 +363,10 @@ describe("GET /api/reimbursements/:id detail enhancements", () => {
totalAmount: "188.80",
originalName: "receipt.pdf",
linkedItemId: "item-1",
amountEvidenceStatus: "verified",
},
],
invoiceAmountManualReviewRequired: false,
approvalHistory: [
{
id: "history-start",
@@ -394,6 +401,47 @@ describe("GET /api/reimbursements/:id detail enhancements", () => {
await app.close();
});
it("keeps the manual amount-review marker visible to a finance detail reader", async () => {
const reimbursement = buildReimbursementRecord({
invoice_pool_items: [{
id: "invoice-off",
status: "bound",
invoice_code: null,
invoice_number: null,
invoice_date: null,
seller_name: null,
total_amount: null,
original_name: "manual-review.pdf",
linked_item_id: "item-1",
ocr_attempts: 0,
ocr_last_requested_at: null,
ocr_last_error: null,
ocr_completed_at: null,
}],
});
const { app } = await buildApp({
employeeId: "finance-reader",
permissionCodes: ["expense.view_all"],
reimbursement,
});
const response = await app.inject({
method: "GET",
url: `/api/reimbursements/${REIMBURSEMENT_ID}`,
headers: { authorization: "Bearer test" },
});
expect(response.statusCode, response.body).toBe(200);
expect(response.json().data).toEqual(expect.objectContaining({
invoiceAmountManualReviewRequired: true,
invoices: [expect.objectContaining({
id: "invoice-off",
amountEvidenceStatus: "manual_review",
})],
}));
await app.close();
});
it("returns null submittedAt and empty approvalHistory when reimbursement has never been submitted", async () => {
const reimbursement = buildReimbursementRecord({
status: "draft",