Files
HoruxDespachosNuevo/docs/sessions/2026-05-22-facturacion-csd-personalizacion.md

5.1 KiB

Sesión: Facturación — Personalización CSD, Fecha Emisión, Precio sin IVA, Cuenta Predial

Fecha: 2026-05-22
Commits: 5ba31b71bde5700c8ae05a91a2f4


1. Fix: Personalización logo/color por contribuyente (no tenant)

Problema

En /configuracion/csd, al modificar logo y color para un contribuyente específico, los cambios aplicaban al tenant-level (Horux 360) en lugar de la org Facturapi del contribuyente seleccionado.

Root cause

CustomizationSection usaba endpoints hardcodeados /facturacion/logo, /facturacion/color, /facturacion/customization — todos a nivel tenant. No usaba selectedContribuyenteId.

Solución

  • Backend (contribuyente-facturapi.service.ts): 3 nuevas funciones:
    • getCustomizationContribuyente(pool, contribuyenteId)
    • uploadLogoContribuyente(pool, contribuyenteId, logoBase64)
    • updateColorContribuyente(pool, contribuyenteId, color)
  • Backend (facturacion.controller.ts): 3 controllers per-contribuyente
  • Backend (contribuyente.routes.ts): nuevas rutas:
    • GET /contribuyentes/:id/facturapi/customization
    • POST /contribuyentes/:id/facturapi/logo
    • PUT /contribuyentes/:id/facturapi/color
  • Frontend (configuracion/csd/page.tsx):
    • CustomizationSection ahora recibe contribuyenteId como prop
    • Usa endpoints per-contribuyente
    • Corregido useState mal aplicado → useEffect
    • queryKey incluye contribuyenteId para evitar caché cruzada

2. Fecha de emisión personalizada (I, E, T)

Requerimiento

En facturas tipo I (Ingreso), E (Egreso) y T (Traslado), permitir modificar la fecha de emisión:

  • Máximo 72 horas en el pasado
  • No fechas a futuro
  • Default: día actual a las 12:00

Cambios

  • Frontend (facturacion/page.tsx):
    • Nuevo estado fechaEmision con default a fecha actual 12:00
    • Input datetime-local visible solo para I, E, T (no P)
    • Validación en handleSubmit: now - 72h ≤ fecha ≤ now
  • Frontend (lib/api/facturacion.ts): fechaEmision?: string en InvoiceData
  • Backend (facturacion.controller.ts):
    • Validación idéntica antes de consumir timbre (evita gastar timbres si fecha inválida)
  • Backend (facturapi.service.ts + contribuyente-facturapi.service.ts):
    • Si viene fechaEmision, se envía como date (ISO 8601) en el payload de Facturapi

Nota: Facturapi no documenta explícitamente date para invoices I/E/T, pero el SDK acepta Record<string, any>. Si Facturapi lo rechaza, el error 400 se propaga al usuario sin consumir timbre.


3. Precio unitario sin IVA (subtotal)

Problema

El campo "Precio Unitario" esperaba que el usuario ingresara el precio con IVA incluido. El frontend dividía internamente price / (1 + tasa_iva) para calcular la base.

Solución

  • Frontend (facturacion/page.tsx):
    • Label cambiado: "Precio Unitario (IVA incluido)""Precio Unitario (sin IVA)"
    • Eliminada la división /(1+trasladoRates) en calcConcepto()
    • Ahora price ingresado por el usuario es directamente la base
    • Subtotal, traslados y retenciones se calculan sobre esa base
  • Frontend (facturacion/page.tsx): taxIncluded: truetaxIncluded: false en payload
  • Backend (facturapi.service.ts + contribuyente-facturapi.service.ts):
    • tax_included: item.taxIncluded ?? truetax_included: item.taxIncluded ?? false

4. Cuenta Predial para régimen 606 (Arrendamiento)

Contexto

El SAT exige el nodo CuentaPredial en CFDI de arrendamiento (CFF Art. 29-A, RLISR Art. 199). Facturapi expone property_tax_account en el tipo InvoiceItem del SDK.

Cambios

  • Frontend (facturacion/page.tsx):
    • Nueva sección "Datos del Inmueble" visible solo cuando emisorRegimen === '606'
    • Ubicada antes de la card de Conceptos
    • Input "No. Cuenta Predial" (alfanumérico, mayúsculas, max 150)
    • Se resetea al cambiar de contribuyente
  • Frontend (lib/api/facturacion.ts): cuentaPredial?: string en InvoiceData
  • Backend (facturapi.service.ts + contribuyente-facturapi.service.ts):
    • Al mapear items, agrega property_tax_account a nivel de cada item:
      ...(data.cuentaPredial ? { property_tax_account: data.cuentaPredial } : {})
      

Archivos modificados en esta sesión

Archivo Cambio
apps/web/app/(dashboard)/configuracion/csd/page.tsx CustomizationSection per-contribuyente
apps/web/app/(dashboard)/facturacion/page.tsx Fecha emisión, precio sin IVA, cuenta predial
apps/web/lib/api/facturacion.ts fechaEmision, cuentaPredial en InvoiceData
apps/api/src/controllers/facturacion.controller.ts Validación fecha emisión; controllers customization per-contribuyente
apps/api/src/routes/contribuyente.routes.ts Rutas customization per-contribuyente
apps/api/src/services/contribuyente-facturapi.service.ts getCustomizationContribuyente, uploadLogoContribuyente, updateColorContribuyente, property_tax_account
apps/api/src/services/facturapi.service.ts property_tax_account, date en payload