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

244 行
6.1 KiB
TypeScript

import { apiRequestData } from "./http";
export type PurchaseRequestStatus = "draft" | "reviewing" | "approved" | "rejected" | "cancelled";
export type PurchaseRequestSummary = {
id: string;
requestNo: string | null;
applicantId: string;
submitterId: string | null;
departmentId: string;
legalEntityId: string | null;
costCenterId: string | null;
supplierId: string | null;
documentCategoryId: string | null;
budgetPoolId: string | null;
title: string;
reason: string | null;
currencyCode: string;
totalAmount: string;
expectedDeliveryDate: string | null;
status: PurchaseRequestStatus;
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;
supplier: {
id: string;
code: string;
name: string;
} | null;
documentCategory: {
id: string;
code: string;
name: string;
entityType: string;
} | null;
budgetPool: {
id: string;
code: string;
name: string;
} | null;
};
export type PurchaseRequestItem = {
id: string;
description: string;
specification: string | null;
quantity: string;
unitPrice: string;
lineAmount: string;
isAssetCandidate: boolean;
createdAt: string | null;
updatedAt: string | null;
};
export type PurchaseAttachment = {
id: string;
originalName: string;
mimeType: string;
fileSize: number;
uploadedBy: string;
createdAt: string | null;
uploader: {
id: string;
employeeNo: string;
name: string;
} | null;
};
export type PurchaseRequestDetail = PurchaseRequestSummary & {
items: PurchaseRequestItem[];
attachments: PurchaseAttachment[];
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;
};
};
export type PurchaseRequestListResponse = {
page: number;
pageSize: number;
total: number;
mine: boolean;
items: PurchaseRequestSummary[];
};
export type PurchaseRequestListQuery = {
status?: PurchaseRequestStatus;
page?: number;
pageSize?: number;
mine?: boolean;
supplierId?: string;
legalEntityId?: string;
costCenterId?: string;
};
export type PurchaseRequestItemPayload = {
description: string;
specification?: string | null;
quantity: string;
unitPrice: string;
isAssetCandidate?: boolean;
};
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 fetchPurchaseRequests(query: PurchaseRequestListQuery = {}) {
return apiRequestData<PurchaseRequestListResponse>(
`/api/purchase-requests${toQueryString(query as Record<string, string | number | boolean | undefined>)}`,
);
}
export async function createPurchaseRequest() {
return apiRequestData<{ purchaseRequest: PurchaseRequestDetail }>("/api/purchase-requests", {
method: "POST",
body: JSON.stringify({}),
});
}
export async function fetchPurchaseRequestDetail(purchaseRequestId: string) {
return apiRequestData<PurchaseRequestDetail>(`/api/purchase-requests/${purchaseRequestId}`);
}
export async function updatePurchaseRequest(
purchaseRequestId: string,
payload: Partial<{
title: string;
reason: string | null;
currencyCode: string;
legalEntityId: string | null;
costCenterId: string | null;
supplierId: string | null;
documentCategoryId: string | null;
budgetPoolId: string | null;
expectedDeliveryDate: string | null;
items: PurchaseRequestItemPayload[];
}>,
) {
return apiRequestData<{ purchaseRequest: PurchaseRequestDetail }>(`/api/purchase-requests/${purchaseRequestId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export async function submitPurchaseRequest(purchaseRequestId: string) {
return apiRequestData<{
purchaseRequestId: string;
requestNo: string;
status: "reviewing";
workflowInstanceId: string;
currentTaskId: string;
budgetPoolId: string | null;
warnings: string[];
}>(`/api/purchase-requests/${purchaseRequestId}/submit`, {
method: "POST",
body: JSON.stringify({}),
});
}
export async function withdrawPurchaseRequest(purchaseRequestId: string) {
return apiRequestData<{
purchaseRequestId: string;
status: "draft";
workflowInstanceId: string | null;
}>(`/api/purchase-requests/${purchaseRequestId}/withdraw`, {
method: "POST",
body: JSON.stringify({}),
});
}
export async function uploadPurchaseAttachment(purchaseRequestId: string, file: File) {
const formData = new FormData();
formData.append("file", file);
return apiRequestData<{ attachment: PurchaseAttachment }>(`/api/purchase-requests/${purchaseRequestId}/attachments`, {
method: "POST",
body: formData,
});
}
export async function deletePurchaseAttachment(purchaseRequestId: string, attachmentId: string) {
return apiRequestData<{ outcome: "deleted"; attachmentId: string }>(
`/api/purchase-requests/${purchaseRequestId}/attachments/${attachmentId}`,
{
method: "DELETE",
},
);
}
export async function issuePurchaseAttachmentSignedUrl(purchaseRequestId: string, attachmentId: string) {
return apiRequestData<{ signedUrl: string; expiresAt: string }>(
`/api/purchase-requests/${purchaseRequestId}/attachments/${attachmentId}/signed-url`,
{
method: "POST",
body: JSON.stringify({}),
},
);
}