Merge PR #1988 into 16.0

Signed-off-by rafaelbn
This commit is contained in:
OCA-git-bot
2024-04-12 08:58:24 +00:00
14 changed files with 403 additions and 49 deletions

View File

@@ -7,7 +7,7 @@ Stock Packaging Qty
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:ac3969cedf86da72ca20a93812053d6d9054a9f5d8e0c896eab57fe2b835eefc
!! source digest: sha256:5b4005788a852d7f71c44042e6c8839be5963fd5d6ac95822b93c48c660a2730
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
@@ -28,13 +28,22 @@ Stock Packaging Qty
|badge1| |badge2| |badge3| |badge4| |badge5|
Add packaging fields in the stock moves and their reports.
Add packaging fields in the stock moves, their lines and their reports.
**Table of contents**
.. contents::
:local:
Known issues / Roadmap
======================
* Maybe we should track also reserved packaging quantities?
* Since we store done product packaging quantities in the stock move lines, we
should be able to use this information in quants to provide real
packaging-based stock data.
Bug Tracker
===========

View File

@@ -10,7 +10,8 @@
"depends": ["stock"],
"data": [
"views/report_stock_picking.xml",
"views/stock_move_tree_view.xml",
"views/stock_move_line_view.xml",
"views/stock_move_view.xml",
"views/stock_picking_form_view.xml",
],
"license": "LGPL-3",

View File

@@ -1 +1,2 @@
from . import stock_move
from . import stock_move_line

View File

@@ -1,17 +1,24 @@
# Copyright 2020 Camptocamp SA
# Copyright 2021 ForgeFlow, S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import api, fields, models
# 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
class StockPicking(models.Model):
class StockMove(models.Model):
_inherit = "stock.move"
product_packaging_qty = fields.Float(
string="Pkg. Qty.",
string="Pkgs. Demand",
compute="_compute_product_packaging_qty",
inverse="_inverse_product_packaging_qty",
help="Amount of packages demanded.",
help="Amount of product packagings demanded.",
)
product_packaging_qty_done = fields.Float(
string="Pkgs. Done",
compute="_compute_product_packaging_qty_done",
inverse="_inverse_product_packaging_qty_done",
help="Amount of product packagings done.",
)
@api.depends(
@@ -31,6 +38,18 @@ class StockPicking(models.Model):
move.product_uom_qty / move._get_single_package_uom_qty()
)
@api.depends(
"move_line_ids.product_packaging_qty_done",
"move_line_nosuggest_ids.product_packaging_qty_done",
)
def _compute_product_packaging_qty_done(self):
"""Get the sum of done packaging qtys from move lines."""
for move in self:
lines = move._get_move_lines()
move.product_packaging_qty_done = sum(
lines.mapped("product_packaging_qty_done")
)
@api.onchange("product_packaging_qty")
def _inverse_product_packaging_qty(self):
"""Store the quantity in the product's UoM.
@@ -43,6 +62,23 @@ class StockPicking(models.Model):
uom_factor = move._get_single_package_uom_qty()
move.product_uom_qty = move.product_packaging_qty * uom_factor
def _inverse_product_packaging_qty_done(self):
"""Store the done packaging dqty in the move line if there's just one."""
for move in self:
lines = move._get_move_lines()
# Setting 0 done pkgs with no lines? Nothing to do
if not lines and not move.product_packaging_qty_done:
continue
if len(lines) != 1:
raise exceptions.UserError(
_(
"There are %d move lines involved. "
"Please set their product packaging done qty directly.",
len(lines),
)
)
lines.product_packaging_qty_done = move.product_packaging_qty_done
@api.onchange("product_packaging_id")
def _onchange_product_packaging(self):
"""Add a default qty if the packaging has an invalid value."""
@@ -62,3 +98,32 @@ class StockPicking(models.Model):
return self.product_packaging_id.product_uom_id._compute_quantity(
self.product_packaging_id.qty, self.product_uom
)
def _set_quantities_to_reservation(self):
"""Add packaging qtys when clicking on "Set Quantities"."""
result = super()._set_quantities_to_reservation()
digits = self.env["stock.move.line"].fields_get(["qty_done"], ["digits"])[
"qty_done"
]["digits"][1]
for line in self.move_line_ids:
if float_compare(line.qty_done, line.reserved_uom_qty, digits):
continue
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
)
return result
def _clear_quantities_to_zero(self):
"""Clear packaging qtys when clicking on "Clear Quantities"."""
result = super()._clear_quantities_to_zero()
for line in self.move_line_ids:
if line.qty_done:
continue
line.product_packaging_qty_done = 0
return result

View File

@@ -0,0 +1,49 @@
# Copyright 2024 Moduon Team S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0)
from odoo import fields, models
class StockMoveLine(models.Model):
_inherit = "stock.move.line"
product_packaging_id = fields.Many2one(
related="move_id.product_packaging_id", readonly=True
)
product_packaging_qty_done = fields.Float(
string="Done Pkg. Qty.",
help="Product packaging quantity done.",
)
def _get_aggregated_properties(self, move_line=False, move=False):
"""Aggregate by product packaging too."""
result = super()._get_aggregated_properties(move_line, move)
pkg = result["move"].product_packaging_id
result["product_packaging"] = pkg
result["line_key"] += f"_{pkg.id}"
return result
def _get_aggregated_product_quantities(self, **kwargs):
"""Aggregate by product packaging too."""
result = super()._get_aggregated_product_quantities(**kwargs)
# Know all involved move lines, following upstream criteria
all_lines = self.browse()
processed_moves = all_lines.move_id
if kwargs.get("except_package"):
all_lines |= self - self.filtered("result_package_id")
if not kwargs.get("strict"):
moves = (self.picking_id | self.picking_id.backorder_ids).move_ids
all_lines |= moves.move_line_ids | moves.move_line_nosuggest_ids
# Aggregate product packaging quantities
for move_line in all_lines:
props = self._get_aggregated_properties(move_line)
try:
agg = result[props["line_key"]]
except KeyError:
continue # Missing aggregation; nothing to do
agg.setdefault("product_packaging_qty", 0.0)
agg.setdefault("product_packaging_qty_done", 0.0)
agg["product_packaging_qty_done"] += move_line.product_packaging_qty_done
if move_line.move_id not in processed_moves:
agg["product_packaging_qty"] += move_line.move_id.product_packaging_qty
processed_moves |= move_line.move_id
return result

View File

@@ -1 +1 @@
Add packaging fields in the stock moves and their reports.
Add packaging fields in the stock moves, their lines and their reports.

View File

@@ -0,0 +1,5 @@
* Maybe we should track also reserved packaging quantities?
* Since we store done product packaging quantities in the stock move lines, we
should be able to use this information in quants to provide real
packaging-based stock data.

View File

@@ -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,24 +367,34 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:ac3969cedf86da72ca20a93812053d6d9054a9f5d8e0c896eab57fe2b835eefc
!! source digest: sha256:5b4005788a852d7f71c44042e6c8839be5963fd5d6ac95822b93c48c660a2730
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<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&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>Add packaging fields in the stock moves and their reports.</p>
<p>Add packaging fields in the stock moves, their lines and their reports.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-1">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-2">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-3">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-4">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-5">Maintainers</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="toc-entry-1">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#toc-entry-1">Known issues / Roadmap</a></h1>
<ul class="simple">
<li>Maybe we should track also reserved packaging quantities?</li>
<li>Since we store done product packaging quantities in the stock move lines, we
should be able to use this information in quants to provide real
packaging-based stock data.</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-1">Bug Tracker</a></h1>
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/issues">GitHub Issues</a>.
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
@@ -391,15 +402,15 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#toc-entry-2">Credits</a></h1>
<h1><a class="toc-backref" href="#toc-entry-3">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-3">Authors</a></h2>
<h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
<ul class="simple">
<li>ForgeFlow</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-4">Contributors</a></h2>
<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
<ul class="simple">
<li>Mateu Griful &lt;<a class="reference external" href="mailto:mateu.griful&#64;forgeflow.com">mateu.griful&#64;forgeflow.com</a>&gt;</li>
<li>Lois Rilo &lt;<a class="reference external" href="mailto:lois.rilo&#64;forgeflow.com">lois.rilo&#64;forgeflow.com</a>&gt;</li>
@@ -407,7 +418,7 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-5">Maintainers</a></h2>
<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose

View File

@@ -12,7 +12,14 @@ class TestStockMovePackagingQty(TransactionCase):
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.env.user.groups_id |= cls.env.ref("product.group_stock_packaging")
cls.partner = cls.env.ref("base.res_partner_12")
cls.product = cls.env.ref("product.product_product_9")
cls.product = cls.env["product.product"].create(
{
"name": "Test product",
"type": "consu",
"uom_id": cls.env.ref("uom.product_uom_unit").id,
"uom_po_id": cls.env.ref("uom.product_uom_unit").id,
}
)
cls.packaging = cls.env["product.packaging"].create(
{"name": "Test packaging", "product_id": cls.product.id, "qty": 5.0}
)
@@ -59,3 +66,20 @@ class TestStockMovePackagingQty(TransactionCase):
move_f.product_packaging_id = self.packaging
self.assertEqual(move_f.product_uom_qty, 5)
self.assertEqual(move_f.product_packaging_qty, 1)
picking = picking_f.save()
self.assertEqual(picking.state, "draft")
picking.action_assign()
picking.action_set_quantities_to_reservation()
self.assertRecordValues(
picking.move_ids_without_package,
[
{
"product_id": self.product.id,
"product_packaging_id": self.packaging.id,
"product_packaging_qty_done": 1,
"product_packaging_qty": 1,
"product_uom_qty": 5,
}
],
)
picking.button_validate()

View File

@@ -40,5 +40,72 @@
<span t-field="move.product_packaging_qty" />
</div>
</xpath>
<xpath
expr="//table[@name='stock_move_table']/tbody//td[
1 + count(
//table[@name='stock_move_table']
//th[@name='th_sm_quantity']
/preceding-sibling::*
)
]"
position="inside"
>
<div
t-if="move.product_packaging_id"
class="text-secondary"
groups="product.group_stock_packaging"
>
<span t-field="move.product_packaging_id" />:
<span t-field="move.product_packaging_qty_done" />
</div>
</xpath>
</template>
<template
id="stock_report_delivery_aggregated_move_lines"
inherit_id="stock.stock_report_delivery_aggregated_move_lines"
>
<xpath expr="//td[@name='move_line_aggregated_qty_ordered']">
<div
t-if="aggregated_lines[line]['product_packaging']"
class="text-secondary"
groups="product.group_stock_packaging"
>
<span
t-esc="aggregated_lines[line]['product_packaging'].display_name"
/>:
<span t-esc="aggregated_lines[line]['product_packaging_qty']" />
</div>
</xpath>
<xpath expr="//td[@name='move_line_aggregated_qty_done']">
<div
t-if="aggregated_lines[line]['product_packaging']"
class="text-secondary"
groups="product.group_stock_packaging"
>
<span
t-esc="aggregated_lines[line]['product_packaging'].display_name"
/>:
<span t-esc="aggregated_lines[line]['product_packaging_qty_done']" />
</div>
</xpath>
</template>
<template
id="stock_report_delivery_has_serial_move_line"
inherit_id="stock.stock_report_delivery_has_serial_move_line"
>
<xpath expr="//td[@name='move_line_lot_qty_done']">
<div
t-if="ml.product_packaging_id"
class="text-secondary"
groups="product.group_stock_packaging"
>
<span t-field="ml.product_packaging_id" />:
<span t-field="ml.product_packaging_qty_done" />
</div>
</xpath>
</template>
</data>

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2024 Moduon Team S.L.
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) -->
<data>
<record id="view_stock_move_line_operation_tree" model="ir.ui.view">
<field name="name">Add product packaging qty done info</field>
<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="qty_done" position="before">
<field name="product_packaging_id" invisible="1" />
<field
name="product_packaging_qty_done"
attrs="{'invisible': [('product_packaging_id', '=', False)]}"
groups="product.group_stock_packaging"
/>
</field>
</field>
</record>
<record id="view_stock_move_line_mobile_form_extension" model="ir.ui.view">
<field name="name">Add product packaging qty done info</field>
<field name="model">stock.move.line</field>
<field name="inherit_id" ref="stock.view_move_line_mobile_form" />
<field name="arch" type="xml">
<xpath expr="//label[@for='qty_done']" position="before">
<label for="product_packaging_qty_done" />
<div class="o_row">
<field
name="product_packaging_qty_done"
attrs="{'invisible': [('product_packaging_id', '=', False)]}"
groups="product.group_stock_packaging"
/>
<field
name="product_packaging_id"
groups="product.group_stock_packaging"
/>
</div>
</xpath>
</field>
</record>
<record id="view_stock_move_line_detailed_operation_tree" model="ir.ui.view">
<field name="name">Add product packaging qty done info</field>
<field name="model">stock.move.line</field>
<field
name="inherit_id"
ref="stock.view_stock_move_line_detailed_operation_tree"
/>
<field name="arch" type="xml">
<field name="qty_done" position="before">
<field
name="product_packaging_qty_done"
attrs="{'invisible': [('product_packaging_id', '=', False)]}"
groups="product.group_stock_packaging"
/>
<field
name="product_packaging_id"
groups="product.group_stock_packaging"
/>
</field>
</field>
</record>
</data>

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<data>
<record id="view_move_extra_tree" model="ir.ui.view">
<field name="name">view.move.extra.tree</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_move_tree" />
<field name="arch" type="xml">
<field name="product_packaging_id" position="after">
<field name="is_initial_demand_editable" invisible="1" />
<field
name="product_packaging_qty"
groups="product.group_stock_packaging"
attrs="{
'readonly': [
'|',
('product_packaging_id', '=', False),
('is_initial_demand_editable', '=', False),
],
}"
/>
</field>
</field>
</record>
</data>

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2024 Moduon Team S.L.
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) -->
<data>
<record id="view_move_extra_tree" model="ir.ui.view">
<field name="name">view.move.extra.tree</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_move_tree" />
<field name="arch" type="xml">
<field name="product_packaging_id" position="after">
<field name="is_initial_demand_editable" invisible="1" />
<field
name="product_packaging_qty"
groups="product.group_stock_packaging"
attrs="{
'readonly': [
'|',
('product_packaging_id', '=', False),
('is_initial_demand_editable', '=', False),
],
}"
/>
</field>
</field>
</record>
<record id="view_stock_move_operations" model="ir.ui.view">
<field name="name">Add product packaging qty info</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_stock_move_operations" />
<field name="arch" type="xml">
<xpath expr="//group" position="inside">
<group
groups="product.group_stock_packaging"
name="packaging"
attrs="{'invisible': [('product_packaging_id', '=', False)]}"
>
<label for="product_packaging_qty" string="Packagings demanded" />
<div class="o_row">
<span><field
name="product_packaging_qty"
readonly="1"
nolabel="1"
/></span>
<span><field
name="product_packaging_id"
readonly="1"
nolabel="1"
/></span>
</div>
<label for="product_packaging_qty_done" string="Packagings done" />
<div class="o_row">
<span><field
name="product_packaging_qty_done"
readonly="1"
nolabel="1"
/></span>
<span> / </span>
<span><field
name="product_packaging_qty"
readonly="1"
nolabel="1"
/></span>
<span><field
name="product_packaging_id"
readonly="1"
nolabel="1"
/></span>
</div>
</group>
</xpath>
</field>
</record>
</data>

View File

@@ -13,9 +13,6 @@
name="product_packaging_qty"
groups="product.group_stock_packaging"
attrs="{
'column_invisible': [
('parent.immediate_transfer', '=', True),
],
'readonly': [
'|',
('product_packaging_id', '=', False),
@@ -24,6 +21,17 @@
}"
/>
</xpath>
<xpath
expr="//field[@name='move_ids_without_package']/tree/field[@name='quantity_done']"
position="before"
>
<field
name="product_packaging_qty_done"
groups="product.group_stock_packaging"
attrs="{'invisible': [('product_packaging_id', '=', False)]}"
/>
</xpath>
</field>
</record>
</data>