mrp_report_bom_structure.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. # -*- coding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. from odoo import api, models, _, fields
  4. class ReportBomStructure(models.AbstractModel):
  5. _inherit = 'report.mrp.report_bom_structure'
  6. def _get_subcontracting_line(self, bom, seller, level, bom_quantity):
  7. ratio_uom_seller = seller.product_uom.ratio / bom.product_uom_id.ratio
  8. price = seller.currency_id._convert(seller.price, self.env.company.currency_id, (bom.company_id or self.env.company), fields.Date.today())
  9. return {
  10. 'name': seller.partner_id.display_name,
  11. 'partner_id': seller.partner_id.id,
  12. 'quantity': bom_quantity,
  13. 'uom': bom.product_uom_id.name,
  14. 'prod_cost': price / ratio_uom_seller * bom_quantity,
  15. 'bom_cost': price / ratio_uom_seller * bom_quantity,
  16. 'level': level or 0
  17. }
  18. def _get_bom_data(self, bom, warehouse, product=False, line_qty=False, bom_line=False, level=0, parent_bom=False, index=0, product_info=False, ignore_stock=False):
  19. res = super()._get_bom_data(bom, warehouse, product, line_qty, bom_line, level, parent_bom, index, product_info, ignore_stock)
  20. if bom.type == 'subcontract' and not self.env.context.get('minimized', False):
  21. seller = res['product']._select_seller(quantity=res['quantity'], uom_id=bom.product_uom_id, params={'subcontractor_ids': bom.subcontractor_ids})
  22. if seller:
  23. res['subcontracting'] = self._get_subcontracting_line(bom, seller, level + 1, res['quantity'])
  24. if not self.env.context.get('minimized', False):
  25. res['bom_cost'] += res['subcontracting']['bom_cost']
  26. return res
  27. def _get_bom_array_lines(self, data, level, unfolded_ids, unfolded, parent_unfolded):
  28. lines = super()._get_bom_array_lines(data, level, unfolded_ids, unfolded, parent_unfolded)
  29. if data.get('subcontracting'):
  30. subcontract_info = data['subcontracting']
  31. lines.append({
  32. 'name': _("Subcontracting: %s", subcontract_info['name']),
  33. 'type': 'subcontract',
  34. 'uom': False,
  35. 'quantity': subcontract_info['quantity'],
  36. 'bom_cost': subcontract_info['bom_cost'],
  37. 'prod_cost': subcontract_info['prod_cost'],
  38. 'level': subcontract_info['level'],
  39. 'visible': level == 1 or unfolded or parent_unfolded
  40. })
  41. return lines
  42. @api.model
  43. def _need_special_rules(self, product_info, parent_bom=False, parent_product_id=False):
  44. if parent_bom and parent_product_id:
  45. parent_info = product_info.get(parent_product_id, {}).get(parent_bom.id, {})
  46. return parent_info and parent_info.get('route_type') == 'subcontract'
  47. return super()._need_special_rules(product_info, parent_bom, parent_product_id)
  48. @api.model
  49. def _find_special_rules(self, product, product_info, parent_bom=False, parent_product_id=False):
  50. res = super()._find_special_rules(product, product_info, parent_bom, parent_product_id)
  51. # If no rules could be found within the warehouse, check if the product is a component from a subcontracted product.
  52. parent_info = product_info.get(parent_product_id, {}).get(parent_bom.id, {})
  53. if parent_info and parent_info.get('route_type') == 'subcontract':
  54. # Since the product is subcontracted, check the subcontracted location for rules instead of the warehouse.
  55. subcontracting_loc = parent_info['supplier'].partner_id.property_stock_subcontractor
  56. return product._get_rules_from_location(subcontracting_loc)
  57. return res
  58. @api.model
  59. def _format_route_info(self, rules, rules_delay, warehouse, product, bom, quantity):
  60. res = super()._format_route_info(rules, rules_delay, warehouse, product, bom, quantity)
  61. subcontract_rules = [rule for rule in rules if rule.action == 'buy' and bom and bom.type == 'subcontract']
  62. if subcontract_rules:
  63. supplier = product._select_seller(quantity=quantity, uom_id=product.uom_id, params={'subcontractor_ids': bom.subcontractor_ids})
  64. if supplier:
  65. return {
  66. 'route_type': 'subcontract',
  67. 'route_name': subcontract_rules[0].route_id.display_name,
  68. 'route_detail': supplier.display_name,
  69. 'lead_time': supplier.delay + rules_delay,
  70. 'supplier_delay': supplier.delay + rules_delay,
  71. 'manufacture_delay': product.produce_delay,
  72. 'supplier': supplier,
  73. }
  74. return res
  75. @api.model
  76. def _get_quantities_info(self, product, bom_uom, parent_bom, product_info):
  77. if parent_bom and parent_bom.type == 'subcontract' and product.detailed_type == 'product':
  78. parent_product_id = self.env.context.get('parent_product_id', False)
  79. route_info = product_info.get(parent_product_id, {}).get(parent_bom.id, {})
  80. if route_info and route_info['route_type'] == 'subcontract':
  81. subcontracting_loc = route_info['supplier'].partner_id.property_stock_subcontractor
  82. subloc_product = product.with_context(location=subcontracting_loc.id, warehouse=False).read(['free_qty', 'qty_available'])[0]
  83. stock_loc = f"subcontract_{subcontracting_loc.id}"
  84. if not product_info[product.id]['consumptions'].get(stock_loc, False):
  85. product_info[product.id]['consumptions'][stock_loc] = 0
  86. return {
  87. 'free_qty': product.uom_id._compute_quantity(subloc_product['free_qty'], bom_uom),
  88. 'on_hand_qty': product.uom_id._compute_quantity(subloc_product['qty_available'], bom_uom),
  89. 'stock_loc': stock_loc,
  90. }
  91. return super()._get_quantities_info(product, bom_uom, parent_bom, product_info)
  92. @api.model
  93. def _get_resupply_availability(self, route_info, components):
  94. if route_info.get('route_type') == 'subcontract':
  95. max_component_delay = self._get_max_component_delay(components)
  96. if max_component_delay is False:
  97. return ('unavailable', False)
  98. produce_delay = route_info.get('manufacture_delay', 0) + max_component_delay
  99. supplier_delay = route_info.get('supplier_delay', 0)
  100. return ('estimated', max(produce_delay, supplier_delay))
  101. return super()._get_resupply_availability(route_info, components)