@@ -690,59 +690,139 @@ def _execute_load_batch( # noqa: C901
690690
691691 try :
692692 log .debug (f"Attempting `load` for chunk of batch { batch_number } ..." )
693- # Sanitize the id column values to prevent XML ID constraint violations
694- from .lib .internal .tools import to_xmlid
695- sanitized_load_lines = []
696- for line in load_lines :
697- sanitized_line = list (line )
698- if uid_index < len (sanitized_line ):
699- # Sanitize the source_id (which is in the id column)
700- sanitized_line [uid_index ] = to_xmlid (sanitized_line [uid_index ])
701- sanitized_load_lines .append (sanitized_line )
693+ log .debug (f"Load header: { load_header } " )
694+ log .debug (f"Load lines count: { len (sanitized_load_lines )} " )
695+ if sanitized_load_lines :
696+ log .debug (f"First load line (first 10 fields): { sanitized_load_lines [0 ][:10 ] if len (sanitized_load_lines [0 ]) > 10 else sanitized_load_lines [0 ]} { '...' if len (sanitized_load_lines [0 ]) > 10 else '' } " )
697+ log .debug (f"Full header: { load_header } " )
698+ # Log the full header and first line for debugging
699+ if len (load_header ) > 10 :
700+ log .debug (f"Full load_header: { load_header } " )
701+ if len (sanitized_load_lines [0 ]) > 10 :
702+ log .debug (f"Full first load_line: { sanitized_load_lines [0 ]} " )
702703
703704 res = model .load (load_header , sanitized_load_lines , context = context )
704705
705- # Check for any messages/warnings in the response that might indicate issues
706+ # DEBUG: Log what we got back from Odoo
707+ log .debug (
708+ f"Load response - messages: { res .get ('messages' , 'None' )} , "
709+ f"ids: { res .get ('ids' , 'None' )} , "
710+ f"data: { type (res )} "
711+ )
706712 if res .get ("messages" ):
707713 for message in res ["messages" ]:
708714 msg_type = message .get ("type" , "unknown" )
709715 msg_text = message .get ("message" , "" )
716+ log .debug (f"Load message { msg_type } : { msg_text } " )
710717 if msg_type in ["warning" , "error" ]:
711718 log .warning (f"Load operation returned { msg_type } : { msg_text } " )
712719 else :
713720 log .info (f"Load operation returned { msg_type } : { msg_text } " )
714721
715- # Verify that the load operation actually created records
722+ # Check for any Odoo server errors in the response
723+ if res .get ("messages" ):
724+ error = res ["messages" ][0 ].get ("message" , "Batch load failed." )
725+ # Don't raise immediately, log and continue to capture in fail file
726+ log .error (f"Odoo server error during load: { error } " )
727+
716728 created_ids = res .get ("ids" , [])
729+ log .debug (f"Expected records: { len (sanitized_load_lines )} , Created records: { len (created_ids )} " )
730+
731+ # Always log detailed information about record creation
717732 if len (created_ids ) != len (sanitized_load_lines ):
718733 log .warning (
719734 f"Record creation mismatch: Expected { len (sanitized_load_lines )} records, "
720735 f"but only { len (created_ids )} were created"
721736 )
722737 if len (created_ids ) == 0 :
723738 log .error (
724- "No records were created in this batch. This may indicate silent failures "
725- "in the Odoo load operation. Check Odoo server logs for validation errors."
739+ f"No records were created in this batch of { len (sanitized_load_lines )} . "
740+ f"This may indicate silent failures in the Odoo load operation. "
741+ f"Check Odoo server logs for validation errors."
742+ )
743+ # Log the actual data being sent for debugging
744+ if sanitized_load_lines :
745+ log .debug (f"First few lines being sent:" )
746+ for i , line in enumerate (sanitized_load_lines [:3 ]):
747+ log .debug (f" Line { i } : { dict (zip (load_header , line ))} " )
748+ else :
749+ log .warning (
750+ f"Partial record creation: { len (created_ids )} /{ len (sanitized_load_lines )} "
751+ f"records were created. Some records may have failed validation."
726752 )
727753 if res .get ("messages" ):
728754 error = res ["messages" ][0 ].get ("message" , "Batch load failed." )
729755 raise ValueError (error )
730756
731757 created_ids = res .get ("ids" , [])
758+ log .debug (f"Expected records: { len (sanitized_load_lines )} , Created records: { len (created_ids )} " )
759+
760+ # Always log detailed information about record creation
732761 if len (created_ids ) != len (sanitized_load_lines ):
733- raise ValueError ("Record count mismatch after load." )
734-
762+ log .warning (
763+ f"Record creation mismatch: Expected { len (sanitized_load_lines )} records, "
764+ f"but only { len (created_ids )} were created"
765+ )
766+ if len (created_ids ) == 0 :
767+ log .error (
768+ f"No records were created in this batch of { len (sanitized_load_lines )} . "
769+ f"This may indicate silent failures in the Odoo load operation. "
770+ f"Check Odoo server logs for validation errors."
771+ )
772+ # Log the actual data being sent for debugging
773+ if sanitized_load_lines :
774+ log .debug (f"First few lines being sent:" )
775+ for i , line in enumerate (sanitized_load_lines [:3 ]):
776+ log .debug (f" Line { i } : { dict (zip (load_header , line ))} " )
777+ else :
778+ log .warning (
779+ f"Partial record creation: { len (created_ids )} /{ len (sanitized_load_lines )} "
780+ f"records were created. Some records may have failed validation."
781+ )
782+
783+ # Instead of raising an exception, capture failures for the fail file
784+ # But still create what records we can
785+ if res .get ("messages" ):
786+ # Extract error information and add to failed_lines to be written to fail file
787+ error_msg = res ["messages" ][0 ].get ("message" , "Batch load failed." )
788+ log .error (f"Capturing load failure for fail file: { error_msg } " )
789+ # We'll add the failed lines to aggregated_failed_lines at the end
790+
735791 # Use sanitized IDs for the id_map to match what was actually sent to Odoo
736792 id_map = {
737- to_xmlid (line [uid_index ]): created_ids [i ] for i , line in enumerate (current_chunk )
793+ to_xmlid (line [uid_index ]): created_ids [i ] if i < len (created_ids ) else None
794+ for i , line in enumerate (current_chunk )
738795 }
739796
797+ # Remove None entries (failed creations) from id_map
798+ id_map = {k : v for k , v in id_map .items () if v is not None }
799+
740800 # Log id_map information for debugging
741801 log .debug (f"Created { len (id_map )} records in batch { batch_number } " )
742802 if id_map :
743803 log .debug (f"Sample id_map entries: { dict (list (id_map .items ())[:3 ])} " )
744804 else :
745805 log .warning (f"No id_map entries created for batch { batch_number } " )
806+
807+ # Capture failed lines for writing to fail file
808+ successful_count = len (created_ids )
809+ total_count = len (sanitized_load_lines )
810+
811+ if successful_count < total_count :
812+ failed_count = total_count - successful_count
813+ log .info (f"Capturing { failed_count } failed records for fail file" )
814+ # Add error information to the lines that failed
815+ for i , line in enumerate (current_chunk ):
816+ # Check if this line corresponds to a created record
817+ if i >= len (created_ids ) or created_ids [i ] is None :
818+ # This record failed, add it to failed_lines with error info
819+ error_msg = "Record creation failed"
820+ if res .get ("messages" ):
821+ error_msg = res ["messages" ][0 ].get ("message" , error_msg )
822+
823+ failed_line = list (line ) + [f"Load failed: { error_msg } " ]
824+ aggregated_failed_lines .append (failed_line )
825+
746826 aggregated_id_map .update (id_map )
747827 lines_to_process = lines_to_process [chunk_size :]
748828
0 commit comments