feat(reimbursement-small-team-form-validation): 完善小团队报销填报校验
这个提交包含在:
@@ -119,6 +119,18 @@ export function mapApiErrorMessage(
|
||||
}
|
||||
|
||||
if (code === "VALID_001") {
|
||||
if (details?.amount === "must_be_positive") {
|
||||
return "明细金额必须大于 0。";
|
||||
}
|
||||
|
||||
if (details?.items === "at_least_one_required") {
|
||||
return "请先添加至少 1 条报销明细。";
|
||||
}
|
||||
|
||||
if (details?.items === "positive_amount_required") {
|
||||
return "所有报销明细金额都必须大于 0,请修改后再提交。";
|
||||
}
|
||||
|
||||
const passwordErrors = readStringList(details?.password);
|
||||
if (passwordErrors.length) {
|
||||
return `新密码不符合要求:${passwordErrors.join(";")}。`;
|
||||
|
||||
@@ -46,6 +46,24 @@ export type PrecheckResponse = {
|
||||
} | null;
|
||||
budgetHint: Record<string, unknown> | null;
|
||||
standardChecks: ExpenseStandardCheck[];
|
||||
evidenceSummary?: {
|
||||
invoiceCount: number;
|
||||
attachmentCount: number;
|
||||
items: Array<{
|
||||
itemId: string;
|
||||
linkedInvoiceCount: number;
|
||||
invoiceState: "linked" | "unlinked";
|
||||
}>;
|
||||
};
|
||||
timelinessChecks?: Array<{
|
||||
itemId: string;
|
||||
expenseDate: string;
|
||||
ageDays: number;
|
||||
maxAgeDays: 180 | 365;
|
||||
overdue: boolean;
|
||||
crossCalendarYear: boolean;
|
||||
}>;
|
||||
assetHint?: { required: boolean; itemCount: number };
|
||||
warnings: Array<{ code: string; title: string; detail: string }>;
|
||||
blockingReasons: Array<{ code: string; title: string; detail: string }>;
|
||||
riskFlags: Array<{ code: string; level: "low" | "medium" | "high"; title: string; detail: string; willOpenOnSubmit: boolean }>;
|
||||
|
||||
@@ -301,6 +301,11 @@ export type ReimbursementTravelSummaryPayload = {
|
||||
travelEndDate: string | null;
|
||||
};
|
||||
|
||||
export type ReimbursementUpdatePayload = Partial<ReimbursementTravelSummaryPayload> & {
|
||||
reimbursementDate?: string;
|
||||
description?: string | null;
|
||||
};
|
||||
|
||||
export type ReimbursementCreateResponse = {
|
||||
id: string;
|
||||
formNumber: string | null;
|
||||
@@ -486,10 +491,24 @@ export async function updateReimbursementTravelSummary(
|
||||
reimbursementId: string,
|
||||
payload: ReimbursementTravelSummaryPayload,
|
||||
) {
|
||||
return apiRequestData<{ travelSummary: Omit<ReimbursementDetail["travelSummary"], "isTravel"> }>(
|
||||
`/api/reimbursements/${reimbursementId}`,
|
||||
{ method: "PATCH", body: JSON.stringify(payload) },
|
||||
);
|
||||
return updateReimbursement(reimbursementId, payload);
|
||||
}
|
||||
|
||||
export async function updateReimbursement(
|
||||
reimbursementId: string,
|
||||
payload: ReimbursementUpdatePayload,
|
||||
) {
|
||||
return apiRequestData<{
|
||||
id: string;
|
||||
status: ReimbursementStatus;
|
||||
reimbursementDate: string;
|
||||
description: string | null;
|
||||
totalAmount: string;
|
||||
travelSummary: Omit<ReimbursementDetail["travelSummary"], "isTravel">;
|
||||
}>(`/api/reimbursements/${reimbursementId}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export async function createReimbursementItem(
|
||||
|
||||
在新工单中引用
屏蔽一个用户