mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
@@ -7,7 +7,7 @@ Stock Packaging Qty
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:f762cbab1bc1e42ef6e66f92bbd7557223f9c6faa21797ac22edb0e40fedad71
|
||||
!! source digest: sha256:07cdace5e7277b3685db9d74679a278ea321ae54914c9c32bc9be33a179b7947
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
||||
@@ -85,10 +85,13 @@ promote its widespread use.
|
||||
.. |maintainer-yajo| image:: https://github.com/yajo.png?size=40px
|
||||
:target: https://github.com/yajo
|
||||
:alt: yajo
|
||||
.. |maintainer-EmilioPascual| image:: https://github.com/EmilioPascual.png?size=40px
|
||||
:target: https://github.com/EmilioPascual
|
||||
:alt: EmilioPascual
|
||||
|
||||
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|
||||
Current `maintainers <https://odoo-community.org/page/maintainer-role>`__:
|
||||
|
||||
|maintainer-yajo|
|
||||
|maintainer-yajo| |maintainer-EmilioPascual|
|
||||
|
||||
This module is part of the `OCA/stock-logistics-warehouse <https://github.com/OCA/stock-logistics-warehouse/tree/16.0/stock_move_packaging_qty>`_ project on GitHub.
|
||||
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
],
|
||||
"license": "LGPL-3",
|
||||
"installable": True,
|
||||
"maintainers": ["yajo"],
|
||||
"maintainers": ["yajo", "EmilioPascual"],
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Copyright 2021 ForgeFlow, S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
|
||||
from odoo import _, api, exceptions, fields, models
|
||||
from odoo.tools import float_compare
|
||||
from odoo.tools import float_compare, float_round
|
||||
|
||||
|
||||
class StockMove(models.Model):
|
||||
@@ -111,12 +111,7 @@ class StockMove(models.Model):
|
||||
if not line.product_packaging_id:
|
||||
line.product_packaging_qty_done = 0
|
||||
continue
|
||||
line.product_packaging_qty_done = (
|
||||
line.product_packaging_id._check_qty(
|
||||
line.qty_done, line.product_uom_id, "DOWN"
|
||||
)
|
||||
/ line.product_packaging_id.qty
|
||||
)
|
||||
line.product_packaging_qty_done = line.product_packaging_qty_reserved
|
||||
return result
|
||||
|
||||
def _clear_quantities_to_zero(self):
|
||||
@@ -127,3 +122,63 @@ class StockMove(models.Model):
|
||||
continue
|
||||
line.product_packaging_qty_done = 0
|
||||
return result
|
||||
|
||||
def _action_assign(self, force_qty=False):
|
||||
"""Set the packaging qty reserved when assigning."""
|
||||
res = super()._action_assign(force_qty=force_qty)
|
||||
moves_to_assign = self
|
||||
move_lines_origin = self.env["stock.move.line"].read_group(
|
||||
[
|
||||
("move_id", "in", moves_to_assign.mapped("move_orig_ids").ids),
|
||||
(
|
||||
"move_id.state",
|
||||
"not in",
|
||||
("draft", "waiting", "confirmed", "cancel"),
|
||||
),
|
||||
("move_id.product_packaging_id", "!=", False),
|
||||
],
|
||||
["qty_done", "product_packaging_qty_done"],
|
||||
["move_id", "location_dest_id", "lot_id", "result_package_id", "owner_id"],
|
||||
lazy=False,
|
||||
)
|
||||
move_lines_origin_dict = {
|
||||
(
|
||||
ml.get("location_dest_id")[0] if ml.get("location_dest_id") else False,
|
||||
ml.get("lot_id")[0] if ml.get("lot_id") else False,
|
||||
ml.get("result_package_id")[0]
|
||||
if ml.get("result_package_id")
|
||||
else False,
|
||||
ml.get("owner_id")[0] if ml.get("owner_id") else False,
|
||||
): {
|
||||
"qty_done": ml.get("qty_done"),
|
||||
"product_packaging_qty_done": ml.get("product_packaging_qty_done"),
|
||||
}
|
||||
for ml in move_lines_origin
|
||||
}
|
||||
for line in moves_to_assign.move_line_ids.filtered("product_packaging_id"):
|
||||
found_move_lines_origin = move_lines_origin_dict.get(
|
||||
(
|
||||
line.location_id.id,
|
||||
line.lot_id.id,
|
||||
line.package_id.id,
|
||||
line.owner_id.id,
|
||||
),
|
||||
{},
|
||||
)
|
||||
if found_move_lines_origin and found_move_lines_origin.get(
|
||||
"qty_done"
|
||||
) == round(
|
||||
line.reserved_uom_qty,
|
||||
self.env["decimal.precision"].precision_get("Product Unit of Measure"),
|
||||
):
|
||||
line.product_packaging_qty_reserved = found_move_lines_origin.get(
|
||||
"product_packaging_qty_done"
|
||||
)
|
||||
elif not line.product_packaging_id.qty:
|
||||
continue
|
||||
else:
|
||||
line.product_packaging_qty_reserved = float_round(
|
||||
line.reserved_qty / line.product_packaging_id.qty,
|
||||
precision_rounding=line.product_packaging_id.product_uom_id.rounding,
|
||||
)
|
||||
return res
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Copyright 2024 Moduon Team S.L.
|
||||
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0)
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import float_round
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class StockMoveLine(models.Model):
|
||||
@@ -13,26 +12,12 @@ class StockMoveLine(models.Model):
|
||||
product_packaging_qty_reserved = fields.Float(
|
||||
string="Reserved Pkg. Qty.",
|
||||
help="Product packaging quantity reserved.",
|
||||
compute="_compute_product_packaging_qty_reserved",
|
||||
store=True,
|
||||
)
|
||||
product_packaging_qty_done = fields.Float(
|
||||
string="Done Pkg. Qty.",
|
||||
help="Product packaging quantity done.",
|
||||
)
|
||||
|
||||
@api.depends("product_packaging_id", "reserved_qty")
|
||||
def _compute_product_packaging_qty_reserved(self):
|
||||
"""Get the quantity done in product packaging."""
|
||||
self.product_packaging_qty_reserved = False
|
||||
for line in self:
|
||||
if not line.product_packaging_id.qty:
|
||||
continue
|
||||
line.product_packaging_qty_reserved = float_round(
|
||||
line.reserved_qty / line.product_packaging_id.qty,
|
||||
precision_rounding=line.product_packaging_id.product_uom_id.rounding,
|
||||
)
|
||||
|
||||
def _get_aggregated_properties(self, move_line=False, move=False):
|
||||
"""Aggregate by product packaging too."""
|
||||
result = super()._get_aggregated_properties(move_line, move)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
@@ -366,7 +367,7 @@ ul.auto-toc {
|
||||
!! This file is generated by oca-gen-addon-readme !!
|
||||
!! changes will be overwritten. !!
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
!! source digest: sha256:f762cbab1bc1e42ef6e66f92bbd7557223f9c6faa21797ac22edb0e40fedad71
|
||||
!! source digest: sha256:07cdace5e7277b3685db9d74679a278ea321ae54914c9c32bc9be33a179b7947
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
|
||||
<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/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/stock-logistics-warehouse/tree/16.0/stock_move_packaging_qty"><img alt="OCA/stock-logistics-warehouse" src="https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/stock-logistics-warehouse-16-0/stock-logistics-warehouse-16-0-stock_move_packaging_qty"><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/stock-logistics-warehouse&target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
|
||||
<p>Add packaging fields in the stock moves, their lines and their reports.</p>
|
||||
@@ -424,8 +425,8 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
|
||||
<p>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.</p>
|
||||
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
|
||||
<p><a class="reference external image-reference" href="https://github.com/yajo"><img alt="yajo" src="https://github.com/yajo.png?size=40px" /></a></p>
|
||||
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainers</a>:</p>
|
||||
<p><a class="reference external image-reference" href="https://github.com/yajo"><img alt="yajo" src="https://github.com/yajo.png?size=40px" /></a> <a class="reference external image-reference" href="https://github.com/EmilioPascual"><img alt="EmilioPascual" src="https://github.com/EmilioPascual.png?size=40px" /></a></p>
|
||||
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/tree/16.0/stock_move_packaging_qty">OCA/stock-logistics-warehouse</a> project on GitHub.</p>
|
||||
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
|
||||
</div>
|
||||
|
||||
@@ -33,6 +33,9 @@ class TestStockMovePackagingQty(TransactionCase):
|
||||
)
|
||||
cls.customer_location = cls.env.ref("stock.stock_location_customers")
|
||||
cls.supplier_location = cls.env.ref("stock.stock_location_suppliers")
|
||||
cls.picking_type_out = cls.env.ref("stock.picking_type_out")
|
||||
cls.pack_location = cls.env.ref("stock.location_pack_zone")
|
||||
cls.stock_location = cls.env.ref("stock.stock_location_stock")
|
||||
|
||||
def test_product_packaging_qty(self):
|
||||
move = self.env["stock.move"].create(
|
||||
@@ -57,7 +60,7 @@ class TestStockMovePackagingQty(TransactionCase):
|
||||
def test_product_uom_qty_change(self):
|
||||
picking_f = Form(self.env["stock.picking"])
|
||||
picking_f.partner_id = self.partner
|
||||
picking_f.picking_type_id = self.env.ref("stock.picking_type_out")
|
||||
picking_f.picking_type_id = self.picking_type_out
|
||||
with picking_f.move_ids_without_package.new() as move_f:
|
||||
move_f.product_id = self.product
|
||||
self.assertEqual(move_f.product_uom_qty, 1)
|
||||
@@ -83,3 +86,142 @@ class TestStockMovePackagingQty(TransactionCase):
|
||||
],
|
||||
)
|
||||
picking.button_validate()
|
||||
|
||||
def test_product_packaging_qty_reserved(self):
|
||||
"""Test that the product packaging quantity reserved is correctly set."""
|
||||
product_a = self.env["product.product"].create(
|
||||
{"name": "Product A", "type": "product"}
|
||||
)
|
||||
packaging_product_a = self.env["product.packaging"].create(
|
||||
{"name": "Test packaging", "product_id": product_a.id, "qty": 5.0}
|
||||
)
|
||||
self.pack_location.active = True
|
||||
MoveObj = self.env["stock.move"]
|
||||
picking_client = self.env["stock.picking"].create(
|
||||
{
|
||||
"location_id": self.pack_location.id,
|
||||
"location_dest_id": self.customer_location.id,
|
||||
"picking_type_id": self.picking_type_out.id,
|
||||
}
|
||||
)
|
||||
dest = MoveObj.create(
|
||||
{
|
||||
"name": product_a.name,
|
||||
"product_id": product_a.id,
|
||||
"product_uom_qty": 10,
|
||||
"product_uom": product_a.uom_id.id,
|
||||
"picking_id": picking_client.id,
|
||||
"location_id": self.pack_location.id,
|
||||
"location_dest_id": self.customer_location.id,
|
||||
"state": "waiting",
|
||||
"procure_method": "make_to_order",
|
||||
"product_packaging_id": packaging_product_a.id,
|
||||
}
|
||||
)
|
||||
picking_pick = self.env["stock.picking"].create(
|
||||
{
|
||||
"location_id": self.stock_location.id,
|
||||
"location_dest_id": self.pack_location.id,
|
||||
"picking_type_id": self.picking_type_out.id,
|
||||
}
|
||||
)
|
||||
MoveObj.create(
|
||||
{
|
||||
"name": product_a.name,
|
||||
"product_id": product_a.id,
|
||||
"product_uom_qty": 10,
|
||||
"product_uom": product_a.uom_id.id,
|
||||
"picking_id": picking_pick.id,
|
||||
"location_id": self.stock_location.id,
|
||||
"location_dest_id": self.pack_location.id,
|
||||
"move_dest_ids": [(4, dest.id)],
|
||||
"state": "confirmed",
|
||||
"product_packaging_id": packaging_product_a.id,
|
||||
}
|
||||
)
|
||||
self.env["stock.quant"]._update_available_quantity(
|
||||
product_a, self.stock_location, 10
|
||||
)
|
||||
picking_pick.action_assign()
|
||||
self.assertRecordValues(
|
||||
picking_pick.move_ids_without_package,
|
||||
[
|
||||
{
|
||||
"product_id": product_a.id,
|
||||
"product_packaging_id": packaging_product_a.id,
|
||||
"product_packaging_qty": 2,
|
||||
"product_uom_qty": 10,
|
||||
"quantity_done": 0,
|
||||
"product_packaging_qty_done": 0,
|
||||
}
|
||||
],
|
||||
)
|
||||
self.assertRecordValues(
|
||||
picking_pick.move_line_ids_without_package,
|
||||
[
|
||||
{
|
||||
"product_id": product_a.id,
|
||||
"product_packaging_id": packaging_product_a.id,
|
||||
"reserved_uom_qty": 10,
|
||||
"product_packaging_qty_reserved": 2,
|
||||
"qty_done": 0,
|
||||
"product_packaging_qty_done": 0,
|
||||
}
|
||||
],
|
||||
)
|
||||
picking_pick.action_set_quantities_to_reservation()
|
||||
self.assertRecordValues(
|
||||
picking_pick.move_ids_without_package,
|
||||
[
|
||||
{
|
||||
"product_id": product_a.id,
|
||||
"product_packaging_id": packaging_product_a.id,
|
||||
"product_packaging_qty": 2,
|
||||
"product_uom_qty": 10,
|
||||
"quantity_done": 10,
|
||||
"product_packaging_qty_done": 2,
|
||||
}
|
||||
],
|
||||
)
|
||||
picking_pick._action_done()
|
||||
picking_client.action_assign()
|
||||
self.assertRecordValues(
|
||||
picking_client.move_ids_without_package,
|
||||
[
|
||||
{
|
||||
"product_id": product_a.id,
|
||||
"product_packaging_id": packaging_product_a.id,
|
||||
"product_packaging_qty": 2,
|
||||
"product_uom_qty": 10,
|
||||
"quantity_done": 0,
|
||||
"product_packaging_qty_done": 0,
|
||||
}
|
||||
],
|
||||
)
|
||||
self.assertRecordValues(
|
||||
picking_client.move_line_ids_without_package,
|
||||
[
|
||||
{
|
||||
"product_id": product_a.id,
|
||||
"product_packaging_id": packaging_product_a.id,
|
||||
"reserved_uom_qty": 10,
|
||||
"product_packaging_qty_reserved": 2,
|
||||
"qty_done": 0,
|
||||
"product_packaging_qty_done": 0,
|
||||
}
|
||||
],
|
||||
)
|
||||
picking_pick.action_set_quantities_to_reservation()
|
||||
self.assertRecordValues(
|
||||
picking_pick.move_ids_without_package,
|
||||
[
|
||||
{
|
||||
"product_id": product_a.id,
|
||||
"product_packaging_id": packaging_product_a.id,
|
||||
"product_packaging_qty": 2,
|
||||
"product_uom_qty": 10,
|
||||
"quantity_done": 10,
|
||||
"product_packaging_qty_done": 2,
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
<field name="model">stock.move.line</field>
|
||||
<field name="inherit_id" ref="stock.view_stock_move_line_operation_tree" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="reserved_uom_qty" position="before">
|
||||
<field
|
||||
name="product_packaging_qty_reserved"
|
||||
attrs="{'invisible': [('product_packaging_id', '=', False)]}"
|
||||
groups="product.group_stock_packaging"
|
||||
/>
|
||||
</field>
|
||||
<field name="qty_done" position="before">
|
||||
<field name="product_packaging_id" invisible="1" />
|
||||
<field
|
||||
|
||||
Reference in New Issue
Block a user