feat(plsql): CUI strict search + country filter for ANAF
PL/SQL cauta_partener_dupa_cod_fiscal gains p_strict_search param: - strict (=1): search only exact CUI form (ANAF-determined) - dual (NULL): search all forms (existing anti-dedup behavior) Skip denomination fallback when strict to force new partner creation. Python: country filter excludes foreign companies from ANAF batch, anaf_strict flag threaded sync→import→PL/SQL, normalize RO-space in cod_fiscal_adjusted comparison to eliminate false positives. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -201,7 +201,7 @@ def build_articles_json(items, order=None, settings=None) -> str:
|
|||||||
return json.dumps(articles)
|
return json.dumps(articles)
|
||||||
|
|
||||||
|
|
||||||
def import_single_order(order, id_pol: int = None, id_sectie: int = None, app_settings: dict = None, id_gestiuni: list[int] = None, cod_fiscal_override: str = None) -> dict:
|
def import_single_order(order, id_pol: int = None, id_sectie: int = None, app_settings: dict = None, id_gestiuni: list[int] = None, cod_fiscal_override: str = None, anaf_strict: int = None) -> dict:
|
||||||
"""Import a single order into Oracle ROA.
|
"""Import a single order into Oracle ROA.
|
||||||
|
|
||||||
Returns dict with:
|
Returns dict with:
|
||||||
@@ -257,7 +257,7 @@ def import_single_order(order, id_pol: int = None, id_sectie: int = None, app_se
|
|||||||
is_pj = 0
|
is_pj = 0
|
||||||
|
|
||||||
cur.callproc("PACK_IMPORT_PARTENERI.cauta_sau_creeaza_partener", [
|
cur.callproc("PACK_IMPORT_PARTENERI.cauta_sau_creeaza_partener", [
|
||||||
cod_fiscal, denumire, registru, is_pj, id_partener
|
cod_fiscal, denumire, registru, is_pj, anaf_strict, id_partener
|
||||||
])
|
])
|
||||||
|
|
||||||
partner_id = id_partener.getvalue()
|
partner_id = id_partener.getvalue()
|
||||||
|
|||||||
@@ -653,10 +653,11 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
|||||||
_log_line(run_id, f"ANAF pre-populare eroare: {e}")
|
_log_line(run_id, f"ANAF pre-populare eroare: {e}")
|
||||||
logger.warning(f"ANAF cache pre-population failed: {e}")
|
logger.warning(f"ANAF cache pre-population failed: {e}")
|
||||||
|
|
||||||
# Step 4: ANAF batch verification for company CUIs
|
# Step 4: ANAF batch verification for company CUIs (RO companies only)
|
||||||
company_cuis = set()
|
company_cuis = set()
|
||||||
for order in truly_importable:
|
for order in truly_importable:
|
||||||
if order.billing.is_company and order.billing.company_code:
|
is_ro = (order.billing.country or "").strip().lower() == "romania"
|
||||||
|
if order.billing.is_company and order.billing.company_code and is_ro:
|
||||||
raw_cf = import_service.clean_web_text(order.billing.company_code) or ""
|
raw_cf = import_service.clean_web_text(order.billing.company_code) or ""
|
||||||
bare = anaf_service.strip_ro_prefix(raw_cf)
|
bare = anaf_service.strip_ro_prefix(raw_cf)
|
||||||
if anaf_service.validate_cui(bare):
|
if anaf_service.validate_cui(bare):
|
||||||
@@ -709,11 +710,19 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
|||||||
_log_line(run_id, f"#{order.number} CUI corectat: {raw_cf} → {correct_cf}")
|
_log_line(run_id, f"#{order.number} CUI corectat: {raw_cf} → {correct_cf}")
|
||||||
cod_fiscal_override = correct_cf
|
cod_fiscal_override = correct_cf
|
||||||
|
|
||||||
|
# Determine strict search mode: only when RO company + ANAF data available
|
||||||
|
is_ro_company = (order.billing.is_company
|
||||||
|
and (order.billing.country or "").strip().lower() == "romania")
|
||||||
|
anaf_strict = None
|
||||||
|
if is_ro_company and anaf_data_for_order and anaf_data_for_order.get("scpTVA") is not None:
|
||||||
|
anaf_strict = 1 # ANAF data available → strict search
|
||||||
|
|
||||||
result = await asyncio.to_thread(
|
result = await asyncio.to_thread(
|
||||||
import_service.import_single_order,
|
import_service.import_single_order,
|
||||||
order, id_pol=id_pol, id_sectie=id_sectie,
|
order, id_pol=id_pol, id_sectie=id_sectie,
|
||||||
app_settings=app_settings, id_gestiuni=id_gestiuni,
|
app_settings=app_settings, id_gestiuni=id_gestiuni,
|
||||||
cod_fiscal_override=cod_fiscal_override
|
cod_fiscal_override=cod_fiscal_override,
|
||||||
|
anaf_strict=anaf_strict
|
||||||
)
|
)
|
||||||
|
|
||||||
# Build order items data for storage (R9)
|
# Build order items data for storage (R9)
|
||||||
@@ -770,7 +779,12 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
|||||||
"denumire_roa": result.get("denumire_roa"),
|
"denumire_roa": result.get("denumire_roa"),
|
||||||
"anaf_platitor_tva": (1 if anaf_data_for_order.get("scpTVA") else 0) if anaf_data_for_order and anaf_data_for_order.get("scpTVA") is not None else None,
|
"anaf_platitor_tva": (1 if anaf_data_for_order.get("scpTVA") else 0) if anaf_data_for_order and anaf_data_for_order.get("scpTVA") is not None else None,
|
||||||
"anaf_checked_at": anaf_data_for_order.get("checked_at") if anaf_data_for_order else None,
|
"anaf_checked_at": anaf_data_for_order.get("checked_at") if anaf_data_for_order else None,
|
||||||
"anaf_cod_fiscal_adjusted": 1 if cod_fiscal_override and cod_fiscal_override != raw_cf else 0,
|
"anaf_cod_fiscal_adjusted": 1 if (
|
||||||
|
cod_fiscal_override
|
||||||
|
and result.get("cod_fiscal_roa")
|
||||||
|
and anaf_service.strip_ro_prefix(result["cod_fiscal_roa"]) == anaf_service.strip_ro_prefix(raw_cf)
|
||||||
|
and result["cod_fiscal_roa"].strip().upper().replace("RO ", "RO") != raw_cf.strip().upper().replace("RO ", "RO")
|
||||||
|
) else 0,
|
||||||
"adresa_livrare_gomag": json.dumps({"address": order.shipping.address, "city": order.shipping.city, "region": order.shipping.region}) if order.shipping else None,
|
"adresa_livrare_gomag": json.dumps({"address": order.shipping.address, "city": order.shipping.city, "region": order.shipping.region}) if order.shipping else None,
|
||||||
"adresa_facturare_gomag": json.dumps({"address": order.billing.address, "city": order.billing.city, "region": order.billing.region}),
|
"adresa_facturare_gomag": json.dumps({"address": order.billing.address, "city": order.billing.city, "region": order.billing.region}),
|
||||||
"adresa_livrare_roa": json.dumps(result.get("adresa_livrare_roa")) if result.get("adresa_livrare_roa") else None,
|
"adresa_livrare_roa": json.dumps(result.get("adresa_livrare_roa")) if result.get("adresa_livrare_roa") else None,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ CREATE OR REPLACE PACKAGE PACK_IMPORT_PARTENERI AS
|
|||||||
-- 20.03.2026 - import parteneri GoMag: PJ/PF, shipping/billing, cautare/creare automata
|
-- 20.03.2026 - import parteneri GoMag: PJ/PF, shipping/billing, cautare/creare automata
|
||||||
-- 31.03.2026 - parser inteligent adrese: split numar in bloc/scara/apart/etaj (fix ORA-12899 pe NUMAR max 10 chars)
|
-- 31.03.2026 - parser inteligent adrese: split numar in bloc/scara/apart/etaj (fix ORA-12899 pe NUMAR max 10 chars)
|
||||||
-- 01.04.2026 - ANAF dedup: cautare duala CUI, adrese pe strada+diacritics, strip diacritics la stocare
|
-- 01.04.2026 - ANAF dedup: cautare duala CUI, adrese pe strada+diacritics, strip diacritics la stocare
|
||||||
|
-- 02.04.2026 - cautare CUI strict (p_strict_search=1) sau dual anti-dedup (NULL)
|
||||||
|
|
||||||
-- ====================================================================
|
-- ====================================================================
|
||||||
-- CONSTANTS
|
-- CONSTANTS
|
||||||
@@ -66,6 +67,7 @@ CREATE OR REPLACE PACKAGE PACK_IMPORT_PARTENERI AS
|
|||||||
p_denumire IN VARCHAR2,
|
p_denumire IN VARCHAR2,
|
||||||
p_registru IN VARCHAR2,
|
p_registru IN VARCHAR2,
|
||||||
p_is_persoana_juridica IN NUMBER DEFAULT NULL,
|
p_is_persoana_juridica IN NUMBER DEFAULT NULL,
|
||||||
|
p_strict_search IN NUMBER DEFAULT NULL,
|
||||||
p_id_partener OUT NUMBER);
|
p_id_partener OUT NUMBER);
|
||||||
|
|
||||||
procedure cauta_sau_creeaza_adresa(p_id_part IN NUMBER,
|
procedure cauta_sau_creeaza_adresa(p_id_part IN NUMBER,
|
||||||
@@ -106,7 +108,8 @@ CREATE OR REPLACE PACKAGE PACK_IMPORT_PARTENERI AS
|
|||||||
* @param p_cod_fiscal Codul fiscal de cautat
|
* @param p_cod_fiscal Codul fiscal de cautat
|
||||||
* @return ID_PART sau NULL daca nu gaseste
|
* @return ID_PART sau NULL daca nu gaseste
|
||||||
*/
|
*/
|
||||||
FUNCTION cauta_partener_dupa_cod_fiscal(p_cod_fiscal IN VARCHAR2)
|
FUNCTION cauta_partener_dupa_cod_fiscal(p_cod_fiscal IN VARCHAR2,
|
||||||
|
p_strict_search IN NUMBER DEFAULT NULL)
|
||||||
RETURN NUMBER;
|
RETURN NUMBER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -229,7 +232,9 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
|
|||||||
-- ====================================================================
|
-- ====================================================================
|
||||||
|
|
||||||
-- 01.04.2026 - cautare duala cod_fiscal cu/fara prefix RO (anti-duplicare parteneri)
|
-- 01.04.2026 - cautare duala cod_fiscal cu/fara prefix RO (anti-duplicare parteneri)
|
||||||
FUNCTION cauta_partener_dupa_cod_fiscal(p_cod_fiscal IN VARCHAR2)
|
-- 02.04.2026 - p_strict_search=1: cautare doar forma exacta (+ varianta cu spatiu pt RO)
|
||||||
|
FUNCTION cauta_partener_dupa_cod_fiscal(p_cod_fiscal IN VARCHAR2,
|
||||||
|
p_strict_search IN NUMBER DEFAULT NULL)
|
||||||
RETURN NUMBER IS
|
RETURN NUMBER IS
|
||||||
v_id_part NUMBER;
|
v_id_part NUMBER;
|
||||||
v_cod_fiscal_curat VARCHAR2(50);
|
v_cod_fiscal_curat VARCHAR2(50);
|
||||||
@@ -250,19 +255,42 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
|
|||||||
END IF;
|
END IF;
|
||||||
v_ro_cui := 'RO' || v_bare_cui;
|
v_ro_cui := 'RO' || v_bare_cui;
|
||||||
|
|
||||||
-- 01.04.2026 - cautare duala cod_fiscal cu/fara prefix RO (anti-duplicare parteneri)
|
|
||||||
-- Search 3 forms: bare, RO+bare, RO+space+bare (index-friendly)
|
|
||||||
-- Priority: active + exact form > active + alternate > inactive
|
|
||||||
BEGIN
|
BEGIN
|
||||||
SELECT id_part INTO v_id_part FROM (
|
IF p_strict_search = 1 THEN
|
||||||
SELECT id_part
|
-- Cautare STRICT: doar forma primita + varianta cu spatiu
|
||||||
FROM nom_parteneri
|
IF REGEXP_LIKE(v_cod_fiscal_curat, '^RO\d') THEN
|
||||||
WHERE UPPER(TRIM(cod_fiscal)) IN (v_bare_cui, v_ro_cui, 'RO ' || v_bare_cui)
|
-- Input "RO123" → cauta si "RO 123"
|
||||||
AND NVL(sters, 0) = 0
|
SELECT id_part INTO v_id_part FROM (
|
||||||
ORDER BY NVL(inactiv, 0) ASC,
|
SELECT id_part
|
||||||
CASE WHEN UPPER(TRIM(cod_fiscal)) = v_cod_fiscal_curat THEN 0 ELSE 1 END ASC,
|
FROM nom_parteneri
|
||||||
id_part DESC
|
WHERE UPPER(TRIM(cod_fiscal)) IN (v_cod_fiscal_curat, 'RO ' || v_bare_cui)
|
||||||
) WHERE ROWNUM = 1;
|
AND NVL(sters, 0) = 0
|
||||||
|
ORDER BY NVL(inactiv, 0) ASC, id_part DESC
|
||||||
|
) WHERE ROWNUM = 1;
|
||||||
|
ELSE
|
||||||
|
-- Input "123" → cauta doar "123"
|
||||||
|
SELECT id_part INTO v_id_part FROM (
|
||||||
|
SELECT id_part
|
||||||
|
FROM nom_parteneri
|
||||||
|
WHERE UPPER(TRIM(cod_fiscal)) = v_bare_cui
|
||||||
|
AND NVL(sters, 0) = 0
|
||||||
|
ORDER BY NVL(inactiv, 0) ASC, id_part DESC
|
||||||
|
) WHERE ROWNUM = 1;
|
||||||
|
END IF;
|
||||||
|
ELSE
|
||||||
|
-- Cautare DUALA anti-dedup: toate formele (comportament original)
|
||||||
|
-- Search 3 forms: bare, RO+bare, RO+space+bare (index-friendly)
|
||||||
|
-- Priority: active + exact form > active + alternate > inactive
|
||||||
|
SELECT id_part INTO v_id_part FROM (
|
||||||
|
SELECT id_part
|
||||||
|
FROM nom_parteneri
|
||||||
|
WHERE UPPER(TRIM(cod_fiscal)) IN (v_bare_cui, v_ro_cui, 'RO ' || v_bare_cui)
|
||||||
|
AND NVL(sters, 0) = 0
|
||||||
|
ORDER BY NVL(inactiv, 0) ASC,
|
||||||
|
CASE WHEN UPPER(TRIM(cod_fiscal)) = v_cod_fiscal_curat THEN 0 ELSE 1 END ASC,
|
||||||
|
id_part DESC
|
||||||
|
) WHERE ROWNUM = 1;
|
||||||
|
END IF;
|
||||||
|
|
||||||
RETURN v_id_part;
|
RETURN v_id_part;
|
||||||
|
|
||||||
@@ -609,6 +637,7 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
|
|||||||
p_denumire IN VARCHAR2,
|
p_denumire IN VARCHAR2,
|
||||||
p_registru IN VARCHAR2,
|
p_registru IN VARCHAR2,
|
||||||
p_is_persoana_juridica IN NUMBER DEFAULT NULL,
|
p_is_persoana_juridica IN NUMBER DEFAULT NULL,
|
||||||
|
p_strict_search IN NUMBER DEFAULT NULL,
|
||||||
p_id_partener OUT NUMBER) IS
|
p_id_partener OUT NUMBER) IS
|
||||||
|
|
||||||
v_id_part NUMBER;
|
v_id_part NUMBER;
|
||||||
@@ -642,7 +671,7 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
|
|||||||
-- STEP 1: Cautare dupa cod fiscal (prioritate 1)
|
-- STEP 1: Cautare dupa cod fiscal (prioritate 1)
|
||||||
IF v_cod_fiscal_curat IS NOT NULL AND
|
IF v_cod_fiscal_curat IS NOT NULL AND
|
||||||
LENGTH(v_cod_fiscal_curat) >= C_MIN_COD_FISCAL THEN
|
LENGTH(v_cod_fiscal_curat) >= C_MIN_COD_FISCAL THEN
|
||||||
v_id_part := cauta_partener_dupa_cod_fiscal(v_cod_fiscal_curat);
|
v_id_part := cauta_partener_dupa_cod_fiscal(v_cod_fiscal_curat, p_strict_search);
|
||||||
|
|
||||||
IF v_id_part IS NOT NULL THEN
|
IF v_id_part IS NOT NULL THEN
|
||||||
-- pINFO('Partener gasit dupa cod_fiscal. ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
|
-- pINFO('Partener gasit dupa cod_fiscal. ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
|
||||||
@@ -653,13 +682,16 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_PARTENERI AS
|
|||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- STEP 2: Cautare dupa denumire exacta (prioritate 2)
|
-- STEP 2: Cautare dupa denumire exacta (prioritate 2)
|
||||||
v_id_part := cauta_partener_dupa_denumire(v_denumire_curata);
|
-- Skip cand cautare stricta ANAF — vrem partener nou cu CUI corect
|
||||||
|
IF p_strict_search IS NULL THEN
|
||||||
IF v_id_part IS NOT NULL THEN
|
v_id_part := cauta_partener_dupa_denumire(v_denumire_curata);
|
||||||
-- pINFO('Partener gasit dupa denumire. ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
|
|
||||||
-- pINFO('=== SFARSIT cauta_sau_creeaza_partener ===', 'IMPORT_PARTENERI');
|
IF v_id_part IS NOT NULL THEN
|
||||||
p_id_partener := v_id_part;
|
-- pINFO('Partener gasit dupa denumire. ID_PART=' || v_id_part, 'IMPORT_PARTENERI');
|
||||||
RETURN;
|
-- pINFO('=== SFARSIT cauta_sau_creeaza_partener ===', 'IMPORT_PARTENERI');
|
||||||
|
p_id_partener := v_id_part;
|
||||||
|
RETURN;
|
||||||
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- STEP 3: Creare partener nou
|
-- STEP 3: Creare partener nou
|
||||||
|
|||||||
Reference in New Issue
Block a user