-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
TA#70700 [14.0][ADD] stock_picking_correct_reservation (#201)
- Loading branch information
Showing
21 changed files
with
343 additions
and
191 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 "" |
2 changes: 2 additions & 0 deletions
2
stock_picking_correct_reservation/security/ir.model.access.csv
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Binary file added
BIN
+397 KB
...k_picking_correct_reservation/static/description/correct_reservation_action.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+321 KB
...k_picking_correct_reservation/static/description/correct_reservation_window.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
2 changes: 1 addition & 1 deletion
2
...ing_remove_reservation/wizard/__init__.py → ...ng_correct_reservation/wizard/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
192
stock_picking_correct_reservation/wizard/stock_picking_reservation.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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', | ||
}) |
32 changes: 32 additions & 0 deletions
32
stock_picking_correct_reservation/wizard/stock_picking_reservation_view.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
Oops, something went wrong.