feat(G2-1,G2-2,G2-3): 扩展预算编号与工作流跨域桥接

这个提交包含在:
2026-03-07 18:59:44 +08:00
父节点 09b6f4fbb0
当前提交 52558f22c3
共修改 12 个文件,包含 1211 行新增和 175 行删除
+12 -39
查看文件
@@ -2,7 +2,7 @@ const { z } = require("zod");
const { AppError } = require("../utils/AppError");
const { sendData } = require("../utils/reply");
const { buildAuditLogData } = require("../utils/audit");
const { compactDateString, currentDateString, parseDateOnlyString } = require("../utils/date");
const { currentDateString, parseDateOnlyString } = require("../utils/date");
const { findActiveDelegationForDelegate } = require("../utils/delegation");
const {
createNotifications,
@@ -12,6 +12,8 @@ const { sendNotificationEmailSafe } = require("../services/email");
const { enqueueIntegrationEvent } = require("../services/integrations/outbox");
const { buildErpPayload } = require("../services/integrations/erp");
const { FEATURE_FLAG_KEYS, evaluateFeatureFlag } = require("../services/featureFlags");
const { resolveApplicableBudgetPoolByContext } = require("../services/budgetPool");
const { issueDocumentNumber } = require("../services/documentSequence");
const {
buildWorkflowStepsFromApprovalRule,
resolveActionableStep,
@@ -90,12 +92,6 @@ function ensureApplicantOrAdmin(request, reimbursement) {
}
}
function formatFormNumber(sequence, timezone) {
const datePart = compactDateString(timezone);
const serial = String(sequence.current_number).padStart(sequence.number_length, "0");
return `${sequence.prefix}${sequence.separator}${datePart}${sequence.separator}${serial}`;
}
function parseWithdrawBody(body) {
const bodySchema = z.object({
comment: z.string().trim().min(1).max(500).optional(),
@@ -350,14 +346,9 @@ async function loadApprovalRuleForAmount(tx, totalAmount) {
}
async function resolveApplicableBudgetPool(tx, reimbursement) {
return tx.budgetPool.findFirst({
where: {
status: "active",
OR: [{ department_id: reimbursement.department_id }, { department_id: null }],
period_start: { lte: reimbursement.reimbursement_date },
period_end: { gte: reimbursement.reimbursement_date },
},
orderBy: [{ department_id: "desc" }, { created_at: "asc" }],
return resolveApplicableBudgetPoolByContext(tx, {
departmentId: reimbursement.department_id,
effectiveDate: reimbursement.reimbursement_date,
});
}
@@ -1204,7 +1195,7 @@ async function reimbursementRoutes(fastify) {
await fastify.prisma.$transaction(async (tx) => {
const reimbursementRows = await tx.$queryRaw`
SELECT id, owner_id, submitter_id, department_id, status, form_number, total_amount
SELECT id, owner_id, submitter_id, department_id, reimbursement_date, status, form_number, total_amount
FROM "reimbursement_forms"
WHERE id = ${reimbursementId}::uuid
FOR UPDATE
@@ -1371,31 +1362,13 @@ async function reimbursementRoutes(fastify) {
let formNumber = reimbursement.form_number;
if (!formNumber) {
const sequence = await tx.documentSequence.upsert({
where: { document_type: "reimbursement" },
update: {
current_number: { increment: 1 },
updated_by: request.employee.id,
},
create: {
document_type: "reimbursement",
prefix: "BX",
date_format: "YYYYMMDD",
separator: "-",
number_length: 4,
reset_period: "none",
current_number: 1,
updated_by: request.employee.id,
},
select: {
prefix: true,
separator: true,
number_length: true,
current_number: true,
},
const issued = await issueDocumentNumber(tx, {
documentType: "reimbursement",
actorId: request.employee.id,
timeZone: fastify.config.appTimezone,
});
formNumber = formatFormNumber(sequence, fastify.config.appTimezone);
formNumber = issued.documentNumber;
}
await tx.reimbursementForm.update({