function toDateOnly(value) { if (!value) { return null; } const date = new Date(`${String(value).slice(0, 10)}T00:00:00.000Z`); return Number.isNaN(date.getTime()) ? null : date; } function toDateTime(value) { if (!value) { return null; } const date = new Date(value); return Number.isNaN(date.getTime()) ? null : date; } function toMoneyString(value) { if (value == null || value === "") { return null; } const numeric = Number(value); return Number.isFinite(numeric) ? numeric.toFixed(2) : null; } function normalizeTravelImportRows(rows) { if (!Array.isArray(rows)) { return []; } return rows .map((item) => { const source = String(item?.source ?? "manual").trim().toLowerCase(); const externalOrderNo = String(item?.externalOrderNo ?? "").trim(); const employeeId = String(item?.employeeId ?? "").trim(); if (!externalOrderNo || !employeeId) { return null; } const hasTravelRequestId = Object.prototype.hasOwnProperty.call(item ?? {}, "travelRequestId"); const rawTravelRequestId = String(item?.travelRequestId ?? "").trim(); return { source, externalOrderNo, employeeId, linkedReimbursementId: item?.linkedReimbursementId && String(item.linkedReimbursementId).trim() ? String(item.linkedReimbursementId).trim() : null, travelRequestId: rawTravelRequestId || null, hasTravelRequestId, amount: toMoneyString(item?.amount), currency: String(item?.currency ?? "CNY").trim().toUpperCase() || "CNY", occurredAt: toDateOnly(item?.occurredAt), rawPayload: item ?? null, }; }) .filter(Boolean); } function normalizeTravelAuthorizationRows(rows) { if (!Array.isArray(rows)) { return []; } return rows .map((item) => { const source = String(item?.source ?? "manual").trim().toLowerCase(); const externalAuthNo = String(item?.externalAuthNo ?? item?.externalAuthNumber ?? "").trim(); const employeeId = String(item?.employeeId ?? "").trim(); const travelRequestId = String(item?.travelRequestId ?? "").trim(); if (!externalAuthNo || !employeeId || !travelRequestId) { return null; } return { source, externalAuthNo, employeeId, travelRequestId, budgetPoolId: item?.budgetPoolId && String(item.budgetPoolId).trim() ? String(item.budgetPoolId).trim() : null, authorizedAmount: toMoneyString(item?.authorizedAmount), settledAmount: toMoneyString(item?.settledAmount) ?? "0.00", reversedAmount: toMoneyString(item?.reversedAmount) ?? "0.00", currency: String(item?.currency ?? "CNY").trim().toUpperCase() || "CNY", occurredAt: toDateTime(item?.occurredAt), expiresAt: toDateTime(item?.expiresAt), status: String(item?.status ?? "authorized").trim().toLowerCase() || "authorized", rawPayload: item ?? null, }; }) .filter(Boolean); } function normalizeTravelTransactionRows(rows) { if (!Array.isArray(rows)) { return []; } return rows .map((item) => { const source = String(item?.source ?? "manual").trim().toLowerCase(); const externalTxnId = String(item?.externalTxnId ?? item?.externalTransactionId ?? "").trim(); const employeeId = String(item?.employeeId ?? "").trim(); const travelRequestId = String(item?.travelRequestId ?? "").trim(); if (!externalTxnId || !employeeId || !travelRequestId) { return null; } const rawCardAuthorizationId = String(item?.cardAuthorizationId ?? "").trim(); const rawTravelOrderId = String(item?.travelOrderId ?? "").trim(); return { source, externalTxnId, employeeId, travelRequestId, cardAuthorizationId: rawCardAuthorizationId || null, hasCardAuthorizationId: Object.prototype.hasOwnProperty.call(item ?? {}, "cardAuthorizationId"), travelOrderId: rawTravelOrderId || null, hasTravelOrderId: Object.prototype.hasOwnProperty.call(item ?? {}, "travelOrderId"), amount: toMoneyString(item?.amount), currency: String(item?.currency ?? "CNY").trim().toUpperCase() || "CNY", txnType: String(item?.txnType ?? "charge").trim().toLowerCase() || "charge", status: String(item?.status ?? "pending").trim().toLowerCase() || "pending", occurredAt: toDateTime(item?.occurredAt), rawPayload: item ?? null, }; }) .filter(Boolean); } module.exports = { normalizeTravelImportRows, normalizeTravelAuthorizationRows, normalizeTravelTransactionRows, };