import logging from .. import database logger = logging.getLogger(__name__) def validate_skus(skus: set[str]) -> dict: """Validate a set of SKUs against Oracle. Returns: {mapped: set, direct: set, missing: set} - mapped: found in ARTICOLE_TERTI (active) - direct: found in NOM_ARTICOLE by codmat (not in ARTICOLE_TERTI) - missing: not found anywhere """ if not skus: return {"mapped": set(), "direct": set(), "missing": set()} mapped = set() direct = set() sku_list = list(skus) 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 """, params) for row in cur: mapped.add(row[0]) # Check NOM_ARTICOLE for remaining remaining = [s for s in batch if s not in mapped] if remaining: placeholders2 = ",".join([f":n{j}" for j in range(len(remaining))]) params2 = {f"n{j}": sku for j, sku in enumerate(remaining)} cur.execute(f""" SELECT DISTINCT codmat FROM NOM_ARTICOLE WHERE codmat IN ({placeholders2}) """, params2) for row in cur: direct.add(row[0]) finally: 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} 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 find_new_orders(order_numbers: list[str]) -> set[str]: """Check which order numbers do NOT already exist in Oracle COMENZI. Returns: set of order numbers that are truly new (not yet imported). """ if not order_numbers: return set() existing = set() num_list = list(order_numbers) conn = database.get_oracle_connection() try: with conn.cursor() as cur: for i in range(0, len(num_list), 500): batch = num_list[i:i+500] placeholders = ",".join([f":o{j}" for j in range(len(batch))]) params = {f"o{j}": num for j, num in enumerate(batch)} cur.execute(f""" SELECT DISTINCT comanda_externa FROM COMENZI WHERE comanda_externa IN ({placeholders}) AND sters = 0 """, params) for row in cur: existing.add(row[0]) finally: database.pool.release(conn) new_orders = set(order_numbers) - existing logger.info(f"Order check: {len(new_orders)} new, {len(existing)} already exist out of {len(order_numbers)} total") return new_orders def validate_prices(codmats: set[str], id_pol: int) -> dict: """Check which CODMATs have a price entry in CRM_POLITICI_PRET_ART for the given policy. Returns: {"has_price": set_of_codmats, "missing_price": set_of_codmats} """ if not codmats: return {"has_price": set(), "missing_price": set()} codmat_to_id = {} ids_with_price = set() codmat_list = list(codmats) conn = database.get_oracle_connection() try: with conn.cursor() as cur: # Step 1: Get ID_ARTICOL for each CODMAT 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)} cur.execute(f""" SELECT id_articol, codmat FROM NOM_ARTICOLE WHERE codmat IN ({placeholders}) """, params) for row in cur: codmat_to_id[row[1]] = row[0] # Step 2: 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: 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): """Insert price 0 entries for CODMATs missing from the given price policy.""" if not codmats: return 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] for codmat in codmats: # Get ID_ARTICOL cur.execute(""" SELECT id_articol FROM NOM_ARTICOLE WHERE codmat = :codmat """, {"codmat": codmat}) row = cur.fetchone() if not row: logger.warning(f"CODMAT {codmat} not found in NOM_ARTICOLE, skipping price insert") continue id_articol = row[0] cur.execute(""" INSERT INTO CRM_POLITICI_PRET_ART (ID_POL_ART, ID_POL, ID_ARTICOL, PRET, ID_VALUTA, ID_UTIL, DATAORA, PROC_TVAV, PRETFTVA, PRETCTVA) VALUES (SEQ_CRM_POLITICI_PRET_ART.NEXTVAL, :id_pol, :id_articol, 0, :id_valuta, -3, SYSDATE, 1.19, 0, 0) """, {"id_pol": id_pol, "id_articol": id_articol, "id_valuta": id_valuta}) logger.info(f"Pret 0 adaugat pentru CODMAT {codmat} in politica {id_pol}") conn.commit() finally: database.pool.release(conn) logger.info(f"Ensure prices done: {len(codmats)} CODMATs processed for policy {id_pol}")