This commit is contained in:
2026-01-20 16:42:52 +01:00
parent e5e76e2dcb
commit 797783b59e

View File

@@ -7,6 +7,7 @@ from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction from trytond.transaction import Transaction
import logging import logging
from sql import Table from sql import Table
import traceback
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -68,9 +69,14 @@ class AutomationCron(ModelSQL, ModelView):
t.FintradeBookingKey, t.FintradeBookingKey,
)) ))
rows = cursor.fetchall()
logger.info(f"Nombre total de lignes à traiter : {len(rows)}")
# ---- PREMIÈRE TRANSACTION : Création des objets de référence ---- # ---- PREMIÈRE TRANSACTION : Création des objets de référence ----
with Transaction().new_transaction() as trans1: with Transaction().new_transaction() as trans1:
try: try:
logger.info("Début de la création des objets de référence...")
parties_to_save = [] parties_to_save = []
vessels_to_save = [] vessels_to_save = []
locations_to_save = [] locations_to_save = []
@@ -80,7 +86,6 @@ class AutomationCron(ModelSQL, ModelView):
locations_cache = {} locations_cache = {}
# Collecter les données des objets de référence # Collecter les données des objets de référence
rows = cursor.fetchall()
for row in rows: for row in rows:
( (
si_number, si_date, si_quantity, si_unit, si_number, si_date, si_quantity, si_unit,
@@ -96,7 +101,7 @@ class AutomationCron(ModelSQL, ModelView):
def get_or_create_party(name): def get_or_create_party(name):
if not name: if not name:
return None return None
name_upper = name.upper() name_upper = str(name).strip().upper()
if name_upper in parties_cache: if name_upper in parties_cache:
return parties_cache[name_upper] return parties_cache[name_upper]
@@ -117,7 +122,7 @@ class AutomationCron(ModelSQL, ModelView):
def get_or_create_vessel(name): def get_or_create_vessel(name):
if not name: if not name:
return None return None
name_upper = name.upper() name_upper = str(name).strip().upper()
if name_upper in vessels_cache: if name_upper in vessels_cache:
return vessels_cache[name_upper] return vessels_cache[name_upper]
@@ -136,12 +141,13 @@ class AutomationCron(ModelSQL, ModelView):
def get_or_create_location(name, type_): def get_or_create_location(name, type_):
if not name: if not name:
return None return None
key = f"{name.upper()}_{type_}" name_upper = str(name).strip().upper()
key = f"{name_upper}_{type_}"
if key in locations_cache: if key in locations_cache:
return locations_cache[key] return locations_cache[key]
existing = Location.search([ existing = Location.search([
('name', '=', name.upper()), ('name', '=', name_upper),
('type', '=', type_) ('type', '=', type_)
], limit=1) ], limit=1)
@@ -150,7 +156,7 @@ class AutomationCron(ModelSQL, ModelView):
return existing[0] return existing[0]
new_loc = Location() new_loc = Location()
new_loc.name = name.upper() new_loc.name = name_upper
new_loc.type = type_ new_loc.type = type_
locations_cache[key] = new_loc locations_cache[key] = new_loc
locations_to_save.append(new_loc) locations_to_save.append(new_loc)
@@ -165,10 +171,15 @@ class AutomationCron(ModelSQL, ModelView):
# Sauvegarder tous les objets de référence # Sauvegarder tous les objets de référence
if parties_to_save: if parties_to_save:
logger.info(f"Création de {len(parties_to_save)} parties...")
Party.save(parties_to_save) Party.save(parties_to_save)
if vessels_to_save: if vessels_to_save:
logger.info(f"Création de {len(vessels_to_save)} vessels...")
Vessel.save(vessels_to_save) Vessel.save(vessels_to_save)
if locations_to_save: if locations_to_save:
logger.info(f"Création de {len(locations_to_save)} locations...")
Location.save(locations_to_save) Location.save(locations_to_save)
trans1.commit() trans1.commit()
@@ -176,47 +187,55 @@ class AutomationCron(ModelSQL, ModelView):
except Exception as e: except Exception as e:
trans1.rollback() trans1.rollback()
logger.error(f"Erreur dans la première transaction : {e}") logger.error(f"Erreur dans la création des objets de référence : {e}")
logger.error(traceback.format_exc())
raise raise
# ---- DEUXIÈME TRANSACTION : Création des shipments ---- # ---- TRANSACTIONS INDIVIDUELLES pour chaque shipment ----
with Transaction().new_transaction() as trans2: successful_shipments = 0
failed_shipments = []
# Recréer le curseur après la nouvelle transaction
cursor2 = Transaction().connection.cursor()
cursor2.execute(*t.select(
t.ShippingInstructionNumber,
t.ShippingInstructionDate,
t.ShippingInstructionQuantity,
t.ShippingInstructionQuantityUnit,
t.NumberOfContainers,
t.ContainerType,
t.Loading,
t.Destination,
t.BookingAgent,
t.Carrier,
t.Vessel,
t.BL_Number,
t.ETD_Date,
t.BL_Date,
t.ExpectedController,
t.Comments,
t.FintradeBookingKey,
))
rows2 = cursor2.fetchall()
for i, row in enumerate(rows2, 1):
(
si_number, si_date, si_quantity, si_unit,
container_number, container_type,
loading_name, destination_name,
agent_name, carrier_name,
vessel_name, bl_number,
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: try:
# Recréer les curseurs après la nouvelle transaction with Transaction().new_transaction() as trans_shipment:
cursor2 = Transaction().connection.cursor() logger.info(f"Début transaction pour SI {si_number}")
cursor2.execute(*t.select(
t.ShippingInstructionNumber,
t.ShippingInstructionDate,
t.ShippingInstructionQuantity,
t.ShippingInstructionQuantityUnit,
t.NumberOfContainers,
t.ContainerType,
t.Loading,
t.Destination,
t.BookingAgent,
t.Carrier,
t.Vessel,
t.BL_Number,
t.ETD_Date,
t.BL_Date,
t.ExpectedController,
t.Comments,
t.FintradeBookingKey,
))
rows2 = cursor2.fetchall()
shipments_to_save = []
for row in rows2:
(
si_number, si_date, si_quantity, si_unit,
container_number, container_type,
loading_name, destination_name,
agent_name, carrier_name,
vessel_name, bl_number,
etd_date, bl_date, controller,
comments, fintrade_booking_key
) = row
# Vérifier si le shipment existe déjà # Vérifier si le shipment existe déjà
existing_shipment = ShipmentIn.search([ existing_shipment = ShipmentIn.search([
@@ -224,46 +243,128 @@ class AutomationCron(ModelSQL, ModelView):
], limit=1) ], limit=1)
if existing_shipment: if existing_shipment:
logger.info(f"Shipment existe déjà : {si_number}") logger.info(f"Shipment {si_number} existe déjà, ignoré")
trans_shipment.commit()
continue continue
# Récupérer les objets (maintenant ils existent dans la base) # Récupérer les objets (maintenant ils existent dans la base)
carrier = Party.search([('name', '=', carrier_name.upper())], limit=1) if carrier_name else None carrier = None
agent = Party.search([('name', '=', agent_name.upper())], limit=1) if agent_name else None if carrier_name:
vessel = Vessel.search([('vessel_name', '=', vessel_name.upper())], limit=1) if vessel_name else None carrier_list = Party.search([('name', '=', str(carrier_name).strip().upper())], limit=1)
loc_from = Location.search([ if carrier_list:
('name', '=', loading_name.upper()), carrier = carrier_list[0]
('type', '=', 'supplier') logger.info(f"Carrier trouvé pour {si_number}: {carrier.name}")
], limit=1) if loading_name else None else:
loc_to = Location.search([ logger.warning(f"Carrier NON TROUVÉ pour {si_number}: '{carrier_name}'")
('name', '=', destination_name.upper()),
('type', '=', 'customer') agent = None
], limit=1) if destination_name else None if agent_name:
agent_list = Party.search([('name', '=', str(agent_name).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)
if vessel_list:
vessel = vessel_list[0]
loc_from = None
if loading_name:
loc_from_list = Location.search([
('name', '=', str(loading_name).strip().upper()),
('type', '=', 'supplier')
], limit=1)
if loc_from_list:
loc_from = loc_from_list[0]
loc_to = None
if destination_name:
loc_to_list = Location.search([
('name', '=', str(destination_name).strip().upper()),
('type', '=', 'customer')
], 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}')"
logger.error(error_msg)
raise ValueError(error_msg)
# Créer le shipment # Créer le shipment
shipment = ShipmentIn() shipment = ShipmentIn()
shipment.reference = si_number shipment.reference = si_number
shipment.from_location = loc_from[0] if loc_from else None shipment.from_location = loc_from
shipment.to_location = loc_to[0] if loc_to else None shipment.to_location = loc_to
shipment.carrier = carrier[0] if carrier else None shipment.carrier = carrier
shipment.supplier = agent[0] if agent else None shipment.supplier = agent
shipment.vessel = vessel[0] if vessel else None shipment.vessel = vessel
shipment.cargo_mode = 'bulk' shipment.cargo_mode = 'bulk'
shipment.bl_number = bl_number shipment.bl_number = bl_number
shipment.bl_date = bl_date shipment.bl_date = bl_date
shipment.etd = etd_date shipment.etd = etd_date
shipments_to_save.append(shipment) # Sauvegarder ce shipment uniquement
logger.info(f"Shipment préparé : {si_number}") ShipmentIn.save([shipment])
# Sauvegarder tous les shipments trans_shipment.commit()
if shipments_to_save: successful_shipments += 1
ShipmentIn.save(shipments_to_save) logger.info(f"✓ Shipment {si_number} créé avec succès")
logger.info(f"{len(shipments_to_save)} shipments créés")
trans2.commit()
except Exception as e: except Exception as e:
trans2.rollback() # Cette transaction échoue mais les autres continuent
logger.error(f"Erreur dans la deuxième transaction : {e}") error_details = {
raise 'si_number': si_number,
'carrier_name': carrier_name,
'error': str(e),
'traceback': traceback.format_exc()
}
failed_shipments.append(error_details)
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}'")
logger.error(" Traceback complet:")
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("=" * 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)}")
if failed_shipments:
logger.info("\nDétail des échecs :")
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 :")
problematic_carriers = {}
for error in failed_shipments:
carrier = error['carrier_name']
if carrier in problematic_carriers:
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)
if existing:
logger.info(f" → EXISTE DANS LA BASE (ID: {existing[0].id})")
else:
logger.info(f" → N'EXISTE PAS DANS LA BASE")
logger.info("=" * 60)