完成五人团队正式内用最小收口(07-18-small-team-internal-readiness)
这个提交包含在:
@@ -7,8 +7,7 @@ export type ApprovalRoleCode =
|
||||
| "finance"
|
||||
| "finance_mgr"
|
||||
| "administration"
|
||||
| "hr"
|
||||
| "admin";
|
||||
| "hr";
|
||||
export type ApprovalOwnerSelfFallbackRoleCode = "finance_mgr";
|
||||
|
||||
export type ApprovalRule = {
|
||||
|
||||
@@ -164,7 +164,7 @@ export type EmployeeContract = {
|
||||
startDate: string | null;
|
||||
endDate: string | null;
|
||||
terminatedDate: string | null;
|
||||
attachmentPath: string | null;
|
||||
hasAttachment: boolean;
|
||||
attachmentUrl: string | null;
|
||||
status: EmployeeContractStatus;
|
||||
createdAt: string | null;
|
||||
@@ -285,6 +285,7 @@ export type FetchEmployeesParams = {
|
||||
hireDateFrom?: string;
|
||||
hireDateTo?: string;
|
||||
employmentStatus?: EmployeeEmploymentStatus;
|
||||
scope?: "directory" | "management";
|
||||
};
|
||||
|
||||
export type BatchOperationResult = {
|
||||
@@ -384,6 +385,7 @@ export async function fetchEmployees(params: FetchEmployeesParams) {
|
||||
if (params.hireDateFrom) search.set("hireDateFrom", params.hireDateFrom);
|
||||
if (params.hireDateTo) search.set("hireDateTo", params.hireDateTo);
|
||||
if (params.employmentStatus) search.set("employmentStatus", params.employmentStatus);
|
||||
if (params.scope) search.set("scope", params.scope);
|
||||
|
||||
return apiRequestData<EmployeeListResponse>(`/api/employees?${search.toString()}`);
|
||||
}
|
||||
@@ -547,7 +549,6 @@ export async function createEmployeeContract(
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
terminatedDate?: string | null;
|
||||
attachmentPath?: string | null;
|
||||
},
|
||||
) {
|
||||
return apiRequestData<EmployeeContract>(`/api/employees/${employeeId}/contracts`, {
|
||||
@@ -565,7 +566,6 @@ export async function patchEmployeeContract(
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
terminatedDate: string | null;
|
||||
attachmentPath: string | null;
|
||||
}>,
|
||||
) {
|
||||
return apiRequestData<EmployeeContract>(`/api/employees/${employeeId}/contracts/${contractId}`, {
|
||||
@@ -588,9 +588,10 @@ export async function downloadEmployeeContractAttachment(attachmentUrl: string):
|
||||
return apiRequestBlob(attachmentUrl);
|
||||
}
|
||||
|
||||
export async function deleteEmployeeContract(employeeId: string, contractId: string) {
|
||||
export async function deleteEmployeeContract(employeeId: string, contractId: string, reason: string) {
|
||||
return apiRequestData<{ contractId: string; deleted: boolean }>(`/api/employees/${employeeId}/contracts/${contractId}`, {
|
||||
method: "DELETE",
|
||||
body: JSON.stringify({ reason }),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,23 @@ export function mapApiErrorMessage(
|
||||
}
|
||||
|
||||
if (details?.payment === "already_paid_conflict") {
|
||||
return "该报销单已支付且付款信息冲突,请刷新后确认。";
|
||||
return "该单据已由其他付款动作处理,请刷新后核对付款记录。";
|
||||
}
|
||||
|
||||
if (details?.payment === "source_amount_changed") {
|
||||
return "源单金额已发生变化,本次付款未执行,请刷新后重新制单。";
|
||||
}
|
||||
|
||||
if (details?.payment === "payment_batch_v1_required") {
|
||||
return "付款批次已启用,请从付款页面完成制单、复核和执行。";
|
||||
}
|
||||
|
||||
if (details?.entityIds === "contains_active_payment_batch") {
|
||||
return "所选报销已进入其他有效付款批次,请刷新候选列表。";
|
||||
}
|
||||
|
||||
if (details?.receiptAttachment === "void_required") {
|
||||
return "银行回单不能直接删除,请填写原因后作废。";
|
||||
}
|
||||
|
||||
if (details?.status === "invalid_pay_status") {
|
||||
@@ -149,14 +165,6 @@ export function mapApiErrorMessage(
|
||||
return "复核人与批次创建人不能是同一人。";
|
||||
}
|
||||
|
||||
if (details?.review === "second_reviewer_conflict") {
|
||||
return "二次复核人不能与首个复核人相同。";
|
||||
}
|
||||
|
||||
if (details?.review === "high_risk_second_review_required") {
|
||||
return "当前为高风险付款批次,完成二次复核后才能执行。";
|
||||
}
|
||||
|
||||
if (details?.review === "batch_review_required") {
|
||||
return "当前批次尚未完成复核,不能执行。";
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export type PaymentBatch = {
|
||||
thresholdAmount: string;
|
||||
requiredReviewCount: number;
|
||||
completedReviewCount: number;
|
||||
nextStep: "first_review" | "second_review" | "ready_for_execute";
|
||||
nextStep: "first_review" | "ready_for_execute";
|
||||
makerCheckerRequired: boolean;
|
||||
reviewerPermission: string;
|
||||
};
|
||||
@@ -57,9 +57,32 @@ export type PaymentBatch = {
|
||||
executedBy: string | null;
|
||||
cancelledAt: string | null;
|
||||
cancelledBy: string | null;
|
||||
cancelledReason: string | null;
|
||||
items: PaymentBatchItem[];
|
||||
};
|
||||
|
||||
export type PaymentBatchCandidate = {
|
||||
entityId: string;
|
||||
entityType: "reimbursement";
|
||||
businessNo: string;
|
||||
employee: {
|
||||
id: string;
|
||||
employeeNo: string;
|
||||
name: string;
|
||||
};
|
||||
amount: string;
|
||||
currencyCode: "CNY";
|
||||
businessDate: string | null;
|
||||
summary: string | null;
|
||||
};
|
||||
|
||||
export type PaymentBatchCandidatesResponse = {
|
||||
items: PaymentBatchCandidate[];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
};
|
||||
|
||||
export type PaymentBatchReceiptAttachment = {
|
||||
id: string;
|
||||
originalName: string;
|
||||
@@ -89,6 +112,15 @@ export async function fetchPaymentBatchDetail(batchId: string) {
|
||||
return apiRequestData<{ item: PaymentBatch }>(`/api/payment-batches/${batchId}`);
|
||||
}
|
||||
|
||||
export async function fetchPaymentBatchCandidates(query: { q?: string; page?: number; pageSize?: number } = {}) {
|
||||
const params = new URLSearchParams();
|
||||
if (query.q?.trim()) params.set("q", query.q.trim());
|
||||
if (query.page) params.set("page", String(query.page));
|
||||
if (query.pageSize) params.set("pageSize", String(query.pageSize));
|
||||
const suffix = params.toString() ? `?${params.toString()}` : "";
|
||||
return apiRequestData<PaymentBatchCandidatesResponse>(`/api/payment-batches/candidates${suffix}`);
|
||||
}
|
||||
|
||||
export async function createPaymentBatch(payload: {
|
||||
entityType: PaymentBatchEntityType;
|
||||
name: string;
|
||||
@@ -131,10 +163,10 @@ export async function executePaymentBatch(batchId: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function cancelPaymentBatch(batchId: string) {
|
||||
export async function cancelPaymentBatch(batchId: string, reason: string) {
|
||||
return apiRequestData<{ item: PaymentBatch }>(`/api/payment-batches/${batchId}/cancel`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({}),
|
||||
body: JSON.stringify({ reason }),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -170,9 +202,9 @@ export async function downloadPaymentBatchReceiptAttachment(
|
||||
return apiRequestBlob(buildAttachmentFileUrl(paymentBatchReceiptAttachmentFileUrl(batchId, attachmentId), reason));
|
||||
}
|
||||
|
||||
export async function deletePaymentBatchReceiptAttachment(batchId: string, attachmentId: string) {
|
||||
return apiRequestData<{ attachmentId: string; outcome: "deleted" }>(
|
||||
`/api/payment-batches/${batchId}/receipt-attachments/${attachmentId}`,
|
||||
{ method: "DELETE" },
|
||||
export async function voidPaymentBatchReceiptAttachment(batchId: string, attachmentId: string, reason: string) {
|
||||
return apiRequestData<{ attachmentId: string; outcome: "voided"; voidedAt: string }>(
|
||||
`/api/payment-batches/${batchId}/receipt-attachments/${attachmentId}/void`,
|
||||
{ method: "POST", body: JSON.stringify({ reason }) },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -377,11 +377,12 @@ export async function uploadPurchaseAttachment(purchaseRequestId: string, file:
|
||||
});
|
||||
}
|
||||
|
||||
export async function deletePurchaseAttachment(purchaseRequestId: string, attachmentId: string) {
|
||||
export async function deletePurchaseAttachment(purchaseRequestId: string, attachmentId: string, reason: string) {
|
||||
return apiRequestData<{ outcome: "deleted"; attachmentId: string }>(
|
||||
`/api/purchase-requests/${purchaseRequestId}/attachments/${attachmentId}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
body: JSON.stringify({ reason }),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -505,11 +505,12 @@ export async function uploadReimbursementAttachments(reimbursementId: string, fi
|
||||
);
|
||||
}
|
||||
|
||||
export async function deleteReimbursementAttachment(reimbursementId: string, attachmentId: string) {
|
||||
export async function deleteReimbursementAttachment(reimbursementId: string, attachmentId: string, reason: string) {
|
||||
return apiRequestData<{ attachmentId: string; outcome: string }>(
|
||||
`/api/reimbursements/${reimbursementId}/attachments/${attachmentId}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
body: JSON.stringify({ reason }),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户