文件
hyb-oa/client/src/api/feeControl.ts
T

124 行
3.6 KiB
TypeScript

import { apiRequestData } from "./http";
export type RiskFlag = {
id: string;
entityType: string;
entityId: string;
riskCode: string;
level: "low" | "medium" | "high";
title: string;
detail: string | null;
source: string;
status: "open" | "resolved" | "dismissed";
payloadJson: Record<string, unknown> | null;
createdAt: string | null;
createdBy: string | null;
resolvedAt: string | null;
resolvedBy: string | null;
};
export type PrecheckDecision = "pass" | "warn" | "manual_review" | "block";
export type ExpenseStandardCheck = {
itemId: string;
expenseTypeId: string;
expenseTypeName: string;
amount: string;
standardId: string;
standardName: string;
maxAmount: string;
perUnit: "item" | "day" | "trip";
controlMode: "warn" | "block";
scopeType: "global" | "department";
scopeKey: string | null;
exceeded: boolean;
exceededAmount: string;
};
export type PrecheckResponse = {
entityType: "reimbursement" | "purchase_request" | "travel_request";
entityId: string;
decision: PrecheckDecision;
matchedBudgetControlRule: {
id: string;
name: string;
controlMode: "warn" | "block" | "manual_review";
} | 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 }>;
nextActions: string[];
};
export type RiskFlagListResponse = {
items: RiskFlag[];
};
function buildRiskFlagsSearch(query: {
entityType?: string;
entityId?: string;
status?: "open" | "resolved" | "dismissed";
source?: string;
}) {
const params = new URLSearchParams();
if (query.entityType) params.set("entityType", query.entityType);
if (query.entityId) params.set("entityId", query.entityId);
if (query.status) params.set("status", query.status);
if (query.source) params.set("source", query.source);
const search = params.toString();
return search ? `?${search}` : "";
}
export async function precheckEntity(payload: {
entityType: "reimbursement" | "purchase_request" | "travel_request";
entityId: string;
}) {
return apiRequestData<PrecheckResponse>("/api/fee-control/precheck", {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function fetchRiskFlags(query: {
entityType?: string;
entityId?: string;
status?: "open" | "resolved" | "dismissed";
source?: string;
} = {}) {
return apiRequestData<RiskFlagListResponse>(`/api/risk-flags${buildRiskFlagsSearch(query)}`);
}
export async function resolveRiskFlag(riskFlagId: string, payload: { note?: string | null } = {}) {
return apiRequestData<{ item: RiskFlag }>(`/api/risk-flags/${riskFlagId}/resolve`, {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function dismissRiskFlag(riskFlagId: string, payload: { note?: string | null } = {}) {
return apiRequestData<{ item: RiskFlag }>(`/api/risk-flags/${riskFlagId}/dismiss`, {
method: "POST",
body: JSON.stringify(payload),
});
}