From 1a912b5fa489d778040f2a7cc9e2b8825eb86d0b Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Wed, 1 Apr 2026 10:23:09 +0000 Subject: [PATCH] fix(price): normalize cantitate_roa < 1 in price comparison (false mismatch) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kit detection only checked cantitate_roa > 1, missing fractional values like 0.5 (GoMag 50buc/set → ROA 100buc/set). This caused false price difference alerts (e.g. GoMag 7.00 vs ROA 14.00 for order #481595156). Co-Authored-By: Claude Opus 4.6 (1M context) --- api/app/services/validation_service.py | 2 +- api/tests/test_business_rules.py | 125 ++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/api/app/services/validation_service.py b/api/app/services/validation_service.py index 6bc3bb8..490e1d0 100644 --- a/api/app/services/validation_service.py +++ b/api/app/services/validation_service.py @@ -705,7 +705,7 @@ def get_prices_for_order(items: list[dict], app_settings: dict, conn=None) -> di is_kit = len(codmat_details) > 1 or ( len(codmat_details) == 1 - and float(codmat_details[0].get("cantitate_roa") or 1) > 1 + and float(codmat_details[0].get("cantitate_roa") or 1) != 1 ) pret_roa_total = 0.0 diff --git a/api/tests/test_business_rules.py b/api/tests/test_business_rules.py index 9d30769..854a640 100644 --- a/api/tests/test_business_rules.py +++ b/api/tests/test_business_rules.py @@ -60,7 +60,7 @@ def make_order(items, discount_total=0.0, delivery_cost=0.0, discount_vat=None): def is_kit(comps): """Kit detection pattern used in validation_service and price_sync_service.""" return len(comps) > 1 or ( - len(comps) == 1 and (comps[0].get("cantitate_roa") or 1) > 1 + len(comps) == 1 and (comps[0].get("cantitate_roa") or 1) != 1 ) @@ -492,3 +492,126 @@ class TestResolveCodmatIds: codmats = [c["codmat"] for c in result["SKU1"]] assert "COD1" in codmats assert "COD2" in codmats + + +# =========================================================================== +# Group 6: get_prices_for_order() — cantitate_roa price normalization +# =========================================================================== + +from app.services.validation_service import get_prices_for_order + + +def _mock_oracle_conn(pol_cu_tva=False, price_map=None): + """Build a mock Oracle connection for get_prices_for_order. + + price_map: {id_articol: (pret, proc_tvav)} + """ + if price_map is None: + price_map = {} + conn = MagicMock() + + def cursor_ctx(): + cur = MagicMock() + # CRM_POLITICI_PRETURI — PRETURI_CU_TVA flag + cu_tva_row = [1 if pol_cu_tva else 0] + # CRM_POLITICI_PRET_ART — prices + price_rows = [ + (1, id_art, pret, proc_tvav) + for id_art, (pret, proc_tvav) in price_map.items() + ] + # fetchone for PRETURI_CU_TVA, __iter__ for price rows + cur.fetchone = MagicMock(return_value=cu_tva_row) + cur.__iter__ = MagicMock(return_value=iter(price_rows)) + return cur + + cm = MagicMock() + cm.__enter__ = MagicMock(side_effect=cursor_ctx) + cm.__exit__ = MagicMock(return_value=False) + conn.cursor.return_value = cm + return conn + + +class TestGetPricesForOrderCantitateRoa: + """Regression: cantitate_roa < 1 must be treated as kit for price normalization. + + Bug: SKU with cantitate_roa=0.5 (GoMag 50buc=7lei, ROA 100buc=14lei) + was reported as price mismatch because is_kit only checked > 1. + """ + + def test_cantitate_roa_half_matches(self): + """cantitate_roa=0.5: ROA price 14.00 * 0.5 = 7.00 should match GoMag 7.00.""" + items = [{ + "sku": "1057308134545", + "price": 7.00, + "quantity": 60, + "codmat_details": [{ + "codmat": "8OZLRLP", + "cantitate_roa": 0.5, + "id_articol": 100, + "cont": "345", + }], + }] + conn = _mock_oracle_conn(pol_cu_tva=True, price_map={100: (14.00, 1.19)}) + result = get_prices_for_order(items, {"id_pol": "1"}, conn=conn) + + assert result["items"][0]["match"] is True + assert abs(result["items"][0]["pret_roa"] - 7.00) < 0.01 + assert result["summary"]["mismatches"] == 0 + + def test_cantitate_roa_half_mismatch(self): + """cantitate_roa=0.5: ROA price 10.00 * 0.5 = 5.00 != GoMag 7.00.""" + items = [{ + "sku": "SKU-HALF", + "price": 7.00, + "quantity": 1, + "codmat_details": [{ + "codmat": "COD1", + "cantitate_roa": 0.5, + "id_articol": 200, + "cont": "345", + }], + }] + conn = _mock_oracle_conn(pol_cu_tva=True, price_map={200: (10.00, 1.19)}) + result = get_prices_for_order(items, {"id_pol": "1"}, conn=conn) + + assert result["items"][0]["match"] is False + assert abs(result["items"][0]["pret_roa"] - 5.00) < 0.01 + assert result["summary"]["mismatches"] == 1 + + def test_cantitate_roa_one_simple_item(self): + """cantitate_roa=1 (default): simple item, direct price comparison.""" + items = [{ + "sku": "SKU-SIMPLE", + "price": 63.79, + "quantity": 8, + "codmat_details": [{ + "codmat": "COD-DIRECT", + "cantitate_roa": 1, + "id_articol": 300, + "cont": "345", + }], + }] + conn = _mock_oracle_conn(pol_cu_tva=True, price_map={300: (63.79, 1.19)}) + result = get_prices_for_order(items, {"id_pol": "1"}, conn=conn) + + assert result["items"][0]["match"] is True + assert result["summary"]["mismatches"] == 0 + + def test_cantitate_roa_gt1_kit(self): + """cantitate_roa=2: kit with 2 ROA units per GoMag unit.""" + items = [{ + "sku": "SKU-KIT2", + "price": 20.00, + "quantity": 1, + "codmat_details": [{ + "codmat": "COD-KIT", + "cantitate_roa": 2, + "id_articol": 400, + "cont": "345", + }], + }] + conn = _mock_oracle_conn(pol_cu_tva=True, price_map={400: (10.00, 1.19)}) + result = get_prices_for_order(items, {"id_pol": "1"}, conn=conn) + + assert result["items"][0]["match"] is True + assert abs(result["items"][0]["pret_roa"] - 20.00) < 0.01