From df5efb2ca2b0cf74d9160ffe2b6c215c4ddc9c99 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期四, 21 五月 2026 17:48:17 +0800
Subject: [PATCH] 差旅报销费用报销
---
src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js | 623 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 623 insertions(+), 0 deletions(-)
diff --git a/src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js b/src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js
new file mode 100644
index 0000000..7a82873
--- /dev/null
+++ b/src/views/officeProcessAutomation/ReimburseManage/shared/finReimbursementMappers.js
@@ -0,0 +1,623 @@
+import dayjs from "dayjs";
+import { APPROVAL_MODULE_KEYS } from "../../ApproveManage/approve-shared/approvalModuleRegistry.js";
+import { mapSignModeToApi } from "../../ApproveManage/approve-template/approveTemplateConstants.js";
+import { EXPENSE_CATEGORY_OPTIONS } from "../cost-reimburse/costReimburseUtils.js";
+import { EXPENSE_SUBJECT_OPTIONS } from "../travel-reimburse/travelReimburseUtils.js";
+import { applyFinReimbursementDetailEnrichment } from "./finReimbursementDetailExtras.js";
+
+/** 鎶ラ攢绫诲瀷锛�1-宸梾鎶ラ攢锛�2-璐圭敤鎶ラ攢 */
+export const FIN_REIMBURSEMENT_TYPE = {
+ TRAVEL: "1",
+ COST: "2",
+};
+
+const REIMBURSEMENT_TYPE_LABEL = {
+ [FIN_REIMBURSEMENT_TYPE.TRAVEL]: "宸梾鎶ラ攢",
+ [FIN_REIMBURSEMENT_TYPE.COST]: "璐圭敤鎶ラ攢",
+};
+
+/** 褰掍竴鍖栨姤閿�绫诲瀷锛�1-宸梾锛�2-璐圭敤 */
+export function normalizeReimbursementType(val) {
+ const s = String(val ?? "").trim();
+ if (s === "1" || s === FIN_REIMBURSEMENT_TYPE.TRAVEL) {
+ return FIN_REIMBURSEMENT_TYPE.TRAVEL;
+ }
+ if (s === "2" || s === FIN_REIMBURSEMENT_TYPE.COST) {
+ return FIN_REIMBURSEMENT_TYPE.COST;
+ }
+ return "";
+}
+
+export function reimbursementTypeLabel(type) {
+ return REIMBURSEMENT_TYPE_LABEL[normalizeReimbursementType(type)] || "鈥�";
+}
+
+export function getModuleKeyByReimbursementType(type) {
+ const t = normalizeReimbursementType(type);
+ if (t === FIN_REIMBURSEMENT_TYPE.TRAVEL) {
+ return APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE;
+ }
+ if (t === FIN_REIMBURSEMENT_TYPE.COST) {
+ return APPROVAL_MODULE_KEYS.COST_REIMBURSE;
+ }
+ return "";
+}
+
+/** 浼樺厛鎺ュ彛 reimbursementType锛屽叾娆¢〉闈� moduleKey / 鍏ュ弬 */
+export function resolveReimbursementType(raw, fallback) {
+ const fromApi = normalizeReimbursementType(raw?.reimbursementType);
+ if (fromApi) return fromApi;
+ return (
+ normalizeReimbursementType(fallback) ||
+ getReimbursementTypeByModuleKey(fallback) ||
+ ""
+ );
+}
+
+export function isTravelReimbursementType(type) {
+ return (
+ resolveReimbursementType({ reimbursementType: type }, type) ===
+ FIN_REIMBURSEMENT_TYPE.TRAVEL
+ );
+}
+
+export function filterRowsByReimbursementType(rows, expectedType) {
+ const expected = normalizeReimbursementType(expectedType);
+ if (!expected) return rows || [];
+ return (rows || []).filter((row) => {
+ const t = resolveReimbursementType(row, expected);
+ return t === expected;
+ });
+}
+
+export function getReimbursementTypeByModuleKey(moduleKey) {
+ if (moduleKey === APPROVAL_MODULE_KEYS.TRAVEL_REIMBURSE) {
+ return FIN_REIMBURSEMENT_TYPE.TRAVEL;
+ }
+ if (moduleKey === APPROVAL_MODULE_KEYS.COST_REIMBURSE) {
+ return FIN_REIMBURSEMENT_TYPE.COST;
+ }
+ return "";
+}
+
+export function unwrapFinReimbursementPage(res) {
+ const data = res?.data ?? res;
+ if (!data || typeof data !== "object") {
+ return { records: [], total: 0 };
+ }
+ if (Array.isArray(data.records)) {
+ return { records: data.records, total: Number(data.total ?? 0) };
+ }
+ const nested = data.data;
+ if (nested && typeof nested === "object" && Array.isArray(nested.records)) {
+ return { records: nested.records, total: Number(nested.total ?? 0) };
+ }
+ return { records: [], total: 0 };
+}
+
+/** 璇︽儏鎺ュ彛 data 瑙e寘 */
+export function unwrapFinReimbursementDetail(res) {
+ const data = res?.data ?? res;
+ if (!data || typeof data !== "object") return {};
+ if (data.billNo != null || data.id != null || data.reimbursementType != null) {
+ return data;
+ }
+ const nested = data.data;
+ if (nested && typeof nested === "object" && !Array.isArray(nested)) {
+ return nested;
+ }
+ if (data.finReimbursementDto && typeof data.finReimbursementDto === "object") {
+ return data.finReimbursementDto;
+ }
+ return data;
+}
+
+/** 璇︽儏鏌ヨ鍙傛暟锛坬uery finReimbursementDto锛� */
+export function buildFinReimbursementDetailParams(id) {
+ const raw = id?.id != null ? id.id : id;
+ const n = toNumber(raw);
+ return { finReimbursementDto: { id: n != null ? n : raw } };
+}
+
+/** 璇︽儏 DTO 鈫� 椤甸潰琛岋紙鎸� reimbursementType 鏄犲皠锛屽惈 tasks / storageBlobVOList锛� */
+export function mapFinReimbursementDetailRow(raw, reimbursementTypeOrModuleKey) {
+ const type = resolveReimbursementType(raw, reimbursementTypeOrModuleKey);
+ let mapped = {};
+ if (type === FIN_REIMBURSEMENT_TYPE.TRAVEL) {
+ mapped = mapTravelReimbursementRow(raw);
+ } else if (type === FIN_REIMBURSEMENT_TYPE.COST) {
+ mapped = mapCostReimbursementRow(raw);
+ } else {
+ mapped = raw || {};
+ }
+ return {
+ ...applyFinReimbursementDetailEnrichment(mapped, raw),
+ reimbursementType: type,
+ reimbursementTypeLabel: reimbursementTypeLabel(type),
+ moduleKey: getModuleKeyByReimbursementType(type),
+ };
+}
+
+/** 鍗曟嵁鐘舵�� 鈫� 椤甸潰 approvalResult锛堝吋瀹� statusLabel锛� */
+export function mapBillStatusToApprovalResult(billStatus) {
+ const upper = String(billStatus ?? "").trim().toUpperCase();
+ if (upper === "DRAFT") return "draft";
+ if (upper === "IN_APPROVAL") return "pending";
+ if (upper === "APPROVED") return "approved";
+ if (upper === "REJECTED") return "rejected";
+ if (upper === "WITHDRAWN") return "cancelled";
+ if (upper === "PAID") return "paid";
+ return "pending";
+}
+
+function pickApplicantQuery(searchForm = {}) {
+ const kw = (searchForm.applicantKeyword || "").trim();
+ if (!kw) return {};
+ if (/[\u4e00-\u9fa5]/.test(kw)) return { applicantName: kw };
+ return { applicantCode: kw };
+}
+
+/** 缁勮 listPage 鏌ヨ鍙傛暟锛坧age + finReimbursementDto锛� */
+export function buildFinReimbursementListParams({
+ page,
+ searchForm,
+ reimbursementType,
+ extraDto = {},
+}) {
+ const dto = {
+ reimbursementType,
+ ...pickApplicantQuery(searchForm),
+ ...(extraDto && typeof extraDto === "object" ? extraDto : {}),
+ };
+
+ if (searchForm?.billStatus) {
+ dto.billStatus = searchForm.billStatus;
+ }
+
+ const range =
+ searchForm?.createTimeRange ??
+ searchForm?.applyDateRange ??
+ (searchForm?.applyTimeFrom || searchForm?.applyTimeTo
+ ? [searchForm.applyTimeFrom, searchForm.applyTimeTo]
+ : null);
+
+ if (Array.isArray(range) && range[0]) {
+ dto.createTimeStart = range[0];
+ }
+ if (Array.isArray(range) && range[1]) {
+ dto.createTimeEnd = range[1];
+ }
+
+ if (reimbursementType === FIN_REIMBURSEMENT_TYPE.TRAVEL) {
+ if (searchForm?.travelStartFrom) {
+ dto.startTimeStart = searchForm.travelStartFrom;
+ }
+ if (searchForm?.travelEndTo) {
+ dto.endTimeEnd = searchForm.travelEndTo;
+ }
+ }
+
+ return {
+ page: {
+ current: page.current,
+ size: page.size,
+ },
+ finReimbursementDto: dto,
+ };
+}
+
+function pickTravelField(obj, keys) {
+ if (!obj || typeof obj !== "object") return "";
+ for (const key of keys) {
+ const v = obj[key];
+ if (v != null && v !== "") return v;
+ }
+ return "";
+}
+
+/** 鍏煎 list/detail 澶氱宸梾瀛愬璞$粨鏋� */
+export function pickTravelFromRow(row) {
+ if (!row || typeof row !== "object") return {};
+ const nested =
+ (row.travel && typeof row.travel === "object" ? row.travel : null) ||
+ row.finReimbursementTravel ||
+ row.finReimbursementTravelDto ||
+ row.travelDto ||
+ row.travelVO ||
+ {};
+ const src =
+ nested && typeof nested === "object" && Object.keys(nested).length
+ ? nested
+ : row;
+ return {
+ startTime: pickTravelField(src, [
+ "startTime",
+ "travelStartTime",
+ "startDate",
+ "travelStartDate",
+ "departureTime",
+ ]),
+ endTime: pickTravelField(src, [
+ "endTime",
+ "travelEndTime",
+ "endDate",
+ "travelEndDate",
+ "returnTime",
+ ]),
+ travelDays: src.travelDays,
+ departureCity: pickTravelField(src, [
+ "departureCity",
+ "departurePlace",
+ "departure",
+ ]),
+ destinationCity: pickTravelField(src, [
+ "destinationCity",
+ "destination",
+ "destinationPlace",
+ ]),
+ hotelStandard: src.hotelStandard,
+ lodgingDays: src.lodgingDays ?? src.hotelDays,
+ mealAllowance: src.mealAllowance ?? src.livingSubsidy,
+ transportAllowance: src.transportAllowance ?? src.transportSubsidy,
+ lodgingLimit: src.lodgingLimit,
+ withinStandard: src.withinStandard,
+ standardTag: src.standardTag || "",
+ id: src.id,
+ reimbursementId: src.reimbursementId,
+ };
+}
+
+/** 鍒楄〃/璇︽儏鏃堕棿灞曠ず锛圛SO 鈫� YYYY-MM-DD HH:mm:ss锛� */
+export function formatReimbursementDateTime(val) {
+ if (val == null || val === "") return "";
+ const d = dayjs(val);
+ if (!d.isValid()) return String(val);
+ const raw = String(val);
+ const hasTime = raw.includes("T") || /:\d{2}/.test(raw);
+ return hasTime ? d.format("YYYY-MM-DD HH:mm:ss") : d.format("YYYY-MM-DD");
+}
+
+/** 鎺ュ彛琛� 鈫� 宸梾鎶ラ攢鍒楄〃琛岋紙鍏煎 useTravelReimburse 瀛楁锛� */
+export function mapTravelReimbursementRow(row) {
+ if (!row) return {};
+ const travel = pickTravelFromRow(row);
+ const details = Array.isArray(row.details) ? row.details : [];
+
+ const base = {
+ ...row,
+ id: row.id,
+ reimbursementId: row.id,
+ approvalInstanceId: row.approvalInstanceId,
+ reimburseNo: row.billNo || "",
+ applicantId: row.applicantId,
+ applicantNo: row.applicantCode || "",
+ applicantName: row.applicantName || "",
+ employeeNo: row.applicantCode || "",
+ employeeName: row.applicantName || "",
+ applicantDeptName: row.applicantDeptName || "",
+ reimburseReason: row.reason || "",
+ travelStartTime: formatReimbursementDateTime(travel.startTime),
+ travelEndTime: formatReimbursementDateTime(travel.endTime),
+ travelDays: travel.travelDays,
+ departurePlace: travel.departureCity || "",
+ destination: travel.destinationCity || "",
+ hotelStandard: travel.hotelStandard,
+ hotelDays: travel.lodgingDays,
+ livingSubsidy: travel.mealAllowance,
+ transportSubsidy: travel.transportAllowance,
+ lodgingLimit: travel.lodgingLimit,
+ needSpecialApproval: travel.withinStandard === "0" || travel.withinStandard === 0,
+ standardTag: travel.standardTag || "",
+ applyAmount: row.applyAmount,
+ payee: row.payeeName || "",
+ payeeAccount: row.payeeAccount || "",
+ payeeBank: row.payeeBank || "",
+ billStatus: row.billStatus,
+ approvalResult: mapBillStatusToApprovalResult(row.billStatus),
+ createTime: formatReimbursementDateTime(row.createTime),
+ expenseDetails: details.map((d) => ({
+ ...d,
+ expenseSubject: d.expenseCategory,
+ })),
+ travel:
+ row.travel && typeof row.travel === "object" && Object.keys(row.travel).length
+ ? row.travel
+ : travel,
+ details,
+ nodes: row.nodes || [],
+ approvalFlowNodes: mapNodesToFormFlow(row.nodes),
+ tasks: row.tasks || [],
+ };
+ return base;
+}
+
+/** 鎺ュ彛琛� 鈫� 璐圭敤鎶ラ攢鍒楄〃琛岋紙鍏煎 useCostReimburse 瀛楁锛� */
+export function mapCostReimbursementRow(row) {
+ if (!row) return {};
+ const details = Array.isArray(row.details) ? row.details : [];
+
+ return {
+ ...row,
+ id: row.id,
+ reimbursementId: row.id,
+ approvalInstanceId: row.approvalInstanceId,
+ reimburseNo: row.billNo || "",
+ applicantId: row.applicantId,
+ applicantNo: row.applicantCode || "",
+ applicantName: row.applicantName || "",
+ employeeNo: row.applicantCode || "",
+ employeeName: row.applicantName || "",
+ applicantDeptName: row.applicantDeptName || "",
+ reimburseReason: row.reason || "",
+ expenseCategory: row.expenseType || "",
+ applyAmount: row.applyAmount,
+ applyTime: row.createTime || "",
+ payee: row.payeeName || "",
+ payeeAccount: row.payeeAccount || "",
+ bankBranch: row.payeeBank || "",
+ billStatus: row.billStatus,
+ approvalResult: mapBillStatusToApprovalResult(row.billStatus),
+ createTime: formatReimbursementDateTime(row.createTime),
+ expenseDetails: details.map((d) => ({
+ ...d,
+ expenseSubject: d.expenseCategory,
+ })),
+ details,
+ nodes: row.nodes || [],
+ approvalFlowNodes: mapNodesToFormFlow(row.nodes),
+ tasks: row.tasks || [],
+ };
+}
+
+function toNumber(val) {
+ if (val == null || val === "") return undefined;
+ const n = Number(val);
+ return Number.isNaN(n) ? undefined : n;
+}
+
+function expenseSubjectToCategory(subject) {
+ const hit = EXPENSE_SUBJECT_OPTIONS.find((x) => x.value === subject);
+ return hit?.label || subject || "";
+}
+
+function expenseCategoryToType(category) {
+ const hit = EXPENSE_CATEGORY_OPTIONS.find((x) => x.value === category);
+ return hit?.label || category || "";
+}
+
+/** 鎺ュ彛 nodes 鈫� 椤甸潰瀹℃壒娴侊紙鍗曞鎵逛汉鑺傜偣锛� */
+export function mapNodesToFormFlow(nodes = []) {
+ return (Array.isArray(nodes) ? nodes : []).map((n, i) => {
+ const first = Array.isArray(n.approvers) ? n.approvers[0] : null;
+ return {
+ ...n,
+ nodeOrder: n.levelNo ?? n.nodeOrder ?? i + 1,
+ signMode: String(n.approveType || "").toUpperCase() === "OR" ? "or_sign" : "countersign",
+ approverId: first?.approverId ?? n.approverId ?? null,
+ approverName: first?.approverName ?? n.approverName ?? "",
+ };
+ });
+}
+
+/** 椤甸潰瀹℃壒鑺傜偣 鈫� 鎺ュ彛 nodes */
+export function mapApprovalFlowNodesToApi(nodes = [], templateId) {
+ const list = Array.isArray(nodes) ? nodes : [];
+ return list
+ .map((n, i) => {
+ let approvers = [];
+ if (Array.isArray(n.approvers) && n.approvers.length) {
+ approvers = n.approvers
+ .filter((a) => a?.approverId != null && a.approverId !== "")
+ .map((a, idx) => ({
+ id: a.id,
+ nodeId: a.nodeId,
+ templateId: a.templateId ?? templateId,
+ approverId: toNumber(a.approverId) ?? a.approverId,
+ approverName: a.approverName || "",
+ sortNo: a.sortNo ?? idx + 1,
+ }));
+ } else if (n.approverId != null && n.approverId !== "") {
+ approvers = [
+ {
+ approverId: toNumber(n.approverId) ?? n.approverId,
+ approverName: n.approverName || "",
+ sortNo: 1,
+ },
+ ];
+ }
+ if (!approvers.length) return null;
+
+ const node = {
+ levelNo: n.levelNo ?? n.nodeOrder ?? i + 1,
+ approveType: n.approveType || mapSignModeToApi(n.signMode),
+ approvers,
+ };
+ if (n.id != null) node.id = n.id;
+ if (n.templateId != null) node.templateId = n.templateId;
+ else if (templateId != null) node.templateId = templateId;
+ return node;
+ })
+ .filter(Boolean);
+}
+
+function mapDetailsToApi(details = []) {
+ return (details || []).map((d, i) => {
+ const item = {
+ rowNo: d.rowNo ?? i + 1,
+ invoiceDate: d.invoiceDate || undefined,
+ expenseCategory: expenseSubjectToCategory(d.expenseSubject ?? d.expenseCategory),
+ amount: toNumber(d.amount),
+ description: d.description || "",
+ invoiceNo: d.invoiceNo || undefined,
+ invoiceType: d.invoiceType || undefined,
+ invoiceAmount: toNumber(d.invoiceAmount),
+ taxRate: toNumber(d.taxRate),
+ taxAmount: toNumber(d.taxAmount),
+ remark: d.remark || undefined,
+ };
+ if (d.id != null && !String(d.id).startsWith("ed_")) {
+ item.id = toNumber(d.id) ?? d.id;
+ }
+ if (d.reimbursementId != null) item.reimbursementId = toNumber(d.reimbursementId);
+ return item;
+ });
+}
+
+function sumDetailAmount(details = []) {
+ const sum = (details || []).reduce((s, d) => s + (Number(d.amount) || 0), 0);
+ return Math.round(sum * 100) / 100;
+}
+
+/** 淇敼鏃惰ˉ榻愪富琛ㄤ笌瀛愯〃鍏宠仈 ID */
+function applyReimbursementRelations(dto) {
+ const rid = dto?.id;
+ if (rid == null) return dto;
+ if (dto.travel && typeof dto.travel === "object") {
+ dto.travel.reimbursementId = rid;
+ }
+ if (Array.isArray(dto.details)) {
+ dto.details.forEach((d) => {
+ d.reimbursementId = rid;
+ });
+ }
+ return dto;
+}
+
+function resolveReimbursementId(form) {
+ const rawId = form?.reimbursementId ?? form?.id;
+ if (rawId == null || rawId === "" || String(rawId).startsWith("local_")) {
+ return undefined;
+ }
+ return toNumber(rawId) ?? rawId;
+}
+
+/** 宸梾琛ㄥ崟 鈫� FinReimbursementDto */
+export function buildTravelReimbursementSaveDto(form, { computeTravelDays } = {}) {
+ const details = mapDetailsToApi(form.expenseDetails);
+ const detailTotal = sumDetailAmount(form.expenseDetails);
+ const applyAmount = toNumber(form.applyAmount) ?? detailTotal;
+ const travelDays =
+ form.travelDays != null
+ ? toNumber(form.travelDays)
+ : computeTravelDays?.(form.travelStartTime, form.travelEndTime);
+
+ const dto = {
+ reimbursementType: FIN_REIMBURSEMENT_TYPE.TRAVEL,
+ expenseType: "宸梾璐�",
+ applicantId: toNumber(form.applicantId),
+ applicantCode: form.employeeNo || form.applicantNo || "",
+ applicantName: form.employeeName || form.applicantName || "",
+ applicantDeptId: toNumber(form.applicantDeptId),
+ applicantDeptName: form.applicantDeptName || form.deptName || "",
+ reason: (form.reimburseReason || "").trim(),
+ applyAmount,
+ detailTotalAmount: detailTotal,
+ payeeName: form.payee || "",
+ payeeAccount: form.payeeAccount || undefined,
+ payeeBank: form.payeeBank || undefined,
+ billStatus: "IN_APPROVAL",
+ deptId: toNumber(form.deptId),
+ travel: {
+ startTime: form.travelStartTime || undefined,
+ endTime: form.travelEndTime || undefined,
+ travelDays,
+ departureCity: form.departurePlace || "",
+ destinationCity: form.destination || "",
+ hotelStandard: toNumber(form.hotelStandard),
+ lodgingDays: toNumber(form.hotelDays),
+ mealAllowance: toNumber(form.livingSubsidy),
+ transportAllowance: toNumber(form.transportSubsidy),
+ lodgingLimit: toNumber(form.lodgingLimit),
+ standardTag: form.standardTag || (form.needSpecialApproval ? "瓒呮爣鐗规壒" : "鍦ㄦ爣鍑嗚寖鍥村唴"),
+ withinStandard: form.needSpecialApproval ? "0" : "1",
+ },
+ details,
+ nodes: mapApprovalFlowNodesToApi(form.approvalFlowNodes, form.templateId),
+ };
+
+ const id = resolveReimbursementId(form);
+ if (id != null) dto.id = id;
+ if (form.billNo || form.reimburseNo) {
+ dto.billNo = form.billNo || form.reimburseNo;
+ }
+ if (form.approvalInstanceId != null) {
+ dto.approvalInstanceId = toNumber(form.approvalInstanceId);
+ }
+ if (form.approveProcessId != null) {
+ dto.approveProcessId = toNumber(form.approveProcessId);
+ }
+ if (form.travel?.id != null) dto.travel.id = toNumber(form.travel.id);
+
+ return applyReimbursementRelations(dto);
+}
+
+/** 璐圭敤琛ㄥ崟 鈫� FinReimbursementDto */
+export function buildCostReimbursementSaveDto(form) {
+ const details = mapDetailsToApi(form.expenseDetails);
+ const detailTotal = sumDetailAmount(form.expenseDetails);
+ const applyAmount = toNumber(form.applyAmount) ?? detailTotal;
+
+ const dto = {
+ reimbursementType: FIN_REIMBURSEMENT_TYPE.COST,
+ expenseType: expenseCategoryToType(form.expenseCategory),
+ applicantId: toNumber(form.applicantId),
+ applicantCode: form.employeeNo || form.applicantNo || "",
+ applicantName: form.employeeName || form.applicantName || "",
+ applicantDeptId: toNumber(form.applicantDeptId),
+ applicantDeptName: form.applicantDeptName || form.deptName || "",
+ reason: (form.reimburseReason || "").trim(),
+ applyAmount,
+ detailTotalAmount: detailTotal,
+ payeeName: form.payee || "",
+ payeeAccount: form.payeeAccount || "",
+ payeeBank: form.bankBranch || form.payeeBank || "",
+ billStatus: "IN_APPROVAL",
+ deptId: toNumber(form.deptId),
+ details,
+ nodes: mapApprovalFlowNodesToApi(form.approvalFlowNodes, form.templateId),
+ };
+
+ const id = resolveReimbursementId(form);
+ if (id != null) dto.id = id;
+ if (form.billNo || form.reimburseNo) {
+ dto.billNo = form.billNo || form.reimburseNo;
+ }
+ if (form.approvalInstanceId != null) {
+ dto.approvalInstanceId = toNumber(form.approvalInstanceId);
+ }
+ if (form.approveProcessId != null) {
+ dto.approveProcessId = toNumber(form.approveProcessId);
+ }
+
+ return applyReimbursementRelations(dto);
+}
+
+/** 鍒楄〃琛屼富閿紙鍒犻櫎/淇敼鐢� fin_reimbursement.id锛� */
+export function resolveReimbursementDeleteId(row) {
+ const raw = row?.reimbursementId ?? row?.id;
+ if (raw == null || raw === "" || String(raw).startsWith("local_")) {
+ return undefined;
+ }
+ const n = toNumber(raw);
+ return n != null ? n : raw;
+}
+
+/** 鏄惁鍏佽鍒犻櫎锛堝鎵逛腑銆佸凡閫氳繃銆佸凡浠樻涓嶅彲鍒狅級 */
+export function canDeleteReimbursementRow(row) {
+ const key = mapBillStatusToApprovalResult(
+ row?.billStatus ?? row?.approvalResult ?? row?.status
+ );
+ return key !== "pending" && key !== "approved" && key !== "paid";
+}
+
+/** 鏄惁鍏佽缂栬緫锛堜笌鍒犻櫎瑙勫垯涓�鑷达級 */
+export function canEditReimbursementRow(row) {
+ return canDeleteReimbursementRow(row);
+}
+
+/** 淇敼鍦烘櫙蹇呴』甯︿富閿� ID */
+export function validateReimbursementPersistDto(dto, isEdit) {
+ if (!isEdit) return { ok: true };
+ if (dto?.id != null && dto.id !== "") return { ok: true };
+ return { ok: false, message: "鏃犳硶淇敼锛氱己灏戞姤閿�鍗� ID" };
+}
--
Gitblit v1.9.3