From 9fbf28a46d10a8150b6038c90a82c2d102c8b82c Mon Sep 17 00:00:00 2001 From: sergio-teruel Date: Tue, 2 Jul 2024 16:30:37 +0200 Subject: [PATCH 01/12] [IMP] stock_barcodes: Add pending move with difference between demand and reserved [FIX] stock_barcodes: Singletone if mixing in a batch picking distinct picking_type_id. By default batch picking don't allow mix picking_type_id, but is a simple change. TT50288 --- stock_barcodes/i18n/es.po | 202 ++++++------------ stock_barcodes/i18n/stock_barcodes.pot | 70 +++++- stock_barcodes/models/__init__.py | 1 + stock_barcodes/models/stock_move.py | 29 +++ stock_barcodes/models/stock_move_line.py | 18 +- stock_barcodes/models/stock_picking.py | 42 +--- stock_barcodes/models/stock_picking_type.py | 2 +- .../wizard/stock_barcodes_read_picking.py | 41 ++-- .../stock_barcodes_read_picking_views.xml | 10 +- .../wizard/stock_barcodes_read_todo.py | 70 +++++- .../wizard/stock_barcodes_read_todo_view.xml | 47 +++- 11 files changed, 312 insertions(+), 220 deletions(-) create mode 100644 stock_barcodes/models/stock_move.py diff --git a/stock_barcodes/i18n/es.po b/stock_barcodes/i18n/es.po index 6fef90e92947..a20cb2cb245f 100644 --- a/stock_barcodes/i18n/es.po +++ b/stock_barcodes/i18n/es.po @@ -6,16 +6,16 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 11.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-02-06 20:07+0000\n" -"PO-Revision-Date: 2023-10-30 21:36+0000\n" -"Last-Translator: Ivorra78 \n" +"POT-Creation-Date: 2024-07-19 08:31+0000\n" +"PO-Revision-Date: 2024-07-19 10:32+0200\n" +"Last-Translator: Sergio Teruel \n" "Language-Team: \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.17\n" +"X-Generator: Poedit 3.0.1\n" #. module: stock_barcodes #: model:ir.model.fields,help:stock_barcodes.field_wiz_candidate_picking__state @@ -152,11 +152,11 @@ msgstr "" #. module: stock_barcodes #: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_read_picking_form msgid "" -"" +"" msgstr "" -"" +"" #. module: stock_barcodes #: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_read_form @@ -330,6 +330,11 @@ msgstr "Poner en paquete automáticamente antes de la validación del albarán" msgid "Back" msgstr "Atrás" +#. module: stock_barcodes +#: model:ir.model.fields,field_description:stock_barcodes.field_stock_move__barcode_backorder_action +msgid "Backorder action" +msgstr "Acción entrega parcial" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read__barcode #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_inventory__barcode @@ -515,6 +520,12 @@ msgstr "movimientos confirmados" msgid "Context" msgstr "Contexto" +#. module: stock_barcodes +#: model:ir.model.fields.selection,name:stock_barcodes.selection__stock_move__barcode_backorder_action__create_backorder +#: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_todo_kanban +msgid "Create Backorder" +msgstr "Crear entrega parcial" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_stock_barcodes_option_group__create_lot msgid "Create lots if not match" @@ -578,6 +589,13 @@ msgstr "Registro del producto escaneado" msgid "Customers" msgstr "Clientes" +#. module: stock_barcodes +#: model:ir.model.fields,help:stock_barcodes.field_wiz_stock_barcodes_read__product_uom_id +#: model:ir.model.fields,help:stock_barcodes.field_wiz_stock_barcodes_read_inventory__product_uom_id +#: model:ir.model.fields,help:stock_barcodes.field_wiz_stock_barcodes_read_picking__product_uom_id +msgid "Default unit of measure used for all stock operations." +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,help:stock_barcodes.field_stock_barcodes_option_group__manual_entry msgid "Default value when open scan interface" @@ -900,6 +918,12 @@ msgstr "Cantidad inventariada" msgid "Inventory quants" msgstr "Cantds de Inventario" +#. module: stock_barcodes +#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_picking__todo_line_is_extra_line +#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_todo__is_extra_line +msgid "Is Extra Line" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_stock_barcodes_option_group__is_manual_confirm #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read__is_manual_confirm @@ -1159,6 +1183,11 @@ msgstr "Se ha encontrado más de un producto" msgid "Move Line" msgstr "Línea de Movimiento" +#. module: stock_barcodes +#: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_todo_kanban +msgid "NOT AVAILABLE" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_stock_barcodes_action__name #: model:ir.model.fields,field_description:stock_barcodes.field_stock_barcodes_option__name @@ -1183,6 +1212,12 @@ msgstr "Nuevo movimiento:" msgid "New Picking Barcode Option Group" msgstr "Nuevo Grupo de Opciones de Código de Barras de Albarán" +#. module: stock_barcodes +#: model:ir.model.fields.selection,name:stock_barcodes.selection__stock_move__barcode_backorder_action__skip_backorder +#: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_todo_kanban +msgid "No Backorder" +msgstr "No crear entrega parcial" + #. module: stock_barcodes #: model:ir.model.fields.selection,name:stock_barcodes.selection__wiz_stock_barcodes_read__message_type__not_found #: model:ir.model.fields.selection,name:stock_barcodes.selection__wiz_stock_barcodes_read_inventory__message_type__not_found @@ -1213,6 +1248,11 @@ msgstr "Notas" msgid "OK" msgstr "Aceptar" +#. module: stock_barcodes +#: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_todo_kanban +msgid "Odoo will not create a backorder for this move. Are you sure?" +msgstr "Odoo no creará una entrega parcial para este movimiento. ¿Está seguro?" + #. module: stock_barcodes #: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_read_picking_form msgid "Open" @@ -1306,6 +1346,7 @@ msgid "Partner" msgstr "Empresa" #. module: stock_barcodes +#: model:ir.model.fields.selection,name:stock_barcodes.selection__stock_move__barcode_backorder_action__pending #: model:ir.model.fields.selection,name:stock_barcodes.selection__stock_move_line__barcode_scan_state__pending #: model:ir.model.fields.selection,name:stock_barcodes.selection__wiz_stock_barcodes_read_todo__state__pending msgid "Pending" @@ -1416,13 +1457,6 @@ msgstr "Cdad. de producto" msgid "Product Qty. Done" msgstr "" -#. module: stock_barcodes -#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read__product_uom_id -#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_inventory__product_uom_id -#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_picking__product_uom_id -msgid "Product Uom" -msgstr "Udm del Producto" - #. module: stock_barcodes #: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_read_picking_form msgid "Put in pack" @@ -1506,6 +1540,11 @@ msgstr "Modelo" msgid "Reserved" msgstr "Reservado" +#. module: stock_barcodes +#: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_todo_kanban +msgid "Restore to pending" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read__result_package_id #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_inventory__result_package_id @@ -1655,6 +1694,7 @@ msgid "Steps to scan" msgstr "Pasos a escanear" #. module: stock_barcodes +#: model:ir.model,name:stock_barcodes.model_stock_move #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_todo__stock_move_ids msgid "Stock Move" msgstr "Movimiento de Existencias" @@ -1691,6 +1731,11 @@ msgstr "" "botón \"Nuevo\" en un tipo de operación. Se utilizará para crear un albarán " "no planificado." +#. module: stock_barcodes +#: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_todo_kanban +msgid "This move will be set to pending. Are you sure?" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_picking__todo_line_ids msgid "To Do Lines" @@ -1734,6 +1779,13 @@ msgstr "Tipo de operación" msgid "Type of operation" msgstr "Tipo de operación" +#. module: stock_barcodes +#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read__product_uom_id +#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_inventory__product_uom_id +#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_picking__product_uom_id +msgid "Unit of Measure" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_todo__uom_id msgid "Uom" @@ -1830,9 +1882,8 @@ msgstr "Asistente para leer códigos de barras desde el inventario" #. module: stock_barcodes #: model:ir.model,name:stock_barcodes.model_wiz_stock_barcodes_read_picking -msgid "Wizard to read barcode on picking batch" -msgstr "" -"Asistente para leer códigos de barras desde las agrupaciones de albaranes" +msgid "Wizard to read barcode on picking" +msgstr "Asistente para leer códigos de barras desde los albaranes" #. module: stock_barcodes #: model:ir.model,name:stock_barcodes.model_wiz_stock_barcodes_read_todo @@ -1920,118 +1971,3 @@ msgstr "desbloquear el albarán" #, python-format msgid "{name} is required" msgstr "" - -#~ msgid "Apply inventory" -#~ msgstr "Aplicar inventario" - -#~ msgid "Total Qty" -#~ msgstr "Cdad. total" - -#~ msgid "" -#~ msgstr "" - -#~ msgid "Wizard to read barcode on picking" -#~ msgstr "Asistente para leer códigos de barras desde los albaranes" - -#~ msgid "List view of lines" -#~ msgstr "Vista lista de líneas" - -#, python-format -#~ msgid "There is no lots to assign quantities" -#~ msgstr "No hay lotes para asignar la cantidad" - -#~ msgid "New lot" -#~ msgstr "Nuevo lote" - -#~ msgid "Barcode not found" -#~ msgstr "Código de barras no encontrado" - -#~ msgid "Barcode: %s (%s)" -#~ msgstr "Cód. Barras: %s (%s)" - -#~ msgid "Clean lot" -#~ msgstr "Borrar lote" - -#~ msgid "Companies" -#~ msgstr "Compañías" - -#~ msgid "Confirmed moves allowed" -#~ msgstr "Permitir movimientos confirmados" - -#~ msgid "Demand:" -#~ msgstr "Demanda:" - -#~ msgid "Done:" -#~ msgstr "Hecho:" - -#~ msgid "Get lots automatically for inventories" -#~ msgstr "Obtener lotes automáticamente para inventarios" - -#~ msgid "" -#~ "If checked the lot will be set automatically with the same removal " -#~ "strategy" -#~ msgstr "" -#~ "Si esta marcado el lote se establecerá automáticamente según la " -#~ "estrategia de retirada" - -#~ msgid "" -#~ "If checked, after a product barcode is read, the quantities will be " -#~ "distributed\n" -#~ " among all existing lots by removal " -#~ "strategy order." -#~ msgstr "" -#~ "Si está marcado, después de escanear un código de barras de producto, las " -#~ "cantidades se distribuirán entre todos los lotes existentes por orden " -#~ "según la estrategia de retirada" - -#~ msgid "" -#~ "If user input more quantities than existing it, the exceeded quantities " -#~ "will be assigned to last lot" -#~ msgstr "" -#~ "Si el usuario introduce más cantidades de las existentes, las cantidades " -#~ "sobrantes se asignarán al último lote" - -#~ msgid "Manual entry data" -#~ msgstr "Entrada manual de datos" - -#~ msgid "New lot" -#~ msgstr "Nuevo lote" - -#~ msgid "OCA Stock Barcodes reader" -#~ msgstr "Lector de códigos de barras (OCA)" - -#~ msgid "Reserved:" -#~ msgstr "Reservado:" - -#~ msgid "Set a default value for scanner." -#~ msgstr "Establece un valor por defecto para el lector." - -#~ msgid "There are no stock moves to assign this operation" -#~ msgstr "No hay movimientos de stock para realizar esta operación" - -#~ msgid "Waiting for input lot" -#~ msgstr "A la espera de leer lote" - -#~ msgid "Procurement purchase grouping settings" -#~ msgstr "Agrupación de la compra abastecida" - -#, fuzzy -#~| msgid "Picking" -#~ msgid "Stock Picking" -#~ msgstr "Albarán" - -#~ msgid "The operation type determines the picking view" -#~ msgstr "El tipo de albarán determina la vista de albarán" - -#~ msgid "barcode" -#~ msgstr "código de barras" - -#~ msgid "This product has not prepare to receipt" -#~ msgstr "Este producto no está listo para ser recibido" - -#~ msgid "# Scan log" -#~ msgstr "Lecturas" - -#, fuzzy -#~ msgid "Barcode read successfully" -#~ msgstr "Leer más" diff --git a/stock_barcodes/i18n/stock_barcodes.pot b/stock_barcodes/i18n/stock_barcodes.pot index 73afca3975fe..f1cb42070485 100644 --- a/stock_barcodes/i18n/stock_barcodes.pot +++ b/stock_barcodes/i18n/stock_barcodes.pot @@ -6,6 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 15.0\n" "Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-07-19 08:31+0000\n" +"PO-Revision-Date: 2024-07-19 08:31+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -262,6 +264,11 @@ msgstr "" msgid "Back" msgstr "" +#. module: stock_barcodes +#: model:ir.model.fields,field_description:stock_barcodes.field_stock_move__barcode_backorder_action +msgid "Backorder action" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read__barcode #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_inventory__barcode @@ -448,6 +455,12 @@ msgstr "" msgid "Context" msgstr "" +#. module: stock_barcodes +#: model:ir.model.fields.selection,name:stock_barcodes.selection__stock_move__barcode_backorder_action__create_backorder +#: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_todo_kanban +msgid "Create Backorder" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_stock_barcodes_option_group__create_lot msgid "Create lots if not match" @@ -511,6 +524,13 @@ msgstr "" msgid "Customers" msgstr "" +#. module: stock_barcodes +#: model:ir.model.fields,help:stock_barcodes.field_wiz_stock_barcodes_read__product_uom_id +#: model:ir.model.fields,help:stock_barcodes.field_wiz_stock_barcodes_read_inventory__product_uom_id +#: model:ir.model.fields,help:stock_barcodes.field_wiz_stock_barcodes_read_picking__product_uom_id +msgid "Default unit of measure used for all stock operations." +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,help:stock_barcodes.field_stock_barcodes_option_group__manual_entry msgid "Default value when open scan interface" @@ -825,6 +845,12 @@ msgstr "" msgid "Inventory quants" msgstr "" +#. module: stock_barcodes +#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_picking__todo_line_is_extra_line +#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_todo__is_extra_line +msgid "Is Extra Line" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_stock_barcodes_option_group__is_manual_confirm #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read__is_manual_confirm @@ -1081,6 +1107,11 @@ msgstr "" msgid "Move Line" msgstr "" +#. module: stock_barcodes +#: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_todo_kanban +msgid "NOT AVAILABLE" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_stock_barcodes_action__name #: model:ir.model.fields,field_description:stock_barcodes.field_stock_barcodes_option__name @@ -1105,6 +1136,12 @@ msgstr "" msgid "New Picking Barcode Option Group" msgstr "" +#. module: stock_barcodes +#: model:ir.model.fields.selection,name:stock_barcodes.selection__stock_move__barcode_backorder_action__skip_backorder +#: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_todo_kanban +msgid "No Backorder" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields.selection,name:stock_barcodes.selection__wiz_stock_barcodes_read__message_type__not_found #: model:ir.model.fields.selection,name:stock_barcodes.selection__wiz_stock_barcodes_read_inventory__message_type__not_found @@ -1135,6 +1172,11 @@ msgstr "" msgid "OK" msgstr "" +#. module: stock_barcodes +#: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_todo_kanban +msgid "Odoo will not create a backorder for this move. Are you sure?" +msgstr "" + #. module: stock_barcodes #: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_read_picking_form msgid "Open" @@ -1228,6 +1270,7 @@ msgid "Partner" msgstr "" #. module: stock_barcodes +#: model:ir.model.fields.selection,name:stock_barcodes.selection__stock_move__barcode_backorder_action__pending #: model:ir.model.fields.selection,name:stock_barcodes.selection__stock_move_line__barcode_scan_state__pending #: model:ir.model.fields.selection,name:stock_barcodes.selection__wiz_stock_barcodes_read_todo__state__pending msgid "Pending" @@ -1338,13 +1381,6 @@ msgstr "" msgid "Product Qty. Done" msgstr "" -#. module: stock_barcodes -#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read__product_uom_id -#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_inventory__product_uom_id -#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_picking__product_uom_id -msgid "Product Uom" -msgstr "" - #. module: stock_barcodes #: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_read_picking_form msgid "Put in pack" @@ -1428,6 +1464,11 @@ msgstr "" msgid "Reserved" msgstr "" +#. module: stock_barcodes +#: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_todo_kanban +msgid "Restore to pending" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read__result_package_id #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_inventory__result_package_id @@ -1575,6 +1616,7 @@ msgid "Steps to scan" msgstr "" #. module: stock_barcodes +#: model:ir.model,name:stock_barcodes.model_stock_move #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_todo__stock_move_ids msgid "Stock Move" msgstr "" @@ -1608,6 +1650,11 @@ msgid "" " an operation type. It will be used to create a non planned picking." msgstr "" +#. module: stock_barcodes +#: model_terms:ir.ui.view,arch_db:stock_barcodes.view_stock_barcodes_todo_kanban +msgid "This move will be set to pending. Are you sure?" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_picking__todo_line_ids msgid "To Do Lines" @@ -1651,6 +1698,13 @@ msgstr "" msgid "Type of operation" msgstr "" +#. module: stock_barcodes +#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read__product_uom_id +#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_inventory__product_uom_id +#: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_picking__product_uom_id +msgid "Unit of Measure" +msgstr "" + #. module: stock_barcodes #: model:ir.model.fields,field_description:stock_barcodes.field_wiz_stock_barcodes_read_todo__uom_id msgid "Uom" @@ -1746,7 +1800,7 @@ msgstr "" #. module: stock_barcodes #: model:ir.model,name:stock_barcodes.model_wiz_stock_barcodes_read_picking -msgid "Wizard to read barcode on picking batch" +msgid "Wizard to read barcode on picking" msgstr "" #. module: stock_barcodes diff --git a/stock_barcodes/models/__init__.py b/stock_barcodes/models/__init__.py index 38b0f8d03c46..5b74abcc093b 100644 --- a/stock_barcodes/models/__init__.py +++ b/stock_barcodes/models/__init__.py @@ -1,6 +1,7 @@ from . import stock_barcodes_action from . import stock_barcodes_option from . import stock_barcodes_read_log +from . import stock_move from . import stock_move_line from . import stock_picking from . import stock_picking_type diff --git a/stock_barcodes/models/stock_move.py b/stock_barcodes/models/stock_move.py new file mode 100644 index 000000000000..13a031f591d5 --- /dev/null +++ b/stock_barcodes/models/stock_move.py @@ -0,0 +1,29 @@ +# Copyright 2024 Tecnativa - Sergio Teruel +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class StockMove(models.Model): + _inherit = "stock.move" + + barcode_backorder_action = fields.Selection( + [ + ("pending", "Pending"), + ("create_backorder", "Create Backorder"), + ("skip_backorder", "No Backorder"), + ], + string="Backorder action", + default="pending", + ) + + def _action_done(self, cancel_backorder=False): + moves_cancel_backorder = self.browse() + if not cancel_backorder: + moves_cancel_backorder = self.filtered( + lambda sm: sm.barcode_backorder_action == "skip_backorder" + ) + super(StockMove, moves_cancel_backorder)._action_done(cancel_backorder=True) + return super(StockMove, self - moves_cancel_backorder)._action_done( + cancel_backorder=cancel_backorder + ) diff --git a/stock_barcodes/models/stock_move_line.py b/stock_barcodes/models/stock_move_line.py index 30d530e0e72f..159be24ee989 100644 --- a/stock_barcodes/models/stock_move_line.py +++ b/stock_barcodes/models/stock_move_line.py @@ -28,19 +28,13 @@ def _barcodes_process_line_to_unlink(self): def action_barcode_detailed_operation_unlink(self): for sml in self: - if sml.product_uom_qty: - sml._barcodes_process_line_to_unlink() - else: - sml.unlink() + stock_move = sml.move_id + stock_move.barcode_backorder_action = "pending" + sml.unlink() # HACK: To force refresh wizard values wiz_barcode = self.env["wiz.stock.barcodes.read.picking"].browse( self.env.context.get("wiz_barcode_id", False) ) - if wiz_barcode.option_group_id.barcode_guided_mode == "guided": - wiz_barcode.todo_line_id.line_ids = wiz_barcode.todo_line_id.line_ids - if not any(wiz_barcode.todo_line_id.line_ids.mapped("qty_done")): - wiz_barcode.fill_todo_records() - wiz_barcode.determine_todo_action() - else: - wiz_barcode.fill_todo_records() - wiz_barcode.todo_line_id.line_ids = wiz_barcode.todo_line_id.line_ids + stock_move._action_assign() + wiz_barcode.fill_todo_records() + wiz_barcode.determine_todo_action() diff --git a/stock_barcodes/models/stock_picking.py b/stock_barcodes/models/stock_picking.py index 9d4145961cb7..b3970c062b21 100644 --- a/stock_barcodes/models/stock_picking.py +++ b/stock_barcodes/models/stock_picking.py @@ -1,7 +1,6 @@ # Copyright 2019 Sergio Teruel # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from odoo import models -from odoo.tools.float_utils import float_compare class StockPicking(models.Model): @@ -33,8 +32,8 @@ def action_barcode_scan(self, option_group=False): wiz = self.env["wiz.stock.barcodes.read.picking"].create( self._prepare_barcode_wiz_vals(option_group) ) - wiz.determine_todo_action() wiz.fill_pending_moves() + wiz.determine_todo_action() action = self.env["ir.actions.actions"]._for_xml_id( "stock_barcodes.action_stock_barcodes_read_picking" ) @@ -42,41 +41,16 @@ def action_barcode_scan(self, option_group=False): return action def button_validate(self): - if ( - self.picking_type_id.barcode_option_group_id.auto_put_in_pack - and not self.move_line_ids.mapped("result_package_id") - ): - self.action_put_in_pack() - create_backorder = False + put_in_pack_picks = self.filtered( + lambda p: p.picking_type_id.barcode_option_group_id.auto_put_in_pack + and not p.move_line_ids.result_package_id + ) + if put_in_pack_picks: + put_in_pack_picks.action_put_in_pack() # Variable initialized as True to optimize break loop - skip_backorder = True if self.env.context.get("stock_barcodes_validate_picking", False): - # Avoid backorder when all move lines are processed (done or done_forced) - prec = self.env["decimal.precision"].precision_get( - "Product Unit of Measure" - ) - for move in self.move_lines.filtered(lambda sm: sm.state != "cancel"): - if ( - float_compare( - move.quantity_done, move.product_uom_qty, precision_digits=prec - ) - < 0 - ): - # In normal conditions backorder will be created - create_backorder = True - if not move.move_line_ids or any( - sml.barcode_scan_state in ["pending"] - for sml in move.move_line_ids - ): - # If any move are not processed we can not skip backorder - skip_backorder = False - break - if create_backorder and skip_backorder: res = super( - StockPicking, - self.with_context( - picking_ids_not_to_backorder=self.ids, skip_backorder=True - ), + StockPicking, self.with_context(skip_backorder=True) ).button_validate() else: res = super().button_validate() diff --git a/stock_barcodes/models/stock_picking_type.py b/stock_barcodes/models/stock_picking_type.py index 6f0277723fb5..f83b5b3d5eb7 100644 --- a/stock_barcodes/models/stock_picking_type.py +++ b/stock_barcodes/models/stock_picking_type.py @@ -46,8 +46,8 @@ def action_barcode_scan(self): ): vals["location_dest_id"] = self.default_location_dest_id.id wiz = self.env["wiz.stock.barcodes.read.picking"].create(vals) - wiz.determine_todo_action() wiz.fill_pending_moves() + wiz.determine_todo_action() action = self.env["ir.actions.actions"]._for_xml_id( "stock_barcodes.action_stock_barcodes_read_picking" ) diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking.py b/stock_barcodes/wizard/stock_barcodes_read_picking.py index d868550fd5ab..85198c40b788 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_picking.py +++ b/stock_barcodes/wizard/stock_barcodes_read_picking.py @@ -74,6 +74,7 @@ def _field_candidate_ids(self): picking_location_id = fields.Many2one(related="picking_id.location_id") picking_location_dest_id = fields.Many2one(related="picking_id.location_dest_id") company_id = fields.Many2one(related="picking_id.company_id") + todo_line_is_extra_line = fields.Boolean(related="todo_line_id.is_extra_line") @api.depends("todo_line_id") def _compute_todo_line_display_ids(self): @@ -141,8 +142,8 @@ def onchange_picking_id(self): # view, so for create a candidate picking with the same default picking # we need create it in this onchange self._set_default_picking() - self.determine_todo_action() self.fill_pending_moves() + self.determine_todo_action() def get_sorted_move_lines(self, move_lines): location_field = self.option_group_id.location_field_to_sort @@ -180,12 +181,8 @@ def _get_stock_move_lines_todo(self): return move_lines def fill_pending_moves(self): - if ( - self.option_group_id.barcode_guided_mode != "guided" - and self.option_group_id.show_pending_moves - and not self.todo_line_ids - ): - self.fill_todo_records() + # TODO: Unify method + self.fill_todo_records() def get_moves_or_move_lines(self): if self.option_group_id.source_pending_moves == "move_line_ids": @@ -193,6 +190,9 @@ def get_moves_or_move_lines(self): else: return self.picking_id.move_lines + def get_moves(self): + return self.picking_id.move_lines + def fill_todo_records(self): move_lines = self.get_sorted_move_lines(self.get_moves_or_move_lines()) self.env["wiz.stock.barcodes.read.todo"].fill_records(self, [move_lines]) @@ -211,12 +211,6 @@ def determine_todo_action(self, forced_todo_line=False): self.visible_force_done = self.env.context.get("visible_force_done", False) if not self.option_group_id.barcode_guided_mode == "guided": return False - if not self.todo_line_ids: - self.fill_todo_records() - # When scanning all information in one step (e.g. using GS-1), the - # status and qty processed might have not been update, we ensure it - # invalidating the cache. - self.todo_line_ids.invalidate_cache() self.todo_line_id = ( forced_todo_line or self.todo_line_ids.filtered(lambda t: t._origin.state == "pending")[:1] @@ -278,9 +272,9 @@ def action_done(self): if not self.keep_screen_values or self.todo_line_id.state != "pending": if not self.env.context.get("skip_clean_values", False): self.action_clean_values() - self.determine_todo_action() - else: - self.action_show_step() + self.fill_todo_records() + self.determine_todo_action() + self.action_show_step() # Now we can add read log with details. _logger.info("Add scanned log barcode:{}".format(self.barcode)) self._add_read_log(log_detail=move_dic) @@ -615,13 +609,14 @@ def _process_stock_move_line(self): # noqa: C901 # link this new lines to the todo line details # If user scan a product distinct of the todo line we need link to other # alternative move - if move_to_link_in_todo_line and self.todo_line_id: - todo_line = self.todo_line_id - else: - todo_line = self.todo_line_ids.filtered( - lambda ln: ln.product_id == self.product_id - ) - todo_line.line_ids = [(4, sml.id) for sml in stock_move_lines] + if self.option_group_id.source_pending_moves != "move_line_ids": + if move_to_link_in_todo_line and self.todo_line_id: + todo_line = self.todo_line_id + else: + todo_line = self.todo_line_ids.filtered( + lambda ln: ln.product_id == self.product_id + ) + todo_line.line_ids = [(4, sml.id) for sml in stock_move_lines] self.update_fields_after_process_stock(moves_todo) return move_lines_dic diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml b/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml index 6dd81fa3e0b2..92ed8320584f 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml +++ b/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml @@ -127,6 +127,7 @@ + + + + {'invisible': [('todo_line_is_extra_line', '!=', False)]} + - + + diff --git a/stock_barcodes/wizard/stock_barcodes_read_todo.py b/stock_barcodes/wizard/stock_barcodes_read_todo.py index bd69356fa965..a2e7819ff342 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_todo.py +++ b/stock_barcodes/wizard/stock_barcodes_read_todo.py @@ -1,8 +1,9 @@ # Copyright 2019 Sergio Teruel # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from collections import OrderedDict +from collections import OrderedDict, defaultdict from odoo import api, fields, models +from odoo.tools.float_utils import float_compare from odoo.tools.safe_eval import safe_eval @@ -62,6 +63,7 @@ class WizStockBarcodesReadTodo(models.TransientModel): stock_move_ids = fields.Many2many(comodel_name="stock.move") position_index = fields.Integer() picking_code = fields.Char("Type of Operation") + is_extra_line = fields.Boolean() def _group_key(self, wiz, line): group_key_for_todo_records = wiz.option_group_id.group_key_for_todo_records @@ -82,13 +84,14 @@ def _get_all_products_quantities_in_package(self, package): def _prepare_fill_record_values(self, wiz_barcode, line, position): vals = { + "wiz_barcode_id": wiz_barcode.id, "product_id": line.product_id.id, "product_uom_qty": line.product_uom_qty, "name": "To do action", "position_index": position, "picking_code": line.picking_code, } - if wiz_barcode.option_group_id.source_pending_moves == "move_line_ids": + if line._name == "stock.move.line": package_product_dic = self._get_all_products_quantities_in_package( line.package_id ) @@ -147,9 +150,13 @@ def fill_records(self, wiz_barcode, lines_list): :param lines_list: browse list :return: """ - wiz_barcode.todo_line_ids = self.browse() + wiz_barcode.todo_line_ids.unlink() + wiz_barcode.todo_line_id = False + self.position_index = 0 todo_vals = OrderedDict() position = 0 + move_qty_dic = defaultdict(float) + is_stock_move_line_origin = lines_list[0]._name == "stock.move.line" for lines in lines_list: for line in lines: key = self._group_key(wiz_barcode, line) @@ -162,11 +169,53 @@ def fill_records(self, wiz_barcode, lines_list): todo_vals[key] = self._update_fill_record_values( wiz_barcode, line, todo_vals[key] ) + if is_stock_move_line_origin: + move_qty_dic[line.move_id] += max( + line.product_uom_qty, line.qty_done + ) + else: + move_qty_dic[line] += max(line.product_uom_qty, line.quantity_done) + for move in wiz_barcode.get_moves(): + qty = move_qty_dic[move] + if ( + move.barcode_backorder_action == "pending" + and move.product_uom_qty > qty + ): + vals = self._prepare_fill_record_values(wiz_barcode, move, position) + vals.update( + { + "product_uom_qty": move.product_uom_qty - qty, + "product_qty_reserved": 0.0, + "line_ids": False, + "is_extra_line": True, + } + ) + todo_vals[ + ( + move, + "M", + ) + ] = vals + position += 1 wiz_barcode.todo_line_ids = self.create(list(todo_vals.values())) def action_todo_next(self): self.state = "done_forced" self.line_ids.barcode_scan_state = "done_forced" + for sml in self.line_ids: + if sml.product_uom_qty != sml.qty_done: + sml.product_uom_qty = sml.qty_done + if self.is_extra_line: + barcode_backorder_action = self.env.context.get( + "barcode_backorder_action", "create_backorder" + ) + self.stock_move_ids.barcode_backorder_action = barcode_backorder_action + if barcode_backorder_action == "pending": + self.stock_move_ids.move_line_ids.unlink() + self.stock_move_ids._action_assign() + wiz_barcode = self.wiz_barcode_id + self.wiz_barcode_id.fill_todo_records() + self.wiz_barcode_id = wiz_barcode self.wiz_barcode_id.determine_todo_action() def action_reset_lines(self): @@ -174,6 +223,7 @@ def action_reset_lines(self): self.line_ids.barcode_scan_state = "pending" self.line_ids.qty_done = 0.0 self.wiz_barcode_id.action_clean_values() + self.wiz_barcode_id.fill_todo_records() self.wiz_barcode_id.determine_todo_action() def action_back_line(self): @@ -201,11 +251,21 @@ def _compute_qty_done(self): ) def _compute_state(self): for rec in self: - if rec.qty_done >= rec.product_uom_qty or ( + if float_compare( + rec.qty_done, + rec.product_uom_qty, + precision_rounding=rec.uom_id.rounding, + ) > -1 or ( rec.wiz_barcode_id.option_group_id.source_pending_moves == "move_line_ids" and rec.line_ids - and not any(ln.barcode_scan_state == "pending" for ln in rec.line_ids) + and ( + sum(rec.stock_move_ids.mapped("quantity_done")) + >= sum(rec.stock_move_ids.mapped("product_uom_qty")) + or not any( + ln.barcode_scan_state == "pending" for ln in rec.line_ids + ) + ) ): rec.state = "done" else: diff --git a/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml b/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml index 35ab1be6264a..d0def9962446 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml +++ b/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml @@ -20,11 +20,12 @@ +
@@ -100,6 +101,10 @@ t-esc="record.product_uom_qty.value" /> + NOT AVAILABLE
@@ -135,7 +140,7 @@ class="btn btn-warning pull-right btn-sm" context="{'wiz_barcode_id': parent.id}" data-hotkey="3" - attrs="{'invisible': [('qty_done', '=', 0.0)]} " + attrs="{'invisible': ['|', ('qty_done', '=', 0.0), ('is_extra_line', '=', True)]} " > Ignore rest @@ -145,7 +150,7 @@ class="btn btn-danger pull-right btn-sm" context="{'wiz_barcode_id': parent.id}" data-hotkey="3" - attrs="{'invisible': [('qty_done', '!=', 0.0)]} " + attrs="{'invisible': ['|', ('qty_done', '!=', 0.0), ('is_extra_line', '=', True)]} " confirm="You have not set any quantity to this operation and it will be removed from pending moves. Are you sure?" > Ignore rest @@ -163,6 +168,42 @@
+
+
+ + + +
+
From 2bc5eb755ae517479a9d20df86a3fca5f20e8528 Mon Sep 17 00:00:00 2001 From: Carlos Dauden Date: Wed, 24 Jul 2024 14:35:43 +0200 Subject: [PATCH 02/12] [FIX] stock_barcodes: Unexpected done quantities must reduce qty_available TT50148 --- stock_barcodes/wizard/stock_barcodes_read.py | 17 +++++++++++++++++ .../stock_barcodes_read_picking_views.xml | 2 ++ 2 files changed, 19 insertions(+) diff --git a/stock_barcodes/wizard/stock_barcodes_read.py b/stock_barcodes/wizard/stock_barcodes_read.py index 23cc9b9aa2bf..0637783f038f 100644 --- a/stock_barcodes/wizard/stock_barcodes_read.py +++ b/stock_barcodes/wizard/stock_barcodes_read.py @@ -3,6 +3,7 @@ import logging from odoo import _, api, fields, models +from odoo.tools import float_round _logger = logging.getLogger(__name__) @@ -139,6 +140,22 @@ def _compute_qty_available(self): domain_quant, ["quantity"], [], orderby="id" ) self.qty_available = groups[0]["quantity"] + # Unexpected done quantities must reduce qty_available + if self.lot_id: + done_move_lines = self.move_line_ids.filtered( + lambda m: m.product_id == self.product_id and m.lot_id == self.lot_id + ) + else: + done_move_lines = self.move_line_ids.filtered( + lambda m: m.product_id == self.product_id + ) + for sml in done_move_lines: + over_done_qty = float_round( + sml.qty_done - sml.product_uom_qty, + precision_rounding=sml.product_uom_id.rounding, + ) + if over_done_qty > 0.0: + self.qty_available -= over_done_qty @api.depends("product_id") def _compute_display_assign_serial(self): diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml b/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml index 92ed8320584f..a3bf11998367 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml +++ b/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml @@ -186,6 +186,8 @@ + + Date: Fri, 2 Aug 2024 16:19:31 +0200 Subject: [PATCH 03/12] [FIX] stock_barcodes: In not manual validation view is not refresh when not pending moves Force refresh candidate pickings to show green if not pending moves TT48788 --- stock_barcodes/wizard/stock_barcodes_read_picking.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking.py b/stock_barcodes/wizard/stock_barcodes_read_picking.py index 85198c40b788..e4e0fc84ce89 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_picking.py +++ b/stock_barcodes/wizard/stock_barcodes_read_picking.py @@ -275,6 +275,9 @@ def action_done(self): self.fill_todo_records() self.determine_todo_action() self.action_show_step() + # Force refresh candidate pickings to show green if not pending moves + if not self.pending_move_ids: + self._set_candidate_pickings(self.picking_id) # Now we can add read log with details. _logger.info("Add scanned log barcode:{}".format(self.barcode)) self._add_read_log(log_detail=move_dic) From 6fe8b3d1749cd1e89bc75455c1d3bff9b6e9843c Mon Sep 17 00:00:00 2001 From: Carlos Dauden Date: Mon, 5 Aug 2024 13:06:03 +0200 Subject: [PATCH 04/12] [FIX] stock_barcodes: Keep values don't work after force refresh changes. TT50421 --- stock_barcodes/wizard/stock_barcodes_read_picking.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking.py b/stock_barcodes/wizard/stock_barcodes_read_picking.py index e4e0fc84ce89..12f56a66eb42 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_picking.py +++ b/stock_barcodes/wizard/stock_barcodes_read_picking.py @@ -272,9 +272,14 @@ def action_done(self): if not self.keep_screen_values or self.todo_line_id.state != "pending": if not self.env.context.get("skip_clean_values", False): self.action_clean_values() + keep_vals = {} + else: + keep_vals = self._convert_to_write(self._cache) self.fill_todo_records() self.determine_todo_action() self.action_show_step() + if keep_vals: + self.update_keep_values(keep_vals) # Force refresh candidate pickings to show green if not pending moves if not self.pending_move_ids: self._set_candidate_pickings(self.picking_id) @@ -287,6 +292,13 @@ def action_done(self): self._add_read_log() return res + def update_keep_values(self, keep_vals): + options = self.option_group_id.option_ids + fields_to_keep = options.filtered( + lambda op: self._fields[op.field_name].type != "float" + ).mapped("field_name") + self.update({f_name: keep_vals[f_name] for f_name in fields_to_keep}) + def action_manual_entry(self): result = super().action_manual_entry() if result: From f1e9a8b5ebb1a637e8037e61f785edd20f9f9d76 Mon Sep 17 00:00:00 2001 From: sergio-teruel Date: Mon, 2 Sep 2024 10:21:41 +0200 Subject: [PATCH 05/12] [IMP] stock_barcodes_*: Move todo record methods to picking read wizard --- .../wizard/stock_barcodes_read_picking.py | 138 +++++++++++++++++- .../wizard/stock_barcodes_read_todo.py | 136 ----------------- stock_barcodes_elaboration/wizard/__init__.py | 1 + .../wizard/stock_barcodes_read_picking.py | 12 ++ .../wizard/stock_barcodes_read_todo.py | 5 - .../wizard/stock_barcodes_read_picking.py | 28 ++++ .../wizard/stock_barcodes_read_todo.py | 28 ---- 7 files changed, 178 insertions(+), 170 deletions(-) create mode 100644 stock_barcodes_elaboration/wizard/stock_barcodes_read_picking.py diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking.py b/stock_barcodes/wizard/stock_barcodes_read_picking.py index 12f56a66eb42..887eaffb68bb 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_picking.py +++ b/stock_barcodes/wizard/stock_barcodes_read_picking.py @@ -1,11 +1,13 @@ # Copyright 2019 Sergio Teruel # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging +from collections import OrderedDict, defaultdict from odoo import _, api, fields, models from odoo.exceptions import ValidationError from odoo.fields import first from odoo.tools.float_utils import float_compare +from odoo.tools.safe_eval import safe_eval _logger = logging.getLogger(__name__) @@ -195,7 +197,7 @@ def get_moves(self): def fill_todo_records(self): move_lines = self.get_sorted_move_lines(self.get_moves_or_move_lines()) - self.env["wiz.stock.barcodes.read.todo"].fill_records(self, [move_lines]) + self.fill_records([move_lines]) @api.model def _get_fields_filled_special(self): @@ -794,6 +796,140 @@ def _option_required_hook(self, option_required): return bool(self.location_dest_id) return super()._option_required_hook(option_required) + def _group_key(self, line): + group_key_for_todo_records = self.option_group_id.group_key_for_todo_records + if group_key_for_todo_records: + return safe_eval(group_key_for_todo_records, globals_dict={"object": line}) + if self.option_group_id.source_pending_moves == "move_line_ids": + return (line.location_id, line.product_id, line.lot_id, line.package_id) + else: + return (line.location_id, line.product_id) + + def _get_all_products_quantities_in_package(self, package): + res = {} + for quant in package._get_contained_quants(): + if quant.product_id not in res: + res[quant.product_id] = 0 + res[quant.product_id] += quant.quantity + return res + + def _prepare_fill_record_values(self, line, position): + vals = { + "wiz_barcode_id": self.id, + "product_id": line.product_id.id, + "product_uom_qty": line.product_uom_qty, + "name": "To do action", + "position_index": position, + "picking_code": line.picking_code, + } + if line._name == "stock.move.line": + package_product_dic = self._get_all_products_quantities_in_package( + line.package_id + ) + vals.update( + { + "location_id": line.location_id.id, + "location_dest_id": line.location_dest_id.id, + "lot_id": line.lot_id.id, + "package_id": line.package_id.id, + "result_package_id": line.result_package_id.id, + "uom_id": line.product_uom_id.id, + "product_qty_reserved": line.product_qty, + "line_ids": [(6, 0, line.ids)], + "stock_move_ids": [(6, 0, line.move_id.ids)], + "package_product_qty": package_product_dic + and package_product_dic[line.product_id] + or 0.0, + } + ) + else: + vals.update( + { + "location_id": (line.move_line_ids[:1] or line).location_id.id, + "location_dest_id": ( + line.move_line_ids[:1] or line + ).location_dest_id.id, + "uom_id": line.product_uom.id, + "product_qty_reserved": line.move_line_ids + and sum(line.move_line_ids.mapped("product_qty")) + or line.product_uom_qty, + "line_ids": [(6, 0, line.move_line_ids.ids)], + "stock_move_ids": [(6, 0, line.ids)], + } + ) + return vals + + def _update_fill_record_values(self, line, vals): + vals["product_uom_qty"] += line.product_uom_qty + if self.option_group_id.source_pending_moves == "move_line_ids": + vals["product_qty_reserved"] += line.product_qty + vals["line_ids"][0][2].append(line.id) + vals["stock_move_ids"][0][2].append(line.move_id.id) + else: + vals["product_qty_reserved"] += ( + line.move_line_ids + and sum(line.move_line_ids.mapped("product_qty")) + or line.product_uom_qty + ) + vals["line_ids"][0][2].extend(line.move_line_ids.ids) + vals["stock_move_ids"][0][2].extend(line.ids) + return vals + + @api.model + def fill_records(self, lines_list): + """ + :param lines_list: browse list + :return: + """ + self.todo_line_ids.unlink() + self.todo_line_id = False + # self.position_index = 0 + todo_vals = OrderedDict() + position = 0 + move_qty_dic = defaultdict(float) + is_stock_move_line_origin = lines_list[0]._name == "stock.move.line" + for lines in lines_list: + for line in lines: + key = self._group_key(line) + if key not in todo_vals: + todo_vals[key] = self._prepare_fill_record_values(line, position) + position += 1 + else: + todo_vals[key] = self._update_fill_record_values( + line, todo_vals[key] + ) + if is_stock_move_line_origin: + move_qty_dic[line.move_id] += max( + line.product_uom_qty, line.qty_done + ) + else: + move_qty_dic[line] += max(line.product_uom_qty, line.quantity_done) + for move in self.get_moves(): + qty = move_qty_dic[move] + if ( + move.barcode_backorder_action == "pending" + and move.product_uom_qty > qty + ): + vals = self._prepare_fill_record_values(move, position) + vals.update( + { + "product_uom_qty": move.product_uom_qty - qty, + "product_qty_reserved": 0.0, + "line_ids": False, + "is_extra_line": True, + } + ) + todo_vals[ + ( + move, + "M", + ) + ] = vals + position += 1 + self.todo_line_ids = self.env["wiz.stock.barcodes.read.todo"].create( + list(todo_vals.values()) + ) + class WizCandidatePicking(models.TransientModel): """ diff --git a/stock_barcodes/wizard/stock_barcodes_read_todo.py b/stock_barcodes/wizard/stock_barcodes_read_todo.py index a2e7819ff342..26d5bd081ac4 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_todo.py +++ b/stock_barcodes/wizard/stock_barcodes_read_todo.py @@ -1,10 +1,8 @@ # Copyright 2019 Sergio Teruel # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from collections import OrderedDict, defaultdict from odoo import api, fields, models from odoo.tools.float_utils import float_compare -from odoo.tools.safe_eval import safe_eval class WizStockBarcodesReadTodo(models.TransientModel): @@ -65,140 +63,6 @@ class WizStockBarcodesReadTodo(models.TransientModel): picking_code = fields.Char("Type of Operation") is_extra_line = fields.Boolean() - def _group_key(self, wiz, line): - group_key_for_todo_records = wiz.option_group_id.group_key_for_todo_records - if group_key_for_todo_records: - return safe_eval(group_key_for_todo_records, globals_dict={"object": line}) - if wiz.option_group_id.source_pending_moves == "move_line_ids": - return (line.location_id, line.product_id, line.lot_id, line.package_id) - else: - return (line.location_id, line.product_id) - - def _get_all_products_quantities_in_package(self, package): - res = {} - for quant in package._get_contained_quants(): - if quant.product_id not in res: - res[quant.product_id] = 0 - res[quant.product_id] += quant.quantity - return res - - def _prepare_fill_record_values(self, wiz_barcode, line, position): - vals = { - "wiz_barcode_id": wiz_barcode.id, - "product_id": line.product_id.id, - "product_uom_qty": line.product_uom_qty, - "name": "To do action", - "position_index": position, - "picking_code": line.picking_code, - } - if line._name == "stock.move.line": - package_product_dic = self._get_all_products_quantities_in_package( - line.package_id - ) - vals.update( - { - "location_id": line.location_id.id, - "location_dest_id": line.location_dest_id.id, - "lot_id": line.lot_id.id, - "package_id": line.package_id.id, - "result_package_id": line.result_package_id.id, - "uom_id": line.product_uom_id.id, - "product_qty_reserved": line.product_qty, - "line_ids": [(6, 0, line.ids)], - "stock_move_ids": [(6, 0, line.move_id.ids)], - "package_product_qty": package_product_dic - and package_product_dic[line.product_id] - or 0.0, - } - ) - else: - vals.update( - { - "location_id": (line.move_line_ids[:1] or line).location_id.id, - "location_dest_id": ( - line.move_line_ids[:1] or line - ).location_dest_id.id, - "uom_id": line.product_uom.id, - "product_qty_reserved": line.move_line_ids - and sum(line.move_line_ids.mapped("product_qty")) - or line.product_uom_qty, - "line_ids": [(6, 0, line.move_line_ids.ids)], - "stock_move_ids": [(6, 0, line.ids)], - } - ) - return vals - - def _update_fill_record_values(self, wiz_barcode, line, vals): - vals["product_uom_qty"] += line.product_uom_qty - if wiz_barcode.option_group_id.source_pending_moves == "move_line_ids": - vals["product_qty_reserved"] += line.product_qty - vals["line_ids"][0][2].append(line.id) - vals["stock_move_ids"][0][2].append(line.move_id.id) - else: - vals["product_qty_reserved"] += ( - line.move_line_ids - and sum(line.move_line_ids.mapped("product_qty")) - or line.product_uom_qty - ) - vals["line_ids"][0][2].extend(line.move_line_ids.ids) - vals["stock_move_ids"][0][2].extend(line.ids) - return vals - - @api.model - def fill_records(self, wiz_barcode, lines_list): - """ - :param lines_list: browse list - :return: - """ - wiz_barcode.todo_line_ids.unlink() - wiz_barcode.todo_line_id = False - self.position_index = 0 - todo_vals = OrderedDict() - position = 0 - move_qty_dic = defaultdict(float) - is_stock_move_line_origin = lines_list[0]._name == "stock.move.line" - for lines in lines_list: - for line in lines: - key = self._group_key(wiz_barcode, line) - if key not in todo_vals: - todo_vals[key] = self._prepare_fill_record_values( - wiz_barcode, line, position - ) - position += 1 - else: - todo_vals[key] = self._update_fill_record_values( - wiz_barcode, line, todo_vals[key] - ) - if is_stock_move_line_origin: - move_qty_dic[line.move_id] += max( - line.product_uom_qty, line.qty_done - ) - else: - move_qty_dic[line] += max(line.product_uom_qty, line.quantity_done) - for move in wiz_barcode.get_moves(): - qty = move_qty_dic[move] - if ( - move.barcode_backorder_action == "pending" - and move.product_uom_qty > qty - ): - vals = self._prepare_fill_record_values(wiz_barcode, move, position) - vals.update( - { - "product_uom_qty": move.product_uom_qty - qty, - "product_qty_reserved": 0.0, - "line_ids": False, - "is_extra_line": True, - } - ) - todo_vals[ - ( - move, - "M", - ) - ] = vals - position += 1 - wiz_barcode.todo_line_ids = self.create(list(todo_vals.values())) - def action_todo_next(self): self.state = "done_forced" self.line_ids.barcode_scan_state = "done_forced" diff --git a/stock_barcodes_elaboration/wizard/__init__.py b/stock_barcodes_elaboration/wizard/__init__.py index 8cc0c22b3a38..571c23004134 100644 --- a/stock_barcodes_elaboration/wizard/__init__.py +++ b/stock_barcodes_elaboration/wizard/__init__.py @@ -1 +1,2 @@ +from . import stock_barcodes_read_picking from . import stock_barcodes_read_todo diff --git a/stock_barcodes_elaboration/wizard/stock_barcodes_read_picking.py b/stock_barcodes_elaboration/wizard/stock_barcodes_read_picking.py new file mode 100644 index 000000000000..5b58dd497575 --- /dev/null +++ b/stock_barcodes_elaboration/wizard/stock_barcodes_read_picking.py @@ -0,0 +1,12 @@ +# Copyright 2019 Sergio Teruel +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import models + + +class WizStockBarcodesReadPicking(models.TransientModel): + _inherit = "wiz.stock.barcodes.read.picking" + + def _group_key(self, line): + key = super()._group_key(line) + key += (line.elaboration_ids,) + return key diff --git a/stock_barcodes_elaboration/wizard/stock_barcodes_read_todo.py b/stock_barcodes_elaboration/wizard/stock_barcodes_read_todo.py index d70dde3eabdf..d479c4c86521 100644 --- a/stock_barcodes_elaboration/wizard/stock_barcodes_read_todo.py +++ b/stock_barcodes_elaboration/wizard/stock_barcodes_read_todo.py @@ -21,8 +21,3 @@ def _compute_elaboration_ids(self): line.elaboration_note = ". ".join( m.elaboration_note for m in moves if m.elaboration_note ) - - def _group_key(self, wiz, line): - key = super(WizStockBarcodesReadTodo, self)._group_key(wiz, line) - key += (line.elaboration_ids,) - return key diff --git a/stock_barcodes_gs1_secondary_unit/wizard/stock_barcodes_read_picking.py b/stock_barcodes_gs1_secondary_unit/wizard/stock_barcodes_read_picking.py index 80b55f59f41b..a324fafeab3a 100644 --- a/stock_barcodes_gs1_secondary_unit/wizard/stock_barcodes_read_picking.py +++ b/stock_barcodes_gs1_secondary_unit/wizard/stock_barcodes_read_picking.py @@ -1,6 +1,7 @@ # Copyright 2019 Sergio Teruel # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from odoo import api, fields, models +from odoo.tools import float_compare class WizStockBarcodesReadPicking(models.TransientModel): @@ -51,3 +52,30 @@ def _update_stock_move_line(self, line, sml_vals): line.secondary_uom_qty + self.secondary_uom_qty ) return super()._update_stock_move_line(line, sml_vals) + + def _prepare_fill_record_values(self, line, position): + vals = super()._prepare_fill_record_values(line, position) + vals["secondary_uom_id"] = line.secondary_uom_id.id + if line._name == "stock.move.line": + move = line.move_id + # Set secondary qty when stock.move full match with stock.move.line + if not (move.move_line_ids - line) and not float_compare( + move.product_uom_qty, + line.product_uom_qty, + precision_rounding=line.product_uom_id.rounding, + ): + vals["secondary_uom_qty"] = move.secondary_uom_qty + elif line._name == "stock.move": + # Set secondary qty when stock.move all quantity is available + if not float_compare( + line.reserved_availability, + line.product_uom_qty, + precision_rounding=line.product_uom.rounding, + ): + vals["secondary_uom_qty"] = line.secondary_uom_qty + return vals + + def _group_key(self, line): + key = super()._group_key(line) + key += (line.secondary_uom_id,) + return key diff --git a/stock_barcodes_gs1_secondary_unit/wizard/stock_barcodes_read_todo.py b/stock_barcodes_gs1_secondary_unit/wizard/stock_barcodes_read_todo.py index 84041f9cc81b..edc28e5353d4 100644 --- a/stock_barcodes_gs1_secondary_unit/wizard/stock_barcodes_read_todo.py +++ b/stock_barcodes_gs1_secondary_unit/wizard/stock_barcodes_read_todo.py @@ -2,7 +2,6 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import api, models -from odoo.tools import float_compare class WizStockBarcodesReadTodo(models.TransientModel): @@ -13,35 +12,8 @@ class WizStockBarcodesReadTodo(models.TransientModel): "uom_field": "uom_id", } - def _prepare_fill_record_values(self, wiz_barcode, line, position): - vals = super()._prepare_fill_record_values(wiz_barcode, line, position) - vals["secondary_uom_id"] = line.secondary_uom_id.id - if line._name == "stock.move.line": - move = line.move_id - # Set secondary qty when stock.move full match with stock.move.line - if not (move.move_line_ids - line) and not float_compare( - move.product_uom_qty, - line.product_uom_qty, - precision_rounding=line.product_uom_id.rounding, - ): - vals["secondary_uom_qty"] = move.secondary_uom_qty - elif line._name == "stock.move": - # Set secondary qty when stock.move all quantity is available - if not float_compare( - line.reserved_availability, - line.product_uom_qty, - precision_rounding=line.product_uom.rounding, - ): - vals["secondary_uom_qty"] = line.secondary_uom_qty - return vals - @api.model def fields_to_fill_from_pending_line(self): res = super().fields_to_fill_from_pending_line() res.append("secondary_uom_id") return res - - def _group_key(self, wiz, line): - key = super()._group_key(wiz, line) - key += (line.secondary_uom_id,) - return key From 457d2a9ec853a0f8fab899b5b2bd2702718ffadf Mon Sep 17 00:00:00 2001 From: sergio-teruel Date: Tue, 17 Sep 2024 23:07:14 +0200 Subject: [PATCH 06/12] [IMP] stock_barcodes: Improve backorder management when source of todo lines is stock moves TT50894 --- stock_barcodes/models/stock_move.py | 6 ++++++ stock_barcodes/wizard/stock_barcodes_read_picking.py | 5 +++++ stock_barcodes/wizard/stock_barcodes_read_todo.py | 4 +++- .../wizard/stock_barcodes_read_todo_view.xml | 11 ++++++----- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/stock_barcodes/models/stock_move.py b/stock_barcodes/models/stock_move.py index 13a031f591d5..415e77ddc494 100644 --- a/stock_barcodes/models/stock_move.py +++ b/stock_barcodes/models/stock_move.py @@ -27,3 +27,9 @@ def _action_done(self, cancel_backorder=False): return super(StockMove, self - moves_cancel_backorder)._action_done( cancel_backorder=cancel_backorder ) + + def copy_data(self, default=None): + vals_list = super().copy_data(default=default) + for vals in vals_list: + vals.pop("barcode_backorder_action", None) + return vals_list diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking.py b/stock_barcodes/wizard/stock_barcodes_read_picking.py index 887eaffb68bb..5d3bb79bb1b3 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_picking.py +++ b/stock_barcodes/wizard/stock_barcodes_read_picking.py @@ -88,6 +88,9 @@ def _compute_pending_move_ids(self): if self.option_group_id.show_pending_moves: self.pending_move_ids = self.todo_line_ids.filtered( lambda t: t.state == "pending" + and any( + sm.barcode_backorder_action == "pending" for sm in t.stock_move_ids + ) ) else: self.pending_move_ids = False @@ -840,6 +843,7 @@ def _prepare_fill_record_values(self, line, position): "package_product_qty": package_product_dic and package_product_dic[line.product_id] or 0.0, + "is_stock_move_line_origin": True, } ) else: @@ -855,6 +859,7 @@ def _prepare_fill_record_values(self, line, position): or line.product_uom_qty, "line_ids": [(6, 0, line.move_line_ids.ids)], "stock_move_ids": [(6, 0, line.ids)], + "is_stock_move_line_origin": False, } ) return vals diff --git a/stock_barcodes/wizard/stock_barcodes_read_todo.py b/stock_barcodes/wizard/stock_barcodes_read_todo.py index 26d5bd081ac4..6ea44b6130d6 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_todo.py +++ b/stock_barcodes/wizard/stock_barcodes_read_todo.py @@ -62,6 +62,8 @@ class WizStockBarcodesReadTodo(models.TransientModel): position_index = fields.Integer() picking_code = fields.Char("Type of Operation") is_extra_line = fields.Boolean() + # Used in kanban view + is_stock_move_line_origin = fields.Boolean() def action_todo_next(self): self.state = "done_forced" @@ -69,7 +71,7 @@ def action_todo_next(self): for sml in self.line_ids: if sml.product_uom_qty != sml.qty_done: sml.product_uom_qty = sml.qty_done - if self.is_extra_line: + if self.is_extra_line or not self.is_stock_move_line_origin: barcode_backorder_action = self.env.context.get( "barcode_backorder_action", "create_backorder" ) diff --git a/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml b/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml index d0def9962446..f026fe21352a 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml +++ b/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml @@ -21,6 +21,7 @@ +
Ignore rest @@ -150,7 +151,7 @@ class="btn btn-danger pull-right btn-sm" context="{'wiz_barcode_id': parent.id}" data-hotkey="3" - attrs="{'invisible': ['|', ('qty_done', '!=', 0.0), ('is_extra_line', '=', True)]} " + attrs="{'invisible': ['|', '|', ('qty_done', '!=', 0.0), ('is_extra_line', '=', True), ('is_stock_move_line_origin', '=', False)]} " confirm="You have not set any quantity to this operation and it will be removed from pending moves. Are you sure?" > Ignore rest @@ -176,7 +177,7 @@ class="btn btn-warning pull-right btn-sm" context="{'wiz_barcode_id': parent.id, 'barcode_backorder_action': 'pending'}" data-hotkey="3" - attrs="{'invisible': [('is_extra_line', '=', False)]} " + attrs="{'invisible': [('is_extra_line', '=', False), ('is_stock_move_line_origin', '=', True)]} " confirm="This move will be set to pending. Are you sure?" > Restore to pending @@ -187,7 +188,7 @@ class="btn btn-danger pull-right btn-sm mr-5" context="{'wiz_barcode_id': parent.id, 'barcode_backorder_action': 'skip_backorder'}" data-hotkey="3" - attrs="{'invisible': [('is_extra_line', '=', False)]} " + attrs="{'invisible': [('is_extra_line', '=', False), ('is_stock_move_line_origin', '=', True)]} " confirm="Odoo will not create a backorder for this move. Are you sure?" > No Backorder @@ -198,7 +199,7 @@ class="btn btn-primary pull-right btn-sm mr-3" context="{'wiz_barcode_id': parent.id, 'barcode_backorder_action': 'create_backorder'}" data-hotkey="3" - attrs="{'invisible': [('is_extra_line', '=', False)]} " + attrs="{'invisible': [('is_extra_line', '=', False), ('is_stock_move_line_origin', '=', True)]} " > Create Backorder From dcfbc5b4e97a6d543bbb0aba92adc1581e4b5f6a Mon Sep 17 00:00:00 2001 From: Carlos Dauden Date: Wed, 18 Sep 2024 13:52:41 +0200 Subject: [PATCH 07/12] [IMP] stock_barcodes: Add forced_todo_key to keep selected pending move after recreate records Use id instead of recordset in _group_key method TT50885 --- .../wizard/stock_barcodes_read_picking.py | 24 ++++++++++++++--- .../wizard/stock_barcodes_read_todo.py | 2 +- .../wizard/stock_barcodes_read_picking.py | 27 ++++++++++++++++++- .../wizard/stock_barcodes_read_todo.py | 11 +------- .../wizard/stock_barcodes_read_picking.py | 3 ++- 5 files changed, 50 insertions(+), 17 deletions(-) diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking.py b/stock_barcodes/wizard/stock_barcodes_read_picking.py index 5d3bb79bb1b3..7504dd1012c3 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_picking.py +++ b/stock_barcodes/wizard/stock_barcodes_read_picking.py @@ -77,6 +77,7 @@ def _field_candidate_ids(self): picking_location_dest_id = fields.Many2one(related="picking_id.location_dest_id") company_id = fields.Many2one(related="picking_id.company_id") todo_line_is_extra_line = fields.Boolean(related="todo_line_id.is_extra_line") + forced_todo_key = fields.Char() @api.depends("todo_line_id") def _compute_todo_line_display_ids(self): @@ -281,7 +282,14 @@ def action_done(self): else: keep_vals = self._convert_to_write(self._cache) self.fill_todo_records() - self.determine_todo_action() + if self.forced_todo_key: + self.todo_line_id = self.pending_move_ids.filtered( + lambda ln: str(self._group_key(ln)) == self.forced_todo_key + )[:1] + self.selected_pending_move_id = self.todo_line_id + self.determine_todo_action(self.todo_line_id) + else: + self.determine_todo_action() self.action_show_step() if keep_vals: self.update_keep_values(keep_vals) @@ -804,9 +812,14 @@ def _group_key(self, line): if group_key_for_todo_records: return safe_eval(group_key_for_todo_records, globals_dict={"object": line}) if self.option_group_id.source_pending_moves == "move_line_ids": - return (line.location_id, line.product_id, line.lot_id, line.package_id) + return ( + line.location_id.id, + line.product_id.id, + line.lot_id.id, + line.package_id.id, + ) else: - return (line.location_id, line.product_id) + return (line.location_id.id, line.product_id.id) def _get_all_products_quantities_in_package(self, package): res = {} @@ -866,7 +879,7 @@ def _prepare_fill_record_values(self, line, position): def _update_fill_record_values(self, line, vals): vals["product_uom_qty"] += line.product_uom_qty - if self.option_group_id.source_pending_moves == "move_line_ids": + if vals["is_stock_move_line_origin"]: vals["product_qty_reserved"] += line.product_qty vals["line_ids"][0][2].append(line.id) vals["stock_move_ids"][0][2].append(line.move_id.id) @@ -886,6 +899,9 @@ def fill_records(self, lines_list): :param lines_list: browse list :return: """ + self.forced_todo_key = str( + self._group_key(self.todo_line_id or self.selected_pending_move_id) + ) self.todo_line_ids.unlink() self.todo_line_id = False # self.position_index = 0 diff --git a/stock_barcodes/wizard/stock_barcodes_read_todo.py b/stock_barcodes/wizard/stock_barcodes_read_todo.py index 6ea44b6130d6..8757081d3cb1 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_todo.py +++ b/stock_barcodes/wizard/stock_barcodes_read_todo.py @@ -69,7 +69,7 @@ def action_todo_next(self): self.state = "done_forced" self.line_ids.barcode_scan_state = "done_forced" for sml in self.line_ids: - if sml.product_uom_qty != sml.qty_done: + if sml.product_uom_qty != sml.qty_done and sml.move_id.state != "waiting": sml.product_uom_qty = sml.qty_done if self.is_extra_line or not self.is_stock_move_line_origin: barcode_backorder_action = self.env.context.get( diff --git a/stock_barcodes_elaboration/wizard/stock_barcodes_read_picking.py b/stock_barcodes_elaboration/wizard/stock_barcodes_read_picking.py index 5b58dd497575..8a341d908907 100644 --- a/stock_barcodes_elaboration/wizard/stock_barcodes_read_picking.py +++ b/stock_barcodes_elaboration/wizard/stock_barcodes_read_picking.py @@ -8,5 +8,30 @@ class WizStockBarcodesReadPicking(models.TransientModel): def _group_key(self, line): key = super()._group_key(line) - key += (line.elaboration_ids,) + if not self.option_group_id.group_key_for_todo_records: + key += (str(line.elaboration_ids.ids),) return key + + def _prepare_fill_record_values(self, line, position): + vals = super()._prepare_fill_record_values(line, position) + vals["elaboration_ids"] = [(6, 0, line.elaboration_ids.ids)] + vals["elaboration_note"] = ". ".join( + m.elaboration_note for m in line if m.elaboration_note + ) + return vals + + def _update_fill_record_values(self, line, vals): + vals = super()._update_fill_record_values(line, vals) + if not line.elaboration_ids: + return vals + elaboration_ids = vals.get("elaboration_ids", [(6, 0, [])])[2] + for elaboration_id in line.elaboration_ids.ids: + if elaboration_id not in elaboration_ids: + elaboration_ids.append(elaboration_id) + vals["elaboration_ids"] = elaboration_ids + if ( + line.elaboration_note + and line.elaboration_note not in vals["elaboration_note"] + ): + vals["elaboration_note"] += f". {line.elaboration_note}" + return vals diff --git a/stock_barcodes_elaboration/wizard/stock_barcodes_read_todo.py b/stock_barcodes_elaboration/wizard/stock_barcodes_read_todo.py index d479c4c86521..6baa408a9879 100644 --- a/stock_barcodes_elaboration/wizard/stock_barcodes_read_todo.py +++ b/stock_barcodes_elaboration/wizard/stock_barcodes_read_todo.py @@ -10,14 +10,5 @@ class WizStockBarcodesReadTodo(models.TransientModel): elaboration_ids = fields.Many2many( comodel_name="product.elaboration", string="Elaborations", - compute="_compute_elaboration_ids", ) - elaboration_note = fields.Char(compute="_compute_elaboration_ids") - - def _compute_elaboration_ids(self): - for line in self: - moves = line.stock_move_ids or line.line_ids.mapped("move_id") - line.elaboration_ids = moves.elaboration_ids - line.elaboration_note = ". ".join( - m.elaboration_note for m in moves if m.elaboration_note - ) + elaboration_note = fields.Char() diff --git a/stock_barcodes_gs1_secondary_unit/wizard/stock_barcodes_read_picking.py b/stock_barcodes_gs1_secondary_unit/wizard/stock_barcodes_read_picking.py index a324fafeab3a..acf34b4f05ad 100644 --- a/stock_barcodes_gs1_secondary_unit/wizard/stock_barcodes_read_picking.py +++ b/stock_barcodes_gs1_secondary_unit/wizard/stock_barcodes_read_picking.py @@ -77,5 +77,6 @@ def _prepare_fill_record_values(self, line, position): def _group_key(self, line): key = super()._group_key(line) - key += (line.secondary_uom_id,) + if not self.option_group_id.group_key_for_todo_records: + key += (line.secondary_uom_id.id,) return key From cd791041c4344fcf81dd27db23d478b3bb6fbe2c Mon Sep 17 00:00:00 2001 From: sergio-teruel Date: Tue, 24 Sep 2024 22:37:25 +0200 Subject: [PATCH 08/12] [IMP] stock_barcodes: Reset barcode_backorder_action field in original stock move when a backorder is crated TT50960 --- stock_barcodes/models/stock_move.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stock_barcodes/models/stock_move.py b/stock_barcodes/models/stock_move.py index 415e77ddc494..4d5ad452fca2 100644 --- a/stock_barcodes/models/stock_move.py +++ b/stock_barcodes/models/stock_move.py @@ -24,7 +24,9 @@ def _action_done(self, cancel_backorder=False): lambda sm: sm.barcode_backorder_action == "skip_backorder" ) super(StockMove, moves_cancel_backorder)._action_done(cancel_backorder=True) - return super(StockMove, self - moves_cancel_backorder)._action_done( + moves_backorder = self - moves_cancel_backorder + moves_backorder.barcode_backorder_action = "pending" + return super(StockMove, moves_backorder)._action_done( cancel_backorder=cancel_backorder ) From b0e31887900bceec8d8b0efb652f45aa934b17a3 Mon Sep 17 00:00:00 2001 From: Carlos Dauden Date: Sun, 29 Sep 2024 18:50:14 +0200 Subject: [PATCH 09/12] [IMP] stock_barcodes: Performance and extra checks from 16.0 --- stock_barcodes/hooks.py | 28 ++++++++++--------- .../models/stock_barcodes_action.py | 2 +- stock_barcodes/models/stock_picking.py | 3 +- stock_barcodes/models/stock_picking_type.py | 9 +++--- stock_barcodes/wizard/stock_barcodes_read.py | 4 ++- .../wizard/stock_barcodes_read_picking.py | 4 +++ 6 files changed, 28 insertions(+), 22 deletions(-) diff --git a/stock_barcodes/hooks.py b/stock_barcodes/hooks.py index 4fc7c9ee3f86..daa06938b835 100644 --- a/stock_barcodes/hooks.py +++ b/stock_barcodes/hooks.py @@ -1,18 +1,20 @@ # Copyright 2021 Tecnativa - Sergio Teruel +# Copyright 2024 Tecnativa - Carlos Dauden # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import tools def pre_init_hook(cr): - if not tools.column_exists(cr, "stock_move_line", "barcode_scan_state"): - cr.execute( - """ - ALTER TABLE stock_move_line - ADD COLUMN barcode_scan_state varchar""" - ) - cr.execute( - """ - UPDATE stock_move_line sml - SET barcode_scan_state = 'pending' - """ - ) + cr.execute( + """ + ALTER TABLE stock_move_line + ADD COLUMN IF NOT EXISTS barcode_scan_state VARCHAR DEFAULT 'pending'; + ALTER TABLE stock_move_line ALTER COLUMN barcode_scan_state DROP DEFAULT; + """ + ) + cr.execute( + """ + ALTER TABLE stock_move + ADD COLUMN IF NOT EXISTS barcode_backorder_action VARCHAR DEFAULT 'pending'; + ALTER TABLE stock_move ALTER COLUMN barcode_backorder_action DROP DEFAULT; + """ + ) diff --git a/stock_barcodes/models/stock_barcodes_action.py b/stock_barcodes/models/stock_barcodes_action.py index f0982771d26b..83db2fe0ab97 100644 --- a/stock_barcodes/models/stock_barcodes_action.py +++ b/stock_barcodes/models/stock_barcodes_action.py @@ -43,7 +43,7 @@ def open_inventory_action(self, ctx): } if option_group.get_option_value("location_id", "filled_default"): vals["location_id"] = ( - self.env["stock.warehouse"].search([])[:1].lot_stock_id.id + self.env["stock.warehouse"].search([], limit=1).lot_stock_id.id ) wiz = self.env["wiz.stock.barcodes.read.inventory"].create(vals) action = self.env["ir.actions.actions"]._for_xml_id( diff --git a/stock_barcodes/models/stock_picking.py b/stock_barcodes/models/stock_picking.py index b3970c062b21..13219a5d4d70 100644 --- a/stock_barcodes/models/stock_picking.py +++ b/stock_barcodes/models/stock_picking.py @@ -18,7 +18,7 @@ def _prepare_barcode_wiz_vals(self, option_group): } if self.picking_type_id.code == "outgoing": vals["location_dest_id"] = self.location_dest_id.id - if self.picking_type_id.code == "incoming": + elif self.picking_type_id.code == "incoming": vals["location_id"] = self.location_id.id if option_group.get_option_value("location_id", "filled_default"): @@ -47,7 +47,6 @@ def button_validate(self): ) if put_in_pack_picks: put_in_pack_picks.action_put_in_pack() - # Variable initialized as True to optimize break loop if self.env.context.get("stock_barcodes_validate_picking", False): res = super( StockPicking, self.with_context(skip_backorder=True) diff --git a/stock_barcodes/models/stock_picking_type.py b/stock_barcodes/models/stock_picking_type.py index f83b5b3d5eb7..c57fc0ee5884 100644 --- a/stock_barcodes/models/stock_picking_type.py +++ b/stock_barcodes/models/stock_picking_type.py @@ -26,17 +26,15 @@ def action_barcode_scan(self): "picking_mode": "picking", } if self.code == "outgoing": - location_dest_id = ( + vals["location_dest_id"] = ( self.default_location_dest_id.id or self.env.ref("stock.stock_location_customers").id ) - vals["location_dest_id"] = location_dest_id - if self.code == "incoming": - location_src_id = ( + elif self.code == "incoming": + vals["location_id"] = ( self.default_location_src_id.id or self.env.ref("stock.stock_location_suppliers").id ) - vals["location_id"] = location_src_id if self.barcode_option_group_id.get_option_value( "location_id", "filled_default" ): @@ -55,6 +53,7 @@ def action_barcode_scan(self): return action def action_barcode_new_picking(self): + self.ensure_one() picking = ( self.env["stock.picking"] .with_context(default_immediate_transfer=True) diff --git a/stock_barcodes/wizard/stock_barcodes_read.py b/stock_barcodes/wizard/stock_barcodes_read.py index 0637783f038f..06f8fa52d066 100644 --- a/stock_barcodes/wizard/stock_barcodes_read.py +++ b/stock_barcodes/wizard/stock_barcodes_read.py @@ -647,7 +647,9 @@ def action_create_package(self): def action_clean_values(self): options = self.option_group_id.option_ids - options_to_clean = options.filtered("clean_after_done") + options_to_clean = options.filtered( + lambda op: op.clean_after_done and op.field_name in self + ) for option in options_to_clean: if option.field_name == "result_package_id" and self.keep_result_package: continue diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking.py b/stock_barcodes/wizard/stock_barcodes_read_picking.py index 7504dd1012c3..e43b6dce9f08 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_picking.py +++ b/stock_barcodes/wizard/stock_barcodes_read_picking.py @@ -478,6 +478,10 @@ def _process_stock_move_line(self): # noqa: C901 domain = self._prepare_stock_moves_domain() if self.option_group_id.barcode_guided_mode == "guided": moves_todo = self.todo_line_id.stock_move_ids + elif self.picking_id: + moves_todo = self.picking_id.move_lines.filtered( + lambda sm: sm.product_id == self.product_id + ) else: moves_todo = StockMove.search(domain) if not getattr( From 2110408ab45603246d78c115d5b173c90e1644f4 Mon Sep 17 00:00:00 2001 From: sergio-teruel Date: Thu, 17 Oct 2024 10:23:34 +0200 Subject: [PATCH 10/12] [IMP] stock_barcodes: Fix tests --- stock_barcodes/tests/test_stock_barcodes_picking.py | 1 + 1 file changed, 1 insertion(+) diff --git a/stock_barcodes/tests/test_stock_barcodes_picking.py b/stock_barcodes/tests/test_stock_barcodes_picking.py index c26449f0a611..5bf60e90b574 100644 --- a/stock_barcodes/tests/test_stock_barcodes_picking.py +++ b/stock_barcodes/tests/test_stock_barcodes_picking.py @@ -294,6 +294,7 @@ def test_picking_wizard_scan_product_auto_lot(self): self.product_tracking.categ_id.removal_strategy_id = self.env.ref( "stock.removal_lifo" ) + self.wiz_scan_picking_out.action_clean_values() self.action_barcode_scanned(self.wiz_scan_picking_out, "8433281006850") self.assertEqual(self.wiz_scan_picking_out.lot_id, lot_3) From 833d63fc51096398ee829c40238892555b47ce4d Mon Sep 17 00:00:00 2001 From: Carlos Dauden Date: Mon, 25 Nov 2024 13:48:26 +0100 Subject: [PATCH 11/12] [FIX] stock_barcodes: Quant and stock move line reserve are unsynchronized after button "no create backorder". TT51871 --- stock_barcodes/wizard/stock_barcodes_read_todo.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/stock_barcodes/wizard/stock_barcodes_read_todo.py b/stock_barcodes/wizard/stock_barcodes_read_todo.py index 8757081d3cb1..3b6a76b29fe6 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_todo.py +++ b/stock_barcodes/wizard/stock_barcodes_read_todo.py @@ -69,7 +69,18 @@ def action_todo_next(self): self.state = "done_forced" self.line_ids.barcode_scan_state = "done_forced" for sml in self.line_ids: - if sml.product_uom_qty != sml.qty_done and sml.move_id.state != "waiting": + if ( + float_compare( + sml.product_uom_qty, + sml.qty_done, + precision_rounding=sml.product_uom_id.rounding, + ) + == 0 + ): + continue + if sml.move_id.state == "confirmed" and sml.qty_done: + sml.move_id.state = "partially_available" + if sml.move_id.state in ["partially_available", "assigned"]: sml.product_uom_qty = sml.qty_done if self.is_extra_line or not self.is_stock_move_line_origin: barcode_backorder_action = self.env.context.get( From 85155e7c15135185ae0163ec501a740c3c07908b Mon Sep 17 00:00:00 2001 From: sergio-teruel Date: Fri, 29 Nov 2024 15:16:56 +0100 Subject: [PATCH 12/12] [FIX] stock_barcodes: Move qty_available field from read base to read picking TT51889 --- stock_barcodes/wizard/stock_barcodes_read.py | 36 ------------------ .../wizard/stock_barcodes_read_picking.py | 37 ++++++++++++++++++- .../stock_barcodes_read_picking_views.xml | 1 + .../wizard/stock_barcodes_read_views.xml | 1 - 4 files changed, 37 insertions(+), 38 deletions(-) diff --git a/stock_barcodes/wizard/stock_barcodes_read.py b/stock_barcodes/wizard/stock_barcodes_read.py index 06f8fa52d066..5441a1436dd3 100644 --- a/stock_barcodes/wizard/stock_barcodes_read.py +++ b/stock_barcodes/wizard/stock_barcodes_read.py @@ -3,7 +3,6 @@ import logging from odoo import _, api, fields, models -from odoo.tools import float_round _logger = logging.getLogger(__name__) @@ -77,7 +76,6 @@ class WizStockBarcodesRead(models.AbstractModel): show_scan_log = fields.Boolean(compute="_compute_is_manual_qty") # Technical field to allow use in attrs display_menu = fields.Boolean() - qty_available = fields.Float(compute="_compute_qty_available") auto_lot = fields.Boolean( string="Get lots automatically", help="If checked the lot will be set automatically with the same " @@ -123,40 +121,6 @@ def _compute_create_lot(self): for rec in self: rec.create_lot = rec.option_group_id.create_lot - @api.depends("location_id", "product_id", "lot_id") - def _compute_qty_available(self): - if not self.product_id or self.location_id.usage != "internal": - self.qty_available = 0.0 - return - domain_quant = [ - ("product_id", "=", self.product_id.id), - ("location_id", "=", self.location_id.id), - ] - if self.lot_id: - domain_quant.append(("lot_id", "=", self.lot_id.id)) - # if self.package_id: - # domain_quant.append(('package_id', '=', self.package_id.id)) - groups = self.env["stock.quant"].read_group( - domain_quant, ["quantity"], [], orderby="id" - ) - self.qty_available = groups[0]["quantity"] - # Unexpected done quantities must reduce qty_available - if self.lot_id: - done_move_lines = self.move_line_ids.filtered( - lambda m: m.product_id == self.product_id and m.lot_id == self.lot_id - ) - else: - done_move_lines = self.move_line_ids.filtered( - lambda m: m.product_id == self.product_id - ) - for sml in done_move_lines: - over_done_qty = float_round( - sml.qty_done - sml.product_uom_qty, - precision_rounding=sml.product_uom_id.rounding, - ) - if over_done_qty > 0.0: - self.qty_available -= over_done_qty - @api.depends("product_id") def _compute_display_assign_serial(self): for rec in self: diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking.py b/stock_barcodes/wizard/stock_barcodes_read_picking.py index e43b6dce9f08..42cd8216418b 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_picking.py +++ b/stock_barcodes/wizard/stock_barcodes_read_picking.py @@ -6,7 +6,7 @@ from odoo import _, api, fields, models from odoo.exceptions import ValidationError from odoo.fields import first -from odoo.tools.float_utils import float_compare +from odoo.tools.float_utils import float_compare, float_round from odoo.tools.safe_eval import safe_eval _logger = logging.getLogger(__name__) @@ -78,6 +78,7 @@ def _field_candidate_ids(self): company_id = fields.Many2one(related="picking_id.company_id") todo_line_is_extra_line = fields.Boolean(related="todo_line_id.is_extra_line") forced_todo_key = fields.Char() + qty_available = fields.Float(compute="_compute_qty_available") @api.depends("todo_line_id") def _compute_todo_line_display_ids(self): @@ -115,6 +116,40 @@ def _compute_total_product(self): rec.total_product_uom_qty += line.product_uom_qty rec.total_product_qty_done += line.quantity_done + @api.depends("location_id", "product_id", "lot_id") + def _compute_qty_available(self): + if not self.product_id or self.location_id.usage != "internal": + self.qty_available = 0.0 + return + domain_quant = [ + ("product_id", "=", self.product_id.id), + ("location_id", "=", self.location_id.id), + ] + if self.lot_id: + domain_quant.append(("lot_id", "=", self.lot_id.id)) + # if self.package_id: + # domain_quant.append(('package_id', '=', self.package_id.id)) + groups = self.env["stock.quant"].read_group( + domain_quant, ["quantity"], [], orderby="id" + ) + self.qty_available = groups[0]["quantity"] + # Unexpected done quantities must reduce qty_available + if self.lot_id: + done_move_lines = self.move_line_ids.filtered( + lambda m: m.product_id == self.product_id and m.lot_id == self.lot_id + ) + else: + done_move_lines = self.move_line_ids.filtered( + lambda m: m.product_id == self.product_id + ) + for sml in done_move_lines: + over_done_qty = float_round( + sml.qty_done - sml.product_uom_qty, + precision_rounding=sml.product_uom_id.rounding, + ) + if over_done_qty > 0.0: + self.qty_available -= over_done_qty + def name_get(self): return [ ( diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml b/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml index a3bf11998367..51538d9e8112 100644 --- a/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml +++ b/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml @@ -128,6 +128,7 @@ + -