import { api } from "src/boot/axios"; import { isProxy, toRaw } from "vue"; import { normalizeExpense, validateExpenseUI } from "../../utils/expenses-validators"; import type { ExpenseType } from "../../types/expense.types"; import { ExpensesApiError } from "../../types/expense-validation.interface"; import type { ExpensePayload, PayPeriodExpenses, TimesheetExpense, UpsertExpenseResult, UpsertExpensesBody, UpsertExpensesResponse } from "../../types/expense.interfaces"; /* eslint-disable */ const toPlain = (obj:T): T => { const raw = isProxy(obj) ? toRaw(obj) : obj; if( typeof (globalThis as any).structuredClone === 'function') { return (globalThis as any).structuredClone(raw); } return JSON.parse(JSON.stringify(raw)); }; const normalizePayload = (expense: ExpensePayload): ExpensePayload => { const exp = normalizeExpense(expense as unknown as TimesheetExpense); const out: ExpensePayload = { date: exp.date, type: exp.type as ExpenseType, comment: exp.comment || '', }; if(typeof exp.amount === 'number') out.amount = exp.amount; if(typeof exp.mileage === 'number') out.mileage = exp.mileage; return out; } //GET by email, year and period no export const getPayPeriodExpenses = async ( email: string, pay_year: number, pay_period_no: number ) : Promise => { const encoded_email = encodeURIComponent(email); const encoded_year = encodeURIComponent(String(pay_year)); const encoded_pay_period_no = encodeURIComponent(String(pay_period_no)); try { const { data } = await api.get(`/expenses/${encoded_email}/${encoded_year}/${encoded_pay_period_no}`); const items = Array.isArray(data.expenses) ? data.expenses.map(normalizeExpense) : []; return { ...data, expenses: items, }; } catch(err:any) { const status_code: number = err?.response?.status ?? 500; const data = err?.response?.data ?? {}; throw new ExpensesApiError({ status_code, error_code: data.error_code, message: data.message || data.error || err.message, context: data.context, }); } }; //PUT by email, year and period no export const putPayPeriodExpenses = async ( email: string, pay_year: number, pay_period_no: number, expenses: TimesheetExpense[] ): Promise => { const encoded_email = encodeURIComponent(email); const encoded_year = encodeURIComponent(String(pay_year)); const encoded_pay_period_no = encodeURIComponent(String(pay_period_no)); const plain = Array.isArray(expenses) ? expenses.map(toPlain): []; const normalized: ExpensePayload[] = plain.map((exp) => { const norm = normalizeExpense(exp as TimesheetExpense); validateExpenseUI(norm, 'expense_item'); return normalizePayload(norm as unknown as ExpensePayload); }); const body: UpsertExpensesBody = {expenses: normalized}; try { const { data } = await api.put( `/expenses/${encoded_email}/${encoded_year}/${encoded_pay_period_no}`, body, { headers: {'Content-Type': 'application/json'}} ); const items = Array.isArray(data?.data?.expenses) ? data.data.expenses.map(normalizeExpense) : []; return { ...(data?.data ?? { pay_period_no, pay_year, employee_email: email, is_approved: false, expenses: [], totals: {amount: 0, mileage: 0}, }), expenses: items, }; } catch (err: any) { const status_code: number = err?.response?.status ?? 500; const data = err?.response?.data ?? {}; throw new ExpensesApiError({ status_code, error_code: data.error_code, message: data.message || data.error || err.message, context: data.context, }); } }; export const postPayPeriodExpenses = async ( email: string, pay_year: number, pay_period_no: number, new_expenses: TimesheetExpense[] ): Promise => { const encoded_email = encodeURIComponent(email); const encoded_year = encodeURIComponent(String(pay_year)); const encoded_pp = encodeURIComponent(String(pay_period_no)); const plain = Array.isArray(new_expenses) ? new_expenses.map(toPlain) : []; const normalized: ExpensePayload[] = plain.map((exp) => { const norm = normalizeExpense(exp as TimesheetExpense); validateExpenseUI(norm, 'expense_item'); return normalizePayload(norm as unknown as ExpensePayload); }); const body: UpsertExpensesBody = { expenses: normalized }; try { const { data } = await api.post( `/expenses/${encoded_email}/${encoded_year}/${encoded_pp}`, body, { headers: { 'content-type': 'application/json' } } ); const items = Array.isArray(data?.data?.expenses) ? data.data.expenses.map(normalizeExpense) : []; return { ...(data?.data ?? { pay_period_no, pay_year, employee_email: email, is_approved: false, expenses: [], totals: { amount: 0, mileage: 0 }, }), expenses: items, }; } catch (err: any) { const status_code: number = err?.response?.status ?? 500; const data = err?.response?.data ?? {}; throw new ExpensesApiError({ status_code, error_code: data.error_code, message: data.message || data.error || err.message, context: data.context, }); } }; const resolveDateISO = (a?: ExpensePayload, b?: ExpensePayload): string => { const d = a?.date || b?.date; if(!d) throw new Error('date is required in payload'); return d; }; const makeBody = (obj: { old_expense?: ExpensePayload; new_expense?: ExpensePayload; }) => obj; const postUpsert = async (email: string, date: string, body: { old_expense?: ExpensePayload; new_expense?: ExpensePayload; }): Promise => { try { const url = `/expenses/upsert/${encodeURIComponent(email)}/${date}`; const { data } = await api.post(url, body, { headers: { 'Content-Type': 'application/json'}, }); return data; } catch(err:any) { const status_code: number = err?.response?.status ?? 500; const data = err?.response?.data ?? {}; throw new ExpensesApiError({ status_code, error_code: data.error_code, message: data.message || data.error || err.message, context: data.context, }); } }; //create a new expense export const createExpenseByDate = async ( email: string, payload: ExpensePayload): Promise => { const new_expense = normalizePayload(payload); const date = resolveDateISO(new_expense); return postUpsert(email, date, makeBody({ new_expense: new_expense })); }; //update an expense export const updateExpenseByDate = async ( email: string, old_expense: ExpensePayload, new_expense: ExpensePayload): Promise => { const old_exp = normalizePayload(old_expense); const new_exp = normalizePayload(new_expense); const date = resolveDateISO(new_exp, old_exp); return postUpsert(email, date, makeBody({ old_expense: old_exp,new_expense: new_exp })); }; //delete an expense export const deleteExpenseByDate = async (email: string, old_expense: ExpensePayload): Promise => { const old = normalizePayload(old_expense); const date = resolveDateISO(undefined, old); return postUpsert(email, date, makeBody({ old_expense: old })); };