docs: sesión 2026-05-22 — personalización CSD, fecha emisión, precio sin IVA, cuenta predial
This commit is contained in:
104
docs/sessions/2026-05-22-facturacion-csd-personalizacion.md
Normal file
104
docs/sessions/2026-05-22-facturacion-csd-personalizacion.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Sesión: Facturación — Personalización CSD, Fecha Emisión, Precio sin IVA, Cuenta Predial
|
||||
|
||||
**Fecha:** 2026-05-22
|
||||
**Commits:** `5ba31b7` → `1bde570` → `0c8ae05` → `a91a2f4`
|
||||
|
||||
---
|
||||
|
||||
## 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: true` → `taxIncluded: false` en payload
|
||||
- **Backend** (`facturapi.service.ts` + `contribuyente-facturapi.service.ts`):
|
||||
- `tax_included: item.taxIncluded ?? true` → `tax_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:
|
||||
```ts
|
||||
...(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 |
|
||||
Reference in New Issue
Block a user