Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion sale_stock_product_pack/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Sale Stock Product Pack
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:1ff9837bd310e3cad1e8ee98b36bc66392668683cc483192988a177f52078650
!! source digest: sha256:cffa31ed2b6abd5c6c9680e786d70cf4bcd79a34d0ad7f0bc79651300d3f75a3
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
Expand Down Expand Up @@ -75,6 +75,10 @@ Contributors
* Pedro M. Baeza
* Víctor Martínez

* `Trey <https://www.trey.es/>`_:

* Geyson Gualdron

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

Expand Down
5 changes: 4 additions & 1 deletion sale_stock_product_pack/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"author": "Tecnativa, Odoo Community Association (OCA)",
"maintainers": ["chienandalu"],
"license": "AGPL-3",
"depends": ["sale_product_pack", "stock_product_pack"],
"depends": ["stock", "sale_product_pack", "stock_product_pack"],
"data": [
"views/stock_picking_return_views.xml",
],
"installable": True,
}
54 changes: 54 additions & 0 deletions sale_stock_product_pack/i18n/es.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sale_stock_product_pack
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-13 14:07+0000\n"
"PO-Revision-Date: 2025-11-13 15:47+0100\n"
"Last-Translator: \n"
"Language-Team: Trey\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.0.1\n"

#. module: sale_stock_product_pack
#: model:ir.model,name:sale_stock_product_pack.model_stock_move_line
msgid "Product Moves (Stock Move Line)"
msgstr "Movimientos de producto (línea de movimiento de stock)"

#. module: sale_stock_product_pack
#: model:ir.model,name:sale_stock_product_pack.model_sale_order_line
msgid "Sales Order Line"
msgstr "Línea de pedido de venta"

#. module: sale_stock_product_pack
#. odoo-python
#: code:addons/sale_stock_product_pack/models/stock_move.py:0
#, python-format
msgid ""
"You can not change this line because is part of a pack included in this "
"picking."
msgstr ""
"No se puede cambiar esta línea porque forma parte de un paquete incluido en "
"este albarán."

#. module: sale_stock_product_pack
#. odoo-python
#: code:addons/sale_stock_product_pack/models/stock_return_picking_line.py:0
#, python-format
msgid ""
"You can not change this line because is part of a pack included in this "
"picking. Modify the quantity of the pack and its components will be updated "
"automatically."
msgstr ""
"No puede cambiar esta línea porque forma parte de un pack incluido en "
"este albarán. Modifique la cantidad del pack y sus componentes se "
"actualizarán automáticamente."

#~ msgid "Stock Move"
#~ msgstr "Movimiento de stock"
21 changes: 21 additions & 0 deletions sale_stock_product_pack/i18n/sale_stock_product_pack.pot
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-11-13 14:07+0000\n"
"PO-Revision-Date: 2025-11-13 14:07+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
Expand All @@ -22,3 +24,22 @@ msgstr ""
#: model:ir.model,name:sale_stock_product_pack.model_sale_order_line
msgid "Sales Order Line"
msgstr ""

#. module: sale_stock_product_pack
#. odoo-python
#: code:addons/sale_stock_product_pack/models/stock_move.py:0
#, python-format
msgid ""
"You can not change this line because is part of a pack included in this "
"picking."
msgstr ""

#. module: sale_stock_product_pack
#. odoo-python
#: code:addons/sale_stock_product_pack/models/stock_return_picking_line.py:0
#, python-format
msgid ""
"You can not change this line because is part of a pack included in this "
"picking. Modify the quantity of the pack and its components will be updated "
"automatically."
msgstr ""
2 changes: 2 additions & 0 deletions sale_stock_product_pack/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
from . import sale_order
from . import stock_move_line
from . import stock_move
from . import stock_return_picking
35 changes: 35 additions & 0 deletions sale_stock_product_pack/models/stock_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2024 Tecnativa - Víctor Martínez
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from odoo import _, api, models
from odoo.exceptions import UserError


class StockMove(models.Model):
_inherit = "stock.move"

@api.onchange("quantity_done")
def _onchange_quantity_done(self):
if self.sale_line_id.pack_parent_line_id:
self.quantity_done = self._origin.quantity_done
raise UserError(
_(
"You can not change this line because is part of a pack"
" included in this picking."
)
)
if (
self.product_id.pack_ok
and self.product_id.detailed_type == "consu"
and not self.sale_line_id.pack_parent_line_id
):
pack_child_ids = self.product_id.pack_line_ids
move_ids = self.picking_id._origin.move_ids.filtered(
lambda m: m.sale_line_id.pack_parent_line_id == self.sale_line_id
)
for child in pack_child_ids:
child_move = move_ids.filtered(
lambda m: m.product_id == child.product_id
)[0]
if child_move:
child_move.quantity_done = self.quantity_done * child.quantity
30 changes: 30 additions & 0 deletions sale_stock_product_pack/models/stock_return_picking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
###############################################################################
# For copyright and license notices, see __manifest__.py file in root directory
###############################################################################
from odoo import api, models


class StockReturnPicking(models.TransientModel):
_inherit = "stock.return.picking"

@api.onchange("product_return_moves")
def _onchange_product_return_moves(self):
for return_move in self.product_return_moves:
if (
return_move.product_id.pack_ok
and return_move.product_id.detailed_type == "consu"
and not return_move.move_id.sale_line_id.pack_parent_line_id
):
pack_child_ids = return_move.product_id.pack_line_ids
product_return_moves = self.product_return_moves.filtered(
lambda m: m.move_id.sale_line_id.pack_parent_line_id
== return_move.move_id.sale_line_id
)
for child in pack_child_ids:
child_product_return_move = product_return_moves.filtered(
lambda m: m.product_id == child.product_id
)[0]
if child_product_return_move:
child_product_return_move.quantity = (
return_move.quantity * child.quantity
)
4 changes: 4 additions & 0 deletions sale_stock_product_pack/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
* Ernesto Tejeda
* Pedro M. Baeza
* Víctor Martínez

* `Trey <https://www.trey.es/>`_:

* Geyson Gualdron
6 changes: 5 additions & 1 deletion sale_stock_product_pack/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ <h1>Sale Stock Product Pack</h1>
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:1ff9837bd310e3cad1e8ee98b36bc66392668683cc483192988a177f52078650
!! source digest: sha256:cffa31ed2b6abd5c6c9680e786d70cf4bcd79a34d0ad7f0bc79651300d3f75a3
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/product-pack/tree/16.0/sale_stock_product_pack"><img alt="OCA/product-pack" src="https://img.shields.io/badge/github-OCA%2Fproduct--pack-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/product-pack-16-0/product-pack-16-0-sale_stock_product_pack"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/product-pack&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This modules adds compatibility of product packs with sales and stock altogether:</p>
Expand Down Expand Up @@ -425,6 +425,10 @@ <h3><a class="toc-backref" href="#toc-entry-5">Contributors</a></h3>
<li>Víctor Martínez</li>
</ul>
</li>
<li><a class="reference external" href="https://www.trey.es/">Trey</a>:<ul>
<li>Geyson Gualdron</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
Expand Down
80 changes: 80 additions & 0 deletions sale_stock_product_pack/tests/test_sale_stock_product_pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,83 @@ def test_picking_pack_consu(self):
self.assertEqual(
data_names, ["Pack (consumable)", "Component 1", "Component 2"]
)

def test_picking_validate_partial_pack(self):
sale = self._create_sale_order(self.product_pack, 2)
self._create_stock_quant(self.component_1, 2)
self._create_stock_quant(self.component_2, 2)
sale.action_confirm()
picking = sale.picking_ids
picking_pack = picking.move_ids.filtered(
lambda m: m.product_id == self.product_pack
)
component_moves = picking.move_ids.filtered(
lambda m: m.product_id != self.product_pack
)
for component_move in component_moves:
self.assertEqual(component_move.quantity_done, 0)
picking_pack.quantity_done = 1
picking_pack._onchange_quantity_done()
for component_move in component_moves:
self.assertEqual(component_move.quantity_done, 1)
picking.with_context(skip_backorder=True).button_validate()
self.assertEqual(picking.state, "done")
for order_line in sale.order_line:
self.assertEqual(order_line.product_uom_qty, 2)
self.assertEqual(order_line.qty_delivered, 1)

def test_picking_validate_and_partial_return(self):
sale = self._create_sale_order(self.product_pack, 2)
self._create_stock_quant(self.component_1, 2)
self._create_stock_quant(self.component_2, 2)
sale.action_confirm()
picking = sale.picking_ids
picking_pack = picking.move_ids.filtered(
lambda m: m.product_id == self.product_pack
)
component_moves = picking.move_ids.filtered(
lambda m: m.product_id != self.product_pack
)
for component_move in component_moves:
self.assertEqual(component_move.quantity_done, 0)
picking_pack.quantity_done = 2
picking_pack._onchange_quantity_done()
for component_move in component_moves:
self.assertEqual(component_move.quantity_done, 2)
picking.button_validate()
self.assertEqual(picking.state, "done")
for order_line in sale.order_line:
self.assertEqual(order_line.product_uom_qty, 2)
self.assertEqual(order_line.qty_delivered, 2)
return_wizard = self.env["stock.return.picking"].create(
{
"picking_id": picking.id,
}
)
return_wizard._onchange_picking_id()
product_pack_return_move = return_wizard.product_return_moves.filtered(
lambda m: m.product_id == self.product_pack
)
components_return_moves = return_wizard.product_return_moves.filtered(
lambda m: m.product_id != self.product_pack
)
for components_return_move in components_return_moves:
self.assertEqual(components_return_move.quantity, 2)
components_return_move.write(
{
"quantity": 99,
"to_refund": True,
}
)
return_wizard._onchange_product_return_moves()
self.assertIsNot(product_pack_return_move.quantity, 99)
self.assertEqual(product_pack_return_move.quantity, 2)
product_pack_return_move.quantity = 1
return_wizard._onchange_product_return_moves()
for components_return_move in components_return_moves:
self.assertEqual(components_return_move.quantity, 1)
picking_id = return_wizard.create_returns()["res_id"]
picking_return_return = self.env["stock.picking"].browse(picking_id)
moves = picking_return_return.move_ids
for move in moves:
self.assertEqual(move.product_uom_qty, 1)
14 changes: 14 additions & 0 deletions sale_stock_product_pack/views/stock_picking_return_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_stock_return_picking_form" model="ir.ui.view">
<field name="model">stock.return.picking</field>
<field name="inherit_id" ref="stock.view_stock_return_picking_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='product_return_moves']" position="attributes">
<attribute
name="domain"
>[('move_id.sale_line_id.pack_parent_line_id', '=', False)]</attribute>
</xpath>
</field>
</record>
</odoo>
Loading