fix(import): 3 production bugs — items cache, CUI lookup, ANAF name
1. SQLite order_items overwrite on re-import (VELA CAFE #484669620): add_order_items, save_orders_batch, mark_order_deleted_in_roa now use DELETE + INSERT so GoMag quantity changes propagate to dashboard. 2. PL/SQL strict CUI lookup tolerates whitespace (FG COFFE #485065210): cauta_partener_dupa_cod_fiscal regex ^RO\d → ^RO\s*\d; IN-set uses canonical v_ro_cui. Platitor/neplatitor business rule preserved. Python defensive: re.sub whitespace collapse in determine_partner_data. 3. New PJ partners use ANAF official denumire (denumire_override) instead of GoMag company_name. Existing partners (found by CUI) untouched. Tests: 18 new (5 SQLite unit, 8 Python unit, 5 Oracle PL/SQL). All green locally: 228 unit + 26 oracle + 33 e2e. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import html
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import oracledb
|
||||
from datetime import datetime, timedelta
|
||||
from .. import database
|
||||
@@ -65,7 +66,9 @@ def determine_partner_data(order) -> dict:
|
||||
denumire = clean_web_text(
|
||||
f"{order.billing.lastname} {order.billing.firstname}"
|
||||
).upper()
|
||||
cod_fiscal = clean_web_text(order.billing.company_code) or None
|
||||
raw_cf = clean_web_text(order.billing.company_code) or None
|
||||
# Collapse internal whitespace: "RO 34963277" → "RO34963277"
|
||||
cod_fiscal = re.sub(r'\s+', '', raw_cf) if raw_cf else None
|
||||
registru = clean_web_text(order.billing.company_reg) or None
|
||||
is_pj = 1
|
||||
else:
|
||||
@@ -242,7 +245,7 @@ def build_articles_json(items, order=None, settings=None) -> str:
|
||||
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, anaf_strict: int = 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, denumire_override: str = None) -> dict:
|
||||
"""Import a single order into Oracle ROA.
|
||||
|
||||
Returns dict with:
|
||||
@@ -279,7 +282,11 @@ def import_single_order(order, id_pol: int = None, id_sectie: int = None, app_se
|
||||
id_partener = cur.var(oracledb.DB_TYPE_NUMBER)
|
||||
|
||||
_pdata = determine_partner_data(order)
|
||||
denumire = _pdata["denumire"]
|
||||
# PJ: prefer ANAF official name (denumire_override) over GoMag company_name
|
||||
# (for new partner creation; existing partner lookup is CUI-based)
|
||||
denumire = (denumire_override
|
||||
if (_pdata["is_pj"] and denumire_override)
|
||||
else _pdata["denumire"])
|
||||
cod_fiscal = (cod_fiscal_override or _pdata["cod_fiscal"]) if _pdata["is_pj"] else None
|
||||
registru = _pdata["registru"]
|
||||
is_pj = _pdata["is_pj"]
|
||||
|
||||
@@ -193,10 +193,12 @@ async def save_orders_batch(orders_data: list[dict]):
|
||||
VALUES (?, ?, ?)
|
||||
""", [(d["sync_run_id"], d["order_number"], d["status_at_run"]) for d in orders_data])
|
||||
|
||||
# 3. Order items
|
||||
# 3. Order items — replace semantics (GoMag source of truth)
|
||||
all_items = []
|
||||
order_numbers_with_items = set()
|
||||
for d in orders_data:
|
||||
for item in d.get("items", []):
|
||||
order_numbers_with_items.add(d["order_number"])
|
||||
all_items.append((
|
||||
d["order_number"],
|
||||
item.get("sku"), item.get("product_name"),
|
||||
@@ -206,8 +208,13 @@ async def save_orders_batch(orders_data: list[dict]):
|
||||
item.get("id_articol"), item.get("cantitate_roa")
|
||||
))
|
||||
if all_items:
|
||||
placeholders = ",".join("?" * len(order_numbers_with_items))
|
||||
await db.execute(
|
||||
f"DELETE FROM order_items WHERE order_number IN ({placeholders})",
|
||||
tuple(order_numbers_with_items)
|
||||
)
|
||||
await db.executemany("""
|
||||
INSERT OR IGNORE INTO order_items
|
||||
INSERT INTO order_items
|
||||
(order_number, sku, product_name, quantity, price, baseprice,
|
||||
vat, mapping_status, codmat, id_articol, cantitate_roa)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
@@ -529,13 +536,18 @@ async def get_web_products_batch(skus: list) -> dict:
|
||||
# ── order_items ──────────────────────────────────
|
||||
|
||||
async def add_order_items(order_number: str, items: list):
|
||||
"""Bulk insert order items. Uses INSERT OR IGNORE — PK is (order_number, sku)."""
|
||||
"""Replace order items — delete any existing rows, then insert fresh batch.
|
||||
|
||||
GoMag is source of truth: re-import must reflect quantity changes.
|
||||
Atomic (DELETE + INSERT in one transaction).
|
||||
"""
|
||||
if not items:
|
||||
return
|
||||
db = await get_sqlite()
|
||||
try:
|
||||
await db.execute("DELETE FROM order_items WHERE order_number = ?", (order_number,))
|
||||
await db.executemany("""
|
||||
INSERT OR IGNORE INTO order_items
|
||||
INSERT INTO order_items
|
||||
(order_number, sku, product_name, quantity, price, baseprice,
|
||||
vat, mapping_status, codmat, id_articol, cantitate_roa)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
@@ -930,9 +942,10 @@ async def clear_order_invoice(order_number: str):
|
||||
|
||||
|
||||
async def mark_order_deleted_in_roa(order_number: str):
|
||||
"""Mark an order as deleted in ROA — clears id_comanda and invoice cache."""
|
||||
"""Mark an order as deleted in ROA — clears id_comanda, invoice cache, and stale items."""
|
||||
db = await get_sqlite()
|
||||
try:
|
||||
await db.execute("DELETE FROM order_items WHERE order_number = ?", (order_number,))
|
||||
await db.execute("""
|
||||
UPDATE orders SET
|
||||
status = 'DELETED_IN_ROA',
|
||||
|
||||
@@ -856,12 +856,21 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = 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
|
||||
|
||||
# ANAF official name override: used at partner creation (not lookup).
|
||||
# Strip before truthy check → reject whitespace-only values.
|
||||
denumire_override = None
|
||||
if is_ro_company and anaf_data_for_order:
|
||||
anaf_name_clean = (anaf_data_for_order.get("denumire_anaf") or "").strip()
|
||||
if anaf_name_clean:
|
||||
denumire_override = anaf_name_clean.upper()
|
||||
|
||||
result = await asyncio.to_thread(
|
||||
import_service.import_single_order,
|
||||
order, id_pol=id_pol, id_sectie=id_sectie,
|
||||
app_settings=app_settings, id_gestiuni=id_gestiuni,
|
||||
cod_fiscal_override=cod_fiscal_override,
|
||||
anaf_strict=anaf_strict
|
||||
anaf_strict=anaf_strict,
|
||||
denumire_override=denumire_override,
|
||||
)
|
||||
|
||||
# Build order items data for storage (R9)
|
||||
|
||||
Reference in New Issue
Block a user