Files
HoruxDespachos/apps/web/lib/api/facturacion.ts

174 lines
4.9 KiB
TypeScript

import { apiClient } from './client';
export interface OrgStatus {
configured: boolean;
orgId?: string;
legalName?: string;
hasCsd?: boolean;
}
export interface TimbreStatus {
configured: boolean;
// Backward compat (flat fields representan el pool mensual)
tipo?: string;
limite?: number;
usados?: number;
disponibles?: number;
periodoFin?: string;
// Shape extendido
mensual?: {
tipo: string;
limite: number;
usados: number;
disponibles: number;
periodoFin: string;
};
adicionales?: {
total: number;
usados: number;
disponibles: number;
paquetes: Array<{
id: number;
cantidad: number;
usados: number;
disponibles: number;
adquiridoEn: string;
expiraEn: string;
}>;
};
totalDisponibles?: number;
}
export interface InvoiceCustomer {
legalName: string;
taxId: string;
taxSystem: string;
email?: string;
zip: string;
}
export interface InvoiceLineItem {
description: string;
productKey: string;
unitKey?: string;
unitName?: string;
quantity: number;
price: number;
taxIncluded?: boolean;
taxes?: Array<{ type: string; rate: number }>;
}
export interface InvoiceData {
customer: InvoiceCustomer;
items: InvoiceLineItem[];
use: string;
paymentForm: string;
paymentMethod?: string;
currency?: string;
exchangeRate?: number;
series?: string;
folioNumber?: number;
conditions?: string;
relatedDocuments?: Array<{ relationship: string; uuids: string[] }>;
}
export interface InvoiceResult {
id: string;
uuid: string;
total: number;
status: string;
}
export const getOrgStatus = () => apiClient.get<OrgStatus>('/facturacion/org/status').then(r => r.data);
export const createOrg = () => apiClient.post('/facturacion/org').then(r => r.data);
export const uploadCsd = (data: { cerFile: string; keyFile: string; password: string }) =>
apiClient.post('/facturacion/csd', data).then(r => r.data);
export const getTimbres = () => apiClient.get<TimbreStatus>('/facturacion/timbres').then(r => r.data);
export interface PaqueteCatalogo {
id: number;
cantidad: number;
precio: number;
}
export const getPaquetesCatalogo = () =>
apiClient.get<PaqueteCatalogo[]>('/facturacion/timbres/paquetes-catalogo').then(r => r.data);
export const comprarPaquete = (catalogoId: number) =>
apiClient.post<{ paymentId: string; checkoutUrl: string }>('/facturacion/timbres/paquetes/comprar', { catalogoId })
.then(r => r.data);
export interface PaqueteCatalogoAdmin {
id: number;
cantidad: number;
precio: number;
active: boolean;
updatedAt: string;
}
export const getPaquetesCatalogoAdmin = () =>
apiClient.get<PaqueteCatalogoAdmin[]>('/facturacion/timbres/paquetes-catalogo/admin').then(r => r.data);
export const updatePaqueteCatalogo = (id: number, data: { precio?: number; active?: boolean }) =>
apiClient.put<PaqueteCatalogoAdmin>(`/facturacion/timbres/paquetes-catalogo/${id}`, data).then(r => r.data);
export const emitirFactura = (data: InvoiceData) =>
apiClient.post<InvoiceResult>('/facturacion/emitir', data).then(r => r.data);
export const cancelarFactura = (uuid: string, motive?: string, substitution?: string) =>
apiClient.post(`/facturacion/cancelar/${uuid}`, { motive, substitution }).then(r => r.data);
export const downloadPdf = (id: string) =>
apiClient.get(`/facturacion/pdf/${id}`, { responseType: 'blob' }).then(r => r.data);
export const downloadXml = (id: string) =>
apiClient.get(`/facturacion/xml/${id}`, { responseType: 'blob' }).then(r => r.data);
export interface RfcSearchResult {
id: number;
rfc: string;
razonSocial: string | null;
regimenFiscal: string | null;
codigoPostal: string | null;
}
export const searchRfcs = (q: string) =>
apiClient.get<RfcSearchResult[]>(`/facturacion/rfcs/search?q=${encodeURIComponent(q)}`).then(r => r.data);
export interface CfdiPpdPendiente {
uuid: string;
serie: string | null;
folio: string | null;
totalMxn: number;
fechaEmision: string;
rfcReceptor: string;
nombreReceptor: string;
ivaTrasladoMxn: number;
saldoPendiente: number;
}
export const getCfdisPpd = (rfc: string) =>
apiClient.get<CfdiPpdPendiente[]>(`/facturacion/cfdis-ppd?rfc=${encodeURIComponent(rfc)}`).then(r => r.data);
export interface ConceptoPrevio {
claveProdServ: string;
descripcion: string;
claveUnidad: string | null;
unidad: string | null;
valorUnitario: number;
importe: number;
ivaTraslado: number;
isrRetencion: number;
ivaRetencion: number;
tipoCfdi: string;
rfcEmisor: string;
nombreEmisor: string;
rfcReceptor: string;
nombreReceptor: string;
fechaEmision: string;
}
export const searchConceptos = (q: string, tipo?: string, contribuyenteId?: string | null) => {
const params = new URLSearchParams();
if (q) params.set('q', q);
if (tipo) params.set('tipo', tipo);
if (contribuyenteId) params.set('contribuyenteId', contribuyenteId);
return apiClient.get<ConceptoPrevio[]>(`/facturacion/conceptos/search?${params}`).then(r => r.data);
};