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

250 行
6.5 KiB
TypeScript

import { apiRequestData } from "./http";
import type { PrecheckResponse, RiskFlag } from "./feeControl";
export type TravelRequestStatus = "draft" | "reviewing" | "approved" | "rejected";
export type TravelTripType = "domestic" | "international";
export type TravelSegmentRole = "outbound" | "return" | "connection";
export type TravelTransportType = "flight" | "train" | "car" | "bus" | "other";
export type TravelRequestSummary = {
id: string;
requestNo: string | null;
applicantEmployeeId: string;
submitterId: string | null;
departmentId: string;
legalEntityId: string;
costCenterId: string;
budgetPoolId: string | null;
purpose: string;
tripType: TravelTripType;
startDate: string | null;
endDate: string | null;
estimatedAmount: string;
projectId: string | null;
project: {
id: string;
projectNo: string;
name: string;
} | null;
status: TravelRequestStatus;
createdAt: string | null;
updatedAt: string | null;
applicant: {
id: string;
employeeNo: string;
name: string;
} | null;
submitter: {
id: string;
employeeNo: string;
name: string;
} | null;
department: {
id: string;
name: string;
} | null;
legalEntity: {
id: string;
code: string;
name: string;
} | null;
costCenter: {
id: string;
code: string;
name: string;
legalEntityId: string;
} | null;
budgetPool: {
id: string;
code: string;
name: string;
} | null;
};
export type TravelRequestSegment = {
id: string;
sortOrder: number;
segmentRole: TravelSegmentRole;
transportType: TravelTransportType;
fromCity: string;
toCity: string;
departAt: string | null;
arriveAt: string | null;
createdAt: string | null;
updatedAt: string | null;
};
export type TravelOrderLink = {
id: string;
source: string;
externalOrderNo: string;
employeeId: string;
linkedReimbursementId: string | null;
travelRequestId: string | null;
occurredAt: string | null;
amount: string | null;
currency: string;
status: string;
paymentVoucherNo: string | null;
paymentDate: string | null;
paymentChannel: string | null;
paidAt: string | null;
paidBy: string | null;
createdAt: string | null;
updatedAt: string | null;
};
export type TravelRequestDetail = TravelRequestSummary & {
segments: TravelRequestSegment[];
travelOrders: TravelOrderLink[];
pendingWorkflow: {
instanceId: string;
currentStatus: string;
currentStepId: string;
task: {
id: string;
stepId: string;
stepName: string;
assigneeId: string | null;
assigneeRole: string | null;
} | null;
} | null;
permissions: {
canEditDraft: boolean;
canSubmit: boolean;
canWithdraw: boolean;
canManage: boolean;
};
riskFlags: RiskFlag[];
};
export type TravelRequestListResponse = {
page: number;
pageSize: number;
total: number;
mine: boolean;
items: TravelRequestSummary[];
};
export type TravelRequestListQuery = {
status?: TravelRequestStatus;
mine?: boolean;
tripType?: TravelTripType;
legalEntityId?: string;
costCenterId?: string;
page?: number;
pageSize?: number;
};
export type TravelRequestSegmentPayload = {
segmentRole: TravelSegmentRole;
transportType: TravelTransportType;
fromCity: string;
toCity: string;
departAt: string;
arriveAt: string;
};
export type TravelRequestPayload = {
purpose: string;
tripType: TravelTripType;
startDate: string;
endDate: string;
legalEntityId: string;
costCenterId: string;
estimatedAmount: string;
segments: TravelRequestSegmentPayload[];
};
export type TravelRequestSubmitResponse = {
travelRequestId: string;
requestNo: string;
status: "reviewing";
workflowInstanceId: string;
currentTaskId: string;
budgetPoolId: string | null;
budgetReservation: {
applied: boolean;
reservedAmount?: string;
items?: Array<{ budgetPoolId: string; amount: string }>;
};
precheckDecision: "pass" | "warn" | "manual_review";
openedRiskFlagCount: number;
};
export type TravelRequestWithdrawResponse = {
travelRequestId: string;
status: "draft";
outcome: "withdrawn";
workflowInstanceId: string | null;
budgetRelease: {
applied: boolean;
items?: Array<{ budgetPoolId: string; amount: string }>;
};
};
function toQueryString(query: Record<string, string | number | 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" ? (value ? "1" : "0") : String(value));
}
const encoded = params.toString();
return encoded ? `?${encoded}` : "";
}
export async function fetchTravelRequests(query: TravelRequestListQuery = {}) {
return apiRequestData<TravelRequestListResponse>(
`/api/travel-requests${toQueryString(query as Record<string, string | number | boolean | undefined>)}`,
);
}
export async function createTravelRequest(payload: TravelRequestPayload) {
return apiRequestData<TravelRequestDetail>("/api/travel-requests", {
method: "POST",
body: JSON.stringify(payload),
});
}
export async function fetchTravelRequestDetail(travelRequestId: string) {
return apiRequestData<TravelRequestDetail>(`/api/travel-requests/${travelRequestId}`);
}
export async function updateTravelRequest(travelRequestId: string, payload: Partial<TravelRequestPayload>) {
return apiRequestData<TravelRequestDetail>(`/api/travel-requests/${travelRequestId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export async function precheckTravelRequest(travelRequestId: string) {
return apiRequestData<PrecheckResponse>(`/api/travel-requests/${travelRequestId}/precheck`, {
method: "POST",
body: JSON.stringify({}),
});
}
export async function submitTravelRequest(travelRequestId: string) {
return apiRequestData<TravelRequestSubmitResponse>(`/api/travel-requests/${travelRequestId}/submit`, {
method: "POST",
body: JSON.stringify({}),
});
}
export async function withdrawTravelRequest(travelRequestId: string) {
return apiRequestData<TravelRequestWithdrawResponse>(`/api/travel-requests/${travelRequestId}/withdraw`, {
method: "POST",
body: JSON.stringify({}),
});
}
export async function linkTravelRequestProject(travelRequestId: string, projectId: string | null) {
return apiRequestData<{ projectId: string | null; project: { id: string; projectNo: string; name: string } | null }>(`/api/travel-requests/${travelRequestId}/link-project`, {
method: "POST",
body: JSON.stringify({ projectId }),
});
}