import axios from 'axios'; import { BaseApiService } from '@/services/api/base'; import { BaseApiResponse, GroupedApprovals } from '@/types/api/api-general'; import { BulkApproveExpensePayload, CreateExpensePayload, CreateExpenseRealizationPayload, Expense, UpdateExpensePayload, } from '@/types/api/expense'; import { httpClient } from '@/services/http/client'; import { formatDate } from '@/lib/helper'; export class ExpenseApiService extends BaseApiService< Expense, FormData, FormData > { constructor(basePath: string) { super(basePath); } async create( payload: FormData ): Promise | undefined> { try { const createExpenseRequestRes = await httpClient< BaseApiResponse >(this.basePath, { method: 'POST', body: payload, }); return createExpenseRequestRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async createRealization( id: number, payload: FormData ): Promise | undefined> { try { const createExpenseRealizationRes = await httpClient< BaseApiResponse >(`${this.basePath}/${id}/realizations`, { method: 'POST', body: payload, }); return createExpenseRealizationRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async update( id: number, payload: FormData, deletedDocumentIds?: number[] ): Promise | undefined> { try { for (const deletedDocumentId of deletedDocumentIds ?? []) { await this.deleteExpenseRequestDocument(id, deletedDocumentId); } const updateExpenseRequestRes = await httpClient< BaseApiResponse >(`${this.basePath}/${id}`, { method: 'PATCH', body: payload, }); return updateExpenseRequestRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async updateRealization( id: number, payload: FormData ): Promise | undefined> { try { const updateExpenseRealizationRes = await httpClient< BaseApiResponse >(`${this.basePath}/${id}/realizations`, { method: 'PATCH', body: payload, }); return updateExpenseRealizationRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async uploadRequestDocuments( id: number, files: File[] ): Promise | undefined> { try { const updateExpenseRequestDocumentsFormData = new FormData(); // files (multiple "documents" keys) files.forEach((file) => { updateExpenseRequestDocumentsFormData.append('documents', file); }); const updateExpenseRealizationRes = await httpClient< BaseApiResponse >(`${this.basePath}/${id}`, { method: 'PATCH', body: updateExpenseRequestDocumentsFormData, }); return updateExpenseRealizationRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async uploadRealizationDocuments( id: number, files: File[] ): Promise | undefined> { try { const updateExpenseRealizationDocumentsFormData = new FormData(); // files (multiple "documents" keys) files.forEach((file) => { updateExpenseRealizationDocumentsFormData.append('documents', file); }); const updateExpenseRealizationRes = await httpClient< BaseApiResponse >(`${this.basePath}/${id}/realizations`, { method: 'PATCH', body: updateExpenseRealizationDocumentsFormData, }); return updateExpenseRealizationRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async approveHeadArea( id: number, notes?: string ): Promise | undefined> { try { const approveRes = await httpClient>( `${this.basePath}/approvals/head-area`, { method: 'POST', body: { action: 'APPROVED', approvable_ids: [id], notes: notes, }, } ); return approveRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async bulkApproveHeadArea( ids: number[], notes?: string ): Promise | undefined> { try { const bulkApproveRes = await httpClient>( `${this.basePath}/approvals/head-area`, { method: 'POST', body: { action: 'APPROVED', approvable_ids: ids, notes: notes, }, } ); return bulkApproveRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async approveUnitVicePresident( id: number, notes?: string ): Promise | undefined> { try { const approveRes = await httpClient>( `${this.basePath}/approvals/unit-vice-president`, { method: 'POST', body: { action: 'APPROVED', approvable_ids: [id], notes: notes, }, } ); return approveRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async bulkApproveUnitVicePresident( ids: number[], notes?: string ): Promise | undefined> { try { const bulkApproveRes = await httpClient>( `${this.basePath}/approvals/unit-vice-president`, { method: 'POST', body: { action: 'APPROVED', approvable_ids: ids, notes: notes, }, } ); return bulkApproveRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async approveFinance( id: number, notes?: string ): Promise | undefined> { try { const approveRes = await httpClient>( `${this.basePath}/approvals/finance`, { method: 'POST', body: { action: 'APPROVED', approvable_ids: [id], notes: notes, }, } ); return approveRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async bulkApproveFinance( ids: number[], notes?: string ): Promise | undefined> { try { const bulkApproveRes = await httpClient>( `${this.basePath}/approvals/finance`, { method: 'POST', body: { action: 'APPROVED', approvable_ids: ids, notes: notes, }, } ); return bulkApproveRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async bulkApproveToStatus( payload: BulkApproveExpensePayload ): Promise | undefined> { try { return await httpClient>( `${this.basePath}/approvals/bulk`, { method: 'POST', body: payload, } ); } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async bulkApprovals( ids: number[], status: BulkApproveExpensePayload['status'] | 'SELESAI', date?: string, notes?: string ): Promise | undefined> { if (status === 'SELESAI') { const responses = await Promise.all(ids.map((id) => this.complete(id))); const failedResponse = responses.find( (response) => response?.status !== 'success' ); if (failedResponse) { return failedResponse; } const completedExpenses = responses.flatMap((response) => response?.status === 'success' ? [response.data] : [] ); return { code: 200, status: 'success', message: completedExpenses.length === 1 ? 'Submit expense approval successfully' : 'Submit expense approvals successfully', data: completedExpenses, }; } return this.bulkApproveToStatus({ approvable_ids: ids, status, date: date || undefined, notes: notes || undefined, }); } async rejectHeadArea( id: number, notes?: string ): Promise | undefined> { try { const rejectRes = await httpClient>( `${this.basePath}/approvals/head-area`, { method: 'POST', body: { action: 'REJECTED', approvable_ids: [id], notes: notes, }, } ); return rejectRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async bulkRejectHeadArea( ids: number[], notes?: string ): Promise | undefined> { try { const bulkRejectRes = await httpClient>( `${this.basePath}/approvals/head-area`, { method: 'POST', body: { action: 'REJECTED', approvable_ids: ids, notes: notes, }, } ); return bulkRejectRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async rejectUnitVicePresident( id: number, notes?: string ): Promise | undefined> { try { const rejectRes = await httpClient>( `${this.basePath}/approvals/unit-vice-president`, { method: 'POST', body: { action: 'REJECTED', approvable_ids: [id], notes: notes, }, } ); return rejectRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async bulkRejectUnitVicePresident( ids: number[], notes?: string ): Promise | undefined> { try { const bulkRejectRes = await httpClient>( `${this.basePath}/approvals/unit-vice-president`, { method: 'POST', body: { action: 'REJECTED', approvable_ids: ids, notes: notes, }, } ); return bulkRejectRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async rejectFinance( id: number, notes?: string ): Promise | undefined> { try { const rejectRes = await httpClient>( `${this.basePath}/approvals/finance`, { method: 'POST', body: { action: 'REJECTED', approvable_ids: [id], notes: notes, }, } ); return rejectRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async bulkRejectFinance( ids: number[], notes?: string ): Promise | undefined> { try { const bulkRejectRes = await httpClient>( `${this.basePath}/approvals/finance`, { method: 'POST', body: { action: 'REJECTED', approvable_ids: ids, notes: notes, }, } ); return bulkRejectRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async complete(id: number): Promise | undefined> { try { const completeRes = await httpClient>( `${this.basePath}/${id}/complete`, { method: 'POST', } ); return completeRes; } catch (error) { if (axios.isAxiosError>(error)) { return error.response?.data; } return undefined; } } async deleteExpenseRequestDocument( expenseId: number, documentId: number ): Promise { try { const deleteExpenseRequestDocument = await httpClient( `${this.basePath}/${expenseId}/documents/${documentId}`, { method: 'DELETE', } ); return deleteExpenseRequestDocument; } catch (error) { if (axios.isAxiosError(error)) { return error.response?.data; } return undefined; } } async deleteExpenseRealizationDocument( expenseId: number, documentId: number ): Promise { try { const deleteExpenseRealizationDocument = await httpClient( `${this.basePath}/${expenseId}/realization-documents/${documentId}`, { method: 'DELETE', } ); return deleteExpenseRealizationDocument; } catch (error) { if (axios.isAxiosError(error)) { return error.response?.data; } return undefined; } } async getApprovalHistory( expenseId: number, group: boolean = true, page: number = 1, limit: number = 10 ) { try { const approvalHistoryRes = await httpClient( '/approvals', { query: { module_name: 'EXPENSES', module_id: expenseId, group_step_number: group ? 'true' : 'false', page, limit, }, } ); return approvalHistoryRes; } catch (error) { if (axios.isAxiosError(error)) { return error.response?.data; } return undefined; } } convertExpenseRequestPayloadToFormData = (payload: CreateExpensePayload) => { const formData = new FormData(); formData.append('category', payload.category); formData.append('location_id', String(payload.location_id)); formData.append('transaction_date', payload.transaction_date); formData.append('supplier_id', String(payload.supplier_id)); // files (multiple "documents" keys) payload.documents.forEach((file) => { formData.append('documents', file); }); formData.append( 'expense_nonstocks', JSON.stringify(payload.expense_nonstocks) ); return formData; }; convertExpenseRequestUpdatePayloadToFormData = ( payload: UpdateExpensePayload ) => { const formData = new FormData(); formData.append('category', payload.category); formData.append('location_id', String(payload.location_id)); formData.append('transaction_date', payload.transaction_date); formData.append('supplier_id', String(payload.supplier_id)); // files (multiple "documents" keys) payload.documents.forEach((file) => { formData.append('documents', file); }); formData.append( 'expense_nonstocks', JSON.stringify(payload.expense_nonstocks) ); return formData; }; convertExpenseRealizationPayloadToFormData = ( payload: CreateExpenseRealizationPayload ) => { const formData = new FormData(); formData.append('realization_date', payload.realization_date); // files (multiple "documents" keys) payload.documents.forEach((file) => { formData.append('documents', file); }); formData.append('realizations', JSON.stringify(payload.realizations)); return formData; }; async exportToExcel(initialQueryString: string) { const params = new URLSearchParams(initialQueryString); params.set('export', 'excel'); params.set('type', 'all'); params.set('page', '1'); params.set('limit', '99999999999'); const queryString = `?${params.toString()}`; const res = await httpClient(`${this.basePath}${queryString}`, { method: 'GET', responseType: 'blob', }); const url = window.URL.createObjectURL(new Blob([res])); const link = document.createElement('a'); link.href = url; const fileName = `BOP-${formatDate(Date.now(), 'DD-MM-YYYY')}.xlsx`; link.setAttribute('download', fileName); document.body.appendChild(link); link.click(); link.remove(); } async exportInputProgressToExcel(startDate: string, endDate: string) { const params = new URLSearchParams(); params.set('export', 'excel'); params.set('type', 'progress'); params.set('start_date', formatDate(startDate, 'YYYY-MM-DD')); params.set('end_date', formatDate(endDate, 'YYYY-MM-DD')); const queryString = `?${params.toString()}`; const res = await httpClient(`${this.basePath}${queryString}`, { method: 'GET', responseType: 'blob', }); const url = window.URL.createObjectURL(new Blob([res])); const link = document.createElement('a'); link.href = url; const fileName = `input-progres-BOP-${formatDate(startDate, 'DD-MM-YYYY')}-ke-${formatDate(endDate, 'DD-MM-YYYY')}.xlsx`; link.setAttribute('download', fileName); document.body.appendChild(link); link.click(); link.remove(); } } export const ExpenseApi = new ExpenseApiService('/expenses');