Release v4928.1.4.2 stable

This commit is contained in:
2026-06-09 22:55:58 +01:00
commit 6445044ac6
280 changed files with 41775 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
"""v4.9.11 regression tests for Odoo name-only reconciliation.
Odoo customers may not have VAT/NIF filled in. Reconciliation should still be
able to suggest/link by normalized company name and should not abort the whole
process candidate list when an individual suggestion lookup fails.
"""
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": "rever", "action": code, "action_required": True, "safe_to_post": False}
sys.modules["app.action_catalog"] = action_catalog
from app import reconciliation_service as svc
def test_company_name_normalization_matches_odoo_without_nif():
score = svc._name_match_score(
"ELEGANTLEGACY, LDA VALONGO Portugal",
"Elegantlegacy Lda",
"Oportunidade · elegantlegacy",
)
assert score >= 35
def test_single_odoo_sale_without_nif_becomes_process_candidate(monkeypatch):
monkeypatch.setattr(
svc,
"list_reconciliation_items",
lambda *, status, limit, days: [
{
"id": "odoo-sale-274",
"source_system": "odoo",
"external_type": "odoo_sale_order",
"title": "Venda Odoo sem oportunidade · S00274",
"customer_name": "ELEGANTLEGACY, LDA",
"customer_tax_id": "",
"customer_email": "",
"document_number": "S00274",
"document_date": "2026-06-02",
"amount": "537.00",
"currency": "EUR",
"operation_suggestions": [],
}
],
)
candidates = svc.list_reconciliation_process_candidates(status="open", days=7, limit=10)
assert len(candidates) == 1
candidate = candidates[0]
assert candidate["match_key"] == "name"
assert candidate["confidence"] == "alta"
assert candidate["identity_reason"] == "nome fiscal"
assert candidate["customer_name"] == "ELEGANTLEGACY, LDA"
assert candidate["suggested_stage"] == "ODOO_ORDER_CREATED"
assert candidate["suggested_action"] == "SEND_INVOICE"
assert candidate["item_ids"] == ["odoo-sale-274"]
def test_list_process_candidates_is_resilient_when_item_query_fails(monkeypatch):
def fail_listing(*, status, limit, days):
raise RuntimeError("simulated db lookup failure")
monkeypatch.setattr(svc, "list_reconciliation_items", fail_listing)
assert svc.list_reconciliation_process_candidates(status="open", days=7, limit=10) == []
def test_person_name_only_is_not_high_confidence_identity():
confidence, reason = svc._process_group_identity_label(
"name",
[{"customer_name": "Bruno Oliveira", "customer_tax_id": "", "source_system": "odoo"}],
)
assert confidence == "média"
assert reason == "nome"