import logging from .. import database logger = logging.getLogger(__name__) def check_orders_in_roa(min_date, conn) -> dict: """Check which orders already exist in Oracle COMENZI by date range. Returns: {comanda_externa: id_comanda} for all existing orders. Much faster than IN-clause batching — single query using date index. """ if conn is None: return {} existing = {} try: with conn.cursor() as cur: cur.execute(""" SELECT comanda_externa, id_comanda FROM COMENZI WHERE data_comanda >= :min_date AND comanda_externa IS NOT NULL AND sters = 0 """, {"min_date": min_date}) for row in cur: existing[str(row[0])] = row[1] except Exception as e: logger.error(f"check_orders_in_roa failed: {e}") logger.info(f"ROA order check (since {min_date}): {len(existing)} existing orders found") return existing def resolve_codmat_ids(codmats: set[str], id_gestiuni: list[int] = None, conn=None) -> dict[str, dict]: """Resolve CODMATs to best id_articol + cont: prefers article with stock, then MAX(id_articol). Filters: sters=0 AND inactiv=0. id_gestiuni: list of warehouse IDs to check stock in, or None for all. Returns: {codmat: {"id_articol": int, "cont": str|None}} """ if not codmats: return {} result = {} codmat_list = list(codmats) # Build stoc subquery dynamically for index optimization if id_gestiuni: gest_placeholders = ",".join([f":g{k}" for k in range(len(id_gestiuni))]) stoc_filter = f"AND s.id_gestiune IN ({gest_placeholders})" else: stoc_filter = "" own_conn = conn is None if own_conn: conn = database.get_oracle_connection() try: with conn.cursor() as cur: for i in range(0, len(codmat_list), 500): batch = codmat_list[i:i+500] placeholders = ",".join([f":c{j}" for j in range(len(batch))]) params = {f"c{j}": cm for j, cm in enumerate(batch)} if id_gestiuni: for k, gid in enumerate(id_gestiuni): params[f"g{k}"] = gid cur.execute(f""" SELECT codmat, id_articol, cont FROM ( SELECT na.codmat, na.id_articol, na.cont, ROW_NUMBER() OVER ( PARTITION BY na.codmat ORDER BY CASE WHEN EXISTS ( SELECT 1 FROM stoc s WHERE s.id_articol = na.id_articol {stoc_filter} AND s.an = EXTRACT(YEAR FROM SYSDATE) AND s.luna = EXTRACT(MONTH FROM SYSDATE) AND s.cants + s.cant - s.cante > 0 ) THEN 0 ELSE 1 END, na.id_articol DESC ) AS rn FROM nom_articole na WHERE na.codmat IN ({placeholders}) AND na.sters = 0 AND na.inactiv = 0 ) WHERE rn = 1 """, params) for row in cur: result[row[0]] = {"id_articol": row[1], "cont": row[2]} finally: if own_conn: database.pool.release(conn) logger.info(f"resolve_codmat_ids: {len(result)}/{len(codmats)} resolved (gestiuni={id_gestiuni})") return result def validate_skus(skus: set[str], conn=None, id_gestiuni: list[int] = None) -> dict: """Validate a set of SKUs against Oracle. Returns: {mapped: set, direct: set, missing: set, direct_id_map: {codmat: {"id_articol": int, "cont": str|None}}} - mapped: found in ARTICOLE_TERTI (active) - direct: found in NOM_ARTICOLE by codmat (not in ARTICOLE_TERTI) - missing: not found anywhere - direct_id_map: {codmat: {"id_articol": int, "cont": str|None}} for direct SKUs """ if not skus: return {"mapped": set(), "direct": set(), "missing": set(), "direct_id_map": {}} mapped = set() sku_list = list(skus) own_conn = conn is None if own_conn: conn = database.get_oracle_connection() try: with conn.cursor() as cur: # Check in batches of 500 for i in range(0, len(sku_list), 500): batch = sku_list[i:i+500] placeholders = ",".join([f":s{j}" for j in range(len(batch))]) params = {f"s{j}": sku for j, sku in enumerate(batch)} # Check ARTICOLE_TERTI cur.execute(f""" SELECT DISTINCT sku FROM ARTICOLE_TERTI WHERE sku IN ({placeholders}) AND activ = 1 AND sters = 0 """, params) for row in cur: mapped.add(row[0]) # Resolve remaining SKUs via resolve_codmat_ids (consistent id_articol selection) all_remaining = [s for s in sku_list if s not in mapped] if all_remaining: direct_id_map = resolve_codmat_ids(set(all_remaining), id_gestiuni, conn) direct = set(direct_id_map.keys()) else: direct_id_map = {} direct = set() finally: if own_conn: database.pool.release(conn) missing = skus - mapped - direct logger.info(f"SKU validation: {len(mapped)} mapped, {len(direct)} direct, {len(missing)} missing") return {"mapped": mapped, "direct": direct, "missing": missing, "direct_id_map": direct_id_map} def classify_orders(orders, validation_result): """Classify orders as importable or skipped based on SKU validation. Returns: (importable_orders, skipped_orders) Each skipped entry is a tuple of (order, list_of_missing_skus). """ ok_skus = validation_result["mapped"] | validation_result["direct"] importable = [] skipped = [] for order in orders: order_skus = {item.sku for item in order.items if item.sku} order_missing = order_skus - ok_skus if order_missing: skipped.append((order, list(order_missing))) else: importable.append(order) return importable, skipped def _extract_id_map(direct_id_map: dict) -> dict: """Extract {codmat: id_articol} from either enriched or simple format.""" if not direct_id_map: return {} result = {} for cm, val in direct_id_map.items(): if isinstance(val, dict): result[cm] = val["id_articol"] else: result[cm] = val return result def validate_prices(codmats: set[str], id_pol: int, conn=None, direct_id_map: dict=None) -> dict: """Check which CODMATs have a price entry in CRM_POLITICI_PRET_ART for the given policy. If direct_id_map is provided, skips the NOM_ARTICOLE lookup for those CODMATs. Returns: {"has_price": set_of_codmats, "missing_price": set_of_codmats} """ if not codmats: return {"has_price": set(), "missing_price": set()} codmat_to_id = _extract_id_map(direct_id_map) ids_with_price = set() own_conn = conn is None if own_conn: conn = database.get_oracle_connection() try: with conn.cursor() as cur: # Check which ID_ARTICOLs have a price in the policy id_list = list(codmat_to_id.values()) for i in range(0, len(id_list), 500): batch = id_list[i:i+500] placeholders = ",".join([f":a{j}" for j in range(len(batch))]) params = {f"a{j}": aid for j, aid in enumerate(batch)} params["id_pol"] = id_pol cur.execute(f""" SELECT DISTINCT pa.ID_ARTICOL FROM CRM_POLITICI_PRET_ART pa WHERE pa.ID_POL = :id_pol AND pa.ID_ARTICOL IN ({placeholders}) """, params) for row in cur: ids_with_price.add(row[0]) finally: if own_conn: database.pool.release(conn) # Map back to CODMATs has_price = {cm for cm, aid in codmat_to_id.items() if aid in ids_with_price} missing_price = codmats - has_price logger.info(f"Price validation (policy {id_pol}): {len(has_price)} have price, {len(missing_price)} missing price") return {"has_price": has_price, "missing_price": missing_price} def ensure_prices(codmats: set[str], id_pol: int, conn=None, direct_id_map: dict=None, cota_tva: float = None): """Insert price 0 entries for CODMATs missing from the given price policy. Uses batch executemany instead of individual INSERTs. Relies on TRG_CRM_POLITICI_PRET_ART trigger for ID_POL_ART sequence. cota_tva: VAT rate from settings (e.g. 21) — used for PROC_TVAV metadata. """ if not codmats: return proc_tvav = 1 + (cota_tva / 100) if cota_tva else 1.21 own_conn = conn is None if own_conn: conn = database.get_oracle_connection() try: with conn.cursor() as cur: # Get ID_VALUTA for this policy cur.execute(""" SELECT ID_VALUTA FROM CRM_POLITICI_PRETURI WHERE ID_POL = :id_pol """, {"id_pol": id_pol}) row = cur.fetchone() if not row: logger.error(f"Price policy {id_pol} not found in CRM_POLITICI_PRETURI") return id_valuta = row[0] # Build batch params using direct_id_map (already resolved via resolve_codmat_ids) batch_params = [] codmat_id_map = _extract_id_map(direct_id_map) for codmat in codmats: id_articol = codmat_id_map.get(codmat) if not id_articol: logger.warning(f"CODMAT {codmat} not found in NOM_ARTICOLE, skipping price insert") continue batch_params.append({ "id_pol": id_pol, "id_articol": id_articol, "id_valuta": id_valuta, "proc_tvav": proc_tvav }) if batch_params: cur.executemany(""" INSERT INTO CRM_POLITICI_PRET_ART (ID_POL, ID_ARTICOL, PRET, ID_VALUTA, ID_UTIL, DATAORA, PROC_TVAV, PRETFTVA, PRETCTVA) VALUES (:id_pol, :id_articol, 0, :id_valuta, -3, SYSDATE, :proc_tvav, 0, 0) """, batch_params) logger.info(f"Batch inserted {len(batch_params)} price entries for policy {id_pol} (PROC_TVAV={proc_tvav})") conn.commit() finally: if own_conn: database.pool.release(conn) logger.info(f"Ensure prices done: {len(codmats)} CODMATs processed for policy {id_pol}") def validate_and_ensure_prices_dual(codmats: set[str], id_pol_vanzare: int, id_pol_productie: int, conn, direct_id_map: dict, cota_tva: float = 21) -> dict[str, int]: """Dual-policy price validation: assign each CODMAT to sales or production policy. Logic: 1. Check both policies in one SQL 2. If article in one policy → use that 3. If article in BOTH → prefer id_pol_vanzare 4. If article in NEITHER → check cont: 341/345 → production, else → sales; insert price 0 Returns: codmat_policy_map = {codmat: assigned_id_pol} """ if not codmats: return {} codmat_policy_map = {} id_map = _extract_id_map(direct_id_map) # Collect all id_articol values we need to check id_to_codmats = {} # {id_articol: [codmat, ...]} for cm in codmats: aid = id_map.get(cm) if aid: id_to_codmats.setdefault(aid, []).append(cm) if not id_to_codmats: return {} # Query both policies in one SQL existing = {} # {id_articol: set of id_pol} id_list = list(id_to_codmats.keys()) with conn.cursor() as cur: for i in range(0, len(id_list), 500): batch = id_list[i:i+500] placeholders = ",".join([f":a{j}" for j in range(len(batch))]) params = {f"a{j}": aid for j, aid in enumerate(batch)} params["id_pol_v"] = id_pol_vanzare params["id_pol_p"] = id_pol_productie cur.execute(f""" SELECT pa.ID_ARTICOL, pa.ID_POL FROM CRM_POLITICI_PRET_ART pa WHERE pa.ID_POL IN (:id_pol_v, :id_pol_p) AND pa.ID_ARTICOL IN ({placeholders}) """, params) for row in cur: existing.setdefault(row[0], set()).add(row[1]) # Classify each codmat missing_vanzare = set() # CODMATs needing price 0 in sales policy missing_productie = set() # CODMATs needing price 0 in production policy for aid, cms in id_to_codmats.items(): pols = existing.get(aid, set()) for cm in cms: if pols: if id_pol_vanzare in pols: codmat_policy_map[cm] = id_pol_vanzare elif id_pol_productie in pols: codmat_policy_map[cm] = id_pol_productie else: # Not in any policy — classify by cont info = direct_id_map.get(cm, {}) cont = info.get("cont", "") if isinstance(info, dict) else "" cont_str = str(cont or "").strip() if cont_str in ("341", "345"): codmat_policy_map[cm] = id_pol_productie missing_productie.add(cm) else: codmat_policy_map[cm] = id_pol_vanzare missing_vanzare.add(cm) # Ensure prices for missing articles in each policy if missing_vanzare: ensure_prices(missing_vanzare, id_pol_vanzare, conn, direct_id_map, cota_tva=cota_tva) if missing_productie: ensure_prices(missing_productie, id_pol_productie, conn, direct_id_map, cota_tva=cota_tva) logger.info( f"Dual-policy: {len(codmat_policy_map)} CODMATs assigned " f"(vanzare={sum(1 for v in codmat_policy_map.values() if v == id_pol_vanzare)}, " f"productie={sum(1 for v in codmat_policy_map.values() if v == id_pol_productie)})" ) return codmat_policy_map def resolve_mapped_codmats(mapped_skus: set[str], conn, id_gestiuni: list[int] = None) -> dict[str, list[dict]]: """For mapped SKUs, get their underlying CODMATs from ARTICOLE_TERTI + nom_articole. Uses ROW_NUMBER to pick the best id_articol per (SKU, CODMAT) pair: prefers article with stock in current month, then MAX(id_articol) as fallback. This avoids inflating results when a CODMAT has multiple NOM_ARTICOLE entries. Returns: {sku: [{"codmat": str, "id_articol": int, "cont": str|None, "cantitate_roa": float|None}]} """ if not mapped_skus: return {} # Build stoc subquery gestiune filter (same pattern as resolve_codmat_ids) if id_gestiuni: gest_placeholders = ",".join([f":g{k}" for k in range(len(id_gestiuni))]) stoc_filter = f"AND s.id_gestiune IN ({gest_placeholders})" else: stoc_filter = "" result = {} sku_list = list(mapped_skus) with conn.cursor() as cur: for i in range(0, len(sku_list), 500): batch = sku_list[i:i+500] placeholders = ",".join([f":s{j}" for j in range(len(batch))]) params = {f"s{j}": sku for j, sku in enumerate(batch)} if id_gestiuni: for k, gid in enumerate(id_gestiuni): params[f"g{k}"] = gid cur.execute(f""" SELECT sku, codmat, id_articol, cont, cantitate_roa FROM ( SELECT at.sku, at.codmat, na.id_articol, na.cont, at.cantitate_roa, ROW_NUMBER() OVER ( PARTITION BY at.sku, at.codmat ORDER BY CASE WHEN EXISTS ( SELECT 1 FROM stoc s WHERE s.id_articol = na.id_articol {stoc_filter} AND s.an = EXTRACT(YEAR FROM SYSDATE) AND s.luna = EXTRACT(MONTH FROM SYSDATE) AND s.cants + s.cant - s.cante > 0 ) THEN 0 ELSE 1 END, na.id_articol DESC ) AS rn FROM ARTICOLE_TERTI at JOIN NOM_ARTICOLE na ON na.codmat = at.codmat AND na.sters = 0 AND na.inactiv = 0 WHERE at.sku IN ({placeholders}) AND at.activ = 1 AND at.sters = 0 ) WHERE rn = 1 """, params) for row in cur: sku = row[0] if sku not in result: result[sku] = [] result[sku].append({ "codmat": row[1], "id_articol": row[2], "cont": row[3], "cantitate_roa": row[4] }) logger.info(f"resolve_mapped_codmats: {len(result)} SKUs → {sum(len(v) for v in result.values())} CODMATs") return result def validate_kit_component_prices(mapped_codmat_data: dict, id_pol: int, id_pol_productie: int = None, conn=None) -> dict: """Pre-validate that kit components have non-zero prices in crm_politici_pret_art. Args: mapped_codmat_data: {sku: [{"codmat", "id_articol", "cont"}, ...]} from resolve_mapped_codmats id_pol: default sales price policy id_pol_productie: production price policy (for cont 341/345) Returns: {sku: [missing_codmats]} for SKUs with missing prices, {} if all OK """ missing = {} own_conn = conn is None if own_conn: conn = database.get_oracle_connection() try: with conn.cursor() as cur: for sku, components in mapped_codmat_data.items(): if len(components) == 0: continue if len(components) == 1 and (components[0].get("cantitate_roa") or 1) <= 1: continue # True 1:1 mapping, no kit pricing needed sku_missing = [] for comp in components: cont = str(comp.get("cont") or "").strip() if cont in ("341", "345") and id_pol_productie: pol = id_pol_productie else: pol = id_pol cur.execute(""" SELECT PRET FROM crm_politici_pret_art WHERE id_pol = :pol AND id_articol = :id_art """, {"pol": pol, "id_art": comp["id_articol"]}) row = cur.fetchone() if not row: sku_missing.append(comp["codmat"]) if sku_missing: missing[sku] = sku_missing finally: if own_conn: database.pool.release(conn) return missing def compare_and_update_price(id_articol: int, id_pol: int, web_price_cu_tva: float, conn, tolerance: float = 0.01) -> dict | None: """Compare web price with ROA price and update if different. Handles PRETURI_CU_TVA flag per policy. Returns: {"updated": bool, "old_price": float, "new_price": float, "codmat": str} or None if no price entry. """ with conn.cursor() as cur: cur.execute("SELECT PRETURI_CU_TVA FROM CRM_POLITICI_PRETURI WHERE ID_POL = :pol", {"pol": id_pol}) pol_row = cur.fetchone() if not pol_row: return None preturi_cu_tva = pol_row[0] # 1 or 0 cur.execute(""" SELECT PRET, PROC_TVAV, na.codmat FROM crm_politici_pret_art pa JOIN nom_articole na ON na.id_articol = pa.id_articol WHERE pa.id_pol = :pol AND pa.id_articol = :id_art """, {"pol": id_pol, "id_art": id_articol}) row = cur.fetchone() if not row: return None pret_roa, proc_tvav, codmat = row[0], row[1], row[2] proc_tvav = proc_tvav or 1.19 if preturi_cu_tva == 1: pret_roa_cu_tva = pret_roa else: pret_roa_cu_tva = pret_roa * proc_tvav if abs(pret_roa_cu_tva - web_price_cu_tva) <= tolerance: return {"updated": False, "old_price": pret_roa_cu_tva, "new_price": web_price_cu_tva, "codmat": codmat} if preturi_cu_tva == 1: new_pret = web_price_cu_tva else: new_pret = round(web_price_cu_tva / proc_tvav, 4) cur.execute(""" UPDATE crm_politici_pret_art SET PRET = :pret, DATAORA = SYSDATE WHERE id_pol = :pol AND id_articol = :id_art """, {"pret": new_pret, "pol": id_pol, "id_art": id_articol}) conn.commit() return {"updated": True, "old_price": pret_roa_cu_tva, "new_price": web_price_cu_tva, "codmat": codmat} def sync_prices_from_order(orders, mapped_codmat_data: dict, direct_id_map: dict, codmat_policy_map: dict, id_pol: int, id_pol_productie: int = None, conn=None, settings: dict = None) -> list: """Sync prices from order items to ROA for direct/1:1 mappings. Skips kit components and transport/discount CODMATs. Returns: list of {"codmat", "old_price", "new_price"} for updated prices. """ if settings and settings.get("price_sync_enabled") != "1": return [] transport_codmat = (settings or {}).get("transport_codmat", "") discount_codmat = (settings or {}).get("discount_codmat", "") kit_discount_codmat = (settings or {}).get("kit_discount_codmat", "") skip_codmats = {transport_codmat, discount_codmat, kit_discount_codmat} - {""} # Build set of kit/bax SKUs (>1 component, or single component with cantitate_roa > 1) kit_skus = {sku for sku, comps in mapped_codmat_data.items() if len(comps) > 1 or (len(comps) == 1 and (comps[0].get("cantitate_roa") or 1) > 1)} updated = [] own_conn = conn is None if own_conn: conn = database.get_oracle_connection() try: for order in orders: for item in order.items: sku = item.sku if not sku or sku in skip_codmats: continue if sku in kit_skus: continue # Don't sync prices from kit orders web_price = item.price # already with TVA if not web_price or web_price <= 0: continue # Determine id_articol and price policy for this SKU if sku in mapped_codmat_data and len(mapped_codmat_data[sku]) == 1: # 1:1 mapping via ARTICOLE_TERTI comp = mapped_codmat_data[sku][0] id_articol = comp["id_articol"] cantitate_roa = comp.get("cantitate_roa") or 1 web_price_per_unit = web_price / cantitate_roa if cantitate_roa != 1 else web_price elif sku in (direct_id_map or {}): info = direct_id_map[sku] id_articol = info["id_articol"] if isinstance(info, dict) else info web_price_per_unit = web_price else: continue pol = codmat_policy_map.get(sku, id_pol) result = compare_and_update_price(id_articol, pol, web_price_per_unit, conn) if result and result["updated"]: updated.append(result) finally: if own_conn: database.pool.release(conn) return updated def get_prices_for_order(items: list[dict], app_settings: dict, conn=None) -> dict: """Compare GoMag prices with ROA prices for order items. Args: items: list of order items, each with 'sku', 'price', 'quantity', 'codmat_details' (codmat_details = [{"codmat", "cantitate_roa", "id_articol"?, "cont"?, "direct"?}]) app_settings: dict with 'id_pol', 'id_pol_productie' conn: Oracle connection (optional, will acquire if None) Returns: { "items": {idx: {"pret_roa": float|None, "match": bool|None, "pret_gomag": float}}, "summary": {"mismatches": int, "checked": int, "oracle_available": bool} } """ try: id_pol = int(app_settings.get("id_pol", 0) or 0) id_pol_productie = int(app_settings.get("id_pol_productie", 0) or 0) except (ValueError, TypeError): id_pol = 0 id_pol_productie = 0 def _empty_result(oracle_available: bool) -> dict: return { "items": { idx: {"pret_roa": None, "match": None, "pret_gomag": float(item.get("price") or 0)} for idx, item in enumerate(items) }, "summary": {"mismatches": 0, "checked": 0, "oracle_available": oracle_available} } if not items or not id_pol: return _empty_result(oracle_available=False) own_conn = conn is None try: if own_conn: conn = database.get_oracle_connection() # Step 1: Collect codmats; use id_articol/cont from codmat_details when already known pre_resolved = {} # {codmat: {"id_articol": int, "cont": str}} all_codmats = set() for item in items: for cd in (item.get("codmat_details") or []): codmat = cd.get("codmat") if not codmat: continue all_codmats.add(codmat) if cd.get("id_articol") and codmat not in pre_resolved: pre_resolved[codmat] = { "id_articol": cd["id_articol"], "cont": cd.get("cont") or "", } # Step 2: Resolve missing id_articols via nom_articole need_resolve = all_codmats - set(pre_resolved.keys()) if need_resolve: db_resolved = resolve_codmat_ids(need_resolve, conn=conn) pre_resolved.update(db_resolved) codmat_info = pre_resolved # {codmat: {"id_articol": int, "cont": str}} # Step 3: Get PRETURI_CU_TVA flag once per policy policies = {id_pol} if id_pol_productie and id_pol_productie != id_pol: policies.add(id_pol_productie) pol_cu_tva = {} # {id_pol: bool} with conn.cursor() as cur: for pol in policies: cur.execute( "SELECT PRETURI_CU_TVA FROM CRM_POLITICI_PRETURI WHERE ID_POL = :pol", {"pol": pol}, ) row = cur.fetchone() pol_cu_tva[pol] = (int(row[0] or 0) == 1) if row else False # Step 4: Batch query PRET + PROC_TVAV for all id_articols across both policies all_id_articols = list({ info["id_articol"] for info in codmat_info.values() if info.get("id_articol") }) price_map = {} # {(id_pol, id_articol): (pret, proc_tvav)} if all_id_articols: pol_list = list(policies) pol_placeholders = ",".join([f":p{k}" for k in range(len(pol_list))]) with conn.cursor() as cur: for i in range(0, len(all_id_articols), 500): batch = all_id_articols[i:i + 500] art_placeholders = ",".join([f":a{j}" for j in range(len(batch))]) params = {f"a{j}": aid for j, aid in enumerate(batch)} for k, pol in enumerate(pol_list): params[f"p{k}"] = pol cur.execute(f""" SELECT ID_POL, ID_ARTICOL, PRET, PROC_TVAV FROM CRM_POLITICI_PRET_ART WHERE ID_POL IN ({pol_placeholders}) AND ID_ARTICOL IN ({art_placeholders}) """, params) for row in cur: price_map[(row[0], row[1])] = (row[2], row[3]) # Step 5: Compute pret_roa per item and compare with GoMag price result_items = {} mismatches = 0 checked = 0 for idx, item in enumerate(items): pret_gomag = float(item.get("price") or 0) result_items[idx] = {"pret_gomag": pret_gomag, "pret_roa": None, "match": None} codmat_details = item.get("codmat_details") or [] if not codmat_details: continue is_kit = len(codmat_details) > 1 or ( len(codmat_details) == 1 and float(codmat_details[0].get("cantitate_roa") or 1) > 1 ) pret_roa_total = 0.0 all_resolved = True for cd in codmat_details: codmat = cd.get("codmat") if not codmat: all_resolved = False break info = codmat_info.get(codmat, {}) id_articol = info.get("id_articol") if not id_articol: all_resolved = False break # Dual-policy routing: cont 341/345 → production, else → sales cont = str(info.get("cont") or cd.get("cont") or "").strip() if cont in ("341", "345") and id_pol_productie: pol = id_pol_productie else: pol = id_pol price_entry = price_map.get((pol, id_articol)) if price_entry is None: all_resolved = False break pret, proc_tvav = price_entry proc_tvav = float(proc_tvav or 1.19) if pol_cu_tva.get(pol): pret_cu_tva = float(pret or 0) else: pret_cu_tva = float(pret or 0) * proc_tvav cantitate_roa = float(cd.get("cantitate_roa") or 1) if is_kit: pret_roa_total += pret_cu_tva * cantitate_roa else: pret_roa_total = pret_cu_tva # cantitate_roa==1 for simple items if not all_resolved: continue pret_roa = round(pret_roa_total, 4) match = abs(pret_gomag - pret_roa) < 0.01 result_items[idx]["pret_roa"] = pret_roa result_items[idx]["match"] = match checked += 1 if not match: mismatches += 1 logger.info(f"get_prices_for_order: {checked}/{len(items)} checked, {mismatches} mismatches") return { "items": result_items, "summary": {"mismatches": mismatches, "checked": checked, "oracle_available": True}, } except Exception as e: logger.error(f"get_prices_for_order failed: {e}") return _empty_result(oracle_available=False) finally: if own_conn and conn: database.pool.release(conn)