fix: 加固日历日期与动态错误合同(09-05-error-explainability-payment-readiness,09-08-reimbursement-payment-closeout)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-09-16 20:20:11 +08:00
父节点 842c61e696
当前提交 b1d25d3c99
共修改 20 个文件,包含 253 行新增和 21 行删除
@@ -1011,6 +1011,42 @@ describe("payment batch reimbursement routes", () => {
await app.close();
});
it("rejects overflowing payment dates before creation writes", async () => {
const { app, tx } = await buildApp();
try {
const response = await app.inject({ method: "POST", url: "/api/payment-batches", headers: { authorization: "Bearer test" },
payload: { entityType: "reimbursement", entityIds: [REIMBURSEMENT_ID], defaultPaymentDate: "2026-02-30" } });
expect(response.statusCode).toBe(400);
expect(response.json().error.details.reasonCode).toBe("payment_batch.invalid_input");
expect(response.json().error.details.fieldErrors.defaultPaymentDate).toContain("invalid_calendar_date");
expect(tx.paymentBatch.create).not.toHaveBeenCalled();
expect(tx.paymentBatchItem.create).not.toHaveBeenCalled();
expect(tx.auditLog.create).not.toHaveBeenCalled();
} finally { await app.close(); }
});
it.each(["2026-02-30", "2026-13-01"])("rejects invalid payment date override %s without writes", async (paymentDateOverride) => {
const { app, tx } = await buildApp();
try {
const response = await app.inject({ method: "PATCH", url: `/api/payment-batches/${BATCH_ID}/items/${ITEM_ID}`,
headers: { authorization: "Bearer test" }, payload: { paymentDateOverride } });
expect(response.statusCode).toBe(400);
expect(response.json().error.details.fieldErrors.paymentDateOverride).toContain("invalid_calendar_date");
expect(tx.paymentBatchItem.update).not.toHaveBeenCalled();
expect(tx.auditLog.create).not.toHaveBeenCalled();
} finally { await app.close(); }
});
it.each(["2024-02-29", null])("preserves valid or cleared payment date override %s", async (paymentDateOverride) => {
const { app, state } = await buildApp();
try {
const response = await app.inject({ method: "PATCH", url: `/api/payment-batches/${BATCH_ID}/items/${ITEM_ID}`,
headers: { authorization: "Bearer test" }, payload: { paymentDateOverride } });
expect(response.statusCode).toBe(200);
expect(state.batch.items[0].payment_date_override).toEqual(paymentDateOverride ? new Date(`${paymentDateOverride}T00:00:00.000Z`) : null);
} finally { await app.close(); }
});
it("projects synthetic 0014 omissions in candidates and preflight without writes, and recomputes after repair", async () => {
const { app, tx, state } = await buildApp();
const completeOwner = { ...state.reimbursement.owner };