[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>
This commit is contained in:
parent
eb144dc3ea
commit
f1a9ea3d1f
402
src/services/api/crm.api.ts
Normal file
402
src/services/api/crm.api.ts
Normal file
@ -0,0 +1,402 @@
|
||||
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 Lead extends BaseEntity {
|
||||
name: string;
|
||||
contactName?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
mobile?: string;
|
||||
companyName?: string;
|
||||
title?: string;
|
||||
street?: string;
|
||||
city?: string;
|
||||
stateId?: string;
|
||||
countryId?: string;
|
||||
website?: string;
|
||||
description?: string;
|
||||
source?: string;
|
||||
medium?: string;
|
||||
campaign?: string;
|
||||
priority: 'low' | 'medium' | 'high' | 'very_high';
|
||||
status: 'new' | 'contacted' | 'qualified' | 'proposition' | 'won' | 'lost';
|
||||
stageId?: string;
|
||||
stageName?: string;
|
||||
userId?: string;
|
||||
userName?: string;
|
||||
teamId?: string;
|
||||
expectedRevenue?: number;
|
||||
probability?: number;
|
||||
lostReason?: string;
|
||||
tags?: string[];
|
||||
tenantId: string;
|
||||
}
|
||||
|
||||
export interface Opportunity extends BaseEntity {
|
||||
name: string;
|
||||
partnerId: string;
|
||||
partnerName?: string;
|
||||
contactName?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
description?: string;
|
||||
priority: 'low' | 'medium' | 'high' | 'very_high';
|
||||
status: 'new' | 'qualified' | 'proposition' | 'negotiation' | 'won' | 'lost';
|
||||
stageId: string;
|
||||
stageName?: string;
|
||||
userId?: string;
|
||||
userName?: string;
|
||||
teamId?: string;
|
||||
expectedRevenue: number;
|
||||
probability: number;
|
||||
expectedCloseDate?: string;
|
||||
lostReason?: string;
|
||||
tags?: string[];
|
||||
leadId?: string;
|
||||
tenantId: string;
|
||||
}
|
||||
|
||||
export interface CrmStage extends BaseEntity {
|
||||
name: string;
|
||||
sequence: number;
|
||||
probability: number;
|
||||
isFolded: boolean;
|
||||
requirements?: string;
|
||||
teamId?: string;
|
||||
tenantId: string;
|
||||
}
|
||||
|
||||
export interface PipelineSummary {
|
||||
stages: {
|
||||
id: string;
|
||||
name: string;
|
||||
sequence: number;
|
||||
count: number;
|
||||
totalRevenue: number;
|
||||
weightedRevenue: number;
|
||||
}[];
|
||||
totals: {
|
||||
leads: number;
|
||||
opportunities: number;
|
||||
totalRevenue: number;
|
||||
weightedRevenue: number;
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DTOs
|
||||
// ============================================================================
|
||||
|
||||
export interface CreateLeadDto {
|
||||
name: string;
|
||||
contactName?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
mobile?: string;
|
||||
companyName?: string;
|
||||
title?: string;
|
||||
street?: string;
|
||||
city?: string;
|
||||
stateId?: string;
|
||||
countryId?: string;
|
||||
website?: string;
|
||||
description?: string;
|
||||
source?: string;
|
||||
medium?: string;
|
||||
campaign?: string;
|
||||
priority?: Lead['priority'];
|
||||
stageId?: string;
|
||||
userId?: string;
|
||||
teamId?: string;
|
||||
expectedRevenue?: number;
|
||||
probability?: number;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
export interface UpdateLeadDto {
|
||||
name?: string;
|
||||
contactName?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
mobile?: string;
|
||||
companyName?: string;
|
||||
title?: string;
|
||||
street?: string;
|
||||
city?: string;
|
||||
stateId?: string;
|
||||
countryId?: string;
|
||||
website?: string;
|
||||
description?: string;
|
||||
source?: string;
|
||||
medium?: string;
|
||||
campaign?: string;
|
||||
priority?: Lead['priority'];
|
||||
status?: Lead['status'];
|
||||
stageId?: string;
|
||||
userId?: string;
|
||||
teamId?: string;
|
||||
expectedRevenue?: number;
|
||||
probability?: number;
|
||||
lostReason?: string;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
export interface CreateOpportunityDto {
|
||||
name: string;
|
||||
partnerId: string;
|
||||
contactName?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
description?: string;
|
||||
priority?: Opportunity['priority'];
|
||||
stageId: string;
|
||||
userId?: string;
|
||||
teamId?: string;
|
||||
expectedRevenue: number;
|
||||
probability?: number;
|
||||
expectedCloseDate?: string;
|
||||
tags?: string[];
|
||||
leadId?: string;
|
||||
}
|
||||
|
||||
export interface UpdateOpportunityDto {
|
||||
name?: string;
|
||||
partnerId?: string;
|
||||
contactName?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
description?: string;
|
||||
priority?: Opportunity['priority'];
|
||||
status?: Opportunity['status'];
|
||||
stageId?: string;
|
||||
userId?: string;
|
||||
teamId?: string;
|
||||
expectedRevenue?: number;
|
||||
probability?: number;
|
||||
expectedCloseDate?: string;
|
||||
lostReason?: string;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
export interface ConvertLeadDto {
|
||||
createPartner: boolean;
|
||||
partnerId?: string;
|
||||
createOpportunity: boolean;
|
||||
opportunityName?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Filters
|
||||
// ============================================================================
|
||||
|
||||
export interface LeadFilters extends PaginationParams {
|
||||
status?: Lead['status'];
|
||||
priority?: Lead['priority'];
|
||||
stageId?: string;
|
||||
userId?: string;
|
||||
teamId?: string;
|
||||
source?: string;
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
}
|
||||
|
||||
export interface OpportunityFilters extends PaginationParams {
|
||||
status?: Opportunity['status'];
|
||||
priority?: Opportunity['priority'];
|
||||
stageId?: string;
|
||||
partnerId?: string;
|
||||
userId?: string;
|
||||
teamId?: string;
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
minRevenue?: number;
|
||||
maxRevenue?: number;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// API Client
|
||||
// ============================================================================
|
||||
|
||||
export const crmApi = {
|
||||
// Leads
|
||||
getLeads: async (filters?: LeadFilters): Promise<PaginatedResponse<Lead>> => {
|
||||
const response = await api.get<PaginatedResponse<Lead>>(
|
||||
API_ENDPOINTS.CRM.LEADS,
|
||||
{ params: filters }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getLeadById: async (id: string): Promise<Lead> => {
|
||||
const response = await api.get<ApiResponse<Lead>>(
|
||||
`${API_ENDPOINTS.CRM.LEADS}/${id}`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Lead no encontrado');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
createLead: async (data: CreateLeadDto): Promise<Lead> => {
|
||||
const response = await api.post<ApiResponse<Lead>>(
|
||||
API_ENDPOINTS.CRM.LEADS,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al crear lead');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
updateLead: async (id: string, data: UpdateLeadDto): Promise<Lead> => {
|
||||
const response = await api.patch<ApiResponse<Lead>>(
|
||||
`${API_ENDPOINTS.CRM.LEADS}/${id}`,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al actualizar lead');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
convertLead: async (id: string, data: ConvertLeadDto): Promise<{ partner?: { id: string }; opportunity?: Opportunity }> => {
|
||||
const response = await api.post<ApiResponse<{ partner?: { id: string }; opportunity?: Opportunity }>>(
|
||||
`${API_ENDPOINTS.CRM.LEADS}/${id}/convert`,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al convertir lead');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
markLeadAsWon: async (id: string): Promise<Lead> => {
|
||||
const response = await api.post<ApiResponse<Lead>>(
|
||||
`${API_ENDPOINTS.CRM.LEADS}/${id}/won`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al marcar lead como ganado');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
markLeadAsLost: async (id: string, reason?: string): Promise<Lead> => {
|
||||
const response = await api.post<ApiResponse<Lead>>(
|
||||
`${API_ENDPOINTS.CRM.LEADS}/${id}/lost`,
|
||||
{ reason }
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al marcar lead como perdido');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
deleteLead: async (id: string): Promise<void> => {
|
||||
const response = await api.delete<ApiResponse>(
|
||||
`${API_ENDPOINTS.CRM.LEADS}/${id}`
|
||||
);
|
||||
if (!response.data.success) {
|
||||
throw new Error(response.data.error || 'Error al eliminar lead');
|
||||
}
|
||||
},
|
||||
|
||||
// Opportunities
|
||||
getOpportunities: async (filters?: OpportunityFilters): Promise<PaginatedResponse<Opportunity>> => {
|
||||
const response = await api.get<PaginatedResponse<Opportunity>>(
|
||||
API_ENDPOINTS.CRM.OPPORTUNITIES,
|
||||
{ params: filters }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getOpportunityById: async (id: string): Promise<Opportunity> => {
|
||||
const response = await api.get<ApiResponse<Opportunity>>(
|
||||
`${API_ENDPOINTS.CRM.OPPORTUNITIES}/${id}`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Oportunidad no encontrada');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
createOpportunity: async (data: CreateOpportunityDto): Promise<Opportunity> => {
|
||||
const response = await api.post<ApiResponse<Opportunity>>(
|
||||
API_ENDPOINTS.CRM.OPPORTUNITIES,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al crear oportunidad');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
updateOpportunity: async (id: string, data: UpdateOpportunityDto): Promise<Opportunity> => {
|
||||
const response = await api.patch<ApiResponse<Opportunity>>(
|
||||
`${API_ENDPOINTS.CRM.OPPORTUNITIES}/${id}`,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al actualizar oportunidad');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
markOpportunityAsWon: async (id: string): Promise<Opportunity> => {
|
||||
const response = await api.post<ApiResponse<Opportunity>>(
|
||||
`${API_ENDPOINTS.CRM.OPPORTUNITIES}/${id}/won`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al marcar oportunidad como ganada');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
markOpportunityAsLost: async (id: string, reason?: string): Promise<Opportunity> => {
|
||||
const response = await api.post<ApiResponse<Opportunity>>(
|
||||
`${API_ENDPOINTS.CRM.OPPORTUNITIES}/${id}/lost`,
|
||||
{ reason }
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al marcar oportunidad como perdida');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
deleteOpportunity: async (id: string): Promise<void> => {
|
||||
const response = await api.delete<ApiResponse>(
|
||||
`${API_ENDPOINTS.CRM.OPPORTUNITIES}/${id}`
|
||||
);
|
||||
if (!response.data.success) {
|
||||
throw new Error(response.data.error || 'Error al eliminar oportunidad');
|
||||
}
|
||||
},
|
||||
|
||||
// Pipeline
|
||||
getPipeline: async (teamId?: string): Promise<PipelineSummary> => {
|
||||
const response = await api.get<ApiResponse<PipelineSummary>>(
|
||||
API_ENDPOINTS.CRM.PIPELINE,
|
||||
{ params: teamId ? { teamId } : undefined }
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al obtener pipeline');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
// Stages
|
||||
getStages: async (): Promise<CrmStage[]> => {
|
||||
const response = await api.get<ApiResponse<CrmStage[]>>(
|
||||
API_ENDPOINTS.CRM.STAGES
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al obtener etapas');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
};
|
||||
559
src/services/api/financial.api.ts
Normal file
559
src/services/api/financial.api.ts
Normal file
@ -0,0 +1,559 @@
|
||||
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');
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -5,3 +5,8 @@ export * from './products.api';
|
||||
export * from './inventory.api';
|
||||
export * from './warehouses.api';
|
||||
export * from './billing.api';
|
||||
export * from './sales.api';
|
||||
export * from './purchases.api';
|
||||
export * from './financial.api';
|
||||
export * from './crm.api';
|
||||
export * from './projects.api';
|
||||
|
||||
493
src/services/api/projects.api.ts
Normal file
493
src/services/api/projects.api.ts
Normal file
@ -0,0 +1,493 @@
|
||||
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 Project extends BaseEntity {
|
||||
name: string;
|
||||
code?: string;
|
||||
description?: string;
|
||||
partnerId?: string;
|
||||
partnerName?: string;
|
||||
userId?: string;
|
||||
userName?: string;
|
||||
teamId?: string;
|
||||
status: 'draft' | 'active' | 'on_hold' | 'completed' | 'cancelled';
|
||||
priority: 'low' | 'medium' | 'high' | 'critical';
|
||||
dateStart?: string;
|
||||
dateEnd?: string;
|
||||
plannedHours?: number;
|
||||
effectiveHours?: number;
|
||||
progress?: number;
|
||||
color?: string;
|
||||
isTemplate?: boolean;
|
||||
allowTimesheets: boolean;
|
||||
privacyVisibility: 'employees' | 'followers' | 'portal';
|
||||
tags?: string[];
|
||||
tenantId: string;
|
||||
}
|
||||
|
||||
export interface Task extends BaseEntity {
|
||||
name: string;
|
||||
projectId: string;
|
||||
projectName?: string;
|
||||
description?: string;
|
||||
parentId?: string;
|
||||
userId?: string;
|
||||
userName?: string;
|
||||
assigneeIds?: string[];
|
||||
stageId?: string;
|
||||
stageName?: string;
|
||||
status: 'new' | 'in_progress' | 'done' | 'cancelled';
|
||||
priority: 'low' | 'medium' | 'high' | 'critical';
|
||||
dateStart?: string;
|
||||
dateDeadline?: string;
|
||||
dateEnd?: string;
|
||||
plannedHours?: number;
|
||||
effectiveHours?: number;
|
||||
remainingHours?: number;
|
||||
progress?: number;
|
||||
sequence?: number;
|
||||
color?: string;
|
||||
tags?: string[];
|
||||
milestoneId?: string;
|
||||
milestoneName?: string;
|
||||
tenantId: string;
|
||||
}
|
||||
|
||||
export interface Timesheet extends BaseEntity {
|
||||
projectId: string;
|
||||
projectName?: string;
|
||||
taskId?: string;
|
||||
taskName?: string;
|
||||
userId: string;
|
||||
userName?: string;
|
||||
date: string;
|
||||
hours: number;
|
||||
description?: string;
|
||||
isBillable: boolean;
|
||||
unitAmount?: number;
|
||||
tenantId: string;
|
||||
}
|
||||
|
||||
export interface ProjectStage extends BaseEntity {
|
||||
name: string;
|
||||
sequence: number;
|
||||
isFolded: boolean;
|
||||
projectIds?: string[];
|
||||
tenantId: string;
|
||||
}
|
||||
|
||||
export interface Milestone extends BaseEntity {
|
||||
name: string;
|
||||
projectId: string;
|
||||
projectName?: string;
|
||||
dateDeadline?: string;
|
||||
isReached: boolean;
|
||||
reachedDate?: string;
|
||||
description?: string;
|
||||
tenantId: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DTOs
|
||||
// ============================================================================
|
||||
|
||||
export interface CreateProjectDto {
|
||||
name: string;
|
||||
code?: string;
|
||||
description?: string;
|
||||
partnerId?: string;
|
||||
userId?: string;
|
||||
teamId?: string;
|
||||
priority?: Project['priority'];
|
||||
dateStart?: string;
|
||||
dateEnd?: string;
|
||||
plannedHours?: number;
|
||||
color?: string;
|
||||
isTemplate?: boolean;
|
||||
allowTimesheets?: boolean;
|
||||
privacyVisibility?: Project['privacyVisibility'];
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
export interface UpdateProjectDto {
|
||||
name?: string;
|
||||
code?: string;
|
||||
description?: string;
|
||||
partnerId?: string;
|
||||
userId?: string;
|
||||
teamId?: string;
|
||||
status?: Project['status'];
|
||||
priority?: Project['priority'];
|
||||
dateStart?: string;
|
||||
dateEnd?: string;
|
||||
plannedHours?: number;
|
||||
color?: string;
|
||||
allowTimesheets?: boolean;
|
||||
privacyVisibility?: Project['privacyVisibility'];
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
export interface CreateTaskDto {
|
||||
name: string;
|
||||
projectId: string;
|
||||
description?: string;
|
||||
parentId?: string;
|
||||
userId?: string;
|
||||
assigneeIds?: string[];
|
||||
stageId?: string;
|
||||
priority?: Task['priority'];
|
||||
dateStart?: string;
|
||||
dateDeadline?: string;
|
||||
plannedHours?: number;
|
||||
sequence?: number;
|
||||
color?: string;
|
||||
tags?: string[];
|
||||
milestoneId?: string;
|
||||
}
|
||||
|
||||
export interface UpdateTaskDto {
|
||||
name?: string;
|
||||
description?: string;
|
||||
parentId?: string;
|
||||
userId?: string;
|
||||
assigneeIds?: string[];
|
||||
stageId?: string;
|
||||
status?: Task['status'];
|
||||
priority?: Task['priority'];
|
||||
dateStart?: string;
|
||||
dateDeadline?: string;
|
||||
dateEnd?: string;
|
||||
plannedHours?: number;
|
||||
sequence?: number;
|
||||
color?: string;
|
||||
tags?: string[];
|
||||
milestoneId?: string;
|
||||
}
|
||||
|
||||
export interface CreateTimesheetDto {
|
||||
projectId: string;
|
||||
taskId?: string;
|
||||
date: string;
|
||||
hours: number;
|
||||
description?: string;
|
||||
isBillable?: boolean;
|
||||
unitAmount?: number;
|
||||
}
|
||||
|
||||
export interface UpdateTimesheetDto {
|
||||
projectId?: string;
|
||||
taskId?: string;
|
||||
date?: string;
|
||||
hours?: number;
|
||||
description?: string;
|
||||
isBillable?: boolean;
|
||||
unitAmount?: number;
|
||||
}
|
||||
|
||||
export interface CreateMilestoneDto {
|
||||
name: string;
|
||||
projectId: string;
|
||||
dateDeadline?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface UpdateMilestoneDto {
|
||||
name?: string;
|
||||
dateDeadline?: string;
|
||||
description?: string;
|
||||
isReached?: boolean;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Filters
|
||||
// ============================================================================
|
||||
|
||||
export interface ProjectFilters extends PaginationParams {
|
||||
status?: Project['status'];
|
||||
priority?: Project['priority'];
|
||||
partnerId?: string;
|
||||
userId?: string;
|
||||
teamId?: string;
|
||||
isTemplate?: boolean;
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
}
|
||||
|
||||
export interface TaskFilters extends PaginationParams {
|
||||
projectId?: string;
|
||||
status?: Task['status'];
|
||||
priority?: Task['priority'];
|
||||
stageId?: string;
|
||||
userId?: string;
|
||||
assigneeId?: string;
|
||||
milestoneId?: string;
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
}
|
||||
|
||||
export interface TimesheetFilters extends PaginationParams {
|
||||
projectId?: string;
|
||||
taskId?: string;
|
||||
userId?: string;
|
||||
isBillable?: boolean;
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// API Client
|
||||
// ============================================================================
|
||||
|
||||
export const projectsApi = {
|
||||
// Projects
|
||||
getProjects: async (filters?: ProjectFilters): Promise<PaginatedResponse<Project>> => {
|
||||
const response = await api.get<PaginatedResponse<Project>>(
|
||||
API_ENDPOINTS.PROJECTS.PROJECTS,
|
||||
{ params: filters }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getProjectById: async (id: string): Promise<Project> => {
|
||||
const response = await api.get<ApiResponse<Project>>(
|
||||
`${API_ENDPOINTS.PROJECTS.PROJECTS}/${id}`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Proyecto no encontrado');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
createProject: async (data: CreateProjectDto): Promise<Project> => {
|
||||
const response = await api.post<ApiResponse<Project>>(
|
||||
API_ENDPOINTS.PROJECTS.PROJECTS,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al crear proyecto');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
updateProject: async (id: string, data: UpdateProjectDto): Promise<Project> => {
|
||||
const response = await api.patch<ApiResponse<Project>>(
|
||||
`${API_ENDPOINTS.PROJECTS.PROJECTS}/${id}`,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al actualizar proyecto');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
archiveProject: async (id: string): Promise<Project> => {
|
||||
const response = await api.post<ApiResponse<Project>>(
|
||||
`${API_ENDPOINTS.PROJECTS.PROJECTS}/${id}/archive`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al archivar proyecto');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
unarchiveProject: async (id: string): Promise<Project> => {
|
||||
const response = await api.post<ApiResponse<Project>>(
|
||||
`${API_ENDPOINTS.PROJECTS.PROJECTS}/${id}/unarchive`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al desarchivar proyecto');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
duplicateProject: async (id: string, name?: string): Promise<Project> => {
|
||||
const response = await api.post<ApiResponse<Project>>(
|
||||
`${API_ENDPOINTS.PROJECTS.PROJECTS}/${id}/duplicate`,
|
||||
{ name }
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al duplicar proyecto');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
deleteProject: async (id: string): Promise<void> => {
|
||||
const response = await api.delete<ApiResponse>(
|
||||
`${API_ENDPOINTS.PROJECTS.PROJECTS}/${id}`
|
||||
);
|
||||
if (!response.data.success) {
|
||||
throw new Error(response.data.error || 'Error al eliminar proyecto');
|
||||
}
|
||||
},
|
||||
|
||||
// Tasks
|
||||
getTasks: async (filters?: TaskFilters): Promise<PaginatedResponse<Task>> => {
|
||||
const response = await api.get<PaginatedResponse<Task>>(
|
||||
API_ENDPOINTS.PROJECTS.TASKS,
|
||||
{ params: filters }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getTaskById: async (id: string): Promise<Task> => {
|
||||
const response = await api.get<ApiResponse<Task>>(
|
||||
`${API_ENDPOINTS.PROJECTS.TASKS}/${id}`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Tarea no encontrada');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
createTask: async (data: CreateTaskDto): Promise<Task> => {
|
||||
const response = await api.post<ApiResponse<Task>>(
|
||||
API_ENDPOINTS.PROJECTS.TASKS,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al crear tarea');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
updateTask: async (id: string, data: UpdateTaskDto): Promise<Task> => {
|
||||
const response = await api.patch<ApiResponse<Task>>(
|
||||
`${API_ENDPOINTS.PROJECTS.TASKS}/${id}`,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al actualizar tarea');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
moveTask: async (id: string, stageId: string, sequence?: number): Promise<Task> => {
|
||||
const response = await api.post<ApiResponse<Task>>(
|
||||
`${API_ENDPOINTS.PROJECTS.TASKS}/${id}/move`,
|
||||
{ stageId, sequence }
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al mover tarea');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
deleteTask: async (id: string): Promise<void> => {
|
||||
const response = await api.delete<ApiResponse>(
|
||||
`${API_ENDPOINTS.PROJECTS.TASKS}/${id}`
|
||||
);
|
||||
if (!response.data.success) {
|
||||
throw new Error(response.data.error || 'Error al eliminar tarea');
|
||||
}
|
||||
},
|
||||
|
||||
// Timesheets
|
||||
getTimesheets: async (filters?: TimesheetFilters): Promise<PaginatedResponse<Timesheet>> => {
|
||||
const response = await api.get<PaginatedResponse<Timesheet>>(
|
||||
API_ENDPOINTS.PROJECTS.TIMESHEETS,
|
||||
{ params: filters }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getTimesheetById: async (id: string): Promise<Timesheet> => {
|
||||
const response = await api.get<ApiResponse<Timesheet>>(
|
||||
`${API_ENDPOINTS.PROJECTS.TIMESHEETS}/${id}`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Registro de tiempo no encontrado');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
createTimesheet: async (data: CreateTimesheetDto): Promise<Timesheet> => {
|
||||
const response = await api.post<ApiResponse<Timesheet>>(
|
||||
API_ENDPOINTS.PROJECTS.TIMESHEETS,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al crear registro de tiempo');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
updateTimesheet: async (id: string, data: UpdateTimesheetDto): Promise<Timesheet> => {
|
||||
const response = await api.patch<ApiResponse<Timesheet>>(
|
||||
`${API_ENDPOINTS.PROJECTS.TIMESHEETS}/${id}`,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al actualizar registro de tiempo');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
deleteTimesheet: async (id: string): Promise<void> => {
|
||||
const response = await api.delete<ApiResponse>(
|
||||
`${API_ENDPOINTS.PROJECTS.TIMESHEETS}/${id}`
|
||||
);
|
||||
if (!response.data.success) {
|
||||
throw new Error(response.data.error || 'Error al eliminar registro de tiempo');
|
||||
}
|
||||
},
|
||||
|
||||
// Stages
|
||||
getStages: async (): Promise<ProjectStage[]> => {
|
||||
const response = await api.get<ApiResponse<ProjectStage[]>>(
|
||||
API_ENDPOINTS.PROJECTS.STAGES
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al obtener etapas');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
// Milestones
|
||||
getMilestones: async (projectId: string): Promise<Milestone[]> => {
|
||||
const response = await api.get<ApiResponse<Milestone[]>>(
|
||||
API_ENDPOINTS.PROJECTS.MILESTONES,
|
||||
{ params: { projectId } }
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al obtener hitos');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
createMilestone: async (data: CreateMilestoneDto): Promise<Milestone> => {
|
||||
const response = await api.post<ApiResponse<Milestone>>(
|
||||
API_ENDPOINTS.PROJECTS.MILESTONES,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al crear hito');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
updateMilestone: async (id: string, data: UpdateMilestoneDto): Promise<Milestone> => {
|
||||
const response = await api.patch<ApiResponse<Milestone>>(
|
||||
`${API_ENDPOINTS.PROJECTS.MILESTONES}/${id}`,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al actualizar hito');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
deleteMilestone: async (id: string): Promise<void> => {
|
||||
const response = await api.delete<ApiResponse>(
|
||||
`${API_ENDPOINTS.PROJECTS.MILESTONES}/${id}`
|
||||
);
|
||||
if (!response.data.success) {
|
||||
throw new Error(response.data.error || 'Error al eliminar hito');
|
||||
}
|
||||
},
|
||||
};
|
||||
279
src/services/api/purchases.api.ts
Normal file
279
src/services/api/purchases.api.ts
Normal file
@ -0,0 +1,279 @@
|
||||
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 PurchaseOrderLine {
|
||||
id?: string;
|
||||
productId: string;
|
||||
productName?: string;
|
||||
quantity: number;
|
||||
unitPrice: number;
|
||||
discount?: number;
|
||||
taxIds?: string[];
|
||||
subtotal?: number;
|
||||
dateExpected?: string;
|
||||
}
|
||||
|
||||
export interface PurchaseOrder extends BaseEntity {
|
||||
name: string;
|
||||
partnerId: string;
|
||||
partnerName?: string;
|
||||
dateOrder: string;
|
||||
dateExpected?: string;
|
||||
status: 'draft' | 'confirmed' | 'received' | 'done' | 'cancelled';
|
||||
paymentTermId?: string;
|
||||
warehouseId?: string;
|
||||
lines: PurchaseOrderLine[];
|
||||
amountUntaxed: number;
|
||||
amountTax: number;
|
||||
amountTotal: number;
|
||||
notes?: string;
|
||||
tenantId: string;
|
||||
}
|
||||
|
||||
export interface PurchaseRfq extends BaseEntity {
|
||||
name: string;
|
||||
partnerId: string;
|
||||
partnerName?: string;
|
||||
dateRfq: string;
|
||||
dateExpiry?: string;
|
||||
status: 'draft' | 'sent' | 'confirmed' | 'cancelled' | 'expired';
|
||||
paymentTermId?: string;
|
||||
lines: PurchaseOrderLine[];
|
||||
amountUntaxed: number;
|
||||
amountTax: number;
|
||||
amountTotal: number;
|
||||
notes?: string;
|
||||
tenantId: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// DTOs
|
||||
// ============================================================================
|
||||
|
||||
export interface CreatePurchaseOrderDto {
|
||||
partnerId: string;
|
||||
dateOrder?: string;
|
||||
dateExpected?: string;
|
||||
paymentTermId?: string;
|
||||
warehouseId?: string;
|
||||
lines: Omit<PurchaseOrderLine, 'id' | 'productName' | 'subtotal'>[];
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface UpdatePurchaseOrderDto {
|
||||
partnerId?: string;
|
||||
dateOrder?: string;
|
||||
dateExpected?: string;
|
||||
paymentTermId?: string;
|
||||
warehouseId?: string;
|
||||
lines?: Omit<PurchaseOrderLine, 'productName' | 'subtotal'>[];
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface CreatePurchaseRfqDto {
|
||||
partnerId: string;
|
||||
dateRfq?: string;
|
||||
dateExpiry?: string;
|
||||
paymentTermId?: string;
|
||||
lines: Omit<PurchaseOrderLine, 'id' | 'productName' | 'subtotal'>[];
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface UpdatePurchaseRfqDto {
|
||||
partnerId?: string;
|
||||
dateRfq?: string;
|
||||
dateExpiry?: string;
|
||||
paymentTermId?: string;
|
||||
lines?: Omit<PurchaseOrderLine, 'productName' | 'subtotal'>[];
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Filters
|
||||
// ============================================================================
|
||||
|
||||
export interface PurchaseOrderFilters extends PaginationParams {
|
||||
partnerId?: string;
|
||||
status?: PurchaseOrder['status'];
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
}
|
||||
|
||||
export interface PurchaseRfqFilters extends PaginationParams {
|
||||
partnerId?: string;
|
||||
status?: PurchaseRfq['status'];
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// API Client
|
||||
// ============================================================================
|
||||
|
||||
export const purchasesApi = {
|
||||
// Orders
|
||||
getOrders: async (filters?: PurchaseOrderFilters): Promise<PaginatedResponse<PurchaseOrder>> => {
|
||||
const response = await api.get<PaginatedResponse<PurchaseOrder>>(
|
||||
API_ENDPOINTS.PURCHASES.ORDERS,
|
||||
{ params: filters }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getOrderById: async (id: string): Promise<PurchaseOrder> => {
|
||||
const response = await api.get<ApiResponse<PurchaseOrder>>(
|
||||
`${API_ENDPOINTS.PURCHASES.ORDERS}/${id}`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Orden de compra no encontrada');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
createOrder: async (data: CreatePurchaseOrderDto): Promise<PurchaseOrder> => {
|
||||
const response = await api.post<ApiResponse<PurchaseOrder>>(
|
||||
API_ENDPOINTS.PURCHASES.ORDERS,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al crear orden de compra');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
updateOrder: async (id: string, data: UpdatePurchaseOrderDto): Promise<PurchaseOrder> => {
|
||||
const response = await api.patch<ApiResponse<PurchaseOrder>>(
|
||||
`${API_ENDPOINTS.PURCHASES.ORDERS}/${id}`,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al actualizar orden de compra');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
confirmOrder: async (id: string): Promise<PurchaseOrder> => {
|
||||
const response = await api.post<ApiResponse<PurchaseOrder>>(
|
||||
`${API_ENDPOINTS.PURCHASES.ORDERS}/${id}/confirm`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al confirmar orden de compra');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
receiveOrder: async (id: string): Promise<PurchaseOrder> => {
|
||||
const response = await api.post<ApiResponse<PurchaseOrder>>(
|
||||
`${API_ENDPOINTS.PURCHASES.ORDERS}/${id}/receive`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al recibir orden de compra');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
cancelOrder: async (id: string): Promise<PurchaseOrder> => {
|
||||
const response = await api.post<ApiResponse<PurchaseOrder>>(
|
||||
`${API_ENDPOINTS.PURCHASES.ORDERS}/${id}/cancel`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al cancelar orden de compra');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
deleteOrder: async (id: string): Promise<void> => {
|
||||
const response = await api.delete<ApiResponse>(
|
||||
`${API_ENDPOINTS.PURCHASES.ORDERS}/${id}`
|
||||
);
|
||||
if (!response.data.success) {
|
||||
throw new Error(response.data.error || 'Error al eliminar orden de compra');
|
||||
}
|
||||
},
|
||||
|
||||
// RFQs (Request for Quotation)
|
||||
getRfqs: async (filters?: PurchaseRfqFilters): Promise<PaginatedResponse<PurchaseRfq>> => {
|
||||
const response = await api.get<PaginatedResponse<PurchaseRfq>>(
|
||||
API_ENDPOINTS.PURCHASES.RFQS,
|
||||
{ params: filters }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getRfqById: async (id: string): Promise<PurchaseRfq> => {
|
||||
const response = await api.get<ApiResponse<PurchaseRfq>>(
|
||||
`${API_ENDPOINTS.PURCHASES.RFQS}/${id}`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Solicitud de cotizacion no encontrada');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
createRfq: async (data: CreatePurchaseRfqDto): Promise<PurchaseRfq> => {
|
||||
const response = await api.post<ApiResponse<PurchaseRfq>>(
|
||||
API_ENDPOINTS.PURCHASES.RFQS,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al crear solicitud de cotizacion');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
updateRfq: async (id: string, data: UpdatePurchaseRfqDto): Promise<PurchaseRfq> => {
|
||||
const response = await api.patch<ApiResponse<PurchaseRfq>>(
|
||||
`${API_ENDPOINTS.PURCHASES.RFQS}/${id}`,
|
||||
data
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al actualizar solicitud de cotizacion');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
sendRfq: async (id: string): Promise<PurchaseRfq> => {
|
||||
const response = await api.post<ApiResponse<PurchaseRfq>>(
|
||||
`${API_ENDPOINTS.PURCHASES.RFQS}/${id}/send`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al enviar solicitud de cotizacion');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
confirmRfq: async (id: string): Promise<PurchaseOrder> => {
|
||||
const response = await api.post<ApiResponse<PurchaseOrder>>(
|
||||
`${API_ENDPOINTS.PURCHASES.RFQS}/${id}/confirm`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al confirmar solicitud de cotizacion');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
cancelRfq: async (id: string): Promise<PurchaseRfq> => {
|
||||
const response = await api.post<ApiResponse<PurchaseRfq>>(
|
||||
`${API_ENDPOINTS.PURCHASES.RFQS}/${id}/cancel`
|
||||
);
|
||||
if (!response.data.success || !response.data.data) {
|
||||
throw new Error(response.data.error || 'Error al cancelar solicitud de cotizacion');
|
||||
}
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
deleteRfq: async (id: string): Promise<void> => {
|
||||
const response = await api.delete<ApiResponse>(
|
||||
`${API_ENDPOINTS.PURCHASES.RFQS}/${id}`
|
||||
);
|
||||
if (!response.data.success) {
|
||||
throw new Error(response.data.error || 'Error al eliminar solicitud de cotizacion');
|
||||
}
|
||||
},
|
||||
};
|
||||
274
src/services/api/sales.api.ts
Normal file
274
src/services/api/sales.api.ts
Normal file
@ -0,0 +1,274 @@
|
||||
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');
|
||||
}
|
||||
},
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user