odoo18/addons/website_sale_mrp/models/sale_order.py

61 lines
3.5 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from odoo import models
from odoo.tools import float_is_zero
class SaleOrder(models.Model):
_inherit = 'sale.order'
def _get_unavailable_quantity_from_kits(self, product):
"""
If any line of the order refers to a kit product, the availability of the product
might be impacted (if the product is a kit or a component of one).
This method computes the quantity that becomes unavailable for the product because
of the order lines that do not refer to it directly.
:param ProductProduct product: the product for which the unavailability is computed.
:param float free_qty: the free_qty of the product
"""
self.ensure_one()
unavailable_qty = 0
if product.is_kits:
# Explode the kit to fetch the set of relevant components to track.
kit_bom = self.env['mrp.bom'].sudo()._bom_find(product, company_id=self.company_id.id, bom_type='phantom')[product]
_, bom_sub_lines = kit_bom.explode(product, quantity=1.0)
unavailable_component_qties = {}
qty_per_kit = defaultdict(float)
for bom_line, bom_line_data in bom_sub_lines:
if not bom_line.product_id.is_storable:
# Relevant only for storable components.
continue
if float_is_zero(bom_line_data['qty'], precision_rounding=bom_line.product_uom_id.rounding):
# As BoMs allow components with a quantity of 0 (i.e., optional components), we
# skip those to avoid a division by zero.
continue
component = bom_line.product_id
unavailable_component_qties[component] = sum(self.order_line.filtered(lambda sol: sol.product_id == component).mapped('product_uom_qty'))
uom_qty_per_kit = bom_line_data['qty'] / bom_line_data['original_qty']
qty_per_kit[component] += bom_line.product_uom_id._compute_quantity(uom_qty_per_kit / kit_bom.product_qty, component.uom_id, round=False)
for line in self.order_line.filtered(lambda sol: sol.product_id.is_kits and sol.product_id != product):
# Other kit lines might influence the availability of the product.
line_kit_bom = self.env['mrp.bom'].sudo()._bom_find(line.product_id, company_id=self.company_id.id, bom_type='phantom')[line.product_id]
component_qties = line._get_bom_component_qty(line_kit_bom)
unavailable_qty += component_qties.get(product.id, {}).get('qty', 0) * line.product_uom_qty / line_kit_bom.product_qty
if product.is_kits:
# If the product is a kit, the availability of its components can be influenced by other kits.
for component, _ in unavailable_component_qties.items():
unavailable_component_qties[component] += component_qties.get(component.id, {}).get('qty', 0) * line.product_uom_qty / line_kit_bom.product_qty
if product.is_kits:
# If the product is a kit, recompute availability based on the availability of its components.
max_free_kit_qty = free_qty = product.sudo().free_qty
for component, unavailable_component_qty in unavailable_component_qties.items():
max_free_kit_qty = min(max_free_kit_qty, (component.free_qty - unavailable_component_qty) // qty_per_kit[component])
unavailable_qty += free_qty - max_free_kit_qty
return unavailable_qty