完善借款、编号、付款与预算并发及验收(09-06-release-gate-integrity)

汇总已落盘的借款本金与还款、兼容历史编号的周期化编号、付款责任与回单核验、报销进度和排序、PWA/采购事务、错误原因与日期校验及ERP退役。
统一共享预算池锁序和锁后判断,恢复差旅原预算选择,补原因码固定夹具及反向完整性检查。
纳入配套迁移、测试、受管QA、分层验收合同、规约及任务记录;原始证据留在ignored artifacts。

关联任务:09-17-company-loan-principal-repayment、09-17-erp-exclusive-retirement、09-18-reimbursement-periodic-numbering、09-08-reimbursement-payment-closeout、09-18-local-concurrency-product-fixes、09-19-budget-pool-concurrency、09-05-error-explainability-payment-readiness。
预算补审由Claude定向验证2文件928项通过,原4446/28及r6结果仍仅对应各自历史输入;本轮未重跑测试。
完整新候选门禁、R1真实入口接入与12项公网义务仍开放。生产编号未启用、差旅仍冻结;不推送、不部署,不宣称发布资格。
这个提交包含在:
2026-09-19 13:11:04 +08:00
父节点 729d90576f
当前提交 1f8d5b658a
共修改 367 个文件,包含 21079 行新增和 12287 行删除
@@ -422,6 +422,7 @@ describe("contracts registry routes", () => {
expect.objectContaining({
code: "VALID_001",
details: expect.objectContaining({
reasonCode: "contract_registry.expiry_date_range_invalid",
expiryDateTo: "must_be_after_or_equal_expiryDateFrom",
}),
}),
@@ -896,3 +897,170 @@ describe("contracts registry routes", () => {
await app.close();
});
});
describe("contract calendar input", () => {
const body = { contractNo: "SYNTHETIC-CALENDAR", contractType: "service", counterpartyName: "合成对方", summary: "合成日期验证", archiveLocation: "synthetic-archive" };
const cases = ["POST", "PATCH"].flatMap((method) => ["signedDate", "effectiveDate", "expiryDate"].flatMap((field) => ["2026-02-29", "2026-04-31", "2026-13-01", ""].map((date) => [method, field, date])));
it.each(cases)("rejects %s %s=%s before writes", async (method, field, date) => {
const { app, prisma } = await buildApp();
const transaction = vi.spyOn(prisma, "$transaction");
try {
const response = await app.inject({ method, url: `/api/contracts-registry${method === "PATCH" ? "/55555555-5555-4555-8555-555555555551" : ""}`, payload: { ...(method === "POST" ? body : {}), [field]: date } });
expect(response.statusCode).toBe(400);
expect(response.json().error.details.fieldErrors[field].length).toBeGreaterThan(0);
expect(prisma.contractRegistry.create).not.toHaveBeenCalled();
expect(prisma.contractRegistry.updateMany).not.toHaveBeenCalled();
expect(prisma.auditLog.create).not.toHaveBeenCalled();
expect(transaction).not.toHaveBeenCalled();
} finally { await app.close(); }
});
it.each(["2024-02-29", "2026-04-30", null, undefined])("preserves create and patch nullable/optional dates: %s", async (date) => {
const { prisma, state } = buildStatefulPrisma([buildContractRow({ status: "draft" })]);
const { app } = await buildApp({ prisma });
try {
const dates = date === undefined ? {} : { signedDate: date, effectiveDate: date, expiryDate: date };
const created = await app.inject({ method: "POST", url: "/api/contracts-registry", payload: { ...body, ...dates } });
expect(created.statusCode).toBe(201);
const expected = date ? new Date(`${date}T00:00:00.000Z`) : null;
expect(prisma.contractRegistry.create.mock.calls[0][0].data).toMatchObject({ signed_date: expected, effective_date: expected, expiry_date: expected });
const patched = await app.inject({ method: "PATCH", url: `/api/contracts-registry/${state.contracts[0].id}`, payload: { summary: "合成修改", ...dates } });
expect(patched.statusCode).toBe(200);
expect(state.contracts[0].signed_date).toEqual(date === undefined ? new Date("2026-03-01T00:00:00.000Z") : expected);
} finally { await app.close(); }
});
it.each(["expiryDateFrom", "expiryDateTo"])("rejects invalid filter %s before contract reads", async (field) => {
const { app, prisma } = await buildApp();
try {
const response = await app.inject({ method: "GET", url: `/api/contracts-registry?${field}=2026-02-30` });
expect(response.statusCode).toBe(400);
expect(response.json().error).toMatchObject({ code: "VALID_001", details: { reasonCode: "contract_registry.query_invalid", formErrors: [], fieldErrors: { [field]: expect.arrayContaining(["invalid_calendar_date"]) } } });
expect(prisma.contractRegistry.findMany).not.toHaveBeenCalled();
expect(prisma.auditLog.create).not.toHaveBeenCalled();
} finally { await app.close(); }
});
});
describe("contract expiring query reasons", () => {
it.each(["days=0", "days=366", "days=1.5", "days=invalid", "unexpected=1"])("rejects %s before contract queries", async (query) => {
const { app, prisma } = await buildApp();
try {
const response = await app.inject({ method: "GET", url: `/api/contracts-registry/expiring?${query}` });
expect(response.statusCode).toBe(400);
expect(response.json().error).toMatchObject({ code: "VALID_001", details: { reasonCode: "contract_registry.expiring_query_invalid" } });
const details = response.json().error.details;
if (query.startsWith("days=")) expect(details.fieldErrors.days).toHaveLength(1);
else expect(details.formErrors).toHaveLength(1);
expect(prisma.contractRegistry.findMany).not.toHaveBeenCalled();
expect(prisma.auditLog.create).not.toHaveBeenCalled();
} finally { await app.close(); }
});
it.each([1, 365])("retains the legal %s day boundary", async (days) => {
const { app, prisma } = await buildApp();
try {
const response = await app.inject({ method: "GET", url: `/api/contracts-registry/expiring?days=${days}` });
expect(response.statusCode).toBe(200);
expect(prisma.contractRegistry.findMany).toHaveBeenCalledOnce();
} finally { await app.close(); }
});
});
describe("contract write error reasons", () => {
const id = "55555555-5555-4555-8555-555555555551";
it.each([
["POST", "", {}, "create"],
["PATCH", `/${id}`, {}, "update"],
["POST", `/${id}/terminate`, { reason: "short" }, "terminate"],
["POST", `/${id}/corrections`, { summary: "short" }, "correction"],
])("explains %s %s input before business access", async (method, path, payload, operation) => {
const { app, prisma } = await buildApp();
const transaction = vi.spyOn(prisma, "$transaction");
try {
const response = await app.inject({ method, url: `/api/contracts-registry${path}`, payload });
expect(response.statusCode).toBe(400);
expect(response.json().error).toMatchObject({ code: "VALID_001", details: { reasonCode: `contract_registry.${operation}_input_invalid`, fieldErrors: expect.any(Object), formErrors: expect.any(Array) } });
for (const fn of Object.values(prisma.contractRegistry)) expect(fn).not.toHaveBeenCalled();
expect(prisma.auditLog.create).not.toHaveBeenCalled();
expect(transaction).not.toHaveBeenCalled();
} finally { await app.close(); }
});
const terminated = { status: "terminated", terminated_at: new Date("2026-04-04T08:30:00.000Z"), terminated_reason: "合成终止原因用于测试", terminated_by: ACTOR_ID };
it.each([
["PATCH", "", terminated, { summary: "合成修改" }, "terminated_is_final"],
["PATCH", "", { status: "archived" }, { summary: "合成修改" }, "archived_fields_locked"],
["POST", "/terminate", { status: "draft" }, { reason: "合成终止原因满足原长度要求" }, "terminate_not_allowed"],
["POST", "/corrections", { status: "active" }, { summary: "合成纠错说明满足原长度要求" }, "correction_requires_archived"],
["POST", "/renew", { status: "draft" }, undefined, "renew_not_allowed"],
])("explains %s %s state guard %s", async (method, suffix, row, payload, reason) => {
const stateful = buildStatefulPrisma([buildContractRow(row)]);
const { app, prisma } = await buildApp({ prisma: stateful.prisma });
try {
const response = await app.inject({ method, url: `/api/contracts-registry/${id}${suffix}`, payload });
expect(response.statusCode).toBe(409);
expect(response.json().error).toMatchObject({ code: "VALID_001", details: { status: reason, reasonCode: `contract_registry.${reason}` } });
expect(prisma.contractRegistry.create).not.toHaveBeenCalled();
expect(prisma.contractRegistry.updateMany).not.toHaveBeenCalled();
expect(prisma.auditLog.create).not.toHaveBeenCalled();
expect(stateful.state.contracts).toHaveLength(1);
} finally { await app.close(); }
});
it.each([
["PATCH", "", "active", { summary: "合成修改" }, "contract_state_conflict", "state_conflict"],
["POST", "/terminate", "active", { reason: "合成终止原因满足原长度要求" }, "terminate_conflict", "terminate_conflict"],
["POST", "/corrections", "archived", { summary: "合成纠错说明满足原长度要求" }, "correction_state_conflict", "correction_state_conflict"],
])("explains optimistic conflict for %s %s without success audit", async (method, suffix, status, payload, detail, reason) => {
const stateful = buildStatefulPrisma([buildContractRow({ status })]);
stateful.prisma.contractRegistry.updateMany.mockResolvedValueOnce({ count: 0 });
const { app, prisma } = await buildApp({ prisma: stateful.prisma });
try {
const response = await app.inject({ method, url: `/api/contracts-registry/${id}${suffix}`, payload });
expect(response.statusCode).toBe(409);
expect(response.json().error).toMatchObject({ code: "VALID_001", details: { status: detail, reasonCode: `contract_registry.${reason}` } });
expect(prisma.contractRegistry.updateMany).toHaveBeenCalledOnce();
expect(prisma.contractRegistry.create).not.toHaveBeenCalled();
expect(prisma.auditLog.create).not.toHaveBeenCalled();
expect(stateful.state.contracts[0].status).toBe(status);
} finally { await app.close(); }
});
it("retains renewal's attempted draft before source-version conflict", async () => {
const stateful = buildStatefulPrisma([buildContractRow()]);
stateful.prisma.contractRegistry.updateMany.mockResolvedValueOnce({ count: 0 });
const { app, prisma } = await buildApp({ prisma: stateful.prisma });
try {
const response = await app.inject({ method: "POST", url: `/api/contracts-registry/${id}/renew` });
expect(response.statusCode).toBe(409);
expect(response.json().error.details).toEqual({ status: "contract_state_conflict", reasonCode: "contract_registry.state_conflict" });
expect(prisma.contractRegistry.create).toHaveBeenCalledOnce();
expect(prisma.auditLog.create).not.toHaveBeenCalled();
// This fixture restores its own memory; it does not prove a PostgreSQL rollback.
expect(stateful.state.contracts).toHaveLength(1);
} finally { await app.close(); }
});
it("keeps feature denial before business access", async () => {
const { app, prisma } = await buildApp();
prisma.featureFlag.findUnique.mockImplementation(async ({ where }) => ({ ...buildFeatureFlagRecord(where.flag_key), override_mode: "force_off" }));
try {
const response = await app.inject({ method: "GET", url: "/api/contracts-registry" });
expect(response.statusCode).toBe(409);
expect(response.json().error).toMatchObject({ code: "VALID_001", details: { reasonCode: "contract_registry.feature_disabled", featureFlag: "project_linking_v1_disabled", reason: expect.any(String) } });
for (const fn of Object.values(prisma.contractRegistry)) expect(fn).not.toHaveBeenCalled();
expect(prisma.auditLog.create).not.toHaveBeenCalled();
} finally { await app.close(); }
});
it("preserves management permission ahead of malformed input", async () => {
const { app, prisma } = await buildApp({ actor: { id: ACTOR_ID, permissionCodes: [] } });
try {
const response = await app.inject({ method: "POST", url: "/api/contracts-registry", payload: {} });
expect(response.statusCode).toBe(403);
expect(response.json().error.code).toBe("PERM_001");
expect(prisma.contractRegistry.create).not.toHaveBeenCalled();
} finally { await app.close(); }
});
});