erp-core-frontend-web/src/services/api/financial.api.ts
rckrdmrd f1a9ea3d1f [TASK-2026-01-20-004] feat: Add API clients for sales, purchases, financial, CRM, projects
EPIC-P1-004: Frontend API clients
- Add sales.api.ts (orders, quotations, returns)
- Add purchases.api.ts (purchase orders, suppliers, receipts)
- Add financial.api.ts (accounts, journals, payments)
- Add crm.api.ts (leads, opportunities, campaigns)
- Add projects.api.ts (projects, tasks, timesheets)
- Update api/index.ts with new exports

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 04:06:32 -06:00

560 lines
16 KiB
TypeScript

import api from './axios-instance';
import { API_ENDPOINTS } from '@shared/constants/api-endpoints';
import type { BaseEntity } from '@shared/types/entities.types';
import type { ApiResponse, PaginatedResponse, PaginationParams } from '@shared/types/api.types';
// ============================================================================
// Interfaces
// ============================================================================
export interface Account extends BaseEntity {
code: string;
name: string;
type: 'asset' | 'liability' | 'equity' | 'income' | 'expense';
accountType: string;
parentId?: string;
currencyId?: string;
reconcile: boolean;
isActive: boolean;
balance?: number;
tenantId: string;
}
export interface Journal extends BaseEntity {
code: string;
name: string;
type: 'sale' | 'purchase' | 'cash' | 'bank' | 'general';
defaultDebitAccountId?: string;
defaultCreditAccountId?: string;
currencyId?: string;
isActive: boolean;
tenantId: string;
}
export interface JournalEntryLine {
id?: string;
accountId: string;
accountName?: string;
partnerId?: string;
partnerName?: string;
name?: string;
debit: number;
credit: number;
currencyId?: string;
amountCurrency?: number;
}
export interface JournalEntry extends BaseEntity {
name: string;
journalId: string;
journalName?: string;
date: string;
ref?: string;
status: 'draft' | 'posted' | 'cancelled';
lines: JournalEntryLine[];
tenantId: string;
}
export interface InvoiceLine {
id?: string;
productId?: string;
productName?: string;
accountId: string;
name: string;
quantity: number;
unitPrice: number;
discount?: number;
taxIds?: string[];
subtotal?: number;
}
export interface Invoice extends BaseEntity {
name: string;
type: 'out_invoice' | 'out_refund' | 'in_invoice' | 'in_refund';
partnerId: string;
partnerName?: string;
dateInvoice: string;
dateDue?: string;
journalId: string;
status: 'draft' | 'open' | 'paid' | 'cancelled';
paymentState?: 'not_paid' | 'partial' | 'paid';
lines: InvoiceLine[];
amountUntaxed: number;
amountTax: number;
amountTotal: number;
amountResidual: number;
currencyId?: string;
notes?: string;
tenantId: string;
}
export interface Payment extends BaseEntity {
name: string;
partnerId: string;
partnerName?: string;
paymentType: 'inbound' | 'outbound' | 'transfer';
paymentMethod: 'manual' | 'check' | 'bank_transfer' | 'electronic';
journalId: string;
journalName?: string;
date: string;
amount: number;
currencyId?: string;
memo?: string;
status: 'draft' | 'posted' | 'reconciled' | 'cancelled';
invoiceIds?: string[];
tenantId: string;
}
// ============================================================================
// DTOs
// ============================================================================
export interface CreateAccountDto {
code: string;
name: string;
type: Account['type'];
accountType: string;
parentId?: string;
currencyId?: string;
reconcile?: boolean;
isActive?: boolean;
}
export interface UpdateAccountDto {
code?: string;
name?: string;
type?: Account['type'];
accountType?: string;
parentId?: string;
currencyId?: string;
reconcile?: boolean;
isActive?: boolean;
}
export interface CreateJournalDto {
code: string;
name: string;
type: Journal['type'];
defaultDebitAccountId?: string;
defaultCreditAccountId?: string;
currencyId?: string;
isActive?: boolean;
}
export interface UpdateJournalDto {
code?: string;
name?: string;
type?: Journal['type'];
defaultDebitAccountId?: string;
defaultCreditAccountId?: string;
currencyId?: string;
isActive?: boolean;
}
export interface CreateJournalEntryDto {
journalId: string;
date: string;
ref?: string;
lines: Omit<JournalEntryLine, 'id' | 'accountName' | 'partnerName'>[];
}
export interface CreateInvoiceDto {
type: Invoice['type'];
partnerId: string;
dateInvoice?: string;
dateDue?: string;
journalId: string;
lines: Omit<InvoiceLine, 'id' | 'productName' | 'subtotal'>[];
currencyId?: string;
notes?: string;
}
export interface UpdateInvoiceDto {
partnerId?: string;
dateInvoice?: string;
dateDue?: string;
journalId?: string;
lines?: Omit<InvoiceLine, 'productName' | 'subtotal'>[];
currencyId?: string;
notes?: string;
}
export interface CreatePaymentDto {
partnerId: string;
paymentType: Payment['paymentType'];
paymentMethod: Payment['paymentMethod'];
journalId: string;
date?: string;
amount: number;
currencyId?: string;
memo?: string;
invoiceIds?: string[];
}
export interface UpdatePaymentDto {
partnerId?: string;
paymentMethod?: Payment['paymentMethod'];
journalId?: string;
date?: string;
amount?: number;
currencyId?: string;
memo?: string;
invoiceIds?: string[];
}
// ============================================================================
// Filters
// ============================================================================
export interface AccountFilters extends PaginationParams {
type?: Account['type'];
isActive?: boolean;
code?: string;
}
export interface JournalFilters extends PaginationParams {
type?: Journal['type'];
isActive?: boolean;
}
export interface JournalEntryFilters extends PaginationParams {
journalId?: string;
status?: JournalEntry['status'];
dateFrom?: string;
dateTo?: string;
}
export interface InvoiceFilters extends PaginationParams {
type?: Invoice['type'];
partnerId?: string;
status?: Invoice['status'];
paymentState?: Invoice['paymentState'];
dateFrom?: string;
dateTo?: string;
}
export interface PaymentFilters extends PaginationParams {
partnerId?: string;
paymentType?: Payment['paymentType'];
status?: Payment['status'];
journalId?: string;
dateFrom?: string;
dateTo?: string;
}
// ============================================================================
// API Client
// ============================================================================
export const financialApi = {
// Accounts
getAccounts: async (filters?: AccountFilters): Promise<PaginatedResponse<Account>> => {
const response = await api.get<PaginatedResponse<Account>>(
API_ENDPOINTS.FINANCIAL.ACCOUNTS,
{ params: filters }
);
return response.data;
},
getAccountById: async (id: string): Promise<Account> => {
const response = await api.get<ApiResponse<Account>>(
`${API_ENDPOINTS.FINANCIAL.ACCOUNTS}/${id}`
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Cuenta contable no encontrada');
}
return response.data.data;
},
createAccount: async (data: CreateAccountDto): Promise<Account> => {
const response = await api.post<ApiResponse<Account>>(
API_ENDPOINTS.FINANCIAL.ACCOUNTS,
data
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al crear cuenta contable');
}
return response.data.data;
},
updateAccount: async (id: string, data: UpdateAccountDto): Promise<Account> => {
const response = await api.patch<ApiResponse<Account>>(
`${API_ENDPOINTS.FINANCIAL.ACCOUNTS}/${id}`,
data
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al actualizar cuenta contable');
}
return response.data.data;
},
deleteAccount: async (id: string): Promise<void> => {
const response = await api.delete<ApiResponse>(
`${API_ENDPOINTS.FINANCIAL.ACCOUNTS}/${id}`
);
if (!response.data.success) {
throw new Error(response.data.error || 'Error al eliminar cuenta contable');
}
},
// Journals
getJournals: async (filters?: JournalFilters): Promise<PaginatedResponse<Journal>> => {
const response = await api.get<PaginatedResponse<Journal>>(
API_ENDPOINTS.FINANCIAL.JOURNALS,
{ params: filters }
);
return response.data;
},
getJournalById: async (id: string): Promise<Journal> => {
const response = await api.get<ApiResponse<Journal>>(
`${API_ENDPOINTS.FINANCIAL.JOURNALS}/${id}`
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Diario contable no encontrado');
}
return response.data.data;
},
createJournal: async (data: CreateJournalDto): Promise<Journal> => {
const response = await api.post<ApiResponse<Journal>>(
API_ENDPOINTS.FINANCIAL.JOURNALS,
data
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al crear diario contable');
}
return response.data.data;
},
updateJournal: async (id: string, data: UpdateJournalDto): Promise<Journal> => {
const response = await api.patch<ApiResponse<Journal>>(
`${API_ENDPOINTS.FINANCIAL.JOURNALS}/${id}`,
data
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al actualizar diario contable');
}
return response.data.data;
},
deleteJournal: async (id: string): Promise<void> => {
const response = await api.delete<ApiResponse>(
`${API_ENDPOINTS.FINANCIAL.JOURNALS}/${id}`
);
if (!response.data.success) {
throw new Error(response.data.error || 'Error al eliminar diario contable');
}
},
// Journal Entries (Moves)
getJournalEntries: async (filters?: JournalEntryFilters): Promise<PaginatedResponse<JournalEntry>> => {
const response = await api.get<PaginatedResponse<JournalEntry>>(
API_ENDPOINTS.FINANCIAL.MOVES,
{ params: filters }
);
return response.data;
},
getJournalEntryById: async (id: string): Promise<JournalEntry> => {
const response = await api.get<ApiResponse<JournalEntry>>(
`${API_ENDPOINTS.FINANCIAL.MOVES}/${id}`
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Asiento contable no encontrado');
}
return response.data.data;
},
createJournalEntry: async (data: CreateJournalEntryDto): Promise<JournalEntry> => {
const response = await api.post<ApiResponse<JournalEntry>>(
API_ENDPOINTS.FINANCIAL.MOVES,
data
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al crear asiento contable');
}
return response.data.data;
},
postJournalEntry: async (id: string): Promise<JournalEntry> => {
const response = await api.post<ApiResponse<JournalEntry>>(
`${API_ENDPOINTS.FINANCIAL.MOVES}/${id}/post`
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al publicar asiento contable');
}
return response.data.data;
},
cancelJournalEntry: async (id: string): Promise<JournalEntry> => {
const response = await api.post<ApiResponse<JournalEntry>>(
`${API_ENDPOINTS.FINANCIAL.MOVES}/${id}/cancel`
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al cancelar asiento contable');
}
return response.data.data;
},
deleteJournalEntry: async (id: string): Promise<void> => {
const response = await api.delete<ApiResponse>(
`${API_ENDPOINTS.FINANCIAL.MOVES}/${id}`
);
if (!response.data.success) {
throw new Error(response.data.error || 'Error al eliminar asiento contable');
}
},
// Invoices
getInvoices: async (filters?: InvoiceFilters): Promise<PaginatedResponse<Invoice>> => {
const response = await api.get<PaginatedResponse<Invoice>>(
API_ENDPOINTS.FINANCIAL.INVOICES,
{ params: filters }
);
return response.data;
},
getInvoiceById: async (id: string): Promise<Invoice> => {
const response = await api.get<ApiResponse<Invoice>>(
`${API_ENDPOINTS.FINANCIAL.INVOICES}/${id}`
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Factura no encontrada');
}
return response.data.data;
},
createInvoice: async (data: CreateInvoiceDto): Promise<Invoice> => {
const response = await api.post<ApiResponse<Invoice>>(
API_ENDPOINTS.FINANCIAL.INVOICES,
data
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al crear factura');
}
return response.data.data;
},
updateInvoice: async (id: string, data: UpdateInvoiceDto): Promise<Invoice> => {
const response = await api.patch<ApiResponse<Invoice>>(
`${API_ENDPOINTS.FINANCIAL.INVOICES}/${id}`,
data
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al actualizar factura');
}
return response.data.data;
},
validateInvoice: async (id: string): Promise<Invoice> => {
const response = await api.post<ApiResponse<Invoice>>(
`${API_ENDPOINTS.FINANCIAL.INVOICES}/${id}/validate`
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al validar factura');
}
return response.data.data;
},
cancelInvoice: async (id: string): Promise<Invoice> => {
const response = await api.post<ApiResponse<Invoice>>(
`${API_ENDPOINTS.FINANCIAL.INVOICES}/${id}/cancel`
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al cancelar factura');
}
return response.data.data;
},
deleteInvoice: async (id: string): Promise<void> => {
const response = await api.delete<ApiResponse>(
`${API_ENDPOINTS.FINANCIAL.INVOICES}/${id}`
);
if (!response.data.success) {
throw new Error(response.data.error || 'Error al eliminar factura');
}
},
// Payments
getPayments: async (filters?: PaymentFilters): Promise<PaginatedResponse<Payment>> => {
const response = await api.get<PaginatedResponse<Payment>>(
API_ENDPOINTS.FINANCIAL.PAYMENTS,
{ params: filters }
);
return response.data;
},
getPaymentById: async (id: string): Promise<Payment> => {
const response = await api.get<ApiResponse<Payment>>(
`${API_ENDPOINTS.FINANCIAL.PAYMENTS}/${id}`
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Pago no encontrado');
}
return response.data.data;
},
createPayment: async (data: CreatePaymentDto): Promise<Payment> => {
const response = await api.post<ApiResponse<Payment>>(
API_ENDPOINTS.FINANCIAL.PAYMENTS,
data
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al crear pago');
}
return response.data.data;
},
updatePayment: async (id: string, data: UpdatePaymentDto): Promise<Payment> => {
const response = await api.patch<ApiResponse<Payment>>(
`${API_ENDPOINTS.FINANCIAL.PAYMENTS}/${id}`,
data
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al actualizar pago');
}
return response.data.data;
},
postPayment: async (id: string): Promise<Payment> => {
const response = await api.post<ApiResponse<Payment>>(
`${API_ENDPOINTS.FINANCIAL.PAYMENTS}/${id}/post`
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al publicar pago');
}
return response.data.data;
},
reconcilePayment: async (id: string, invoiceIds: string[]): Promise<Payment> => {
const response = await api.post<ApiResponse<Payment>>(
`${API_ENDPOINTS.FINANCIAL.PAYMENTS}/${id}/reconcile`,
{ invoiceIds }
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al reconciliar pago');
}
return response.data.data;
},
cancelPayment: async (id: string): Promise<Payment> => {
const response = await api.post<ApiResponse<Payment>>(
`${API_ENDPOINTS.FINANCIAL.PAYMENTS}/${id}/cancel`
);
if (!response.data.success || !response.data.data) {
throw new Error(response.data.error || 'Error al cancelar pago');
}
return response.data.data;
},
deletePayment: async (id: string): Promise<void> => {
const response = await api.delete<ApiResponse>(
`${API_ENDPOINTS.FINANCIAL.PAYMENTS}/${id}`
);
if (!response.data.success) {
throw new Error(response.data.error || 'Error al eliminar pago');
}
},
};