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>
275 lines
8.3 KiB
TypeScript
275 lines
8.3 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 SalesOrderLine {
|
|
id?: string;
|
|
productId: string;
|
|
productName?: string;
|
|
quantity: number;
|
|
unitPrice: number;
|
|
discount?: number;
|
|
taxIds?: string[];
|
|
subtotal?: number;
|
|
}
|
|
|
|
export interface SalesOrder extends BaseEntity {
|
|
name: string;
|
|
partnerId: string;
|
|
partnerName?: string;
|
|
dateOrder: string;
|
|
dateDelivery?: string;
|
|
status: 'draft' | 'confirmed' | 'done' | 'cancelled';
|
|
pricelistId?: string;
|
|
paymentTermId?: string;
|
|
warehouseId?: string;
|
|
lines: SalesOrderLine[];
|
|
amountUntaxed: number;
|
|
amountTax: number;
|
|
amountTotal: number;
|
|
notes?: string;
|
|
tenantId: string;
|
|
}
|
|
|
|
export interface Quotation extends BaseEntity {
|
|
name: string;
|
|
partnerId: string;
|
|
partnerName?: string;
|
|
dateQuotation: string;
|
|
dateExpiry?: string;
|
|
status: 'draft' | 'sent' | 'confirmed' | 'cancelled' | 'expired';
|
|
pricelistId?: string;
|
|
paymentTermId?: string;
|
|
lines: SalesOrderLine[];
|
|
amountUntaxed: number;
|
|
amountTax: number;
|
|
amountTotal: number;
|
|
notes?: string;
|
|
tenantId: string;
|
|
}
|
|
|
|
// ============================================================================
|
|
// DTOs
|
|
// ============================================================================
|
|
|
|
export interface CreateSalesOrderDto {
|
|
partnerId: string;
|
|
dateOrder?: string;
|
|
dateDelivery?: string;
|
|
pricelistId?: string;
|
|
paymentTermId?: string;
|
|
warehouseId?: string;
|
|
lines: Omit<SalesOrderLine, 'id' | 'productName' | 'subtotal'>[];
|
|
notes?: string;
|
|
}
|
|
|
|
export interface UpdateSalesOrderDto {
|
|
partnerId?: string;
|
|
dateOrder?: string;
|
|
dateDelivery?: string;
|
|
pricelistId?: string;
|
|
paymentTermId?: string;
|
|
warehouseId?: string;
|
|
lines?: Omit<SalesOrderLine, 'productName' | 'subtotal'>[];
|
|
notes?: string;
|
|
}
|
|
|
|
export interface CreateQuotationDto {
|
|
partnerId: string;
|
|
dateQuotation?: string;
|
|
dateExpiry?: string;
|
|
pricelistId?: string;
|
|
paymentTermId?: string;
|
|
lines: Omit<SalesOrderLine, 'id' | 'productName' | 'subtotal'>[];
|
|
notes?: string;
|
|
}
|
|
|
|
export interface UpdateQuotationDto {
|
|
partnerId?: string;
|
|
dateQuotation?: string;
|
|
dateExpiry?: string;
|
|
pricelistId?: string;
|
|
paymentTermId?: string;
|
|
lines?: Omit<SalesOrderLine, 'productName' | 'subtotal'>[];
|
|
notes?: string;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Filters
|
|
// ============================================================================
|
|
|
|
export interface SalesOrderFilters extends PaginationParams {
|
|
partnerId?: string;
|
|
status?: SalesOrder['status'];
|
|
dateFrom?: string;
|
|
dateTo?: string;
|
|
}
|
|
|
|
export interface QuotationFilters extends PaginationParams {
|
|
partnerId?: string;
|
|
status?: Quotation['status'];
|
|
dateFrom?: string;
|
|
dateTo?: string;
|
|
}
|
|
|
|
// ============================================================================
|
|
// API Client
|
|
// ============================================================================
|
|
|
|
export const salesApi = {
|
|
// Orders
|
|
getOrders: async (filters?: SalesOrderFilters): Promise<PaginatedResponse<SalesOrder>> => {
|
|
const response = await api.get<PaginatedResponse<SalesOrder>>(
|
|
API_ENDPOINTS.SALES.ORDERS,
|
|
{ params: filters }
|
|
);
|
|
return response.data;
|
|
},
|
|
|
|
getOrderById: async (id: string): Promise<SalesOrder> => {
|
|
const response = await api.get<ApiResponse<SalesOrder>>(
|
|
`${API_ENDPOINTS.SALES.ORDERS}/${id}`
|
|
);
|
|
if (!response.data.success || !response.data.data) {
|
|
throw new Error(response.data.error || 'Orden de venta no encontrada');
|
|
}
|
|
return response.data.data;
|
|
},
|
|
|
|
createOrder: async (data: CreateSalesOrderDto): Promise<SalesOrder> => {
|
|
const response = await api.post<ApiResponse<SalesOrder>>(
|
|
API_ENDPOINTS.SALES.ORDERS,
|
|
data
|
|
);
|
|
if (!response.data.success || !response.data.data) {
|
|
throw new Error(response.data.error || 'Error al crear orden de venta');
|
|
}
|
|
return response.data.data;
|
|
},
|
|
|
|
updateOrder: async (id: string, data: UpdateSalesOrderDto): Promise<SalesOrder> => {
|
|
const response = await api.patch<ApiResponse<SalesOrder>>(
|
|
`${API_ENDPOINTS.SALES.ORDERS}/${id}`,
|
|
data
|
|
);
|
|
if (!response.data.success || !response.data.data) {
|
|
throw new Error(response.data.error || 'Error al actualizar orden de venta');
|
|
}
|
|
return response.data.data;
|
|
},
|
|
|
|
confirmOrder: async (id: string): Promise<SalesOrder> => {
|
|
const response = await api.post<ApiResponse<SalesOrder>>(
|
|
`${API_ENDPOINTS.SALES.ORDERS}/${id}/confirm`
|
|
);
|
|
if (!response.data.success || !response.data.data) {
|
|
throw new Error(response.data.error || 'Error al confirmar orden de venta');
|
|
}
|
|
return response.data.data;
|
|
},
|
|
|
|
cancelOrder: async (id: string): Promise<SalesOrder> => {
|
|
const response = await api.post<ApiResponse<SalesOrder>>(
|
|
`${API_ENDPOINTS.SALES.ORDERS}/${id}/cancel`
|
|
);
|
|
if (!response.data.success || !response.data.data) {
|
|
throw new Error(response.data.error || 'Error al cancelar orden de venta');
|
|
}
|
|
return response.data.data;
|
|
},
|
|
|
|
deleteOrder: async (id: string): Promise<void> => {
|
|
const response = await api.delete<ApiResponse>(
|
|
`${API_ENDPOINTS.SALES.ORDERS}/${id}`
|
|
);
|
|
if (!response.data.success) {
|
|
throw new Error(response.data.error || 'Error al eliminar orden de venta');
|
|
}
|
|
},
|
|
|
|
// Quotations
|
|
getQuotations: async (filters?: QuotationFilters): Promise<PaginatedResponse<Quotation>> => {
|
|
const response = await api.get<PaginatedResponse<Quotation>>(
|
|
API_ENDPOINTS.SALES.QUOTATIONS,
|
|
{ params: filters }
|
|
);
|
|
return response.data;
|
|
},
|
|
|
|
getQuotationById: async (id: string): Promise<Quotation> => {
|
|
const response = await api.get<ApiResponse<Quotation>>(
|
|
`${API_ENDPOINTS.SALES.QUOTATIONS}/${id}`
|
|
);
|
|
if (!response.data.success || !response.data.data) {
|
|
throw new Error(response.data.error || 'Cotizacion no encontrada');
|
|
}
|
|
return response.data.data;
|
|
},
|
|
|
|
createQuotation: async (data: CreateQuotationDto): Promise<Quotation> => {
|
|
const response = await api.post<ApiResponse<Quotation>>(
|
|
API_ENDPOINTS.SALES.QUOTATIONS,
|
|
data
|
|
);
|
|
if (!response.data.success || !response.data.data) {
|
|
throw new Error(response.data.error || 'Error al crear cotizacion');
|
|
}
|
|
return response.data.data;
|
|
},
|
|
|
|
updateQuotation: async (id: string, data: UpdateQuotationDto): Promise<Quotation> => {
|
|
const response = await api.patch<ApiResponse<Quotation>>(
|
|
`${API_ENDPOINTS.SALES.QUOTATIONS}/${id}`,
|
|
data
|
|
);
|
|
if (!response.data.success || !response.data.data) {
|
|
throw new Error(response.data.error || 'Error al actualizar cotizacion');
|
|
}
|
|
return response.data.data;
|
|
},
|
|
|
|
confirmQuotation: async (id: string): Promise<SalesOrder> => {
|
|
const response = await api.post<ApiResponse<SalesOrder>>(
|
|
`${API_ENDPOINTS.SALES.QUOTATIONS}/${id}/confirm`
|
|
);
|
|
if (!response.data.success || !response.data.data) {
|
|
throw new Error(response.data.error || 'Error al confirmar cotizacion');
|
|
}
|
|
return response.data.data;
|
|
},
|
|
|
|
sendQuotation: async (id: string): Promise<Quotation> => {
|
|
const response = await api.post<ApiResponse<Quotation>>(
|
|
`${API_ENDPOINTS.SALES.QUOTATIONS}/${id}/send`
|
|
);
|
|
if (!response.data.success || !response.data.data) {
|
|
throw new Error(response.data.error || 'Error al enviar cotizacion');
|
|
}
|
|
return response.data.data;
|
|
},
|
|
|
|
cancelQuotation: async (id: string): Promise<Quotation> => {
|
|
const response = await api.post<ApiResponse<Quotation>>(
|
|
`${API_ENDPOINTS.SALES.QUOTATIONS}/${id}/cancel`
|
|
);
|
|
if (!response.data.success || !response.data.data) {
|
|
throw new Error(response.data.error || 'Error al cancelar cotizacion');
|
|
}
|
|
return response.data.data;
|
|
},
|
|
|
|
deleteQuotation: async (id: string): Promise<void> => {
|
|
const response = await api.delete<ApiResponse>(
|
|
`${API_ENDPOINTS.SALES.QUOTATIONS}/${id}`
|
|
);
|
|
if (!response.data.success) {
|
|
throw new Error(response.data.error || 'Error al eliminar cotizacion');
|
|
}
|
|
},
|
|
};
|