- 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
215 lines
6.3 KiB
TypeScript
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);
|
|
};
|