Registro #89: el timbrado entra a la plataforma (prefactura → CFDI por el puente, EmitCFDI sobre el mismo documento) con dry-run, palabra TIMBRAR, verificación por lectura y bloqueo de reenvío; la cadena completa queda revisada (estado incierto de los POST de escritura cerrado, consecutivo sin la serie B, uso CFDI solo del mismo cliente), probada (212 + 10 pruebas, recorrido completo en navegador) y publicada (f5b3743) para la prueba del 1-oct con Balam; guion de la prueba y capturas del recorrido

This commit is contained in:
Johann
2026-09-30 22:45:22 -06:00
parent 604cb8da8e
commit 987454767f
13 changed files with 117 additions and 5 deletions
+30 -1
View File
@@ -1939,11 +1939,40 @@ La bandeja permite marcar varios tickets (solo los que aún no tienen cotizació
---
## 89 · Sep 30, 2026 — 10:00 PM–12:50 AM · Desarrollo + producción · El TIMBRADO entra a la plataforma (prefactura → CFDI por el puente) y la cadena completa queda revisada, probada y PUBLICADA para la prueba del 1-oct con Balam ⭐🔥
> Contexto: Johann, a las 10:00 PM: *"Mañana haré una prueba completa creando una facturación, asegura que se pueda crear una factura desde una prefactura, una prefactura de una cotización y que se pueda crear una cotización desde un ticket de Jira; revisa todos los edge cases y todas las excepciones que puedan ocurrir."* Ante la pregunta de si el paso prefactura → factura sería humano en BIND (como hasta hoy) o desde la plataforma: *"será en la plataforma, tendré llamada con ellos"*; y autorizó push + publicación esta noche. Evidencia: commit `f5b3743` del repo del cliente (publicado 10:40 PM), capturas del recorrido en [`../planeacion/cadena-completa-timbrado-2026-10-01/`](../planeacion/cadena-completa-timbrado-2026-10-01/), guion de la prueba en [`../planeacion/Guion-prueba-completa-2026-10-01.md`](../planeacion/Guion-prueba-completa-2026-10-01.md).
**1. Hechos con fuente primaria (antes de escribir código):**
- **Cómo convierte BIND una prefactura en factura:** en el `app.js` de su web app, la página de detalle de la venta tiene `emitCFDI()` → `POST /InvoicesAPI/EmitCFDI { ID }` y recarga la misma página. **El documento es el mismo** (misma GUID, mismo folio): al timbrar le aparece el UUID e `IsFiscalInvoice` pasa a true. Lo confirma la **2464** del 15-sep: factura timbrada que conserva el comentario "Generada desde el ticket FAC-163" que puso nuestra cotización (y la 2466 del 29-sep, nacida de la cotización 1082 y timbrada 37 s después).
- **Hasta hoy la plataforma no timbraba:** la cadena publicada llegaba a prefactura; el CFDI era humano en BIND y la plataforma lo reflejaba con «¿Ya se timbró? Verificar» y con el sync (una prefactura abierta queda dentro de la ventana del barrido). **Ninguna prefactura hecha desde la plataforma había llegado a factura** (la 2467 se canceló; la 2463 de Pedro sigue sin timbrar).
- **Usuarios de producción:** `admin@balam.paputec.mx` (Dirección + Finanzas), `finanzas@…` (Finanzas), `sistemas@…` (Administración): los tres pueden operar escritura. Tenant en "Pedir confirmación". Eventos FAC-172/173 resueltos como *Cotizada* (1084/1085, hechas a mano tras "Faltan datos"); FAC-171 sigue *Lista* (ticket borrado: descartar).
- **El filtro OData compuesto funciona:** `Number ge 2460 and Number lt 10000` devuelve 8 filas (serie vacía 2460–2467); `Number ge 2460` a secas devuelve 100 filas de las que **92 son de la serie B (24xxx)** — con más facturas B, el consecutivo de la secuencia compartida quedaba invisible y la prefactura salía con el folio de la BD (atrasada hasta 15 min).
**2. Lo construido (`f5b3743`):**
- **`POST /api/emision/timbrar-ui`** (`PrefacturaUiEndpoints`, `BindUiClient.EmitirCfdiAsync`) con el protocolo entero de ADR-001 porque es la única escritura con efecto fiscal: (1) la venta debe existir en la BD, sin UUID y activa, **y BIND debe decir lo mismo en ese momento** — ya con UUID → 409 y se corrige la BD; cancelada → 409; el API aún no la muestra → 409 "reintenta en un minuto"; (2) **dry-run** (operación `UI_InvoicesAPI_EmitCFDI(timbrar)`) con lo que saldrá en el CFDI: cliente, RFC, total, **método de pago según BIND (PUE/PPD, con aviso si es PUE porque Balam usa PPD)**, forma de pago, el uso CFDI y método que viajaron al crear la prefactura (leídos de su operación), RFC genérico; (3) confirmar exige **`confirmacion=TIMBRAR`** (la pantalla lo pide tecleado en un `prompt`) y `Bind:Ui:Mode=Write`; (4) tras el 2xx se relee el documento hasta ver el UUID (`FacturaDesdeDetalle.Aplicar`: Emitida → Vigente con su transición, compartido con «Verificar»); si el API aún no lo muestra, queda `Confirmed` con `timbrada=false` y «Verificar» lo completa; (5) **bloqueo de 10 min por venta** tras cualquier envío, también si BIND no respondió.
- **Estado incierto cerrado en los dos POST de escritura.** Un `HttpRequestException`/timeout al guardar la prefactura caía como 500, la operación quedaba en DryRun sin `ConfirmedAtUtc` y el usuario podía reintentar → **duplicado posible**. Ahora `BindUiEnvioInciertoException`: se bloquea el reenvío, se busca el documento por folio (prefactura) o se relee el UUID (timbrado) y, si aparece, se sigue como éxito; si no, 502 "no se sabe si lo creó: revísalo en BIND; bloqueado 10 min".
- **Consecutivo de la prefactura:** `Number ge N and Number lt 10000`. **Uso CFDI:** se hereda solo de facturas del mismo cliente (antes el último recurso era la última factura timbrada de quien fuera: un uso ajeno habría salido en el CFDI); sin historial, 23 (S01) con aviso.
- **Frontend:** `TimbradoUiCard` debajo de la prefactura recién creada (tarjeta del puente, así que también en Cotizaciones) y en la puerta del wizard, que ahora dice "ya está facturado" con la cadena completa cuando hay UUID (`facturaUuid` en el detalle del ticket). La maqueta deshabilitada "Timbrar CFDI (etapa futura)" desaparece. Un rechazo del PAC se lee completo en la tarjeta.
- **Pruebas:** `TimbradoUiEndpointTests` (13: dry-run sin escribir, confirmación única con Emitida → Vigente, sin palabra, puente en ReadOnly, rol, ya timbrada según BIND, cancelada, API sin mostrarla, rechazo del PAC con motivo legible y reintento permitido, sin respuesta con UUID por lectura, sin respuesta sin UUID → 502 + bloqueo + Verificar, API lento → Confirmed + Verificar, venta desconocida) + 2 de la prefactura sin respuesta. **Suite: 212 backend (13+14+60+125) + 10 frontend**, en verde. Los falsos (`BindUiFalso`, `BindFalso`, `falsos.py`) saben timbrar, rechazar, callarse y retrasar el UUID.
- **Recorrido completo en navegador** (`deploy/demo-local/cadena.cjs`, 10 capturas): cotización → prefactura → **timbrado rechazado por el PAC (se lee el motivo) → timbrado OK con UUID**; ticket → modal → Aceptar → wizard → prefactura → dry-run del CFDI con el API "lento" (409 "aún no muestra la prefactura") → timbrado aceptado sin UUID aún → **Verificar → Timbrada** → puerta "ya está facturado" con la cadena entera → *Facturas* con la fila en Vigente.
**3. Publicación (10:40 PM):** push de `f5b3743`, respaldo `balam-20261001-044041.sql.gz`, imagen de rollback `balam-api:pre-timbrado-20261001`, `deploy.sh` (frontend + API), arranque limpio, `POST /api/emision/timbrar-ui` responde 401 sin token (existe y exige sesión), bundles servidos con la tarjeta de timbrado y sin la maqueta. Sin migración nueva. La demo local se apagó y su base se borró.
⚠️ **No verificado:** `EmitCFDI` contra el BIND real — la primera vez que corra será mañana, con un CFDI real que luego hay que cancelar. Lo que no se sabe de la respuesta (forma del 2xx, texto de los rechazos) está cubierto: el éxito se confirma por lectura del UUID y el rechazo se muestra tal cual venga. El método de pago con el que BIND timbrará las prefacturas del puente parece ser PUE (así dejó la 2467): el dry-run lo avisa y hay que cambiarlo en BIND antes de timbrar si debe ir PPD.
> **Lectura estratégica:**
> - **La cadena completa existe por primera vez en la plataforma** (ticket → cotización → prefactura → CFDI) y cada eslabón tiene el mismo esqueleto: estado leído de BIND al momento, dry-run auditado, confirmación humana escrita, verificación por lectura y bloqueo contra el reenvío. ADR-001 se cumple también en el paso fiscal.
> - **La regla que más vale la noche:** un POST de escritura sin respuesta no es "no pasó nada". Es lo que diferencia un sistema que duplica documentos bajo presión de uno que se detiene y pregunta.
> - **Mañana lo que se prueba no es el código, es BIND:** el PAC validará el receptor (RFC, régimen, CP, uso CFDI). Si rechaza, la plataforma lo dice en pantalla y no queda nada timbrado; el arreglo es en la ficha del cliente en BIND.
---
## Resumen ejecutivo del hilo (para contexto rápido)
### Estado actual (al 30-sep-2026 — Etapa 2 en producción provisional)
**La plataforma está publicada en https://paputec.mx/balam/** desde el 3-sep (ambiente provisional propio; Azure sigue sin entregarse, [#74](REGISTRO.md)/[#76](REGISTRO.md)) y Balam ya la opera en sesiones acompañadas: cotizaciones reales desde tickets, individuales y en lote ([#77](REGISTRO.md)–[#79](REGISTRO.md)). Producción corre con `Bind__Mode=Write` por decisión de Johann del 30-sep ([#85](REGISTRO.md)). **La cadena ya llegó a la prefactura:** por el API público es imposible — `false` = remisión, `true` = CFDI timbrado; incidente 2386 del 26-ago ([#70](REGISTRO.md)), confirmado con el SDK oficial ([#83](REGISTRO.md)) — pero el puente a la UI de BIND (`BindUiClient`, *Generar Venta → Prefactura*, con el login web que Balam entregó el 29-sep) creó ese mismo día la **primera prefactura real, folio 2467** ([#84](REGISTRO.md)); **desde el 30-sep el puente está encendido y completo en paputec** (credenciales del usuario `CTO` en el servidor) ([#88](REGISTRO.md)). **Y la cadena ya arranca sola:** desde el 30-sep el webhook de Jira avisa a producción cada ticket creado en FAC ([#85](REGISTRO.md)). La vista previa automática de la cotización ("Lista para revisar" en la bandeja) también quedó publicada el 30-sep ([#86](REGISTRO.md)); la confirmación sigue siendo humana (ADR-001). La API key del usuario `cto` entró a producción el 30-sep y el sync volvió; la vista previa se validó contra Jira y BIND reales con FAC-171 ([#87](REGISTRO.md)). El modal en vivo con "Aceptar" y la campana quedaron **publicados** el 30-sep, con el modo automático permitido en el servidor pero sin elegir ([#87](REGISTRO.md)). Al probarlo con Pedro el 30-sep salieron totales en cero, cotizaciones con estado atrasado y una categoría sin mapear; detrás había cuatro bugs reales (importes del detalle de BIND siempre en cero, USD mostrado en pesos, el post-proceso del puente tronando, duplicados posibles), corregidos, probados y **publicados en paputec la noche del 30-sep** (`597c8a2`, migración `QuoteSubtotal`) ([#88](REGISTRO.md)). **Siguiente hito:** primera prefactura real desde producción con Johann frente a la app, y que Balam dé el código de BIND de las 7 categorías de servicio que no resuelven. **Seguimiento con Pedro** (sesiones "Seguimiento PAF" a las 7:00 am o 1:00 pm; informes de Read AI de Noé). Horas al 16-sep: 94.58 h; Excel de avance en Google Sheets ([#82](REGISTRO.md)). **Abiertos:** archivar la autorización de Balam y enmendar ADR-001 antes de que alguien elija el modo automático (el servidor ya lo permite), prefactura 2467 (ya Cancelada en BIND) y cotizaciones 1084/1085 por confirmar con Balam, cancelación SAT de la 2386, remisiones 1000/1001, Azure, a quién se reportan horas, y el error al cargar la solicitud al crear cotización visto el 29-sep.
**La plataforma está publicada en https://paputec.mx/balam/** desde el 3-sep (ambiente provisional propio; Azure sigue sin entregarse, [#74](REGISTRO.md)/[#76](REGISTRO.md)) y Balam ya la opera en sesiones acompañadas: cotizaciones reales desde tickets, individuales y en lote ([#77](REGISTRO.md)–[#79](REGISTRO.md)). Producción corre con `Bind__Mode=Write` por decisión de Johann del 30-sep ([#85](REGISTRO.md)). **La cadena ya llegó a la prefactura:** por el API público es imposible — `false` = remisión, `true` = CFDI timbrado; incidente 2386 del 26-ago ([#70](REGISTRO.md)), confirmado con el SDK oficial ([#83](REGISTRO.md)) — pero el puente a la UI de BIND (`BindUiClient`, *Generar Venta → Prefactura*, con el login web que Balam entregó el 29-sep) creó ese mismo día la **primera prefactura real, folio 2467** ([#84](REGISTRO.md)); **desde el 30-sep el puente está encendido y completo en paputec** (credenciales del usuario `CTO` en el servidor) ([#88](REGISTRO.md)), **y desde la noche del 30-sep la plataforma también TIMBRA** (prefactura → CFDI por `EmitCFDI`, con dry-run, palabra TIMBRAR y verificación por lectura) ([#89](REGISTRO.md)). **Y la cadena ya arranca sola:** desde el 30-sep el webhook de Jira avisa a producción cada ticket creado en FAC ([#85](REGISTRO.md)). La vista previa automática de la cotización ("Lista para revisar" en la bandeja) también quedó publicada el 30-sep ([#86](REGISTRO.md)); la confirmación sigue siendo humana (ADR-001). La API key del usuario `cto` entró a producción el 30-sep y el sync volvió; la vista previa se validó contra Jira y BIND reales con FAC-171 ([#87](REGISTRO.md)). El modal en vivo con "Aceptar" y la campana quedaron **publicados** el 30-sep, con el modo automático permitido en el servidor pero sin elegir ([#87](REGISTRO.md)). Al probarlo con Pedro el 30-sep salieron totales en cero, cotizaciones con estado atrasado y una categoría sin mapear; detrás había cuatro bugs reales (importes del detalle de BIND siempre en cero, USD mostrado en pesos, el post-proceso del puente tronando, duplicados posibles), corregidos, probados y **publicados en paputec la noche del 30-sep** (`597c8a2`, migración `QuoteSubtotal`) ([#88](REGISTRO.md)). **Siguiente hito: la prueba completa del 1-oct con Balam** (ticket → cotización → prefactura → CFDI, [guion](../planeacion/Guion-prueba-completa-2026-10-01.md)) — el primer `EmitCFDI` contra el BIND real —, y que Balam dé el código de BIND de las 7 categorías de servicio que no resuelven. **Seguimiento con Pedro** (sesiones "Seguimiento PAF" a las 7:00 am o 1:00 pm; informes de Read AI de Noé). Horas al 16-sep: 94.58 h; Excel de avance en Google Sheets ([#82](REGISTRO.md)). **Abiertos:** resultado del primer timbrado real (cancelar el CFDI de prueba), archivar la autorización de Balam y enmendar ADR-001 antes de que alguien elija el modo automático (el servidor ya lo permite), prefactura 2467 (ya Cancelada en BIND) y cotizaciones 1084/1085 por confirmar con Balam, cancelación SAT de la 2386, remisiones 1000/1001, Azure, a quién se reportan horas, y el error al cargar la solicitud al crear cotización visto el 29-sep.
### Datos duros confirmados por Balam
- **BIND ERP:** API existe y es viable (confirmado 25-may). Límite 20K req/día. PAC para CFDI integrado en BIND.