feat(client): 报销明细编辑器与费用类型下拉(F6)
- 报销详情新增明细列表与新增/编辑/删除入口 - 接入费用类型只读接口并保持总金额自动刷新 - 补 data-testid 以便 E2E/回归
这个提交包含在:
@@ -8,6 +8,21 @@ export type ReimbursementStatus =
|
||||
| "rejected"
|
||||
| "cancelled";
|
||||
|
||||
export type ReimbursementItem = {
|
||||
id: string;
|
||||
expenseTypeId: string;
|
||||
expenseDate: string | null;
|
||||
amount: string;
|
||||
description: string | null;
|
||||
expenseType: {
|
||||
id: string;
|
||||
code: string;
|
||||
name: string;
|
||||
category: string;
|
||||
enabled: boolean;
|
||||
} | null;
|
||||
};
|
||||
|
||||
export type ReimbursementListItem = {
|
||||
id: string;
|
||||
formNumber: string | null;
|
||||
@@ -81,6 +96,7 @@ export type ReimbursementDetail = {
|
||||
assignee_role: string | null;
|
||||
} | null;
|
||||
} | null;
|
||||
items: ReimbursementItem[];
|
||||
};
|
||||
|
||||
export type ReimbursementListQuery = {
|
||||
@@ -96,6 +112,29 @@ export type ReimbursementPayPayload = {
|
||||
paymentChannel?: "bank" | "alipay" | "wechat" | "cash" | "other";
|
||||
};
|
||||
|
||||
export type ReimbursementItemPayload = {
|
||||
expenseTypeId: string;
|
||||
expenseDate: string;
|
||||
amount: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export type ReimbursementItemMutationResponse = {
|
||||
item: {
|
||||
id: string;
|
||||
expenseTypeId: string;
|
||||
expenseDate: string;
|
||||
amount: string;
|
||||
description: string | null;
|
||||
};
|
||||
totalAmount: string;
|
||||
};
|
||||
|
||||
export type ReimbursementItemDeleteResponse = {
|
||||
outcome: "deleted";
|
||||
totalAmount: string;
|
||||
};
|
||||
|
||||
function toQueryString(query: Record<string, string | number | undefined>) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
@@ -150,3 +189,39 @@ export async function payReimbursement(reimbursementId: string, payload: Reimbur
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export async function createReimbursementItem(
|
||||
reimbursementId: string,
|
||||
payload: ReimbursementItemPayload,
|
||||
) {
|
||||
return apiRequestData<ReimbursementItemMutationResponse>(
|
||||
`/api/reimbursements/${reimbursementId}/items`,
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function updateReimbursementItem(
|
||||
reimbursementId: string,
|
||||
itemId: string,
|
||||
payload: Partial<ReimbursementItemPayload>,
|
||||
) {
|
||||
return apiRequestData<ReimbursementItemMutationResponse>(
|
||||
`/api/reimbursements/${reimbursementId}/items/${itemId}`,
|
||||
{
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function deleteReimbursementItem(reimbursementId: string, itemId: string) {
|
||||
return apiRequestData<ReimbursementItemDeleteResponse>(
|
||||
`/api/reimbursements/${reimbursementId}/items/${itemId}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户