A2-A4-A9 完善报销码值与查重回归

这个提交包含在:
2026-05-08 23:21:39 +08:00
父节点 b75ad226c6
当前提交 7a90e729f0
共修改 10 个文件,包含 615 行新增和 8 行删除
+29 -4
查看文件
@@ -22,6 +22,7 @@ import {
withdrawReimbursement,
linkReimbursementProject,
type ReimbursementItem,
type ReimbursementStatus,
} from "../api/reimbursements";
import {
bindInvoice,
@@ -66,6 +67,23 @@ import { buildPrecheckDialogMessage } from "../lib/precheck";
type ItemEditorState = { mode: "create" } | { mode: "edit"; itemId: string } | null;
type SelectedInvoiceMap = Record<string, string>;
const REIMBURSEMENT_STATUS_LABEL: Record<ReimbursementStatus, string> = {
draft: "草稿",
reviewing: "审批中",
approved: "已通过",
paid: "已支付",
rejected: "已驳回",
cancelled: "已取消",
};
const INVOICE_STATUS_LABEL: Record<string, string> = {
pending_ocr: "识别中",
ocr_failed: "识别失败",
available: "可添加",
bound: "已添加",
archived: "已归档",
};
function formatDateTime(value: string | null) {
if (!value) return "-";
return value.slice(0, 19).replace("T", " ");
@@ -87,6 +105,14 @@ function buildActionErrorMessage(error: unknown, fallbackMessage: string) {
return fallbackMessage;
}
function reimbursementStatusLabel(status: ReimbursementStatus) {
return REIMBURSEMENT_STATUS_LABEL[status] ?? status;
}
function invoiceStatusLabel(status: string) {
return INVOICE_STATUS_LABEL[status] ?? "未知状态";
}
function resolveErpSyncStatus(
exportState:
| {
@@ -939,7 +965,7 @@ export function ReimbursementDetailPage() {
</div>
<div className="rounded-lg border border-zinc-200 p-4 text-sm" data-testid="reimbursement-summary">
<div>状态:{detail.status}</div>
<div>状态:{reimbursementStatusLabel(detail.status)}</div>
<div>报销编号:{displayCode}</div>
<div>报销日期:{detail.reimbursementDate ?? "-"}</div>
<div>金额:{detail.totalAmount ?? "-"}</div>
@@ -1156,9 +1182,8 @@ export function ReimbursementDetailPage() {
>
<TableCell>
<div className="font-medium text-zinc-900">
{item.expenseType?.name ?? item.expenseTypeId}
{item.expenseType?.name ?? "未识别费用类型"}
</div>
<div className="text-xs text-zinc-500">{item.expenseType?.code ?? "-"}</div>
</TableCell>
<TableCell>{item.expenseDate ?? "-"}</TableCell>
<TableCell>
@@ -1295,7 +1320,7 @@ export function ReimbursementDetailPage() {
</div>
<div className="flex items-center gap-2">
<span className="rounded-full bg-zinc-100 px-2 py-1 text-xs text-zinc-600">
{item.status}
{invoiceStatusLabel(item.status)}
</span>
{canUnbindInvoices ? (
<Button
@@ -535,6 +535,9 @@ test.describe("R111 国内报销发票与附件双端体验", () => {
expect(bodyText).not.toContain("不绑定明细");
expect(bodyText).not.toContain("发票池");
expect(bodyText).not.toContain("OCR");
expect(bodyText).not.toMatch(/\bdraft\b/);
expect(bodyText).not.toMatch(/\bbound\b/);
expect(bodyText).not.toContain("business_client_meal");
await page.screenshot({ path: screenshotPath("pc-reimbursement-detail.png"), fullPage: true });
});
@@ -30,6 +30,13 @@ function screenshotPath(fileName: string) {
return path.join(SCREENSHOT_DIR, fileName.endsWith(".png") ? fileName : `${fileName}.png`);
}
async function expectNoRawReimbursementCodes(page: Page) {
const bodyText = await page.locator("body").innerText();
expect(bodyText).not.toMatch(/\bdraft\b/);
expect(bodyText).not.toMatch(/\bbound\b/);
expect(bodyText).not.toContain("business_client_meal");
}
function buildMe() {
return {
id: ownerId,
@@ -400,6 +407,7 @@ test.describe("R125 发票删除后重传", () => {
await expect(page.getByText("发票上传完成:1 张已添加")).toBeVisible({ timeout: 10_000 });
await page.keyboard.press("Escape");
await expect(page.getByText("北京悦海堂餐饮管理有限公司")).toBeVisible();
await expectNoRawReimbursementCodes(page);
await page.screenshot({ path: screenshotPath("pc-reupload-after-unbind.png"), fullPage: true });
});