Skip to content

Commit

Permalink
TA#70700 [14.0][ADD] stock_picking_correct_reservation (#201)
Browse files Browse the repository at this point in the history
  • Loading branch information
majouda authored Nov 11, 2024
1 parent 4102579 commit 2a7a10e
Show file tree
Hide file tree
Showing 21 changed files with 343 additions and 191 deletions.
2 changes: 1 addition & 1 deletion .docker_files/main/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"stock_picking_groupby_parent_affiliate",
"stock_picking_product_supplier",
"stock_picking_responsible_editable",
"stock_picking_remove_reservation",
"stock_picking_correct_reservation",
"stock_picking_secondary_unit_demand",
"stock_picking_show_address",
"stock_picking_split_qty",
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ COPY stock_picking_barcode /mnt/extra-addons/stock_picking_barcode
COPY stock_picking_delivery_carrier_required /mnt/extra-addons/stock_picking_delivery_carrier_required
COPY stock_picking_groupby_parent_affiliate /mnt/extra-addons/stock_picking_groupby_parent_affiliate
COPY stock_picking_responsible_editable /mnt/extra-addons/stock_picking_responsible_editable
COPY stock_picking_remove_reservation /mnt/extra-addons/stock_picking_remove_reservation
COPY stock_picking_correct_reservation /mnt/extra-addons/stock_picking_correct_reservation
COPY stock_picking_secondary_unit_demand /mnt/extra-addons/stock_picking_secondary_unit_demand
COPY stock_picking_show_address /mnt/extra-addons/stock_picking_show_address
COPY stock_picking_split_qty /mnt/extra-addons/stock_picking_split_qty
Expand Down
37 changes: 37 additions & 0 deletions stock_picking_correct_reservation/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Stock Picking Correct Reservation
=================================

Context:
~~~~~~~~
Odoo provides a server action called 'Correct inconsistencies for reservation' to correct problematics products.
This action could unreserve stock.

This action is executed only by users with group ``Administration``.

We want to give access to users with group ``Inventory / Administrator``, to be able to execute this action.
And limit the correction to only selected stock picking.

Description:
~~~~~~~~~~~~

This module allows to execute the same code of 'Correct inconsistencies for reservation' action from a selected transfers.

Usage
-----
As a user with access group ``Inventory / Administrator``, I receive the warning bellow while validating a transfer:

.. image:: static/description/stock_picking_error_message.png

I need to go to ``Action`` button and click on ``Correct Reservation``

.. image:: static/description/correct_reservation_action.png

The action will show a popup to correst reservation.
The correction may unreserve the quantities of the selected transfer lines.

.. image:: static/description/correct_reservation_window.png


Contributors
------------
* Numigi (tm) and all its contributors (https://bit.ly/numigiens)
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
"name": "Stock Picking Remove Reservation",
"name": "Stock Picking Correct Reservation",
"version": "14.0.1.0.0",
"author": "Numigi",
"maintainer": "Numigi",
"website": "https://bit.ly/numigi-com",
"license": "AGPL-3",
"category": "Stock",
"depends": ["stock"],
"summary": "Allow to force stock picking unreservation.",
"summary": "Correct inconsistencies for reservation.",
"data": [
"security/ir.model.access.csv",
"wizard/stock_picking_unreserve_view.xml",
"wizard/stock_picking_reservation_view.xml",
],
"installable": True,
}
74 changes: 74 additions & 0 deletions stock_picking_correct_reservation/i18n/fr.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_picking_correct_reservation
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-11 20:18+0000\n"
"PO-Revision-Date: 2024-11-11 20:18+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: stock_picking_correct_reservation
#: model_terms:ir.ui.view,arch_db:stock_picking_correct_reservation.stock_picking_correct_reservation_view
msgid "Cancel"
msgstr ""

#. module: stock_picking_correct_reservation
#: model:ir.actions.act_window,name:stock_picking_correct_reservation.action_stock_picking_correct_reservation
#: model_terms:ir.ui.view,arch_db:stock_picking_correct_reservation.stock_picking_correct_reservation_view
msgid "Correct Reservation"
msgstr "Corriger la reservation"

#. module: stock_picking_correct_reservation
#: model:ir.model.fields,field_description:stock_picking_correct_reservation.field_stock_picking_reservation__create_uid
msgid "Created by"
msgstr ""

#. module: stock_picking_correct_reservation
#: model:ir.model.fields,field_description:stock_picking_correct_reservation.field_stock_picking_reservation__create_date
msgid "Created on"
msgstr ""

#. module: stock_picking_correct_reservation
#: model:ir.model.fields,field_description:stock_picking_correct_reservation.field_stock_picking_reservation__display_name
msgid "Display Name"
msgstr ""

#. module: stock_picking_correct_reservation
#: model:ir.model.fields,field_description:stock_picking_correct_reservation.field_stock_picking_reservation__id
msgid "ID"
msgstr ""

#. module: stock_picking_correct_reservation
#: model:ir.model.fields,field_description:stock_picking_correct_reservation.field_stock_picking_reservation____last_update
msgid "Last Modified on"
msgstr ""

#. module: stock_picking_correct_reservation
#: model:ir.model.fields,field_description:stock_picking_correct_reservation.field_stock_picking_reservation__write_uid
msgid "Last Updated by"
msgstr ""

#. module: stock_picking_correct_reservation
#: model:ir.model.fields,field_description:stock_picking_correct_reservation.field_stock_picking_reservation__write_date
msgid "Last Updated on"
msgstr ""

#. module: stock_picking_correct_reservation
#: model_terms:ir.ui.view,arch_db:stock_picking_correct_reservation.stock_picking_correct_reservation_view
msgid ""
"You should run this action only if you encounter a picking validation issue "
"related to reservations."
msgstr "Vous devriez exécuter cette action uniquement si vous rencontrez un problème de validation de transfert lié aux réservations."

#. module: stock_picking_correct_reservation
#: model:ir.model,name:stock_picking_correct_reservation.model_stock_picking_reservation
msgid "stock.picking.reservation"
msgstr ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_stock_picking_reservation,access_stock_picking_reservation,model_stock_picking_reservation,stock.group_stock_manager,1,1,1,1
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# © 2024 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from . import stock_picking_unreserve
from . import stock_picking_reservation
192 changes: 192 additions & 0 deletions stock_picking_correct_reservation/wizard/stock_picking_reservation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# © 2024 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import models


class StockPickingReservation(models.TransientModel):
_name = "stock.picking.reservation"
_description = "Stock Picking Reservation"

def action_correct_reservation(self):
""""
This function was inspired by the server action Correct inconsistencies for
reservation
"""
context = dict(self._context or {})
pickings = context.get('active_ids', False)
if not pickings:
return {"type": "ir.actions.act_window_close"}
picking_ids = self.env["stock.picking"].browse(pickings)
move_line_ids = []
move_line_to_recompute_ids = []
logging = ''
for picking in picking_ids:
quants = self.env['stock.quant'].sudo().search(
[('product_id', 'in', picking.move_line_ids.mapped('product_id.id'))]
)
for quant in quants:

move_lines = self._get_matching_move_lines(quant)

move_line_ids += move_lines.ids
reserved_on_move_lines = sum(move_lines.mapped('product_qty'))
move_line_str = ", ".join(map(str, move_lines.ids))

if quant.location_id.should_bypass_reservation():
# If a quant is in a location that should bypass the reservation,
# its `reserved_quantity` field should be 0.
logging += self._handle_bypass_reservation(
quant, move_lines, reserved_on_move_lines, move_line_str
)
else:
# If a quant is in a reservable location, its `reserved_quantity`
# should be exactly the sum of the `product_qty` of all the
# partially_available / assigned move lines with the same
# characteristics.
logging += self._handle_reservable_location(
quant, move_lines, reserved_on_move_lines,
move_line_str, move_line_to_recompute_ids
)

move_lines_to_unreserve, unreserve_logging = self._find_unlinked_move_lines(
move_line_ids
)
logging += unreserve_logging
self._unreserve_move_lines(move_lines_to_unreserve)

if logging:
self._create_logging(logging)

if move_line_to_recompute_ids:
self.env['stock.move.line'].browse(
move_line_to_recompute_ids
).move_id._recompute_state()

return {'type': 'ir.actions.act_window_close'}

def _get_matching_move_lines(self, quant):
return self.env["stock.move.line"].search(
[
("product_id", "=", quant.product_id.id),
("location_id", "=", quant.location_id.id),
("lot_id", "=", quant.lot_id.id),
("package_id", "=", quant.package_id.id),
("owner_id", "=", quant.owner_id.id),
("product_qty", "!=", 0),
]
)

def _handle_bypass_reservation(self, quant, move_lines, reserved_on_move_lines,
move_line_str):
logging = ''
if quant.reserved_quantity != 0:
logging += (f"""Problematic quant found: {quant.id} (quantity: {quant.quantity},
reserved_quantity: {quant.reserved_quantity})\n"""
f"""its `reserved_quantity` field is not 0 while its location
should bypass the reservation\n""")
if move_lines:
logging += (
f"""These move lines are reserved on it:\n"""
f"""{move_line_str} (sum of the reservation:
{reserved_on_move_lines})\n"""
"******************\n"
)
else:
logging += (
f"""no move lines are reserved on it, you can safely reset its
`reserved_quantity` to 0\n"""
f"""{move_line_str} (sum of the reservation:
{reserved_on_move_lines})\n"""
"******************\n"
)
quant.write({'reserved_quantity': 0})
return logging

def _handle_reservable_location(
self,
quant,
move_lines,
reserved_on_move_lines,
move_line_str,
move_line_to_recompute_ids,
):
logging = ""
if (
quant.reserved_quantity in [0, -1]
or quant.reserved_quantity != reserved_on_move_lines
):
logging += (
f"""Problematic quant found: {quant.id} (quantity: {quant.quantity},
reserved_quantity: {quant.reserved_quantity})\n"""
f"""{'its `reserved_quantity` field is 0' if
quant.reserved_quantity == 0 else
'its `reserved_quantity` field is negative' if
quant.reserved_quantity < 0 else
'its `reserved_quantity` does not reflect the move lines reservation'
}\n"""
f"""These move lines are reserved on it: {move_line_str}
(sum of the reservation: {reserved_on_move_lines})\n"""
"******************\n"
)
quant.write({"reserved_quantity": 0})
move_lines.with_context(bypass_reservation_update=True).sudo().write(
{"product_uom_qty": 0}
)
move_line_to_recompute_ids += move_lines.ids
elif any(move_line.product_qty < 0 for move_line in move_lines):
logging += (
f"""Problematic quant found: {quant.id} (quantity: {quant.quantity},
reserved_quantity: {quant.reserved_quantity})\n"""
f"""its `reserved_quantity` correctly reflects the move lines
reservation but some are negative\n"""
f"""These move lines are reserved on it: {move_line_str}
(sum of the reservation: {reserved_on_move_lines})\n"""
"******************\n"
)
quant.write({"reserved_quantity": 0})
move_lines.with_context(bypass_reservation_update=True).sudo().write(
{"product_uom_qty": 0}
)
move_line_to_recompute_ids += move_lines.ids
return logging

def _find_unlinked_move_lines(self, move_line_ids):
move_lines = self.env["stock.move.line"].search(
[
("product_id.type", "=", "product"),
("product_qty", "!=", 0),
("id", "not in", move_line_ids),
]
)
move_lines_to_unreserve = []
logging = ""
for move_line in move_lines:
if not move_line.location_id.should_bypass_reservation():
logging += (
f"""Problematic move line found:
{move_line.id} (reserved_quantity:
{move_line.product_qty})\n"""
"There is no existing quant despite its `reserved_quantity`\n"
"******************\n"
)
move_lines_to_unreserve.append(move_line.id)
return move_lines_to_unreserve, logging

def _unreserve_move_lines(self, move_lines_to_unreserve):
if move_lines_to_unreserve:
query = """ UPDATE stock_move_line SET product_uom_qty = 0,
product_qty = 0 WHERE id IN %s """
self.env.cr.execute(query, (tuple(move_lines_to_unreserve),))

def _create_logging(self, logging):
self.env['ir.logging'].sudo().create({
'name': 'Unreserve stock.quant and stock.move.line',
'type': 'server',
'level': 'DEBUG',
'dbname': self.env.cr.dbname,
'message': logging,
'func': '_update_reserved_quantity',
'path': 'addons/stock/models/stock_quant.py',
'line': '0',
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>

<record id="stock_picking_correct_reservation_view" model="ir.ui.view">
<field name="name">Correct Reservation</field>
<field name="model">stock.picking.reservation</field>
<field name="arch" type="xml">
<form>
<separator string="Correct Reservation"/>
<form class="o_form_label">You should run this action only if you encounter a picking validation issue related to reservations.</form>
<footer>
<button string="Correct Reservation" name="action_correct_reservation" type="object" default_focus="1" class="btn-primary"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>

<record id="action_stock_picking_correct_reservation" model="ir.actions.act_window">
<field name="name">Correct Reservation</field>
<field name="groups_id" eval="[(4,ref('stock.group_stock_manager'))]"/>
<field name="res_model">stock.picking.reservation</field>
<field name="view_mode">form</field>
<field name="view_id" ref="stock_picking_correct_reservation_view"/>
<field name="target">new</field>
<field name="binding_model_id" ref="stock.model_stock_picking" />
<field name="binding_view_types">list,form</field>
</record>

</data>
</odoo>
Loading

0 comments on commit 2a7a10e

Please sign in to comment.