diff --git a/api/app/services/import_service.py b/api/app/services/import_service.py index eaf1c44..4993f69 100644 --- a/api/app/services/import_service.py +++ b/api/app/services/import_service.py @@ -89,6 +89,7 @@ def format_address_for_oracle(address: str, city: str, region: str) -> str: region_clean = clean_web_text(region) city_clean = clean_web_text(city) address_clean = clean_web_text(address) + address_clean = " ".join(address_clean.replace(",", " ").split()) # Strip city name from end of address (users often type it) if city_clean: addr_upper = address_clean.upper().rstrip() diff --git a/api/database-scripts/05_pack_import_parteneri.pck b/api/database-scripts/05_pack_import_parteneri.pck index 9b531d1..c928227 100644 --- a/api/database-scripts/05_pack_import_parteneri.pck +++ b/api/database-scripts/05_pack_import_parteneri.pck @@ -443,6 +443,7 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS -- 31.03.2026 - parser inteligent: split numar in bloc/scara/apart/etaj (fix ORA-12899 pe NUMAR max 10 chars) -- 08.04.2026 - fix: inserare virgule in strada inainte de comma-split (sc/ap/et nu se extrageau fara virgula) + -- 08.04.2026 - fix: BLOC/NR regex require separator (fix false-positive on BLOCURI neighborhood name) PROCEDURE parseaza_adresa_semicolon(p_adresa_text IN VARCHAR2, p_judet OUT VARCHAR2, p_localitate OUT VARCHAR2, @@ -549,14 +550,14 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS IF p_strada IS NOT NULL THEN v_token_upper := UPPER(p_strada); -- Extrage NR din strada - IF REGEXP_LIKE(v_token_upper, '(\s)(NUMARUL|NUMAR|NR\.?)\s*(\S+)') THEN - p_numar := TRIM(REGEXP_REPLACE(v_token_upper, '.*(\s)(NUMARUL|NUMAR|NR\.?)\s*(\S+).*', '\3', 1, 1)); - p_strada := TRIM(REGEXP_REPLACE(p_strada, '(\s)(NUMARUL|NUMAR|NR\.?)\s*\S+', '', 1, 1, 'i')); + IF REGEXP_LIKE(v_token_upper, '(\s)(NUMARUL|NUMAR|NR\.?)[\s.]+(\S+)') THEN + p_numar := TRIM(REGEXP_REPLACE(v_token_upper, '.*(\s)(NUMARUL|NUMAR|NR\.?)[\s.]+(\S+).*', '\3', 1, 1)); + p_strada := TRIM(REGEXP_REPLACE(p_strada, '(\s)(NUMARUL|NUMAR|NR\.?)[\s.]+\S+', '', 1, 1, 'i')); END IF; -- Extrage BLOC din strada - IF REGEXP_LIKE(v_token_upper, '(\s)(BLOC|BL\.?)\s*(\S+)') THEN - p_bloc := TRIM(REGEXP_REPLACE(v_token_upper, '.*(\s)(BLOC|BL\.?)\s*(\S+).*', '\3', 1, 1)); - p_strada := TRIM(REGEXP_REPLACE(p_strada, '(\s)(BLOC|BL\.?)\s*\S+', '', 1, 1, 'i')); + IF REGEXP_LIKE(v_token_upper, '(\s)(BLOC|BL\.?)[\s.]+(\S+)') THEN + p_bloc := TRIM(REGEXP_REPLACE(v_token_upper, '.*(\s)(BLOC|BL\.?)[\s.]+(\S+).*', '\3', 1, 1)); + p_strada := TRIM(REGEXP_REPLACE(p_strada, '(\s)(BLOC|BL\.?)[\s.]+\S+', '', 1, 1, 'i')); END IF; -- Re-read v_token_upper after BLOC removal may have changed p_strada v_token_upper := UPPER(p_strada); diff --git a/api/tests/test_address_rules_oracle.py b/api/tests/test_address_rules_oracle.py index e587553..b8a5380 100644 --- a/api/tests/test_address_rules_oracle.py +++ b/api/tests/test_address_rules_oracle.py @@ -364,6 +364,12 @@ class TestAddressComponentParsing: addr = self._parse_address(oracle_pool, "Strada Victoriei 10") assert "VICTORIEI" in (addr["strada"] or ""), f"strada={addr['strada']}" + def test_blocuri_neighborhood_not_extracted_as_bloc(self, oracle_pool): + """'Blocuri' in street name must NOT be parsed as BLOC keyword.""" + result = self._parse_address(oracle_pool, "Str Principala Modarzau Blocuri", "Zemes", "Bacau") + assert "MODARZAU BLOCURI" in (result.get("strada") or ""), f"strada should contain MODARZAU BLOCURI, got {result}" + assert result.get("bloc") is None, f"bloc should be NULL for neighborhood name, got {result.get('bloc')}" + # --------------------------------------------------------------------------- # Test regresie: comenzi existente în SQLite diff --git a/api/tests/test_business_rules.py b/api/tests/test_business_rules.py index 51ea231..46c9c5f 100644 --- a/api/tests/test_business_rules.py +++ b/api/tests/test_business_rules.py @@ -813,6 +813,12 @@ class TestFormatAddressForOracle: result = format_address_for_oracle("Alba Iulia", "Alba Iulia", "Alba") assert "Alba Iulia" in result # address preserved + def test_comma_stripped_from_address(self): + """Commas in address are replaced with spaces to prevent wrong Oracle splitting.""" + from app.services.import_service import format_address_for_oracle + result = format_address_for_oracle("Str Principala, Modarzau Blocuri", "Zemes", "Bacau") + assert result == "JUD:Bacau;Zemes;Str Principala Modarzau Blocuri" + # =========================================================================== # Group 11: TestRefreshOrderAddress