收敛报销退回动作与审批布局 [09-04-approval-return-reject-actions]
这个提交包含在:
@@ -11,7 +11,7 @@ export function resolveReimbursementApprovalBlockReason({
|
||||
online: boolean;
|
||||
snapshotParseStatus: string | null | undefined;
|
||||
}) {
|
||||
if (!online) return "报销审批需联网核对完整报销内容;当前不能通过、退回、驳回或转审。";
|
||||
if (!online) return "报销审批需联网核对完整报销内容;当前不能通过、退回修改或转审。";
|
||||
if (amountIntegrity && amountIntegrity !== "ready") {
|
||||
return reimbursementAmountIntegrityMessage(amountIntegrity);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import type { WorkflowTaskDetail } from "../../api/workflow";
|
||||
import {
|
||||
approveWorkflowTask,
|
||||
fetchWorkflowApprovers,
|
||||
rejectWorkflowTask,
|
||||
returnWorkflowTask,
|
||||
transferWorkflowTask,
|
||||
} from "../../api/workflow";
|
||||
@@ -40,10 +39,11 @@ import { resolveReimbursementApprovalBlockReason } from "../../lib/reimbursement
|
||||
import { buildReimbursementWorkspaceModel } from "../../lib/reimbursementWorkspace";
|
||||
import { resolveErrorMessage as resolveUserErrorMessage } from "../../lib/resolveErrorMessage";
|
||||
import { isWecomWebView } from "../../lib/runtimeContext";
|
||||
import { getWorkflowConfirmCopy, type WorkflowConfirmAction } from "../../lib/workflowActionConfirm";
|
||||
import { getWorkflowConfirmCopy } from "../../lib/workflowActionConfirm";
|
||||
import { openFileInBrowser, previewFileInWecom } from "../../lib/wecomFilePreview";
|
||||
|
||||
type SecondaryWorkflowAction = "reject" | "return" | "transfer";
|
||||
type SecondaryWorkflowAction = "return" | "transfer";
|
||||
type ReimbursementConfirmAction = "approve" | "return";
|
||||
|
||||
type Props = {
|
||||
detail: WorkflowTaskDetail;
|
||||
@@ -84,13 +84,12 @@ export function ReimbursementApprovalAdapter({ detail, layout, refetchTask, task
|
||||
const { permissions } = useAuth();
|
||||
const backTarget = resolveApprovalBackTarget((location.state as { from?: unknown } | null)?.from, layout);
|
||||
const reimbursementId = detail.reimbursement?.id ?? "";
|
||||
const [rejectComment, setRejectComment] = useState("");
|
||||
const [returnComment, setReturnComment] = useState("");
|
||||
const [transferReason, setTransferReason] = useState("");
|
||||
const [selectedApprover, setSelectedApprover] = useState("");
|
||||
const [actionError, setActionError] = useState("");
|
||||
const [activeSecondaryAction, setActiveSecondaryAction] = useState<SecondaryWorkflowAction | null>(null);
|
||||
const [confirmAction, setConfirmAction] = useState<WorkflowConfirmAction | null>(null);
|
||||
const [confirmAction, setConfirmAction] = useState<ReimbursementConfirmAction | null>(null);
|
||||
const [transferConfirmOpen, setTransferConfirmOpen] = useState(false);
|
||||
|
||||
const reimbursementDetailQuery = useQuery({
|
||||
@@ -168,12 +167,11 @@ export function ReimbursementApprovalAdapter({ detail, layout, refetchTask, task
|
||||
});
|
||||
|
||||
const actionMutation = useMutation({
|
||||
mutationFn: async (action: "approve" | "reject" | "return" | "transfer") => {
|
||||
mutationFn: async (action: "approve" | "return" | "transfer") => {
|
||||
if (!decisionReady) {
|
||||
throw new Error(decisionBlockReason);
|
||||
}
|
||||
if (action === "approve") return approveWorkflowTask(taskId);
|
||||
if (action === "reject") return rejectWorkflowTask(taskId, rejectComment.trim());
|
||||
if (action === "return") return returnWorkflowTask(taskId, returnComment.trim());
|
||||
if (!selectedApprover) throw new Error("请选择转审目标");
|
||||
return transferWorkflowTask(taskId, selectedApprover, transferReason.trim());
|
||||
@@ -188,7 +186,7 @@ export function ReimbursementApprovalAdapter({ detail, layout, refetchTask, task
|
||||
setActiveSecondaryAction(null);
|
||||
setConfirmAction(null);
|
||||
setTransferConfirmOpen(false);
|
||||
toast.success({ approve: "已通过", reject: "已驳回", return: "已退回", transfer: "已转审" }[action]);
|
||||
toast.success({ approve: "已通过", return: "已退回", transfer: "已转审" }[action]);
|
||||
},
|
||||
onError: (error) => {
|
||||
if (isAlreadyProcessedTaskError(error)) void refetchTask();
|
||||
@@ -198,14 +196,14 @@ export function ReimbursementApprovalAdapter({ detail, layout, refetchTask, task
|
||||
},
|
||||
});
|
||||
|
||||
function requestAction(action: WorkflowConfirmAction) {
|
||||
function requestAction(action: ReimbursementConfirmAction) {
|
||||
if (!decisionReady) {
|
||||
setActionError(decisionBlockReason);
|
||||
return;
|
||||
}
|
||||
const comment = action === "reject" ? rejectComment : action === "return" ? returnComment : "";
|
||||
if ((action === "reject" || action === "return") && !comment.trim()) {
|
||||
setActionError(`${action === "reject" ? "驳回" : "退回"}必须填写意见`);
|
||||
const comment = action === "return" ? returnComment : "";
|
||||
if (action === "return" && !comment.trim()) {
|
||||
setActionError("退回修改必须填写意见");
|
||||
return;
|
||||
}
|
||||
setActionError("");
|
||||
@@ -235,8 +233,7 @@ export function ReimbursementApprovalAdapter({ detail, layout, refetchTask, task
|
||||
const disabledActions = (
|
||||
<div className="grid grid-cols-2 gap-2" data-testid="reimbursement-approval-fail-closed-actions">
|
||||
<Button data-testid={layout === "mobile" ? "mobile-approvals-approve" : "workflow-task-approve"} disabled type="button">通过</Button>
|
||||
<Button data-testid={layout === "mobile" ? "mobile-approvals-return" : "workflow-task-return-toggle"} disabled type="button" variant="secondary">退回</Button>
|
||||
<Button data-testid={layout === "mobile" ? "mobile-approvals-reject" : "workflow-task-reject-toggle"} disabled type="button" variant="secondary">驳回</Button>
|
||||
<Button data-testid={layout === "mobile" ? "mobile-approvals-return" : "workflow-task-return-toggle"} disabled type="button" variant="secondary">退回修改</Button>
|
||||
<Button data-testid={layout === "mobile" ? "mobile-approvals-transfer" : "workflow-task-transfer-menu-item"} disabled type="button" variant="secondary">转审</Button>
|
||||
</div>
|
||||
);
|
||||
@@ -250,7 +247,10 @@ export function ReimbursementApprovalAdapter({ detail, layout, refetchTask, task
|
||||
);
|
||||
}
|
||||
|
||||
const confirmCopy = confirmAction ? getWorkflowConfirmCopy(confirmAction) : null;
|
||||
const baseConfirmCopy = confirmAction ? getWorkflowConfirmCopy(confirmAction) : null;
|
||||
const confirmCopy = confirmAction === "return" && baseConfirmCopy
|
||||
? { ...baseConfirmCopy, title: "确认退回修改?", confirmLabel: "确认退回修改" }
|
||||
: baseConfirmCopy;
|
||||
const actionDisabled = actionMutation.isPending || !decisionReady;
|
||||
const visibleIssueAnchors = model.issueAnchors.filter((issue) => issue.label !== decisionBlockReason);
|
||||
const issueAnchors = visibleIssueAnchors.length ? (
|
||||
@@ -279,19 +279,11 @@ export function ReimbursementApprovalAdapter({ detail, layout, refetchTask, task
|
||||
{actionError ? <p className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700" role="alert">{actionError}</p> : null}
|
||||
{activeSecondaryAction === "return" ? (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor={`${layout}-reimbursement-return-comment`}>退回意见</Label>
|
||||
<Label htmlFor={`${layout}-reimbursement-return-comment`}>退回修改意见</Label>
|
||||
<p className="text-xs text-zinc-500">退回后原单可修改并重新提交,单号和审批历史会保留。</p>
|
||||
<Textarea data-testid={layout === "desktop" ? "workflow-task-return-comment" : "mobile-approvals-comment"} id={`${layout}-reimbursement-return-comment`} value={returnComment} onChange={(event) => setReturnComment(event.target.value)} />
|
||||
<ReimbursementReturnReferencePicker evidence={model.evidence} onReference={appendReturnReference} />
|
||||
<Button className="min-h-11 w-full" data-testid={layout === "desktop" ? "workflow-task-return" : "mobile-approvals-return-submit"} disabled={actionDisabled || !returnComment.trim()} onClick={() => requestAction("return")} type="button" variant="secondary">确认退回</Button>
|
||||
</div>
|
||||
) : null}
|
||||
{activeSecondaryAction === "reject" ? (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor={`${layout}-reimbursement-reject-comment`}>驳回意见</Label>
|
||||
<p className="text-xs text-zinc-500">驳回会终止本次流程,申请人通常需要重新发起。</p>
|
||||
<Textarea data-testid={layout === "desktop" ? "workflow-task-reject-comment" : "mobile-approvals-comment"} id={`${layout}-reimbursement-reject-comment`} value={rejectComment} onChange={(event) => setRejectComment(event.target.value)} />
|
||||
<Button className="min-h-11 w-full" data-testid={layout === "desktop" ? "workflow-task-reject" : "mobile-approvals-reject-submit"} disabled={actionDisabled || !rejectComment.trim()} onClick={() => requestAction("reject")} type="button" variant="secondary">确认驳回</Button>
|
||||
<Button className="min-h-11 w-full" data-testid={layout === "desktop" ? "workflow-task-return" : "mobile-approvals-return-submit"} disabled={actionDisabled || !returnComment.trim()} onClick={() => requestAction("return")} type="button" variant="secondary">确认退回修改</Button>
|
||||
</div>
|
||||
) : null}
|
||||
{activeSecondaryAction === "transfer" ? (
|
||||
@@ -365,13 +357,10 @@ export function ReimbursementApprovalAdapter({ detail, layout, refetchTask, task
|
||||
{offlineNotice}
|
||||
{issueAnchors}
|
||||
{commentEditor}
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{allowedActions.has("return") || !online ? <Button className="min-h-11" data-testid="mobile-approvals-return" disabled={actionDisabled} onClick={() => setActiveSecondaryAction(activeSecondaryAction === "return" ? null : "return")} type="button" variant="secondary">退回</Button> : null}
|
||||
{allowedActions.has("reject") || !online ? <Button className="min-h-11" data-testid="mobile-approvals-reject" disabled={actionDisabled} onClick={() => setActiveSecondaryAction(activeSecondaryAction === "reject" ? null : "reject")} type="button" variant="secondary">驳回</Button> : null}
|
||||
{canTransfer || !online ? <Button className="min-h-11" data-testid="mobile-approvals-transfer" disabled={actionDisabled} onClick={() => setActiveSecondaryAction(activeSecondaryAction === "transfer" ? null : "transfer")} type="button" variant="secondary">转审</Button> : null}
|
||||
</div>
|
||||
{canTransfer || !online ? <Button className="min-h-11 w-full" data-testid="mobile-approvals-transfer" disabled={actionDisabled} onClick={() => setActiveSecondaryAction(activeSecondaryAction === "transfer" ? null : "transfer")} type="button" variant="ghost">转审</Button> : null}
|
||||
</section>
|
||||
<MobileStickyActionBar testId="mobile-approvals-sticky-actions">
|
||||
<MobileStickyActionBar contentClassName="grid-cols-2" testId="mobile-approvals-sticky-actions">
|
||||
{allowedActions.has("return") || !online ? <Button className="min-h-12 w-full" data-testid="mobile-approvals-return" disabled={actionDisabled} onClick={() => setActiveSecondaryAction(activeSecondaryAction === "return" ? null : "return")} type="button" variant="secondary">退回修改</Button> : null}
|
||||
<Button className="min-h-12 w-full" data-testid="mobile-approvals-approve" disabled={actionDisabled || !allowedActions.has("approve")} onClick={() => requestAction("approve")} type="button">通过</Button>
|
||||
</MobileStickyActionBar>
|
||||
</>
|
||||
@@ -399,13 +388,13 @@ export function ReimbursementApprovalAdapter({ detail, layout, refetchTask, task
|
||||
<BusinessDetailActionPanel
|
||||
title="审批决定"
|
||||
beforeActions={actionPanelBeforeActions}
|
||||
primaryAction={allowedActions.has("approve") ? <Button data-testid="workflow-task-approve" disabled={actionDisabled} onClick={() => requestAction("approve")} type="button">通过</Button> : null}
|
||||
secondaryActions={(
|
||||
<>
|
||||
{allowedActions.has("return") ? <Button data-testid="workflow-task-return-toggle" disabled={actionDisabled} onClick={() => setActiveSecondaryAction(activeSecondaryAction === "return" ? null : "return")} type="button" variant="secondary">退回</Button> : null}
|
||||
{allowedActions.has("reject") ? <Button data-testid="workflow-task-reject-toggle" disabled={actionDisabled} onClick={() => setActiveSecondaryAction(activeSecondaryAction === "reject" ? null : "reject")} type="button" variant="secondary">驳回</Button> : null}
|
||||
</>
|
||||
)}
|
||||
primaryAction={allowedActions.has("approve") || allowedActions.has("return") ? (
|
||||
<div className="grid grid-cols-2 gap-2" data-testid="reimbursement-approval-primary-actions">
|
||||
{allowedActions.has("approve") ? <Button className="min-h-11 w-full" data-testid="workflow-task-approve" disabled={actionDisabled} onClick={() => requestAction("approve")} type="button">通过</Button> : null}
|
||||
{allowedActions.has("return") ? <Button className="min-h-11 w-full" data-testid="workflow-task-return-toggle" disabled={actionDisabled} onClick={() => setActiveSecondaryAction(activeSecondaryAction === "return" ? null : "return")} type="button" variant="secondary">退回修改</Button> : null}
|
||||
</div>
|
||||
) : null}
|
||||
secondaryActions={null}
|
||||
moreActions={canTransfer ? <Button className="w-full" data-testid="workflow-task-transfer-menu-item" disabled={actionDisabled} onClick={() => setActiveSecondaryAction(activeSecondaryAction === "transfer" ? null : "transfer")} type="button" variant="ghost">转审</Button> : null}
|
||||
summary={null}
|
||||
testId="workflow-task-action-panel"
|
||||
|
||||
@@ -231,7 +231,7 @@ test("E2E 移动审批:报销证据不进入 IndexedDB 且离线动作 fail cl
|
||||
attendanceCorrection: null,
|
||||
purchaseRequest: null,
|
||||
travelRequest: null,
|
||||
allowedActions: ["approve", "reject", "return"],
|
||||
allowedActions: ["approve", "return"],
|
||||
history: [],
|
||||
};
|
||||
const reimbursementDetail = {
|
||||
@@ -351,9 +351,9 @@ test("E2E 移动审批:报销证据不进入 IndexedDB 且离线动作 fail cl
|
||||
}
|
||||
|
||||
await page.context().setOffline(true);
|
||||
await expect(page.getByText("报销审批需联网核对完整报销内容;当前不能通过、退回、驳回或转审。")).toBeVisible();
|
||||
await expect(page.getByText("报销审批需联网核对完整报销内容;当前不能通过、退回修改或转审。")).toBeVisible();
|
||||
await expect(page.getByTestId("mobile-approvals-approve")).toBeDisabled();
|
||||
await expect(page.getByTestId("mobile-approvals-return")).toBeDisabled();
|
||||
await expect(page.getByTestId("mobile-approvals-reject")).toBeDisabled();
|
||||
await expect(page.getByTestId("mobile-approvals-reject")).toHaveCount(0);
|
||||
await expect(page.getByTestId("mobile-approvals-transfer")).toBeDisabled();
|
||||
});
|
||||
|
||||
@@ -77,7 +77,7 @@ const workflowTask = {
|
||||
duration: null,
|
||||
businessStatus: "reviewing",
|
||||
},
|
||||
allowedActions: ["approve", "reject", "return", "transfer"],
|
||||
allowedActions: ["approve", "return", "transfer"],
|
||||
};
|
||||
|
||||
const reimbursementDetail = {
|
||||
|
||||
@@ -52,7 +52,7 @@ test("报销审批五类阻断原因由同一投影给出且正常态保持安
|
||||
};
|
||||
|
||||
expect(resolveReimbursementApprovalBlockReason(base)).toBeNull();
|
||||
expect(resolveReimbursementApprovalBlockReason({ ...base, online: false })).toBe("报销审批需联网核对完整报销内容;当前不能通过、退回、驳回或转审。");
|
||||
expect(resolveReimbursementApprovalBlockReason({ ...base, online: false })).toBe("报销审批需联网核对完整报销内容;当前不能通过、退回修改或转审。");
|
||||
expect(resolveReimbursementApprovalBlockReason({ ...base, detailState: "loading" })).toBe("正在加载完整报销内容和票据;加载完成前不能作出审批决定。");
|
||||
expect(resolveReimbursementApprovalBlockReason({ ...base, detailState: "error" })).toBe("报销内容未加载完整,暂不能审批,请刷新后重试");
|
||||
expect(resolveReimbursementApprovalBlockReason({ ...base, snapshotParseStatus: "invalid" })).toBe("提交时检查记录无法解析,暂时不能审批;请联系系统管理员核对。");
|
||||
@@ -73,6 +73,13 @@ test("动作面板复用唯一阻断投影且预算三态判断保持原合同",
|
||||
expect(adapter).not.toContain("内容未就绪,审批动作已停用");
|
||||
expect(adapter).not.toContain("statusSummary");
|
||||
expect(adapter).toContain("actionError || activeSecondaryAction ?");
|
||||
expect(adapter).toContain('data-testid="reimbursement-approval-primary-actions"');
|
||||
expect(adapter).toContain('className="grid grid-cols-2 gap-2"');
|
||||
expect(adapter).toContain("退回修改");
|
||||
expect(adapter).not.toContain("rejectWorkflowTask");
|
||||
expect(adapter).not.toContain('allowedActions.has("reject")');
|
||||
expect(adapter).not.toContain("workflow-task-reject-toggle");
|
||||
expect(adapter).not.toContain("mobile-approvals-reject");
|
||||
});
|
||||
|
||||
test("旧审批层叠 class 与更多菜单样式无定义或消费者", () => {
|
||||
|
||||
@@ -458,6 +458,7 @@ test.describe("R7E 审批动作二次确认", () => {
|
||||
submitter: { id: "owner", employeeNo: "E001", name: "申请人" },
|
||||
},
|
||||
leaveRequest: null,
|
||||
allowedActions: ["approve", "return", "transfer"],
|
||||
history: Array.from({ length: 6 }, (_, index) => ({
|
||||
id: `history-${index + 1}`,
|
||||
action: index === 0 ? "submit" : "approve",
|
||||
@@ -515,8 +516,18 @@ test.describe("R7E 审批动作二次确认", () => {
|
||||
await expect(actionPanel.getByText(/^审批角色:/)).toHaveCount(0);
|
||||
await expect(page.getByTestId("workflow-task-approve")).toBeEnabled();
|
||||
await expect(page.getByTestId("workflow-task-return-toggle")).toBeEnabled();
|
||||
await expect(page.getByTestId("workflow-task-reject-toggle")).toBeEnabled();
|
||||
await expect(page.getByTestId("workflow-task-reject-toggle")).toHaveCount(0);
|
||||
await expect(page.getByTestId("workflow-task-transfer-menu-item")).toBeEnabled();
|
||||
const primaryActions = page.getByTestId("reimbursement-approval-primary-actions");
|
||||
await expect(primaryActions.getByRole("button")).toHaveCount(2);
|
||||
await expect(primaryActions.getByRole("button", { name: "通过" })).toBeVisible();
|
||||
await expect(primaryActions.getByRole("button", { name: "退回修改" })).toBeVisible();
|
||||
const approveBox = await page.getByTestId("workflow-task-approve").boundingBox();
|
||||
const returnBox = await page.getByTestId("workflow-task-return-toggle").boundingBox();
|
||||
expect(approveBox).not.toBeNull();
|
||||
expect(returnBox).not.toBeNull();
|
||||
expect(Math.abs(approveBox!.height - returnBox!.height)).toBeLessThanOrEqual(1);
|
||||
expect(approveBox!.y).toBe(returnBox!.y);
|
||||
await expect(page.getByTestId("reimbursement-workflow-history").locator(".detail-timeline-item")).toHaveCount(6);
|
||||
await expect(page.getByRole("heading", { name: "处理历史" })).toHaveCount(1);
|
||||
const actionBox = await actionPanel.boundingBox();
|
||||
@@ -544,7 +555,7 @@ test.describe("R7E 审批动作二次确认", () => {
|
||||
await expect(page.getByTestId("workflow-task-return-comment")).toHaveValue(/【明细:交通费 · 2026-09-01 · ¥180\.00】/);
|
||||
});
|
||||
|
||||
test("PC 报销内容加载失败时四类审批入口 fail closed", async ({ page }) => {
|
||||
test("PC 报销内容加载失败时全部可见审批入口 fail closed", async ({ page }) => {
|
||||
const taskDetail = {
|
||||
...pendingTaskDetail,
|
||||
workflowInstance: {
|
||||
@@ -558,6 +569,7 @@ test.describe("R7E 审批动作二次确认", () => {
|
||||
owner: { id: "owner", employeeNo: "E001", name: "申请人" }, submitter: { id: "owner", employeeNo: "E001", name: "申请人" },
|
||||
},
|
||||
leaveRequest: null,
|
||||
allowedActions: ["approve", "return", "transfer"],
|
||||
};
|
||||
await mockWorkflowConfirmApis(page, { taskDetail, reimbursementDetailFailure: true });
|
||||
|
||||
@@ -566,7 +578,7 @@ test.describe("R7E 审批动作二次确认", () => {
|
||||
await expect(page.getByRole("alert")).toContainText("报销内容未加载完整,暂不能审批,请刷新后重试");
|
||||
await expect(page.getByTestId("workflow-task-approve")).toBeDisabled();
|
||||
await expect(page.getByTestId("workflow-task-return-toggle")).toBeDisabled();
|
||||
await expect(page.getByTestId("workflow-task-reject-toggle")).toBeDisabled();
|
||||
await expect(page.getByTestId("workflow-task-reject-toggle")).toHaveCount(0);
|
||||
await expect(page.getByTestId("workflow-task-transfer-menu-item")).toBeDisabled();
|
||||
});
|
||||
|
||||
@@ -590,6 +602,7 @@ test.describe("R7E 审批动作二次确认", () => {
|
||||
submitter: { id: "owner", employeeNo: "E001", name: "申请人" },
|
||||
},
|
||||
leaveRequest: null,
|
||||
allowedActions: ["approve", "return", "transfer"],
|
||||
history: Array.from({ length: 6 }, (_, index) => ({
|
||||
id: `mobile-history-${index + 1}`,
|
||||
action: index === 0 ? "submit" : "approve",
|
||||
@@ -627,6 +640,10 @@ test.describe("R7E 审批动作二次确认", () => {
|
||||
await expect(page.getByTestId("reimbursement-amount-summary")).toContainText("¥100.00");
|
||||
await expect(page.locator("#reimbursement-workspace-snapshot")).toHaveCount(0);
|
||||
await expect(page.getByTestId("reimbursement-workflow-history").locator(".detail-timeline-item")).toHaveCount(6);
|
||||
const stickyActions = page.getByTestId("mobile-approvals-sticky-actions");
|
||||
await expect(stickyActions.getByRole("button", { name: "退回修改" })).toBeVisible();
|
||||
await expect(stickyActions.getByRole("button", { name: "通过" })).toBeVisible();
|
||||
await expect(page.getByTestId("mobile-approvals-reject")).toHaveCount(0);
|
||||
const assertMobileGeometry = async () => {
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth <= document.documentElement.clientWidth + 2)).toBe(true);
|
||||
const lastHistory = page.getByTestId("reimbursement-workflow-history").locator(".detail-timeline-item").last();
|
||||
@@ -657,7 +674,7 @@ test.describe("R7E 审批动作二次确认", () => {
|
||||
expectedMessage: "明细金额无法完整核对,请联系管理员",
|
||||
},
|
||||
]) {
|
||||
test(`PC 报销${scenario.name}时四类审批入口 fail closed`, async ({ page }) => {
|
||||
test(`PC 报销${scenario.name}时全部可见审批入口 fail closed`, async ({ page }) => {
|
||||
const reimbursementId = `reimbursement-integrity-${scenario.itemAmount === "invalid" ? "unavailable" : "mismatch"}`;
|
||||
const taskDetail = {
|
||||
...pendingTaskDetail,
|
||||
@@ -677,6 +694,7 @@ test.describe("R7E 审批动作二次确认", () => {
|
||||
submitter: { id: "owner", employeeNo: "E001", name: "申请人" },
|
||||
},
|
||||
leaveRequest: null,
|
||||
allowedActions: ["approve", "return", "transfer"],
|
||||
};
|
||||
const reimbursementDetail = {
|
||||
id: reimbursementId,
|
||||
@@ -716,7 +734,7 @@ test.describe("R7E 审批动作二次确认", () => {
|
||||
await expect(page.getByText("差额", { exact: true })).toHaveCount(0);
|
||||
await expect(page.getByTestId("workflow-task-approve")).toBeDisabled();
|
||||
await expect(page.getByTestId("workflow-task-return-toggle")).toBeDisabled();
|
||||
await expect(page.getByTestId("workflow-task-reject-toggle")).toBeDisabled();
|
||||
await expect(page.getByTestId("workflow-task-reject-toggle")).toHaveCount(0);
|
||||
await expect(page.getByTestId("workflow-task-transfer-menu-item")).toBeDisabled();
|
||||
});
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户