diff --git a/modules/automation/cron.py b/modules/automation/cron.py index 556f9a1..262d731 100644 --- a/modules/automation/cron.py +++ b/modules/automation/cron.py @@ -11,6 +11,7 @@ import traceback logger = logging.getLogger(__name__) + class Cron(metaclass=PoolMeta): __name__ = 'ir.cron' @@ -18,9 +19,11 @@ class Cron(metaclass=PoolMeta): def __setup__(cls): super().__setup__() cls.method.selection.append( - ('automation.cron|update_shipment', "Update Shipment from freight booking info") + ('automation.cron|update_shipment', + "Update Shipment from freight booking info") ) + class AutomationCron(ModelSQL, ModelView): "Automation Cron" __name__ = 'automation.cron' @@ -30,7 +33,7 @@ class AutomationCron(ModelSQL, ModelView): ('weekly', "Weekly"), ('monthly', "Monthly"), ], "Frequency", required=True, - help="How frequently rates must be updated.") + help="How frequently rates must be updated.") last_update = fields.Date("Last Update", required=True) @@ -70,22 +73,23 @@ class AutomationCron(ModelSQL, ModelView): )) rows = cursor.fetchall() - logger.info(f"Nombre total de lignes à traiter : {len(rows)}") + logger.info(f"Nombre total de lignes a traiter : {len(rows)}") - # ---- PREMIÈRE TRANSACTION : Création des objets de référence ---- + # Premiere transaction : creation des objets de reference with Transaction().new_transaction() as trans1: try: - logger.info("Début de la création des objets de référence...") - + logger.info( + "Debut de la creation des objets de reference...") + parties_to_save = [] vessels_to_save = [] locations_to_save = [] - + parties_cache = {} vessels_cache = {} locations_cache = {} - - # Collecter les données des objets de référence + + # Collecter les donnees des objets de reference for row in rows: ( si_number, si_date, si_quantity, si_unit, @@ -97,47 +101,44 @@ class AutomationCron(ModelSQL, ModelView): comments, fintrade_booking_key ) = row - # Fonction pour obtenir ou créer un Party def get_or_create_party(name): if not name: return None name_upper = str(name).strip().upper() if name_upper in parties_cache: return parties_cache[name_upper] - - # Chercher d'abord dans la base - existing = Party.search([('name', '=', name_upper)], limit=1) + + existing = Party.search( + [('name', '=', name_upper)], limit=1) if existing: parties_cache[name_upper] = existing[0] return existing[0] - - # Créer un nouveau + new_p = Party() new_p.name = name_upper parties_cache[name_upper] = new_p parties_to_save.append(new_p) return new_p - # Fonction pour obtenir ou créer un Vessel def get_or_create_vessel(name): if not name: return None name_upper = str(name).strip().upper() if name_upper in vessels_cache: return vessels_cache[name_upper] - - existing = Vessel.search([('vessel_name', '=', name_upper)], limit=1) + + existing = Vessel.search( + [('vessel_name', '=', name_upper)], limit=1) if existing: vessels_cache[name_upper] = existing[0] return existing[0] - + new_v = Vessel() new_v.vessel_name = name_upper vessels_cache[name_upper] = new_v vessels_to_save.append(new_v) return new_v - # Fonction pour obtenir ou créer une Location def get_or_create_location(name, type_): if not name: return None @@ -145,16 +146,15 @@ class AutomationCron(ModelSQL, ModelView): key = f"{name_upper}_{type_}" if key in locations_cache: return locations_cache[key] - + existing = Location.search([ ('name', '=', name_upper), - ('type', '=', type_) + ('type', '=', type_), ], limit=1) - if existing: locations_cache[key] = existing[0] return existing[0] - + new_loc = Location() new_loc.name = name_upper new_loc.type = type_ @@ -162,40 +162,40 @@ class AutomationCron(ModelSQL, ModelView): locations_to_save.append(new_loc) return new_loc - # Collecter les objets à créer _ = get_or_create_party(carrier_name) _ = get_or_create_party(agent_name) _ = get_or_create_vessel(vessel_name) _ = get_or_create_location(loading_name, 'supplier') _ = get_or_create_location(destination_name, 'customer') - # Sauvegarder tous les objets de référence if parties_to_save: - logger.info(f"Création de {len(parties_to_save)} parties...") + logger.info(f"Creation de {len(parties_to_save)} parties...") Party.save(parties_to_save) - + if vessels_to_save: - logger.info(f"Création de {len(vessels_to_save)} vessels...") + logger.info(f"Creation de {len(vessels_to_save)} vessels...") Vessel.save(vessels_to_save) - + if locations_to_save: - logger.info(f"Création de {len(locations_to_save)} locations...") + logger.info( + f"Creation de {len(locations_to_save)} locations...") Location.save(locations_to_save) - + trans1.commit() - logger.info("Première transaction commitée : objets de référence créés") - + logger.info( + "Premiere transaction commitee : objets de reference crees") + except Exception as e: trans1.rollback() - logger.error(f"Erreur dans la création des objets de référence : {e}") + logger.error( + f"Erreur dans la creation des objets de reference : {e}") logger.error(traceback.format_exc()) raise - # ---- TRANSACTIONS INDIVIDUELLES pour chaque shipment ---- + # Transactions individuelles pour chaque shipment successful_shipments = 0 failed_shipments = [] - - # Recréer le curseur après la nouvelle transaction + cursor2 = Transaction().connection.cursor() cursor2.execute(*t.select( t.ShippingInstructionNumber, @@ -218,7 +218,7 @@ class AutomationCron(ModelSQL, ModelView): )) rows2 = cursor2.fetchall() - + for i, row in enumerate(rows2, 1): ( si_number, si_date, si_quantity, si_unit, @@ -229,46 +229,79 @@ class AutomationCron(ModelSQL, ModelView): etd_date, bl_date, controller, comments, fintrade_booking_key ) = row - + logger.info(f"Traitement shipment {i}/{len(rows2)} : SI {si_number}") - - # ---- TRANSACTION INDIVIDUELLE pour ce shipment ---- + try: with Transaction().new_transaction() as trans_shipment: - logger.info(f"Début transaction pour SI {si_number}") - - # Vérifier si le shipment existe déjà + logger.info(f"Debut transaction pour SI {si_number}") + existing_shipment = ShipmentIn.search([ ('reference', '=', si_number) ], limit=1) - + if existing_shipment: - logger.info(f"Shipment {si_number} existe déjà, ignoré") + shipment = existing_shipment[0] + if shipment.incoming_moves: + logger.info( + "Shipment %s existe deja avec lots, ignore", + si_number) + trans_shipment.commit() + continue + + logger.info( + "Shipment %s existe deja sans lots, verification freight_booking_lots", + si_number) + inv_date, inv_nb = shipment._create_lots_from_fintrade() + shipment = ShipmentIn(shipment.id) + if shipment.incoming_moves: + shipment.controller = shipment.get_controller() + shipment.controller_target = controller + if not shipment.fees: + shipment.create_fee(shipment.controller) + shipment.instructions = shipment.get_instructions_html( + inv_date, inv_nb) + ShipmentIn.save([shipment]) + logger.info( + "Shipment %s mis a jour avec %s incoming move(s)", + si_number, len(shipment.incoming_moves)) + else: + logger.info( + "Shipment %s existe sans lots et aucun lot disponible pour l'instant", + si_number) trans_shipment.commit() continue - # Récupérer les objets (maintenant ils existent dans la base) carrier = None if carrier_name: - carrier_list = Party.search([('name', '=', str(carrier_name).strip().upper())], limit=1) + carrier_list = Party.search([ + ('name', '=', + str(carrier_name).strip().upper()) + ], limit=1) if carrier_list: carrier = carrier_list[0] - logger.info(f"Carrier trouvé pour {si_number}: {carrier.name}") + logger.info( + f"Carrier trouve pour {si_number}: {carrier.name}") else: - logger.warning(f"Carrier NON TROUVÉ pour {si_number}: '{carrier_name}'") - + logger.warning( + f"Carrier NON TROUVE pour {si_number}: '{carrier_name}'") + agent = None - - agent_list = Party.search([('name', '=', str(agent_name or 'TBN').strip().upper())], limit=1) + agent_list = Party.search([ + ('name', '=', str(agent_name or 'TBN').strip().upper()) + ], limit=1) if agent_list: agent = agent_list[0] - + vessel = None if vessel_name: - vessel_list = Vessel.search([('vessel_name', '=', str(vessel_name).strip().upper())], limit=1) + vessel_list = Vessel.search([ + ('vessel_name', '=', + str(vessel_name).strip().upper()) + ], limit=1) if vessel_list: vessel = vessel_list[0] - + loc_from = None if loading_name: loc_from_list = Location.search([ @@ -277,7 +310,7 @@ class AutomationCron(ModelSQL, ModelView): ], limit=1) if loc_from_list: loc_from = loc_from_list[0] - + loc_to = None if destination_name: loc_to_list = Location.search([ @@ -286,19 +319,19 @@ class AutomationCron(ModelSQL, ModelView): ], limit=1) if loc_to_list: loc_to = loc_to_list[0] - - # Vérification critique du carrier + if not carrier: - error_msg = f"ERREUR CRITIQUE: Carrier manquant pour SI {si_number} (valeur: '{carrier_name}')" + error_msg = ( + f"ERREUR CRITIQUE: Carrier manquant pour SI {si_number} " + f"(valeur: '{carrier_name}')") logger.error(error_msg) raise ValueError(error_msg) - - # Créer le shipment + shipment = ShipmentIn() shipment.reference = si_number shipment.from_location = loc_from shipment.to_location = loc_to - shipment.carrier = None #carrier + shipment.carrier = None # carrier shipment.supplier = agent shipment.agent = agent shipment.vessel = vessel @@ -307,21 +340,20 @@ class AutomationCron(ModelSQL, ModelView): shipment.bl_date = bl_date shipment.etd = etd_date shipment.etad = shipment.bl_date + timedelta(days=20) - - # Sauvegarder ce shipment uniquement + ShipmentIn.save([shipment]) - inv_date,inv_nb = shipment._create_lots_from_fintrade() + inv_date, inv_nb = shipment._create_lots_from_fintrade() shipment.controller = shipment.get_controller() shipment.controller_target = controller shipment.create_fee(shipment.controller) - shipment.instructions = shipment.get_instructions_html(inv_date,inv_nb) + shipment.instructions = shipment.get_instructions_html( + inv_date, inv_nb) ShipmentIn.save([shipment]) trans_shipment.commit() successful_shipments += 1 - logger.info(f"✓ Shipment {si_number} créé avec succès") - + logger.info(f"Shipment {si_number} cree avec succes") + except Exception as e: - # Cette transaction échoue mais les autres continuent error_details = { 'si_number': si_number, 'carrier_name': carrier_name, @@ -329,8 +361,8 @@ class AutomationCron(ModelSQL, ModelView): 'traceback': traceback.format_exc() } failed_shipments.append(error_details) - - logger.error(f"✗ ERREUR pour shipment {si_number}: {e}") + + logger.error(f"ERREUR pour shipment {si_number}: {e}") logger.error(f" Carrier: '{carrier_name}'") logger.error(f" Agent: '{agent_name}'") logger.error(f" Vessel: '{vessel_name}'") @@ -338,24 +370,22 @@ class AutomationCron(ModelSQL, ModelView): for line in traceback.format_exc().split('\n'): if line.strip(): logger.error(f" {line}") - - # ---- RÉSUMÉ FINAL ---- + logger.info("=" * 60) - logger.info("RÉSUMÉ DE L'EXÉCUTION") + logger.info("RESUME DE L'EXECUTION") logger.info("=" * 60) - logger.info(f"Total de shipments à traiter : {len(rows2)}") - logger.info(f"Shipments créés avec succès : {successful_shipments}") - logger.info(f"Shipments en échec : {len(failed_shipments)}") - + logger.info(f"Total de shipments a traiter : {len(rows2)}") + logger.info(f"Shipments crees avec succes : {successful_shipments}") + logger.info(f"Shipments en echec : {len(failed_shipments)}") + if failed_shipments: - logger.info("\nDétail des échecs :") + logger.info("\nDetail des echecs :") for i, error in enumerate(failed_shipments, 1): logger.info(f" {i}. SI {error['si_number']}:") logger.info(f" Carrier: '{error['carrier_name']}'") logger.info(f" Erreur: {error['error']}") - - # Log supplémentaire pour debug - logger.info("\nAnalyse des carriers problématiques :") + + logger.info("\nAnalyse des carriers problematiques :") problematic_carriers = {} for error in failed_shipments: carrier = error['carrier_name'] @@ -363,15 +393,16 @@ class AutomationCron(ModelSQL, ModelView): problematic_carriers[carrier] += 1 else: problematic_carriers[carrier] = 1 - + for carrier, count in problematic_carriers.items(): - logger.info(f" Carrier '{carrier}' : {count} échec(s)") - - # Vérifier si ce carrier existe dans la base - existing = Party.search([('name', '=', str(carrier).strip().upper())], limit=1) + logger.info(f" Carrier '{carrier}' : {count} echec(s)") + existing = Party.search([ + ('name', '=', str(carrier).strip().upper()) + ], limit=1) if existing: - logger.info(f" → EXISTE DANS LA BASE (ID: {existing[0].id})") + logger.info( + f" -> EXISTE DANS LA BASE (ID: {existing[0].id})") else: - logger.info(f" → N'EXISTE PAS DANS LA BASE") - - logger.info("=" * 60) \ No newline at end of file + logger.info(" -> N'EXISTE PAS DANS LA BASE") + + logger.info("=" * 60)