Contexto
Context
Un equipo de servicios compartidos recibe cientos de facturas de proveedores por mes. La mayoría está bien. Las que cuestan plata son pocas: una factura cargada dos veces, más unidades que las recibidas, un precio que se fue por encima de la orden de compra, un CUIT mal tipeado, un IVA que no cierra.
A shared-services team receives hundreds of supplier invoices a month. Most are fine. The costly ones are few: an invoice keyed twice, more units than were received, a price that drifted above the purchase order, a mistyped CUIT, IVA that does not add up.
Encontrarlas a ojo es lento y depende de quién revise. Y casi siempre aparecen cuando el pago ya salió.
Finding them by eye is slow and depends on who is reviewing. And they usually surface after the money has left.
Enfoque
Approach
Escribí doce reglas en lenguaje de negocio, cada una con un código, una acción (bloquear o revisar) y un umbral que finanzas puede cambiar sin tocar la lógica. Después cada factura cae en uno de tres estados: lista para pagar, para revisar o bloqueada.
I wrote twelve rules in business language, each with a code, an action (block or review) and a threshold finance can change without touching the logic. Every invoice then lands in one of three states: ready to pay, needs review or blocked.
Las reglas se superponen a propósito: un CUIT mal tipeado (R01) tampoco aparece en el maestro de proveedores (R02). Por eso la suma por regla es mayor que la cantidad de facturas marcadas.
Rules overlap on purpose: a mistyped CUIT (R01) is also missing from the supplier master (R02). That is why the per-rule total is higher than the number of flagged invoices.
# src/apcontrol/rules.py: thresholds finance can edit IVA_RATES = {0.21, 0.105, 0.27, 0.0} PRICE_TOLERANCE = 0.02 # 2 % above the purchase order AMOUNT_TOLERANCE = 0.01 # ARS rounding NO_PO_THRESHOLD = 250_000 # ARS: above this, a PO is required POSSIBLE_DUP_DAYS = 7
Cómo sé que está bien
How I know it is right
Un control de pagos que se equivoca es peor que no tenerlo. Por eso el proceso se valida a sí mismo en cada corrida:
A payment control that is wrong is worse than none. So the process checks itself on every run:
Resultado
Result
Salida del caso de prueba: 1.260 facturas sintéticas por ARS 500,2 M (marzo a agosto de 2026).
Output of the test case: 1,260 synthetic invoices worth ARS 500.2 M (March to August 2026).
El equipo deja de revisar todas las facturas: revisa solo las marcadas, y cada una llega con el motivo escrito.
The team stops reviewing every invoice: it reviews only the flagged ones, and each arrives with its reason written down.
Entregables
Deliverables
exceptions.csv: una fila por factura con las reglas que activó.ap_review.xlsx: estados, reglas, excepciones y totales de control, listo para el equipo.- Catálogo de reglas documentado y 7 tests automáticos.
- Un dashboard interactivo con filtros por estado y regla.
exceptions.csv: one row per invoice with the rules it hit.ap_review.xlsx: states, rules, exceptions and control totals, ready for the team.- Documented rule catalogue and 7 automated tests.
- An interactive dashboard with filters by status and rule.
