feature/AS-timbrado-cfdi-ingreso #10

Merged
jcedilloAS merged 4 commits from feature/AS-timbrado-cfdi-ingreso into main 2026-08-11 15:09:03 +00:00

4 Commits

Author SHA1 Message Date
c99cf2c7e9 Merge remote-tracking branch 'origin/main' into feature/AS-timbrado-cfdi-ingreso
Segunda integración de main: trae el carril hacia EFC y, vía el PR #9, la resolución del
merge anterior que ya se había hecho en esta rama.

Dos conflictos:

- core/config.py: adiciones en el mismo punto. Se conservan los dos bloques, con el de EFC
  antes del del PAC para que el diff de esta rama contra main sea sólo lo añadido.
- La migración de catálogos SAT: las dos ramas la renumeraron a g1h2i3j4k5l6 y sólo difería
  su down_revision. Se toma la de main —c5d6e7f8a9b0, el carril EFC—, que es la publicada.
  Cabeza única: j5k6l7m8n9o0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 10:07:16 -05:00
7c0a04fb3b fix(db): renumera las migraciones de facturación al integrar main
Las dos ramas salieron de d5e6f7a8b9c0 sin verse y crearon una migración con el mismo
identificador, e6f7a8b9c0d1: catálogos SAT aquí, catalog_items del CRM en main. Git no lo
detecta porque los archivos tienen nombres distintos, pero Alembic avisaba de que la
revisión estaba presente más de una vez y quedaban dos cabezas, con lo que `upgrade head`
falla.

Se renumera la de facturación —no la del CRM, que ya está en la rama compartida y a la que
e7f8a9b0c1d2 apunta por id— y se encadena detrás del expediente (d4e5f6a7b8c9). La historia
vuelve a ser lineal con una sola cabeza: j5k6l7m8n9o0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 09:24:50 -05:00
21ea958f33 Merge remote-tracking branch 'origin/main' into feature/AS-timbrado-cfdi-ingreso
# Conflicts:
#	frontend/src/lib/api/crm/types.ts
#	frontend/src/lib/components/crm/AccountFields.svelte
#	frontend/src/lib/components/sidebar/modules.ts
2026-08-11 09:12:09 -05:00
6e208876f7 feat(fin): timbrado de CFDI 4.0 de ingreso con Comercio Digital
Cierra el ciclo de la factura: construcción del comprobante, sellado con el CSD de la
empresa emisora y transmisión al PAC.

- cfdi_builder: XML 4.0 de ingreso en el orden de atributos del XSD, del que depende la
  cadena original y con ella el sello. Todo el dinero con Decimal.
- sealer: cadena original vía el XSLT oficial del SAT y firma con la llave del CSD.
- pac_comercio_digital: cliente de timbrarV5. Conserva el código y el saldo de folios que
  el legado leía en una variable que descartaba (CFDI.cs:19324-19336).
- csd_service y core/crypto: CSD por empresa, con la contraseña cifrada en la base. Antes
  el certificado había que dejarlo a mano en el almacenamiento y su contraseña era una
  variable de entorno global, lo que no funciona con varias empresas emisoras.
- Cada intento —también los rechazados— guarda el XML que se transmitió y el que contestó
  el PAC: sin ese par no hay forma de reconstruir un rechazo cuando termina la petición.

La declaración XML se escribe a mano con comillas dobles. lxml la emite con comillas
simples, que es XML válido, pero Comercio Digital compara la cadena literal version="1.0"
y responde 642 "la versión del XML no es 1.0".

El modo (pruebas o producción) sale de invoices.stamping_mode y no se puede pasar por la
API: es lo único que separa un timbre de prueba de un CFDI con validez fiscal.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11 09:07:05 -05:00