Files
HoruxDespachosNuevo/apps/web/lib/api/facturacion.ts
Horux Dev a91a2f415d feat(facturacion): cuenta predial para régimen 606 (arrendamiento)
- Frontend: muestra input 'No. Cuenta Predial' en sección 'Datos del Inmueble'
  cuando el régimen del emisor es 606 (Arrendamiento), antes de Conceptos
- Frontend: incluye cuentaPredial en payload; se resetea al cambiar contribuyente
- Backend: pasa property_tax_account a nivel de cada item en Facturapi
  para facturapi.service.ts y contribuyente-facturapi.service.ts
- Build y deploy exitosos
2026-05-22 23:20:36 +00:00

215 lines
6.3 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;
fechaEmision?: string;
cuentaPredial?: 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, contribuyenteId?: string) =>
apiClient.post(`/facturacion/cancelar/${uuid}`, { motive, substitution, contribuyenteId }).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, contribuyenteId?: string) => {
const params = new URLSearchParams({ q });
if (contribuyenteId) params.set('contribuyenteId', contribuyenteId);
return apiClient.get<RfcSearchResult[]>(`/facturacion/rfcs/search?${params.toString()}`).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, contribuyenteId?: string) => {
const params = new URLSearchParams({ rfc });
if (contribuyenteId) params.set('contribuyenteId', contribuyenteId);
return apiClient.get<CfdiPpdPendiente[]>(`/facturacion/cfdis-ppd?${params.toString()}`).then(r => r.data);
};
export interface CfdiRelacionable {
uuid: string;
serie: string | null;
folio: string | null;
totalMxn: number;
fechaEmision: string;
tipoComprobante: 'I' | 'E';
metodoPago: string | null;
}
export const getCfdisRelacionables = (rfcReceptor: string, contribuyenteId: string) =>
apiClient
.get<CfdiRelacionable[]>(
`/facturacion/cfdis-relacionables?rfcReceptor=${encodeURIComponent(rfcReceptor)}&contribuyenteId=${encodeURIComponent(contribuyenteId)}`,
)
.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 getPagosSinFactura = () =>
apiClient.get<Array<{
id: string;
tenantId: string;
amount: string;
status: string;
paymentMethod: string | null;
paidAt: string | null;
createdAt: string;
kind: string;
subscription: { plan: string; frequency: string } | null;
tenant: { nombre: string; rfc: string | null };
}>>('/facturacion/pagos-sin-factura').then(r => r.data);
export const emitirFacturaPago = (paymentId: string) =>
apiClient.post<{ success: boolean; invoiceId: string; paymentId: string }>(`/facturacion/emitir-factura-pago/${paymentId}`).then(r => r.data);
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);
};