A2-A4-A9 完善报销码值与查重回归
这个提交包含在:
@@ -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 });
|
||||
});
|
||||
|
||||
|
||||
在新工单中引用
屏蔽一个用户