/* eslint-disable @typescript-eslint/no-explicit-any */ import type { ProcurementApprovalOptions, ProcurementApprovalStepSelection, ProcurementCreateOptions, ProcurementItemAttachment, ProcurementItemComment, ProcurementRequestActivity, } from "@/types/api"; import { buildQuery, type RawRequestFn, type RequestFn } from "./utils"; function parseDownloadFilename(response: Response, fallback: string): string { const disposition = response.headers.get("content-disposition") || ""; const encodedMatch = disposition.match(/filename\*=UTF-8''([^;]+)/); const plainMatch = disposition.match(/filename="?([^";]+)"?/); return encodedMatch?.[1] ? decodeURIComponent(encodedMatch[1]) : plainMatch?.[1] || fallback; } export function createProcurementApi( request: RequestFn, requestRaw: RawRequestFn, ) { return { getProcurementRequests: (params?: Record) => request(`/api/v1/procurement/requests/${buildQuery(params)}`), getProcurementRequest: (id: number) => request(`/api/v1/procurement/requests/${id}/`), getProcurementRequestActivity: ( id: number, ): Promise => request(`/api/v1/procurement/requests/${id}/activity/`), getProcurementRequestCreateOptions: (): Promise => request("/api/v1/procurement/requests/create-options/"), createProcurementRequest: (data: Record) => request("/api/v1/procurement/requests/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }), updateProcurementRequest: (id: number, data: Record) => request(`/api/v1/procurement/requests/${id}/`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }), deleteProcurementRequest: (id: number): Promise => request(`/api/v1/procurement/requests/${id}/`, { method: "DELETE" }), getProcurementApprovalOptions: ( id: number, ): Promise => request(`/api/v1/procurement/requests/${id}/approval-options/`), submitProcurementRequest: ( id: number, approvalSteps?: ProcurementApprovalStepSelection[], ) => request(`/api/v1/procurement/requests/${id}/submit/`, { method: "POST", headers: approvalSteps ? { "Content-Type": "application/json" } : undefined, body: approvalSteps ? JSON.stringify({ approval_steps: approvalSteps }) : undefined, }), approveProcurementRequest: (id: number, comment?: string) => request(`/api/v1/procurement/requests/${id}/approve/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ comment: comment || "" }), }), rejectProcurementRequest: (id: number, comment?: string) => request(`/api/v1/procurement/requests/${id}/reject/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ comment: comment || "" }), }), startWorkProcurementRequest: (id: number) => request(`/api/v1/procurement/requests/${id}/start_work/`, { method: "POST", }), completeProcurementRequest: (id: number) => request(`/api/v1/procurement/requests/${id}/complete/`, { method: "POST", }), markAllReceivedProcurementRequest: (id: number) => request(`/api/v1/procurement/requests/${id}/mark_all_received/`, { method: "POST", }), notifyProcurementRequestArrival: (id: number) => request(`/api/v1/procurement/requests/${id}/notify_arrival/`, { method: "POST", }), setProcurementRequestViewed: (id: number, isViewed: boolean) => request(`/api/v1/procurement/requests/${id}/set_viewed/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ is_viewed: isViewed }), }), setProcurementRequestDeferred: (id: number, isDeferred: boolean) => request(`/api/v1/procurement/requests/${id}/set_deferred/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ is_deferred: isDeferred }), }), markProcurementRequestNotificationsRead: ( id: number, ): Promise<{ status: string; count: number; notification_ids: number[] }> => request(`/api/v1/procurement/requests/${id}/notifications/read/`, { method: "POST", }), markProcurementItemCommentNotificationsRead: ( itemId: number, ): Promise<{ status: string; count: number; notification_ids: number[] }> => request( `/api/v1/procurement/items/${itemId}/comment-notifications/read/`, { method: "POST" }, ), cancelProcurementRequest: (id: number, reason?: string) => request(`/api/v1/procurement/requests/${id}/cancel/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ reason: reason || "" }), }), getProcurementComments: (requestId: number) => request(`/api/v1/procurement/requests/${requestId}/comments/`), addProcurementComment: (requestId: number, text: string) => request(`/api/v1/procurement/requests/${requestId}/comments/`, { method: "POST", body: JSON.stringify({ text }), }), deleteProcurementComment: ( requestId: number, commentId: number, ): Promise => request( `/api/v1/procurement/requests/${requestId}/comments/${commentId}/`, { method: "DELETE" }, ), getMyProcurementRequests: (params?: Record) => request(`/api/v1/procurement/requests/my_requests/${buildQuery(params)}`), getPendingApprovals: (params?: Record) => request( `/api/v1/procurement/requests/pending_approvals/${buildQuery(params)}`, ), // Items getProcurementItems: (params?: Record) => request(`/api/v1/procurement/items/${buildQuery(params)}`), createProcurementItem: (data: Record) => request("/api/v1/procurement/items/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }), updateProcurementItem: (id: number, data: Record) => request(`/api/v1/procurement/items/${id}/`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }), reportProcurementItemIssue: (id: number, text?: string) => request(`/api/v1/procurement/items/${id}/report_issue/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text: text || "" }), }), cancelProcurementItemIssue: (id: number) => request(`/api/v1/procurement/items/${id}/cancel_issue/`, { method: "POST", }), confirmProcurementItemOrdered: ( id: number, orderedQuantity: number, expectedDeliveryDate?: string, ) => request(`/api/v1/procurement/items/${id}/confirm_ordered/`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ordered_quantity: orderedQuantity, expected_delivery_date: expectedDeliveryDate || undefined, }), }), confirmProcurementItemReceived: (id: number, receivedQuantity?: number) => request(`/api/v1/procurement/items/${id}/confirm_received/`, { method: "POST", headers: receivedQuantity === undefined ? undefined : { "Content-Type": "application/json" }, body: receivedQuantity === undefined ? undefined : JSON.stringify({ received_quantity: receivedQuantity }), }), cancelProcurementItemReceived: (id: number, cancelQuantity?: number) => request(`/api/v1/procurement/items/${id}/cancel_received/`, { method: "POST", headers: cancelQuantity === undefined ? undefined : { "Content-Type": "application/json" }, body: cancelQuantity === undefined ? undefined : JSON.stringify({ cancel_quantity: cancelQuantity }), }), deleteProcurementItem: (id: number): Promise => request(`/api/v1/procurement/items/${id}/`, { method: "DELETE" }), getProcurementItemComments: (itemId: number) => request(`/api/v1/procurement/items/${itemId}/comments/`), addProcurementItemComment: ( itemId: number, text: string, files: File[] = [], ): Promise => { const formData = new FormData(); formData.append("text", text); files.forEach((file) => formData.append("files", file)); return request(`/api/v1/procurement/items/${itemId}/comments/`, { method: "POST", body: formData, }); }, deleteProcurementItemComment: ( itemId: number, commentId: number, ): Promise => request(`/api/v1/procurement/items/${itemId}/comments/${commentId}/`, { method: "DELETE", }), getProcurementItemAttachments: ( itemId: number, ): Promise => request(`/api/v1/procurement/items/${itemId}/attachments/`), uploadProcurementItemAttachments: ( itemId: number, files: File[], ): Promise => { const formData = new FormData(); files.forEach((file) => formData.append("files", file)); return request(`/api/v1/procurement/items/${itemId}/attachments/`, { method: "POST", body: formData, }); }, downloadProcurementItemAttachment: async ( itemId: number, attachment: ProcurementItemAttachment, ): Promise<{ blob: Blob; filename: string }> => { const response = await requestRaw( attachment.download_url || `/api/v1/procurement/items/${itemId}/attachments/${attachment.id}/`, ); return { blob: await response.blob(), filename: parseDownloadFilename(response, attachment.file_name), }; }, downloadProcurementItemAttachmentThumbnail: async ( itemId: number, attachment: ProcurementItemAttachment, ): Promise => { const response = await requestRaw( attachment.thumbnail_url || `/api/v1/procurement/items/${itemId}/attachments/${attachment.id}/thumbnail/`, ); return response.blob(); }, deleteProcurementItemAttachment: ( itemId: number, attachmentId: number, ): Promise => request( `/api/v1/procurement/items/${itemId}/attachments/${attachmentId}/`, { method: "DELETE" }, ), // Suppliers getProcurementSuppliers: (params?: Record) => request(`/api/v1/procurement/suppliers/${buildQuery(params)}`), createProcurementSupplier: (data: Record) => request("/api/v1/procurement/suppliers/", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }), updateProcurementSupplier: (id: number, data: Record) => request(`/api/v1/procurement/suppliers/${id}/`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }), deleteProcurementSupplier: (id: number): Promise => request(`/api/v1/procurement/suppliers/${id}/`, { method: "DELETE" }), getTopRatedProcurementSuppliers: () => request("/api/v1/procurement/suppliers/top_rated/"), // Stats getProcurementOverviewStats: () => request("/api/v1/procurement/stats/overview/"), getProcurementDepartmentStats: () => request("/api/v1/procurement/stats/by-department/"), }; }