#181 [Compras · 3/4] Compromisos, proyección de caja y pagos
Un solo proceso de compras, desarrollado en cuatro bloques
Esta solicitud forma parte de la mejora del módulo de Compras y contratación para proyectos de CONSOF. El resultado conjunto debe permitir gestionar una operación desde la requisición hasta su cierre, conservando el vínculo entre solicitud, negociación, orden/contrato, ejecución, documentos, costos y pagos.
Los cuatro issues distribuyen el desarrollo del mismo proceso. Deben compartir registros y permitir continuar la operación entre pantallas con los datos ya capturados y las aprobaciones aplicables. Reutilizar los componentes existentes de presupuesto, costos/CxP, tesorería y logística.
La numeración no define una secuencia rígida: el control financiero comienza al confirmar el compromiso; la logística interviene durante la ejecución; anticipos y prepagos pueden preceder a la recepción y facturación.
Bloques: 1. Requisición y contratación #179 · 2. Documentos, costos y CxP #180 · 3. Compromisos y pagos #181 · 4. Logística y alquileres #182.
Alcance de este bloque: compromiso y proyección desde el acuerdo de compra, y programación, autorización, registro de pagos y saldos durante su ejecución. Utiliza las mismas obligaciones del #180 y los hechos operativos de #179/#182. El recorrido completo y su prueba conjunta están en #179.
Resultado que necesitamos
Que el ERP muestre las salidas previstas desde que se confirma una compra/contratación y permita programar, autorizar y registrar sus pagos. Debe utilizar los registros de compra, factura y recepción existentes, con saldos que se actualicen sin capturas duplicadas.
Adaptar CxP, registro de pagos y dashboard financiero; referencias de pantallas en #57/#58/#75. Configurar actualmente preparación/ejecución para Sirley y autorización para David mediante permisos de #94/#95.
1. Recibir los eventos que afectan dinero
| Evento recibido | Actualización que debe realizar el ERP |
|---|---|
| Solicitud aprobada con reserva | Mostrar cupo reservado y disponible del pedido |
| Acuerdo confirmado en #179 | Convertir reserva aplicable en compromiso y proyectar salidas por hito; incluir compras directas |
| Recepción/corte aceptado | Distinguir ejecutado de pendiente por ejecutar; mostrar ejecución pendiente de factura |
| Obligación reconocida en #180 | Sustituir la estimación relacionada por importe/vencimiento validado |
| Pago programado/autorizado | Mostrar intención de pago y saldo reservado para el lote, sin cancelar deuda |
| Pago efectivo registrado | Registrar salida real y reducir únicamente el saldo al que se aplica |
| Cambio/cancelación/ajuste validado | Actualizar la parte afectada, conservando importe anterior y origen |
Relacionar cada efecto con operación, línea/hito y documento. No sumar la totalidad del compromiso más la totalidad de su factura como dos salidas diferentes. Los pagos/prepagos ya realizados deben cubrir la parte correspondiente de esa proyección.
Un borrador de orden no crea compromiso; su confirmación sí. El acuerdo registrado por un usuario con facultad delegada debe aparecer en la proyección inmediatamente, sin esperar otro clic de autorización ni factura.
2. Pantalla de compromisos y proyección de caja
Mostrar por proyecto, tercero, operación y fecha:
- Total acordado, cambios, ejecutado, pendiente de ejecutar y documento de origen.
- Salidas estimadas por anticipo, corte, saldo, alquiler, flete y otros hitos.
- Obligaciones reconocidas, vencimientos, pagos aplicados y saldo.
- Anticipos/prepagos, retenidos, depósitos y restricciones, identificados por separado.
- Importe con fecha estimada cuando todavía no hay vencimiento definitivo; el usuario debe poder distinguir estimación y obligación confirmada.
- Caja prevista antes/después del pago propuesto y alertas de insuficiencia.
Consumir presupuesto/costos del #97 y proyecciones de ingresos existentes (#78/#80). No volver a pedir esos totales ni duplicar el ingreso al pasar oportunidad a proyecto. Presupuesto, compromiso, costo ejecutado y pagado deben ser columnas distintas; pagar no crea otro costo.
Permitir definir el marco de autorización financiera: periodo, proyectos, importes y condiciones admitidas. #179 debe consultar ese marco y solicitar revisión cuando la nueva fecha/importe lo exceda.
3. Calcular condiciones y vencimientos por operación
Al crear el acuerdo, traer una sugerencia del tercero y permitir elegir condición por hito. Guardar en la operación: importe/porcentaje, evento inicial, plazo, tipo de días, periodicidad, fecha resultante, evidencia y autorización. Cambiar el maestro después no modifica acuerdos existentes.
Configuración inicial
| Hito/tipo | Valor inicial | Opciones ordinarias del selector |
|---|---|---|
| Transporte puntual ≤ $1M | Al terminar o siguiente día hábil | Inmediato / siguiente hábil; semanal si se pacta recurrencia |
| Transporte pequeño recurrente | Corte martes, pago miércoles | Acuerdo semanal con tope y servicios vinculados |
| Anticipo de instalación/contratista | Hasta 3 días hábiles | Fecha anterior dentro del marco autorizado |
| Corte de instalación/contratista | 8 días calendario | 3 / 8 / 15 |
| Saldo final ordinario | 15 días calendario | 8 / 15 / 30 |
| Transporte mayor/servicio con crédito | 15 días calendario | 8 / 15 / 30 |
| Materiales con crédito | 30 días calendario | 15 / 30; contado se registra explícitamente |
| Corte/final de alquiler pospagado | 8 días calendario | 8 / 15 |
| Prepago de alquiler / depósito | Fecha pactada para habilitar entrega | Fecha acordada |
La regla de documento/autoridad del #179 se aplica aparte: exactamente $1M de transporte puede tener pago rápido y requerir orden. Recalcular facultad sobre el acuerdo completo, no permitir eludirla separando viajes.
Seleccionar el evento de inicio según documento/hito: recepción de factura electrónica conforme; entrega/prestación efectiva para cuentas de no obligados y supuestos no electrónicos configurados; solicitud con requisitos objetivos cumplidos o fecha pactada para anticipo; terminación para transporte inmediato; corte pactado para servicios recurrentes.
Conservar recepción original, ejecución, revisión, corrección/subsanación, vencimiento, programación y pago. Las aprobaciones internas no reinician el plazo. Cualquier ajuste de vencimiento debe registrar motivo, fundamento, fechas y autorización aplicable. Permitir parametrizar el límite legal aplicable por caso; no añadir plazos de revisión al final del plazo de pago.
Configurar calendario de días hábiles/no bancarios y zona local de negocio (#120). Si una obligación vence antes del siguiente miércoles, proponer el miércoles previo o pago extraordinario dentro del plazo. Si no hay caja, mostrar la alerta y el vencimiento original, sin ocultar atraso.
4. Bandeja de programación y autorización
- Listar obligación/hito, tercero, proyecto, saldo, fecha de vencimiento, tipo de desembolso, conformidades y restricciones.
- Permitir seleccionar una o varias obligaciones, proponer fecha/importe, pagar parcialmente y ver el efecto sobre caja.
- Enviar la propuesta al autorizador correspondiente; permitir aprobar, devolver con observación o rechazar, conservando usuario/fecha/importes.
- Mostrar estado Por programar, Propuesto, Autorizado, En ejecución, Pagado parcial, Pagado total o Fallido; mantener restricción de pago como dato separado con motivo/importe/hito.
- Traer las validaciones de #179/#180/#182. No pedir de nuevo aprobación técnica de un corte ya aceptado.
- Impedir sobreprogramar/sobrepagar el mismo saldo entre lotes o usuarios concurrentes.
- Mostrar beneficiario, cuenta/medio, importe y aplicaciones antes de autorizar. Si cambian datos relevantes después, pedir revisión de la autorización que quedó desactualizada.
- Usar documentos propios del anticipo/prepago; no exigirles factura de ejecución final para habilitar la solicitud.
Una autorización de compromiso puede incluir autorización de desembolso si así se registra explícitamente. El sistema debe distinguir ambos alcances y el hecho de pago.
Cupo para transportes pequeños
Permitir al autorizador crear un cupo con importe total, inicio/fin de vigencia, proyectos, conceptos y máximo de $1M por servicio. Mostrar usado, reservado y disponible.
Cuando un servicio recibido y soportado cumple esas condiciones, permitir ejecutarlo bajo la autorización del cupo. Validar saldo y vigencia; rechazar aplicación de anticipos, depósitos u otros conceptos no cubiertos. Sin cupo vigente/suficiente, enviar a autorización individual. No configurar un importe semanal fijo no indicado por el usuario autorizado.
5. Registrar un pago efectivo
Desde el pago autorizado, ofrecer la acción Registrar pago con datos precargados y estos datos reales: fecha, importe, medio/cuenta de salida, beneficiario, referencia, soporte y distribución entre obligaciones/conceptos.
Al confirmar:
- Guardar el movimiento y su evidencia.
- Aplicar el importe una sola vez al saldo de origen.
- Actualizar CxP, caja, detalle de operación y vistas relacionadas.
- Vincular/generar el comprobante y permitir su descarga o envío registrado.
Programar, autorizar, cargar un archivo sin confirmación o registrar una transferencia fallida no debe reducir deuda. El botón registra un pago realizado; esta entrega no conecta el ERP al banco para ordenar transferencias.
Admitir número abierto de pagos parciales, una factura con varios pagos y un pago aplicado a varias facturas. La suma de aplicaciones debe coincidir con el movimiento. Doble clic/reintentos no generan otro pago. Un registro previo de contado se cruza al llegar la factura; no se paga otra vez.
Permitir registrar pago realizado en contingencia con fecha original. Una corrección o devolución bancaria debe generar reversión trazable, conservar el movimiento original y reabrir únicamente el saldo afectado.
6. Controlar estos saldos de forma independiente
| Concepto | Datos/acciones requeridos |
|---|---|
| Anticipo de contratista | Entregado, aplicado por amortización, pendiente, soporte y devolución si procede; amortizar no vuelve a registrar pago |
| Prepago de alquiler | Importe/periodo cubierto, consumo aplicado, cobertura restante y próxima necesidad |
| Depósito reembolsable | Entregado al proveedor, condición/fecha esperada de retorno, recuperado/aplicado con soporte y pendiente |
| Retenido contractual | Origen, importe, fecha/condiciones de liberación, validaciones y pago posterior |
| Retención tributaria | Importe descontado al proveedor y obligación/destino correspondiente recibido de #180 |
| Importe discutido | Motivo, parte afectada, responsable y resolución; no convertirlo automáticamente en retenido pactado |
| Deuda ordinaria | Vencimiento y saldo, incluso si falta caja |
Traer datos y ajustes del #180, y devolución/periodos del #182. Una devolución física no prueba recuperación de depósito; pagar factura final no prueba devolución de equipo. Aplicar depósito a daño o reconocer saldo por devolver de un prepago requiere liquidación sustentada; no hacerlo automáticamente.
La liberación del retenido utiliza sus condiciones y fecha propias, no añade por defecto el plazo del saldo final. Mostrar qué requisito impide liberar y la acción pendiente.
Caja menor utiliza entregas y legalizaciones del #180: reposición, devolución o reembolso real deben conservar origen y responsable. No crear un segundo gasto al reponer un fondo.
7. Consultas y consistencia
- Acceso desde CxP al documento, acuerdo, cálculo, restricciones, autorizaciones, pagos y comprobantes.
- Filtros por proyecto/centro, tercero, fecha, vencimiento y estado; cada total indica qué incluye y permite abrir su detalle.
- Encabezado del nuevo pago, detalle de CxP y dashboard muestran el mismo saldo, distinguiendo pendiente por ejecutar, pendiente de factura y pendiente de pagar.
- Importes aprobados y etiquetas legibles; proveedores históricos visibles aunque su vínculo requiera regularización (#58/#75).
- Cierres financiero, técnico, documental y logístico separados; la acción pagar no marca cumplidos los demás.
- Mantener identificadores/documentos y conciliar saldos al migrar; reimportar histórico no debe generar otra deuda/pago.
- Restricción de acceso por función/proyecto; asignar rol operativo no concede acceso financiero general.
8. Pruebas de aceptación
| Prueba | Resultado esperado |
|---|---|
| Compra confirmada antes de factura | Compromiso visible y caja proyectada; borrador no aparece como compromiso real |
| Presupuesto $20M, compra $8M, pago $3M; sin otros ajustes | Afectación $8M, disponible $12M y saldo por pagar $5M |
| Dos lotes intentan pagar el mismo saldo | No sobreprograma ni sobreaplica |
| Aprobación, transferencia fallida y pago real | Solo el último reduce deuda |
| Varios pagos / varias facturas / reintento | Aplicaciones exactas, sin duplicados |
| Cambio del plazo maestro del proveedor | Conserva vencimientos de acuerdos previos |
| Vencimiento antes del miércoles siguiente | Propone pago oportuno; no añade días ni reinicia por revisión interna |
| Pago al final del día/mes local | Fecha correcta; no deriva otro día por conversión UTC |
| Cupo semanal insuficiente o concepto no cubierto | Solicita autorización individual; no usa autorización ajena |
| Anticipo amortizado y contado legalizado | Sin segunda salida de caja |
| Alquiler con prepago, depósito y devolución parcial | Cobertura, costo, devolución y saldo por recuperar distinguibles |
| Retenido con condición pendiente | Conserva saldo y bloqueo de liberación específico |
Enlaces: Contratación #179, Contable #180, Logístico #182.
Continuidad: recoge integración/saldos/pagos de #57/#58 y componentes de #25/#31/#59/#75. Dependencias existentes: #94/#95, #97, #78/#80, #109, #120 y #155. #180 reconoce una obligación única y este bloque aplica sus pagos; no crear otra CxP.
Comentarios (5)
🤖 Pre-análisis automático (NOVA) — 🟡 falta información
¡Gracias por reportarlo! Para poder atender su solicitud correctamente y a la primera, necesitamos aclarar algunos puntos. ¿Nos ayuda respondiendo aquí mismo?
- ¿Podrían adjuntar el documento 'procedimiento COS para proyectos'?
- ¿Podrían adjuntar el documento 'matriz de formalización/pago v1.0'?
- ¿Podrían adjuntar el documento 'matriz COS aprobada' referenciada en la sección 4?
- ¿Podrían proporcionar los detalles relevantes de los issues #57, #58, #25, #31, #59, #94, #95, #97 y #86, o indicar dónde consultarlos si no están en GitHub?
Además, nos ayudaría mucho que adjunte lo siguiente:
- Procedimiento COS para proyectos
- Matriz de formalización/pago v1.0
- Matriz COS aprobada (referenciada en sección 4)
- Contenido de los issues #57, #58, #25, #31, #59, #94, #95, #97, #86 (o acceso a ellos)
Se reemplazaron el título y el cuerpo por los eventos, datos, cálculos, estados y acciones que debe implementar el ERP. La configuración inicial de plazos y su evento de inicio está incluida; los issues citados son referencias del mismo repositorio. No es necesario adjuntar manuales internos de procedimiento para interpretar este alcance. Compromiso, factura y pago comparten origen sin duplicar valores; CxP es el mismo registro reconocido en #180. Se mantienen las verificaciones de saldos y pagos de #57/#58.
🟡 Bloque 3/4 de Compras — 181A completo (compromiso, condiciones de pago, bandeja de autorización, cupo transporte)
Primera mitad de #181 (compromisos, proyección de caja y pagos), construida con /modulo sobre el spine de #179/#180 (ambos ya deployados). Este comentario cierra 181A; 181B (registro de pago efectivo, los 7 saldos independientes, adaptación de /finance/, cierre financiero, conectores para #182) sigue en construcción.
Deploy: PR #218 (integración) + PR #219 (hotfix) → main → Cloud Run revisión consof-00158-zsz, 100% del tráfico confirmado.
Tabla de entregables — 181A
| # | Entregable (issue) | Evidencia | Estado |
|---|---|---|---|
| 1 | §1 Compromiso aparece al confirmar operación, sin esperar factura ni otro clic; borrador NO genera compromiso | Primer receptor real del signal operacion_confirmada (#179, dejado sin receptor desde 179A). Confirmado en prod: seed de datos correcto, tablas creadas |
✅ |
| 2 | §1 Obligación reconocida (#180) sustituye estimación sin doble conteo | Primer receptor real del signal obligacion_reconocida (#180/C9). AjusteCompromiso conserva importe anterior y origen |
✅ |
| 3 | §2 Marco de autorización financiera — informa, nunca bloquea | evaluar_marco_caja() reemplaza el stub {'pendiente_bloque': '#181'} que #179 dejó reservado exactamente para este bloque |
✅ |
| 4 | §2 Presupuesto/compromiso/ejecutado/pagado como columnas separadas, sin duplicar ingreso | proyeccion_caja() — lectura de get_budget_tracking() (#97) y OpportunityFinancialProjection (#78/#80), nunca escribe |
✅ |
| 5 | §3 Condiciones de pago por hito — 9 reglas literales del issue, calendario real | Seed verificado en prod: 9 ReglaCondicionPago + 18 DiaNoHabil (festivos colombianos 2026) |
✅ |
| 6 | §3 Editar la regla NO cambia acuerdos ya calculados | recalcular_al_editar_regla() es no-op verificado por test explícito |
✅ |
| 7 | §4 Bandeja de autorización: anti-sobreprogramación transaccional real | saldo_disponible_para_programar() con select_for_update(), probado con 2 hilos concurrentes reales (no solo secuencial) |
✅ |
| 8 | §4 Cambio de beneficiario post-autorización requiere revisión | RevisionAutorizacionPago + LotePago.requiere_revision |
✅ |
| 9 | §4 Cupo semanal transportes — aplica bajo cupo o cae a autorización individual | aplicar_cupo()/CupoNoAplicableError, tope $1M/servicio respetado |
✅ |
| 10 | Permisos configurables (Sirley=preparación, David=autorización) | Roles "Preparador de pagos"/"Autorizador de pagos" seedados y verificados en prod | ✅ |
Bug real encontrado y corregido durante el smoke prod
El primer deploy (PR #218, revisión consof-00157-6gw) dejó 4 de 6 pantallas nuevas devolviendo HTTP 500 (/compromisos/, /proyeccion-caja/, /marcos/, /condiciones/reglas/): 7 plantillas citaban el par de tags de comentario de bloque de Django como texto literal DENTRO de su propio comentario explicativo, cerrándolo antes de tiempo (mismo patrón ya conocido de _base.html, reintroducido al copiar la explicación en plantillas nuevas). La suite completa (3688 passed) no lo atrapó porque ningún test hacía un GET real contra esas vistas — cerrado con un nuevo test_181_smoke_templates.py que sí lo hace. Hotfix PR #219, redeploy consof-00158-zsz, re-smoke confirmó las 6 pantallas en 200.
Smoke prod (revisión consof-00158-zsz)
- Crawl 200 en las 6 pantallas nuevas:
/tesoreria/compromisos/,/proyeccion-caja/,/marcos/,/condiciones/reglas/,/bandeja/,/cupos/. - Seed de datos verificado directo en la BD de prod: 9
ReglaCondicionPago, 18DiaNoHabil, roles Sirley/David, 7 permisostesoreria. - Backfill de compromisos históricos (
crear_compromisos_historicos, para operaciones de #179 ya confirmadas ANTES de este deploy) queda pendiente de tu confirmación — es opcional y se ejecuta manualmente, no automático (mismo criterio que backfills previos del portafolio).
Gaps honestos documentados (no bloquean, no inventados)
- Concurrencia de
select_for_update(): garantía real en Postgres (prod); en SQLite (tests) no hay garantía equivalente — documentado con test que acepta ambos desenlaces posibles en SQLite y exige la garantía estricta en el branch Postgres. CondicionHitono tiene un campoimporteabsoluto para anticipos sin porcentaje — falla cerrado aDecimal('0'), nunca inventa el monto.- Contrato para 181B/D5 (documentado en código, no en el issue): al registrar un pago real debe marcar
AplicacionLotePago.estado='retirada'en la misma transacción para no restar el saldo dos veces.
Este issue no es reproceso (reproceso_rate.py).
Sigo con 181B (registro de pago, saldos, cierre financiero, conectores #182).
🟡 Bloque 3/4 de Compras — 181B completo (pago efectivo, 7 saldos, consultas y cierre financiero)
Continuación del comentario de 181A. Con 181A + 181B, #181 queda funcionalmente completo. Los gaps reales están documentados abajo.
Deploy: PR #220 → main → Cloud Run revisión consof-00159-2f8, 100% del tráfico confirmado.
Tabla de entregables — 181B
| # | Entregable (issue) | Evidencia | Estado |
|---|---|---|---|
| 1 | §5 Registrar pago efectivo: doble clic no duplica; suma exacta; un pago a varias facturas y varios pagos a una | registrar_pago() idempotente, tests dedicados (AT fila 5) |
✅ (validado por tests) |
| 2 | §5 Programar/autorizar/transferencia fallida no reduce deuda; solo el pago real | LotePago.fallido no crea PagoParcial (AT fila 4) |
✅ (validado por tests) |
| 3 | §5 Aplicar el importe una sola vez; no sobrepagar | Anti-sobrepago + la aplicación del lote pasa a retirada en la misma transacción (contrato de 181A/D3) |
✅ (validado por tests) |
| 4 | §5 Pago de contado ya cruzado no se paga otra vez | Guard sobre CruceDocumento(pago_previo) de #180 (AT fila 10) |
✅ (validado por tests) |
| 5 | §5 Reversión trazable que reabre solo el saldo afectado; pago en contingencia con fecha original | revertir_pago(); fecha local America/Bogota sin desfase UTC (AT fila 8) |
✅ (validado por tests) |
| 6 | §5 Comprobante descargable | HTML + PDF (WeasyPrint) | ✅ |
| 7 | §6 Los 7 saldos independientes con liberación que exige soporte | saldos_operacion(); discutido nunca se vuelve retenido solo; depósito no se libera sin soporte (AT filas 11 y 12) |
✅ (validado por tests) |
| 8 | §7 Mismo saldo en CxP, consultas y pago; tres cifras separadas con leyenda | Slot en detalle de CxP/Gasto + /tesoreria/consultas/ sobre las mismas funciones |
✅ |
| 9 | §7 Cierres separados: pagar no marca cumplidos los otros | evaluar_cierre_financiero() con test negativo |
✅ (validado por tests) |
| 10 | §7 Reimportar histórico no duplica; Sirley prepara, David autoriza | Idempotencia probada re-importando dos veces; reversión y cierre exigen approve |
✅ (validado por tests) |
| 11 | Puntos de conexión para #182 | /api/v1/tesoreria/... (read-only) + signal pago_registrado |
✅ |
Smoke prod (revisión consof-00159-2f8)
- Las 9 pantallas de tesorería responden 200, incluidas las nuevas
/pagos/registrar/,/saldos/,/consultas/. - Detalle de CxP legacy reales (ids 1, 10, 11) y de un Gasto renderizan 200 con el slot nuevo.
/finance/cuentas-por-pagar/,/finance/gastos/y/facturas/bandeja/siguen en 200. - La API responde con su propio mensaje de error (
No existe la operación con código ...): router montado. - Tablas nuevas creadas en prod, vacías; las 37 CxP legacy no cambiaron.
- Lo que NO se ejercitó en prod: el registro de un pago real. No lo hice para no crear un pago sobre una CxP de cliente real. Esa ruta está validada por los tests, no por un pago en producción. Conviene una prueba controlada con una CxP de QA cuando lo decidan.
Gaps y decisiones para revisar (no bloquean)
- ⚠️ Guard "ya cruzado por contado": bloquea cualquier pago sobre una obligación cuyo documento tenga un cruce
pago_previoactivo, sin comparar el importe del cruce con el saldo. Puede bloquear un saldo restante legítimo. - ⚠️ Anticipo "entregado":
AmortizacionAnticipono tiene el importe entregado; se deriva de las amortizaciones, así que un anticipo entregado y nunca amortizado aparece en cero. - ⚠️ Caja menor: no hay un tipo propio en
LiberacionSaldo; se registra comoanticipocon referencia aCajaMenor. - ⚠️ Reversión de pago de cupo:
AplicacionMovimientoPagono enlaza aAplicacionCupoTransporte, así que esa reversión es aproximada. - ℹ️ Filtro por centro de costo: no existe,
AccountsPayableno tiene ese campo; se filtra por proyecto, tercero, fecha, vencimiento y estado. - ℹ️ "Pendiente de factura": el plan no daba fórmula; definí
max(ejecutado − Σ valor_neto de las obligaciones, 0). Confirmen que es lo que quieren. - ℹ️ Importe discutido: existe el servicio para crear la liberación pero no hay pantalla.
- ℹ️ Reversión exige permiso
approve(elegí lo más estricto, ajustable). - Pendiente de tu confirmación (de 181A): backfill opcional de compromisos para operaciones de #179 confirmadas antes del deploy.
Este issue no es reproceso (reproceso_rate.py). Sigo con #182.
✅ Listo para que lo valides — probado por Andrea (Indunnova).
queda bloqueado hasta que david no resuelva dudas con el modulo de compras
Si quedó bien, márcalo como resuelto en tu ticket. Si algo falta, responde aquí mismo.