import { api } from '@/lib/api' export type FinanceReportType = | 'TrialBalance' | 'GeneralLedger' | 'ProfitAndLoss' | 'BalanceSheet' | 'CashFlow' | 'BudgetVsActual' | 'TaxSummary' export interface FinanceReportParams { asOfDate?: string periodStart?: string periodEnd?: string accountCode?: string budgetId?: number } export type FileFormat = 'Pdf' | 'Csv' const cleanParams = (params: FinanceReportParams) => Object.fromEntries(Object.entries(params).filter(([, v]) => v !== undefined && v !== '')) // Reports come from the General Ledger service through the Distribution backend (/reports/finance/{type}). export interface GlAccountOption { accountCode: string accountName: string } export const financeReports = { /** Active GL accounts for the account dropdown. */ accounts: () => api.get('/reports/finance/accounts').then(r => r.data), get: (type: FinanceReportType, params: FinanceReportParams) => api.get(`/reports/finance/${type}`, { params: cleanParams(params) }).then(r => r.data), /** Downloads the GL-rendered PDF or CSV and hands it to the browser. */ async download(type: FinanceReportType, params: FinanceReportParams, format: FileFormat) { const res = await api.get(`/reports/finance/${type}`, { params: { ...cleanParams(params), outputFormat: format }, responseType: 'blob', }) const disposition = String(res.headers['content-disposition'] ?? '') const name = /filename\*?=(?:UTF-8'')?"?([^";]+)"?/i.exec(disposition)?.[1] ?? `${type}.${format.toLowerCase()}` const url = URL.createObjectURL(res.data) const a = document.createElement('a') a.href = url a.download = decodeURIComponent(name) a.click() URL.revokeObjectURL(url) }, } export interface GlStatus { glReachable: boolean setupDone: boolean openingBalancePosted: boolean closedYears: number[] mapping: Record posted: number failed: number } export interface GlPostingRow { id: string event: string sourceId: string reference: string status: 'Pending' | 'Posted' | 'Failed' glJournalNo: string | null error: string | null attempts: number lastAttemptAt: string | null createdAt: string } export interface OpeningBalanceLine { /** A key such as 'Inventory' or 'ShareCapital', or 'BankAccount' together with bankAccountId. */ account: string bankAccountId?: string /** The account's normal balance (>= 0). Debit/credit side is decided by the backend. */ amount: number } export interface YearEndPreview { year: number startDate: string endDate: string totalIncome: number totalExpenses: number netProfit: number closable: boolean reason: string | null alreadyClosed: boolean } export const generalLedger = { yearEndPreview: (year: number) => api.get('/gl/year-end/preview', { params: { year } }).then(r => r.data), closeYear: (year: number) => api.post<{ year: number; glJournalNo: string | null; netProfit: number; periodsLocked: number }>('/gl/year-end/close', { year }).then(r => r.data), postOpeningBalances: (date: string, narration: string, lines: OpeningBalanceLine[]) => api.post<{ status: string; glJournalNo: string | null }>('/gl/opening-balances', { date, narration, lines }).then(r => r.data), status: () => api.get('/gl/status').then(r => r.data), setup: (year?: number) => api.post<{ done: boolean; actions: string[] }>('/gl/setup', { year }).then(r => r.data), postings: (status?: GlPostingRow['status']) => api.get('/gl/postings', { params: { status } }).then(r => r.data), retry: () => api.post<{ posted: number }>('/gl/postings/retry').then(r => r.data), }