|
|
@@ -109,6 +109,18 @@ class SaleOrderTemplate(models.Model):
|
|
|
if rec.date_start and rec.date_end and rec.date_end <= rec.date_start:
|
|
|
raise ValidationError(_('La fecha de fin debe ser mayor a la fecha de inicio.'))
|
|
|
|
|
|
+ @api.constrains('company_id', 'sale_order_template_line_ids', 'sale_order_template_option_ids')
|
|
|
+ def _check_company_id(self):
|
|
|
+ """
|
|
|
+ Sobrescribe la validación del modelo padre para deshabilitarla cuando use_contract_partner está habilitado.
|
|
|
+ """
|
|
|
+ # Filtrar solo los registros que NO tienen use_contract_partner habilitado
|
|
|
+ templates_to_validate = self.filtered(lambda t: not t.use_contract_partner)
|
|
|
+
|
|
|
+ # Llamar al método padre solo para los registros que necesitan validación
|
|
|
+ if templates_to_validate:
|
|
|
+ super(SaleOrderTemplate, templates_to_validate)._check_company_id()
|
|
|
+
|
|
|
@api.constrains('sale_order_template_line_ids')
|
|
|
def _check_employee_required_for_duplicate_partner_project(self):
|
|
|
for rec in self:
|
|
|
@@ -270,15 +282,22 @@ class SaleOrderTemplate(models.Model):
|
|
|
line.product_uom_qty for line in self.sale_order_template_line_ids
|
|
|
if line.project_id and line.project_id.id == project.id and hasattr(line, 'product_uom_qty')
|
|
|
)
|
|
|
+
|
|
|
+ # Determinar el nombre del proyecto
|
|
|
+ project_name = project.name
|
|
|
+ if self.opportunity_id and self.opportunity_id.name:
|
|
|
+ # Si hay oportunidad, usar su nombre para el proyecto
|
|
|
+ project_name = self.opportunity_id.name
|
|
|
+
|
|
|
# Buscar si ya existe un proyecto mensual para este pedido y mes
|
|
|
domain = [
|
|
|
- ('name', '=', f"{project.name} - {order_datetime.strftime('%Y-%m')}")
|
|
|
+ ('name', '=', f"{project_name} - {order_datetime.strftime('%Y-%m')}")
|
|
|
]
|
|
|
if sale_order:
|
|
|
domain.append(('reinvoiced_sale_order_id', '=', sale_order.id))
|
|
|
project_copy = self.env['project.project'].search(domain, limit=1)
|
|
|
vals = {
|
|
|
- 'name': f"{project.name} - {order_datetime.strftime('%Y-%m')}",
|
|
|
+ 'name': f"{project_name} - {order_datetime.strftime('%Y-%m')}",
|
|
|
'date_start': date_start,
|
|
|
'date': date_end,
|
|
|
'allocated_hours': allocated_hours,
|
|
|
@@ -289,6 +308,7 @@ class SaleOrderTemplate(models.Model):
|
|
|
first_line = next((l for l in self.sale_order_template_line_ids if l.project_id and l.project_id.id == project.id and l.contract_partner_id), None)
|
|
|
if first_line:
|
|
|
vals['allow_billable'] = True
|
|
|
+ # Usar siempre el contract_partner_id para proyectos
|
|
|
vals['partner_id'] = self.contract_partner_id.id
|
|
|
# No asignar account_id aquí, se generará automáticamente al crear el proyecto
|
|
|
if sale_order:
|
|
|
@@ -300,6 +320,7 @@ class SaleOrderTemplate(models.Model):
|
|
|
project_copy.write({'sale_line_id': False})
|
|
|
# Actualizar el partner de la cuenta analítica después de crear el proyecto
|
|
|
if project_copy.account_id and first_line:
|
|
|
+ # Usar siempre el contract_partner_id para cuentas analíticas
|
|
|
self._update_analytic_account_partner(project_copy.account_id, first_line.contract_partner_id.id)
|
|
|
else:
|
|
|
# Actualizar el proyecto mensual existente con los valores actuales de la plantilla
|
|
|
@@ -307,6 +328,7 @@ class SaleOrderTemplate(models.Model):
|
|
|
project_copy.write({'sale_line_id': False})
|
|
|
# Actualizar el partner de la cuenta analítica
|
|
|
if project_copy.account_id and first_line:
|
|
|
+ # Usar siempre el contract_partner_id para cuentas analíticas
|
|
|
self._update_analytic_account_partner(project_copy.account_id, first_line.contract_partner_id.id)
|
|
|
project_map[project.id] = project_copy
|
|
|
return project_map
|
|
|
@@ -324,9 +346,15 @@ class SaleOrderTemplate(models.Model):
|
|
|
project_map = {}
|
|
|
unique_projects = {line.project_id for line in self.sale_order_template_line_ids if line.project_id}
|
|
|
for project in unique_projects:
|
|
|
+ # Determinar el nombre del proyecto
|
|
|
+ project_name = project.name
|
|
|
+ if self.opportunity_id and self.opportunity_id.name:
|
|
|
+ # Si hay oportunidad, usar su nombre para el proyecto
|
|
|
+ project_name = self.opportunity_id.name
|
|
|
+
|
|
|
# Buscar si ya existe una copia para este pedido, nombre y fechas
|
|
|
domain = [
|
|
|
- ('name', '=', project.name),
|
|
|
+ ('name', '=', project_name),
|
|
|
('date_start', '=', self.date_start),
|
|
|
('date', '=', self.date_end),
|
|
|
]
|
|
|
@@ -334,7 +362,7 @@ class SaleOrderTemplate(models.Model):
|
|
|
domain.append(('reinvoiced_sale_order_id', '=', sale_order.id))
|
|
|
project_copy = self.env['project.project'].search(domain, limit=1)
|
|
|
vals = {
|
|
|
- 'name': project.name,
|
|
|
+ 'name': project_name,
|
|
|
'date_start': self.date_start,
|
|
|
'date': self.date_end,
|
|
|
'sale_line_id': False
|
|
|
@@ -343,6 +371,7 @@ class SaleOrderTemplate(models.Model):
|
|
|
first_line = next((l for l in self.sale_order_template_line_ids if l.project_id and l.project_id.id == project.id and l.contract_partner_id), None)
|
|
|
if first_line:
|
|
|
vals['allow_billable'] = True
|
|
|
+ # Usar siempre el contract_partner_id para proyectos
|
|
|
vals['partner_id'] = self.contract_partner_id.id
|
|
|
# No asignar account_id aquí, se generará automáticamente al crear el proyecto
|
|
|
if sale_order:
|
|
|
@@ -353,12 +382,14 @@ class SaleOrderTemplate(models.Model):
|
|
|
project_copy.write({'sale_line_id': False})
|
|
|
# Actualizar el partner de la cuenta analítica después de crear el proyecto
|
|
|
if project_copy.account_id and first_line:
|
|
|
+ # Usar siempre el contract_partner_id para cuentas analíticas
|
|
|
self._update_analytic_account_partner(project_copy.account_id, first_line.contract_partner_id.id)
|
|
|
else:
|
|
|
project_copy.write(vals)
|
|
|
project_copy.write({'sale_line_id': False})
|
|
|
# Actualizar el partner de la cuenta analítica
|
|
|
if project_copy.account_id and first_line:
|
|
|
+ # Usar siempre el contract_partner_id para cuentas analíticas
|
|
|
self._update_analytic_account_partner(project_copy.account_id, first_line.contract_partner_id.id)
|
|
|
project_map[project.id] = project_copy
|
|
|
return project_map
|
|
|
@@ -401,6 +432,7 @@ class SaleOrderTemplate(models.Model):
|
|
|
# Buscar un pedido existente para este partner, plantilla y mes/año.
|
|
|
# Usar sale_order_template_id es clave para identificar unívocamente el pedido
|
|
|
# y evitar duplicados, sin importar el estado del pedido.
|
|
|
+ # Usar siempre el contract_partner_id para búsqueda de pedidos existentes
|
|
|
domain = [
|
|
|
('partner_id', '=', self.contract_partner_id.id),
|
|
|
('sale_order_template_id', '=', self.id),
|
|
|
@@ -532,15 +564,24 @@ class SaleOrderTemplate(models.Model):
|
|
|
update_vals['payment_term_id'] = self.payment_term_id.id
|
|
|
if self.opportunity_id and existing_order.opportunity_id != self.opportunity_id:
|
|
|
update_vals['opportunity_id'] = self.opportunity_id.id
|
|
|
+
|
|
|
+ # Actualizar el vendedor si hay oportunidad
|
|
|
+ if self.opportunity_id and self.opportunity_id.user_id:
|
|
|
+ user_id = self.opportunity_id.user_id.id
|
|
|
+ if existing_order.user_id.id != user_id:
|
|
|
+ update_vals['user_id'] = user_id
|
|
|
+
|
|
|
existing_order.write(update_vals)
|
|
|
|
|
|
# Procesar orden y proyectos
|
|
|
self._process_order_and_projects(existing_order, project_map)
|
|
|
else:
|
|
|
# Crear nuevo pedido
|
|
|
- pricelist = self.contract_partner_id.property_product_pricelist
|
|
|
+ # Usar siempre el contract_partner_id para el cliente del pedido
|
|
|
+ partner = self.contract_partner_id
|
|
|
+ pricelist = partner.property_product_pricelist
|
|
|
# Priorizar término de pago de la plantilla, si no, el del partner
|
|
|
- payment_term = self.payment_term_id or self.contract_partner_id.property_payment_term_id
|
|
|
+ payment_term = self.payment_term_id or partner.property_payment_term_id
|
|
|
|
|
|
# Configurar proyectos antes de crear líneas
|
|
|
for line in self.sale_order_template_line_ids:
|
|
|
@@ -560,14 +601,20 @@ class SaleOrderTemplate(models.Model):
|
|
|
vals['analytic_distribution'] = {project_month.account_id.id: 100}
|
|
|
order_lines_vals.append(vals)
|
|
|
|
|
|
+ # Determinar el vendedor: usar opportunity_id.user_id si existe
|
|
|
+ user_id = False
|
|
|
+ if self.opportunity_id and self.opportunity_id.user_id:
|
|
|
+ user_id = self.opportunity_id.user_id.id
|
|
|
+
|
|
|
order_vals = {
|
|
|
- 'partner_id': self.contract_partner_id.id,
|
|
|
+ 'partner_id': partner.id,
|
|
|
'sale_order_template_id': self.id,
|
|
|
'date_order': order_datetime,
|
|
|
'pricelist_id': pricelist.id if pricelist else False,
|
|
|
'payment_term_id': payment_term.id if payment_term else False,
|
|
|
'company_id': (self.company_id or self.env.company).id,
|
|
|
'opportunity_id': self.opportunity_id.id if self.opportunity_id else False,
|
|
|
+ 'user_id': user_id,
|
|
|
'order_line': [Command.create(vals) for vals in order_lines_vals]
|
|
|
}
|
|
|
new_order = SaleOrder.create(order_vals)
|