P25L-1~P25L-5 实现预算控制与AP最小可用运营

合同 CSV 已回填 reviewer/QA 结果;运行时回归受当前环境缺少 node/npm 阻塞。
这个提交包含在:
2026-03-29 09:21:17 +08:00
父节点 e8d16c47f2
当前提交 6dee2daf3e
共修改 19 个文件,包含 1394 行新增和 482 行删除
+90
查看文件
@@ -0,0 +1,90 @@
import { apiRequestData } from "./http";
export type BudgetControlRuleEntityType = "all" | "reimbursement" | "purchase_request" | "travel_request";
export type BudgetControlMode = "warn" | "block" | "manual_review";
export type BudgetControlRule = {
id: string;
name: string;
entityType: BudgetControlRuleEntityType;
departmentId: string | null;
legalEntityId: string | null;
costCenterId: string | null;
expenseTypeId: string | null;
controlMode: BudgetControlMode;
priority: number;
enabled: boolean;
notes: string | null;
archivedAt: string | null;
createdAt: string | null;
updatedAt: string | null;
};
export type BudgetControlRuleListResponse = {
items: BudgetControlRule[];
};
function toQueryString(query: Record<string, string | boolean | undefined>) {
const params = new URLSearchParams();
for (const [key, value] of Object.entries(query)) {
if (value == null || value === "") {
continue;
}
params.set(key, typeof value === "boolean" ? String(value) : value);
}
const encoded = params.toString();
return encoded ? `?${encoded}` : "";
}
export async function fetchBudgetControlRules(query: {
entityType?: BudgetControlRuleEntityType;
enabled?: boolean;
} = {}) {
return apiRequestData<BudgetControlRuleListResponse>(
`/api/budget-control-rules${toQueryString(query as Record<string, string | boolean | undefined>)}`,
);
}
export async function createBudgetControlRule(payload: {
name: string;
entityType: BudgetControlRuleEntityType;
departmentId?: string | null;
legalEntityId?: string | null;
expenseTypeId?: string | null;
controlMode: BudgetControlMode;
priority?: number;
enabled?: boolean;
notes?: string | null;
}) {
return apiRequestData<{ item: BudgetControlRule }>("/api/budget-control-rules", {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function patchBudgetControlRule(
ruleId: string,
payload: Partial<{
name: string;
entityType: BudgetControlRuleEntityType;
departmentId: string | null;
legalEntityId: string | null;
expenseTypeId: string | null;
controlMode: BudgetControlMode;
priority: number;
enabled: boolean;
notes: string | null;
}>,
) {
return apiRequestData<{ item: BudgetControlRule }>(`/api/budget-control-rules/${ruleId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export async function archiveBudgetControlRule(ruleId: string) {
return apiRequestData<{ item: BudgetControlRule }>(`/api/budget-control-rules/${ruleId}/archive`, {
method: "POST",
body: JSON.stringify({}),
});
}