"""v4.9.13 regression tests for applying reconstructed processes. The reconstruction is useful only if confirming a process updates the opportunity pipeline, Odoo links and commercial lines instead of leaving a loose reconciliation reference. """ import sys import types if "sqlalchemy" not in sys.modules: sqlalchemy = types.ModuleType("sqlalchemy") sqlalchemy.text = lambda sql: sql sys.modules["sqlalchemy"] = sqlalchemy if "app.db" not in sys.modules: app_db = types.ModuleType("app.db") app_db.engine = object() sys.modules["app.db"] = app_db if "app.action_catalog" not in sys.modules: action_catalog = types.ModuleType("app.action_catalog") action_catalog.get_action_config = lambda code: { "route": "financeiro" if code == "SEND_INVOICE" else "rever", "action": "Enviar fatura ao cliente" if code == "SEND_INVOICE" else code, "action_required": True, "safe_to_post": False, } sys.modules["app.action_catalog"] = action_catalog from app import reconciliation_service as svc def _odoo_item_with_delivery(): return { "id": "odoo-sale-274", "source_system": "odoo", "external_type": "odoo_sale_order", "external_id": "274", "document_number": "S00274", "document_date": "2026-06-02", "amount": "537.00", "currency": "EUR", "payload": { "record": { "id": 274, "name": "S00274", "partner_external_id": 288, "partner_name": "ELEGANTLEGACY, LDA", "invoice_status": "to invoice", "fulfilment": { "physical_status": "shipped", "delivery_done": True, "invoice_pending": True, "lines": [ {"id": 643, "product_name": "Wallbox 7.4KW", "qty_ordered": 1.0, "qty_delivered": 1.0, "qty_invoiced": 0.0, "price_unit": 160.0, "price_total": 160.0}, {"id": 644, "product_name": "Wallbox 11KW", "qty_ordered": 1.0, "qty_delivered": 1.0, "qty_invoiced": 0.0, "price_unit": 178.0, "price_total": 178.0}, {"id": 645, "product_name": "Wallbox 22KW", "qty_ordered": 1.0, "qty_delivered": 1.0, "qty_invoiced": 0.0, "price_unit": 199.0, "price_total": 199.0}, {"id": 646, "product_name": "[Delivery_007] Standard delivery", "qty_ordered": 1.0, "qty_delivered": 0.0, "qty_invoiced": 0.0, "price_unit": 0.0, "price_total": 0.0}, ], "outgoing_pickings": [{"name": "WH/OUT/00295", "state": "done", "date_done": "2026-06-02 12:12:10"}], "productions": [{"name": "WH/MO/00477", "state": "done", "product_name": "Wallbox 7.4KW"}], }, } }, } def test_odoo_importable_lines_skip_zero_delivery_carrier_line(): lines = svc._odoo_importable_lines(_odoo_item_with_delivery()) assert [line["product_name"] for line in lines] == [ "Wallbox 7.4KW", "Wallbox 11KW", "Wallbox 22KW", ] assert sum(float(line["price_total"]) for line in lines) == 537.0 def test_process_state_for_delivered_odoo_sale_points_to_invoice(): state = svc.infer_reconciliation_process_state([_odoo_item_with_delivery()]) assert state["stage"] == "SHIPPED" assert state["action_code"] == "SEND_INVOICE" assert [step["external_type"] for step in state["steps"]] == [ "odoo_sale_order", "odoo_sale_lines", "odoo_delivery", "odoo_invoice_pending", ] def test_apply_reconstructed_process_hooks_are_present(): source = open("app/reconciliation_service.py", encoding="utf-8").read() assert "def _apply_reconstructed_process_to_opportunity" in source assert "_apply_reconstructed_process_to_opportunity(conn, items, opportunity_id" in source assert "def _upsert_opportunity_items_from_odoo_item" in source assert "metadata->>'source_line_id'" in source assert "system, external_type)" in source and "physical_validation" in source assert "Venda Odoo" not in source or "odoo_invoice_pending" in source def test_reconciliation_ui_supports_active_days_window(): ui = open("app/admin_ui/pages/reconciliation.py", encoding="utf-8").read() assert "days: Optional[int] = 3" in ui assert "Hoje" in ui and "7 dias" in ui and "30 dias" in ui assert "sync_odoo_reconciliation_candidates(limit=50, days=days)" in ui