feat(detail-page-structure-progress-actions): 统一详情处理进度与操作区

这个提交包含在:
2026-08-03 16:00:42 +08:00
父节点 8d428bd51d
当前提交 b7d5761067
共修改 31 个文件,包含 1453 行新增和 383 行删除
+50 -55
查看文件
@@ -33,6 +33,11 @@ const {
resolveEntityPrecheck,
} = require("../services/feeControl");
const { issueDocumentNumber } = require("../services/documentSequence");
const {
PAYMENT_PROCESSING_STAGE_DEFINITIONS,
buildPaymentProcessingSemantics,
requiresBankReceipt,
} = require("../services/paymentProcessingProgress");
const { openReadStream, readFileBuffer, StorageNotFoundError, writeBuffer } = require("../services/storage");
const {
MaterialPreviewError,
@@ -333,8 +338,8 @@ const PROCESSING_STAGE_DEFINITIONS = Object.freeze([
{ key: "submitted", label: "申请提交" },
{ key: "manager_approval", label: "主管审批" },
{ key: "finance_review", label: "财务复核" },
{ key: "payment_batch_review", label: "付款复核" },
{ key: "payment_and_receipt", label: "付款完成" },
PAYMENT_PROCESSING_STAGE_DEFINITIONS.review,
PAYMENT_PROCESSING_STAGE_DEFINITIONS.payment,
]);
function isoDateTimeOrNull(value) {
@@ -514,68 +519,48 @@ function buildProcessingProgress({ reimbursement, workflowInstance, paymentBatch
? { state: "current", result: "待安排", detail: "待财务安排付款" }
: { detail: "待财务安排付款" });
paymentStage.detail = "待付款复核后由财务登记,非审批环节";
} else if (batch.status === "draft") {
Object.assign(batchReviewStage, {
state: "current",
result: "待复核",
detail: "待非制单人复核付款",
targetHref: batchTargetHref,
} else {
const semantics = buildPaymentProcessingSemantics({
status: batch.status,
createdAt: batch.created_at,
reviewedAt: batch.reviewed_at,
executedAt: batch.executed_at,
cancelledAt: batch.cancelled_at,
cancelledReason: batch.cancelled_reason,
requiresBankReceipt: requiresBankReceipt(batch),
receiptCount: batch._count?.receipt_attachments ?? 0,
paymentOrderExportCount: batch.payment_order_export_count ?? 0,
});
Object.assign(paymentStage, {
detail: "待付款复核通过后由财务登记,非审批环节",
targetHref: batchTargetHref,
});
} else if (["reviewed", "executed", "failed"].includes(batch.status)) {
Object.assign(batchReviewStage, {
state: "completed",
actorName: paymentActorNames.get(batch.reviewed_by) ?? null,
occurredAt: isoDateTimeOrNull(batch.reviewed_at),
result: "通过",
detail: batch.status === "reviewed" ? "付款复核已通过" : null,
targetHref: batchTargetHref,
});
if (batch.status === "reviewed") {
Object.assign(paymentStage, {
state: "current",
result: "待付款",
detail: "待财务完成付款并登记回单,非审批环节",
targetHref: batchTargetHref,
});
} else if (batch.status === "failed") {
Object.assign(paymentStage, {
state: "exception",
actorName: paymentActorNames.get(batch.executed_by) ?? null,
occurredAt: isoDateTimeOrNull(batch.executed_at),
result: "付款失败",
detail: "付款失败,待财务重新处理,非审批环节",
const actorIds = {
reviewer: batch.reviewed_by,
executor: batch.executed_by,
canceller: batch.cancelled_by,
};
for (const [target, source] of [
[batchReviewStage, semantics.review],
[paymentStage, semantics.payment],
]) {
Object.assign(target, {
state: source.state,
actorName: source.actorSource
? paymentActorNames.get(actorIds[source.actorSource]) ?? null
: null,
occurredAt: isoDateTimeOrNull(source.occurredAt),
result: source.result,
comment: source.comment,
detail: source.detail,
targetHref: batchTargetHref,
});
}
} else if (batch.status === "cancelled") {
Object.assign(batchReviewStage, {
state: "exception",
actorName: paymentActorNames.get(batch.cancelled_by) ?? null,
occurredAt: isoDateTimeOrNull(batch.cancelled_at),
result: "批次已取消",
comment: batch.cancelled_reason ?? null,
detail: "付款安排已取消,待财务重新处理",
targetHref: batchTargetHref,
});
Object.assign(paymentStage, {
detail: "待财务重新安排付款,非审批环节",
targetHref: batchTargetHref,
});
}
if (batch?.status === "executed" || reimbursement.status === "paid") {
const receiptCount = batch?._count?.receipt_attachments ?? 0;
if (!batch && reimbursement.status === "paid") {
Object.assign(paymentStage, {
state: "completed",
actorName: reimbursement.paid_by_employee?.name ?? paymentActorNames.get(batch?.executed_by) ?? null,
occurredAt: isoDateTimeOrNull(reimbursement.paid_at ?? batch?.executed_at),
actorName: reimbursement.paid_by_employee?.name ?? null,
occurredAt: isoDateTimeOrNull(reimbursement.paid_at),
result: "已付款",
detail: receiptCount > 0 ? "付款和回单已留存" : "付款已完成,回单待留存",
targetHref: batchTargetHref,
detail: "付款已完成,回单待留存",
});
}
@@ -2591,6 +2576,10 @@ async function reimbursementRoutes(fastify) {
select: {
id: true,
status: true,
entity_type: true,
default_payment_channel: true,
payment_order_export_count: true,
created_at: true,
created_by: true,
reviewed_at: true,
reviewed_by: true,
@@ -2599,6 +2588,12 @@ async function reimbursementRoutes(fastify) {
cancelled_at: true,
cancelled_by: true,
cancelled_reason: true,
items: {
select: {
status: true,
payment_channel_override: true,
},
},
_count: {
select: { receipt_attachments: { where: { archived_at: null } } },
},