# -*- coding: utf-8 -*- from odoo import models, fields, api, _ from odoo.exceptions import UserError import logging _logger = logging.getLogger(__name__) class SaleOrder(models.Model): _inherit = 'sale.order' is_lab_request = fields.Boolean( string="Es Orden de Laboratorio", default=False, copy=False, help="Campo técnico para identificar si la orden de venta es una solicitud de laboratorio." ) doctor_id = fields.Many2one( 'res.partner', string="Médico Referente", domain="[('is_doctor', '=', True)]", help="El médico que refirió al paciente para esta solicitud de laboratorio." ) generated_sample_ids = fields.Many2many( 'stock.lot', 'sale_order_stock_lot_rel', 'order_id', 'lot_id', string='Muestras Generadas', domain="[('is_lab_sample', '=', True)]", readonly=True, help="Muestras de laboratorio generadas automáticamente cuando se confirmó esta orden" ) def action_confirm(self): """Override to generate laboratory samples and tests automatically""" res = super(SaleOrder, self).action_confirm() # Generate samples and tests only for laboratory requests for order in self.filtered('is_lab_request'): try: order._generate_lab_samples() order._generate_lab_tests() except Exception as e: _logger.error(f"Error generating samples/tests for order {order.name}: {str(e)}") # Continue with order confirmation even if generation fails # But notify the user order.message_post( body=_("Error al generar muestras/pruebas automáticamente: %s. " "Por favor, genere las muestras y pruebas manualmente.") % str(e), message_type='notification' ) return res def _generate_lab_samples(self): """Generate laboratory samples based on the analyses in the order""" self.ensure_one() _logger.info(f"Generating laboratory samples for order {self.name}") # Group analyses by sample type sample_groups = self._group_analyses_by_sample_type() if not sample_groups: _logger.warning(f"No analyses with sample types found in order {self.name}") return # Create samples for each group created_samples = self.env['stock.lot'] for sample_type_id, group_data in sample_groups.items(): sample = self._create_sample_for_group(group_data) if sample: created_samples |= sample # Link created samples to the order if created_samples: self.generated_sample_ids = [(6, 0, created_samples.ids)] _logger.info(f"Created {len(created_samples)} samples for order {self.name}") # Post message with created samples sample_list = "" self.message_post( body=_("Muestras generadas automáticamente: %s") % sample_list, message_type='notification' ) def _group_analyses_by_sample_type(self): """Group order lines by required sample type""" groups = {} for line in self.order_line: product = line.product_id # Skip non-analysis products if not product.is_analysis: continue # Check if analysis has a required sample type if not product.required_sample_type_id: _logger.warning( f"Analysis {product.name} has no required sample type defined" ) # Post warning message self.message_post( body=_("Advertencia: El análisis '%s' no tiene tipo de muestra definido") % product.name, message_type='notification' ) continue sample_type = product.required_sample_type_id # Initialize group if not exists if sample_type.id not in groups: groups[sample_type.id] = { 'sample_type': sample_type, 'lines': [], 'total_volume': 0.0, 'analyses': [] } # Add line to group groups[sample_type.id]['lines'].append(line) groups[sample_type.id]['analyses'].append(product.name) groups[sample_type.id]['total_volume'] += (product.sample_volume_ml or 0.0) * line.product_uom_qty return groups def _create_sample_for_group(self, group_data): """Create a single sample for a group of analyses""" try: sample_type = group_data['sample_type'] # Prepare sample values vals = { 'product_id': sample_type.product_variant_id.id, 'patient_id': self.partner_id.id, 'doctor_id': self.doctor_id.id if self.doctor_id else False, 'origin': self.name, 'sample_type_product_id': sample_type.id, 'volume_ml': group_data['total_volume'], 'is_lab_sample': True, 'state': 'pending_collection', 'analysis_names': ', '.join(group_data['analyses'][:3]) + ('...' if len(group_data['analyses']) > 3 else '') } # Create the sample sample = self.env['stock.lot'].create(vals) _logger.info( f"Created sample {sample.name} for {len(group_data['analyses'])} analyses" ) return sample except Exception as e: _logger.error(f"Error creating sample: {str(e)}") raise UserError( _("Error al crear muestra para %s: %s") % (sample_type.name, str(e)) ) def _generate_lab_tests(self): """Generate laboratory tests for analysis order lines""" self.ensure_one() _logger.info(f"Generating laboratory tests for order {self.name}") # Get the test model TestModel = self.env['lims.test'] created_tests = TestModel.browse() # Create a test for each analysis line for line in self.order_line: if not line.product_id.is_analysis: continue # Find appropriate sample for this analysis sample = self._find_sample_for_analysis(line.product_id) if not sample: _logger.warning( f"No sample found for analysis {line.product_id.name} in order {self.name}" ) self.message_post( body=_("Advertencia: No se encontró muestra para el análisis '%s'") % line.product_id.name, message_type='notification' ) continue # Create the test try: test = TestModel.create({ 'sale_order_line_id': line.id, 'sample_id': sample.id, }) created_tests |= test _logger.info(f"Created test {test.name} for analysis {line.product_id.name}") except Exception as e: _logger.error(f"Error creating test for {line.product_id.name}: {str(e)}") self.message_post( body=_("Error al crear prueba para '%s': %s") % (line.product_id.name, str(e)), message_type='notification' ) # Post message with created tests if created_tests: test_list = "" self.message_post( body=_("Pruebas generadas automáticamente: %s") % test_list, message_type='notification' ) _logger.info(f"Created {len(created_tests)} tests for order {self.name}") def _find_sample_for_analysis(self, product): """Find the appropriate sample for an analysis product""" # Check if the analysis has a required sample type if not product.required_sample_type_id: return False # Find a generated sample with matching sample type for sample in self.generated_sample_ids: if sample.sample_type_product_id.id == product.required_sample_type_id.id: return sample return False