取消报销资产事前批准强制(08-30-reimbursement-assets-prior-approval-correction)

这个提交包含在:
2026-08-30 12:44:58 +08:00
父节点 0f9f40389e
当前提交 36c86f1dca
共修改 17 个文件,包含 489 行新增和 130 行删除
@@ -89,7 +89,6 @@ type ItemFormState = {
expenseDate: string;
amount: string;
description: string;
assetPriorApprovalBasis: string;
};
type SelectedInvoiceMap = Record<string, string>;
@@ -331,7 +330,6 @@ export function MobileReimbursementDetailPage() {
expenseDate: detail?.reimbursementDate ?? todayDate(),
amount: "",
description: "",
assetPriorApprovalBasis: "",
});
setItemError("");
};
@@ -344,7 +342,6 @@ export function MobileReimbursementDetailPage() {
expenseDate: item.expenseDate ?? detail?.reimbursementDate ?? todayDate(),
amount: item.amount,
description: item.description ?? "",
assetPriorApprovalBasis: item.assetPriorApprovalBasis ?? "",
});
setItemError("");
};
@@ -360,7 +357,6 @@ export function MobileReimbursementDetailPage() {
expenseDate: form.expenseDate,
amount: form.amount,
description: form.description.trim() || undefined,
assetPriorApprovalBasis: form.assetPriorApprovalBasis.trim() || null,
};
if (form.mode === "edit" && form.itemId) {
@@ -1093,29 +1089,17 @@ export function MobileReimbursementDetailPage() {
onChange={(event) => setItemForm((current) => current ? { ...current, description: event.target.value } : current)}
/>
</label>
{expenseTypes.find((type) => type.id === itemForm.expenseTypeId)?.category === "assets" ? (
<label className="col-span-2 block space-y-1 text-sm text-zinc-700">
<span>事前批准依据</span>
<Textarea
disabled={itemSaveMutation.isPending}
placeholder="填写批准人、批准时间和简短依据"
rows={2}
value={itemForm.assetPriorApprovalBasis}
onChange={(event) => setItemForm((current) => current ? { ...current, assetPriorApprovalBasis: event.target.value } : current)}
/>
</label>
) : null}
{shouldShowOfficeElectronicsHint(
expenseTypes.find((type) => type.id === itemForm.expenseTypeId)?.code,
itemForm.amount,
) ? (
<p className="col-span-2 text-xs text-amber-600">该金额通常应选择办公电子设备并填写事前批准依据。</p>
<p className="col-span-2 text-xs text-amber-600">该金额通常应选择办公电子设备。</p>
) : null}
</div>
<div className="mobile-two-action-grid">
<Button
type="button"
disabled={!itemForm.expenseTypeId || !itemForm.expenseDate || !Number.isFinite(Number(itemForm.amount)) || Number(itemForm.amount) <= 0 || (expenseTypes.find((type) => type.id === itemForm.expenseTypeId)?.category === "assets" && !itemForm.assetPriorApprovalBasis.trim()) || itemSaveMutation.isPending}
disabled={!itemForm.expenseTypeId || !itemForm.expenseDate || !Number.isFinite(Number(itemForm.amount)) || Number(itemForm.amount) <= 0 || itemSaveMutation.isPending}
onClick={() => itemSaveMutation.mutate(itemForm)}
>
{itemSaveMutation.isPending ? "保存中..." : "保存"}
@@ -1140,10 +1124,7 @@ export function MobileReimbursementDetailPage() {
</div>
{item.description ? <p className="mt-2 text-sm leading-5 text-zinc-600">{item.description}</p> : null}
{shouldShowOfficeElectronicsHint(item.expenseType?.code, item.amount) ? (
<p className="mt-2 text-xs text-amber-600">该金额通常应选择办公电子设备并填写事前批准依据。</p>
) : null}
{item.assetPriorApprovalBasis ? (
<p className="mt-2 text-xs text-zinc-500">事前批准依据:{item.assetPriorApprovalBasis}</p>
<p className="mt-2 text-xs text-amber-600">该金额通常应选择办公电子设备。</p>
) : null}
{canMaintainReimbursement ? (
<div className="mobile-two-action-grid mt-3">
+1 -9
查看文件
@@ -136,7 +136,6 @@ export function ReimbursementDetailPage() {
expenseDate: "",
amount: "",
description: "",
assetPriorApprovalBasis: "",
});
const currentDetailSource = buildPcDetailSource(location.pathname, location.search);
const navigationState = location.state as PcDetailNavigationState | null;
@@ -291,7 +290,6 @@ export function ReimbursementDetailPage() {
expenseDate: data.expenseDate,
amount: data.amount,
description: data.description.trim() || undefined,
assetPriorApprovalBasis: data.assetPriorApprovalBasis.trim() || null,
};
if (itemEditor.mode === "create") {
@@ -814,7 +812,6 @@ export function ReimbursementDetailPage() {
expenseTypeId: defaultExpenseTypeId,
amount: item?.amount ?? "",
description: item?.description ?? "",
assetPriorApprovalBasis: item?.assetPriorApprovalBasis ?? "",
};
};
@@ -1279,12 +1276,7 @@ export function ReimbursementDetailPage() {
<div>{item.description ?? "-"}</div>
{shouldShowOfficeElectronicsHint(item.expenseType?.code, item.amount) ? (
<div className="mt-1 text-xs text-amber-600">
该金额通常应选择办公电子设备并填写事前批准依据。
</div>
) : null}
{item.assetPriorApprovalBasis ? (
<div className="mt-1 text-xs text-zinc-500">
事前批准依据:{item.assetPriorApprovalBasis}
该金额通常应选择办公电子设备。
</div>
) : null}
{precheckStandardChecks
@@ -22,7 +22,6 @@ export type ItemEditorInitialValues = {
expenseDate: string;
amount: string;
description: string;
assetPriorApprovalBasis: string;
};
export type ItemEditorSaveData = {
@@ -30,7 +29,6 @@ export type ItemEditorSaveData = {
expenseDate: string;
amount: string;
description: string;
assetPriorApprovalBasis: string;
};
export interface ReimbursementItemEditorProps {
@@ -60,7 +58,6 @@ export function ReimbursementItemEditor({
const [expenseDate, setExpenseDate] = useState(initialValues.expenseDate);
const [amount, setAmount] = useState(initialValues.amount);
const [description, setDescription] = useState(initialValues.description);
const [assetPriorApprovalBasis, setAssetPriorApprovalBasis] = useState(initialValues.assetPriorApprovalBasis);
// Reset internal state when initialValues change (e.g. switching between create/edit)
useEffect(() => {
@@ -68,11 +65,9 @@ export function ReimbursementItemEditor({
setExpenseDate(initialValues.expenseDate);
setAmount(initialValues.amount);
setDescription(initialValues.description);
setAssetPriorApprovalBasis(initialValues.assetPriorApprovalBasis);
}, [initialValues.expenseTypeId, initialValues.expenseDate, initialValues.amount, initialValues.description, initialValues.assetPriorApprovalBasis]);
}, [initialValues.expenseTypeId, initialValues.expenseDate, initialValues.amount, initialValues.description]);
const selectedExpenseType = expenseTypes.find((item) => item.id === expenseTypeId) ?? null;
const isAssetExpense = selectedExpenseType?.category === "assets";
const numericAmount = Number(amount || 0);
const shouldWarnOfficeEquipment =
shouldShowOfficeElectronicsHint(selectedExpenseType?.code, numericAmount);
@@ -102,7 +97,7 @@ export function ReimbursementItemEditor({
const hasPositiveAmount = Number.isFinite(numericAmount) && numericAmount > 0;
const handleSave = () => {
onSave({ expenseTypeId, expenseDate, amount, description, assetPriorApprovalBasis });
onSave({ expenseTypeId, expenseDate, amount, description });
};
return (
@@ -185,28 +180,11 @@ export function ReimbursementItemEditor({
) : null}
{shouldWarnOfficeEquipment ? (
<p className="text-xs text-amber-600">
该金额通常应选择办公电子设备并填写事前批准依据。
该金额通常应选择办公电子设备。
</p>
) : null}
</div>
{isAssetExpense ? (
<div className="space-y-1 md:col-span-4">
<label className="text-xs text-zinc-600" htmlFor="assetPriorApprovalBasis">
事前批准依据
</label>
<Textarea
data-testid="reimbursement-item-asset-approval-basis"
disabled={isPending}
id="assetPriorApprovalBasis"
onChange={(event) => setAssetPriorApprovalBasis(event.target.value)}
placeholder="填写批准人、批准时间和简短依据"
rows={2}
value={assetPriorApprovalBasis}
/>
</div>
) : null}
<div className="space-y-1 md:col-span-4">
<label className="text-xs text-zinc-600" htmlFor="itemDescription">
说明
@@ -230,8 +208,7 @@ export function ReimbursementItemEditor({
!expenseTypeId ||
!expenseDate ||
!amount.trim() ||
!hasPositiveAmount ||
(isAssetExpense && !assetPriorApprovalBasis.trim())
!hasPositiveAmount
}
onClick={handleSave}
type="button"