129 lines
5.3 KiB
Python
129 lines
5.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
from odoo import api, fields, models
|
|
|
|
class MrpBom(models.Model):
|
|
_inherit = 'mrp.bom'
|
|
|
|
# Tracks the parent BOM record that this branch BOM is synchronized from
|
|
parent_bom_id = fields.Many2one(
|
|
'mrp.bom',
|
|
string='Parent BOM',
|
|
ondelete='cascade',
|
|
index=True,
|
|
copy=False
|
|
)
|
|
|
|
def _is_compatible_with_company(self, company):
|
|
"""
|
|
Check if the BOM and all of its components are compatible with the target company.
|
|
A product is compatible with a target company if:
|
|
1. It is global (no company_id set).
|
|
2. It belongs to the target company.
|
|
3. The target company is a descendant (child) of the product's company (i.e. parent company's product).
|
|
"""
|
|
def is_product_compatible(product):
|
|
if not product.company_id:
|
|
return True
|
|
# Check if the target company is the same or a descendant of the product's company
|
|
child_companies = self.env['res.company'].search([('id', 'child_of', product.company_id.id)])
|
|
return company.id in child_companies.ids
|
|
|
|
# The main product template must be compatible
|
|
if not is_product_compatible(self.product_tmpl_id):
|
|
return False
|
|
|
|
# The product variant must be compatible
|
|
if self.product_id and not is_product_compatible(self.product_id):
|
|
return False
|
|
|
|
# All component products must be compatible
|
|
for line in self.bom_line_ids:
|
|
if not is_product_compatible(line.product_id):
|
|
return False
|
|
return True
|
|
|
|
def _sync_to_branch_companies(self):
|
|
"""Replicate Bill of Materials (BOM) to all active branch companies recursively."""
|
|
if self.env.context.get('skip_company_dependent_sync'):
|
|
return
|
|
|
|
for bom in self:
|
|
# Prevent synchronization loop if this is already a child BOM
|
|
if bom.parent_bom_id:
|
|
continue
|
|
|
|
# Use the BOM's company as the parent company
|
|
parent_company = bom.company_id
|
|
if not parent_company:
|
|
continue
|
|
|
|
branch_companies = self.env['res.company'].search([('id', 'child_of', parent_company.id)]) - parent_company
|
|
if not branch_companies:
|
|
continue
|
|
|
|
for branch in branch_companies:
|
|
# Check compatibility before creating/updating branch BOM
|
|
if not bom._is_compatible_with_company(branch):
|
|
continue
|
|
|
|
# Search if a branch BOM already exists for this parent BOM
|
|
child_bom = self.env['mrp.bom'].with_company(branch).search([
|
|
('parent_bom_id', '=', bom.id),
|
|
('company_id', '=', branch.id)
|
|
], limit=1)
|
|
|
|
bom_vals = {
|
|
'product_tmpl_id': bom.product_tmpl_id.id,
|
|
'product_id': bom.product_id.id if bom.product_id else False,
|
|
'product_qty': bom.product_qty,
|
|
'product_uom_id': bom.product_uom_id.id,
|
|
'type': bom.type,
|
|
'code': bom.code,
|
|
'consumption': bom.consumption,
|
|
'active': bom.active,
|
|
'company_id': branch.id,
|
|
'parent_bom_id': bom.id,
|
|
}
|
|
|
|
# Prep line values
|
|
line_vals_list = []
|
|
for line in bom.bom_line_ids:
|
|
line_vals_list.append((0, 0, {
|
|
'product_id': line.product_id.id,
|
|
'product_qty': line.product_qty,
|
|
'product_uom_id': line.product_uom_id.id,
|
|
'sequence': line.sequence,
|
|
'company_id': branch.id,
|
|
}))
|
|
|
|
if child_bom:
|
|
# Update existing child BOM
|
|
# 1. Clear existing lines
|
|
child_bom.with_context(skip_company_dependent_sync=True).write({
|
|
'bom_line_ids': [(5, 0, 0)]
|
|
})
|
|
# 2. Re-create settings and lines
|
|
bom_vals['bom_line_ids'] = line_vals_list
|
|
child_bom.with_context(skip_company_dependent_sync=True).write(bom_vals)
|
|
else:
|
|
# Create new child BOM
|
|
bom_vals['bom_line_ids'] = line_vals_list
|
|
self.env['mrp.bom'].with_company(branch).with_context(skip_company_dependent_sync=True).create(bom_vals)
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
boms = super().create(vals_list)
|
|
boms._sync_to_branch_companies()
|
|
return boms
|
|
|
|
def write(self, vals):
|
|
res = super().write(vals)
|
|
# Re-sync if relevant BOM or line fields are changed
|
|
sync_fields = [
|
|
'product_tmpl_id', 'product_id', 'product_qty', 'product_uom_id',
|
|
'type', 'code', 'consumption', 'active', 'bom_line_ids'
|
|
]
|
|
if any(f in vals for f in sync_fields):
|
|
self._sync_to_branch_companies()
|
|
return res
|