Skip to content

Commit

Permalink
Merge PR #572 into 16.0
Browse files Browse the repository at this point in the history
Signed-off-by legalsylvain
  • Loading branch information
OCA-git-bot committed Feb 20, 2025
2 parents 0cee3a2 + 353ed6e commit 2e245dc
Show file tree
Hide file tree
Showing 14 changed files with 885 additions and 30 deletions.
80 changes: 50 additions & 30 deletions purchase_sale_stock_inter_company/models/stock_picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,55 @@ class StockPicking(models.Model):

intercompany_picking_id = fields.Many2one(comodel_name="stock.picking")

def _get_product_intercompany_qty_done_dict(self, sale_move_lines, po_move_lines):
product = po_move_lines[0].product_id
qty_done = sum(sale_move_lines.mapped("qty_done"))
res = {product: qty_done}
return res

def _set_intercompany_picking_qty(self, purchase):
po_picks = self.browse()
sale_line_ids = self.move_line_ids.mapped("move_id.sale_line_id")
for sale_line in sale_line_ids:
sale_move_lines = self.move_line_ids.filtered(
lambda ml: ml.move_id.sale_line_id == sale_line
)
po_move_lines = sale_line.auto_purchase_line_id.move_ids.mapped(
"move_line_ids"
)
if not po_move_lines:
raise UserError(
_(
"There's no corresponding line in PO %(po)s for assigning "
"qty from %(pick_name)s for product %(product)s"
)
% (
{
"po": purchase.name,
"pick_name": self.name,
"product": sale_line.product_id.name,
}
)
)
product_qty_done = self._get_product_intercompany_qty_done_dict(
sale_move_lines, po_move_lines
)
for product, qty_done in product_qty_done.items():
product_po_mls = po_move_lines.filtered(
lambda x: x.product_id == product
)
for po_move_line in product_po_mls:
if po_move_line.reserved_qty >= qty_done:
po_move_line.qty_done = qty_done
qty_done = 0.0
else:
po_move_line.qty_done = po_move_line.reserved_qty
qty_done -= po_move_line.reserved_qty
po_picks |= po_move_line.picking_id
if qty_done and product_po_mls:
product_po_mls[-1:].qty_done += qty_done
return po_picks

def _action_done(self):
# Only DropShip pickings
po_picks = self.browse()
Expand All @@ -21,36 +70,7 @@ def _action_done(self):
if not purchase:
continue
purchase.picking_ids.write({"intercompany_picking_id": pick.id})
for move_line in pick.move_line_ids:
qty_done = move_line.qty_done
sale_line_id = move_line.move_id.sale_line_id
po_move_lines = sale_line_id.auto_purchase_line_id.move_ids.mapped(
"move_line_ids"
)
for po_move_line in po_move_lines:
if po_move_line.reserved_qty >= qty_done:
po_move_line.qty_done = qty_done
qty_done = 0.0
else:
po_move_line.qty_done = po_move_line.reserved_qty
qty_done -= po_move_line.reserved_qty
po_picks |= po_move_line.picking_id
if qty_done and po_move_lines:
po_move_lines[-1:].qty_done += qty_done
elif not po_move_lines:
raise UserError(
_(
"There's no corresponding line in PO %(po)s for assigning "
"qty from %(pick_name)s for product %(product)s"
)
% (
{
"po": purchase.name,
"pick_name": pick.name,
"product": move_line.product_id.name,
}
)
)
po_picks |= pick._set_intercompany_picking_qty(purchase)
# Transfer dropship pickings
for po_pick in po_picks.sudo():
po_pick.with_company(po_pick.company_id.id)._action_done()
Expand Down
81 changes: 81 additions & 0 deletions purchase_sale_stock_inter_company_mrp/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
========================================================
Inter Company Module for Purchase to Sale Order with MRP
========================================================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:06f08f9f770c6fa70a6c80e1adbff5eea334313523e5def11b963a7c74715a31
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmulti--company-lightgray.png?logo=github
:target: https://github.com/OCA/multi-company/tree/16.0/purchase_sale_stock_inter_company_mrp
:alt: OCA/multi-company
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/multi-company-16-0/multi-company-16-0-purchase_sale_stock_inter_company_mrp
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/multi-company&target_branch=16.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module considers the use of Bill of Materials Kits when processing intercompany transfers.

This module is a glue module and is auto installed if `purchase_sale_stock_inter_company` and `mrp` modules are installed.
Full purpose description can be found in `purchase_sale_inter_company`.

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/multi-company/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/multi-company/issues/new?body=module:%20purchase_sale_stock_inter_company_mrp%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
~~~~~~~

* ForgeFlow

Contributors
~~~~~~~~~~~~

* `ForgeFlow <https://www.forgeflow.com>`:

* Jordi Masvidal

Maintainers
~~~~~~~~~~~

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/multi-company <https://github.com/OCA/multi-company/tree/16.0/purchase_sale_stock_inter_company_mrp>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions purchase_sale_stock_inter_company_mrp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
16 changes: 16 additions & 0 deletions purchase_sale_stock_inter_company_mrp/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2024 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Inter Company Module for Purchase to Sale Order with MRP",
"summary": "Intercompany PO/SO rules with MRP",
"version": "16.0.1.0.0",
"category": "Purchase Management",
"website": "https://github.com/OCA/multi-company",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"license": "AGPL-3",
"installable": True,
"auto_install": True,
"depends": ["purchase_sale_stock_inter_company", "mrp"],
"data": [],
}
1 change: 1 addition & 0 deletions purchase_sale_stock_inter_company_mrp/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import stock_picking
92 changes: 92 additions & 0 deletions purchase_sale_stock_inter_company_mrp/models/stock_picking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Copyright 2024 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import models
from odoo.tools import float_is_zero, float_round


class StockPicking(models.Model):
_inherit = "stock.picking"

def _get_product_intercompany_qty_done_dict(self, sale_move_lines, po_move_lines):
sale_bom = sale_move_lines[0].move_id.bom_line_id.bom_id
purchase_bom = po_move_lines[0].move_id.bom_line_id.bom_id
if not sale_bom and not purchase_bom:
return super()._get_product_intercompany_qty_done_dict(
sale_move_lines, po_move_lines
)
res = {}
product = po_move_lines[0].product_id
sale_qty_done = sum(sale_move_lines.mapped("qty_done"))
# Sale Kit: get kit product qty done based on the move lines qty done
if sale_bom:
sale_line = sale_move_lines[0].move_id.sale_line_id
order_qty = sale_line.product_uom._compute_quantity(
sale_line.product_uom_qty, sale_bom.product_uom_id
)
kit_qty = self._compute_kit_quantities_done(
sale_move_lines.mapped("move_id"),
sale_line.product_id,
order_qty,
sale_bom,
)
sale_qty_done = sale_bom.product_uom_id._compute_quantity(
kit_qty, sale_line.product_id.uom_id
)
# Purchase Kit: get components qty done based on the sale qty done
if purchase_bom:
_, bom_sub_lines = purchase_bom.explode(product, sale_qty_done)
for bom_line, bom_line_data in bom_sub_lines:
res[bom_line.product_id] = bom_line_data["qty"]
return res
res[product] = sale_qty_done
return res

def _compute_kit_quantities_done(self, move_ids, product_id, kit_qty, kit_bom):
"""Based on Odoo standard _compute_kit_quantities method.
We use the quantity_done of the moves instead of the product_qty.
"""
qty_ratios = []
boms, bom_sub_lines = kit_bom.explode(product_id, kit_qty)
for bom_line, bom_line_data in bom_sub_lines:
# skip service since we never deliver them
if bom_line.product_id.type == "service":
continue
if float_is_zero(
bom_line_data["qty"],
precision_rounding=bom_line.product_uom_id.rounding,
):
# As BoMs allow components with 0 qty, a.k.a. optionnal components,
# we simply skip those to avoid a division by zero.
continue
bom_line_moves = move_ids.filtered(lambda m: m.bom_line_id == bom_line)
if bom_line_moves:
# We compute the quantities needed of each components to make one kit.
# Then, we collect every relevant moves related to a specific component
# to know how many are considered delivered.
uom_qty_per_kit = bom_line_data["qty"] / bom_line_data["original_qty"]
qty_per_kit = bom_line.product_uom_id._compute_quantity(
uom_qty_per_kit, bom_line.product_id.uom_id, round=False
)
if not qty_per_kit:
continue
# Use quantity_done to get the qty_processed of each component
qty_processed = sum(bom_line_moves.mapped("quantity_done"))
# We compute a ratio to know how many kits we can produce with this
# quantity of that specific component
qty_ratios.append(
float_round(
qty_processed / qty_per_kit,
precision_rounding=bom_line.product_id.uom_id.rounding,
)
)
else:
return 0.0
if qty_ratios:
# Now that we have every ratio by components, we keep the lowest one to
# know how many kits we can produce with the quantities delivered of each
# component. We use the floor division here because a 'partial kit'
# doesn't make sense.
return min(qty_ratios) // 1
else:
return 0.0
3 changes: 3 additions & 0 deletions purchase_sale_stock_inter_company_mrp/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* `ForgeFlow <https://www.forgeflow.com>`:

* Jordi Masvidal
4 changes: 4 additions & 0 deletions purchase_sale_stock_inter_company_mrp/readme/DESCRIPTION.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This module considers the use of Bill of Materials Kits when processing intercompany transfers.

This module is a glue module and is auto installed if `purchase_sale_stock_inter_company` and `mrp` modules are installed.
Full purpose description can be found in `purchase_sale_inter_company`.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 2e245dc

Please sign in to comment.