From 5afdd8a09759fcc5f54fd516bd2e4f06587a2323 Mon Sep 17 00:00:00 2001
From: Francois Poizat
Date: Wed, 28 Feb 2024 16:04:19 +0100
Subject: [PATCH] [MIG] stock_barcodes: Migration to 16.0
---
stock_barcodes/README.rst | 12 +-
stock_barcodes/__manifest__.py | 21 +-
.../migrations/15.0.1.0.0/pre-migration.py | 14 -
.../models/stock_barcodes_read_log.py | 2 +-
stock_barcodes/models/stock_move_line.py | 4 +-
stock_barcodes/static/description/index.html | 8 +-
.../static/src/barcodes_models_utils.esm.js | 19 +
.../static/src/boolean_toggle.esm.js | 17 +
stock_barcodes/static/src/form_view.esm.js | 15 +
.../static/src/js/barcodes_models_mixin.js | 34 --
.../static/src/js/basic_controller.js | 402 ---------------
stock_barcodes/static/src/js/basic_fields.js | 59 ---
stock_barcodes/static/src/js/form_view.js | 24 -
.../static/src/js/kanban_renderer.js | 45 --
stock_barcodes/static/src/js/numeric_step.js | 43 --
.../static/src/kanban_renderer.esm.js | 163 ++++++
stock_barcodes/static/src/numeric_step.esm.js | 80 +++
stock_barcodes/static/src/numeric_step.xml | 17 +
stock_barcodes/static/src/{css => }/stock.css | 0
.../static/src/{css => }/stock.scss | 7 +
stock_barcodes/static/src/view_button.esm.js | 8 +
stock_barcodes/static/src/view_button.xml | 14 +
.../static/src/view_compiler.esm.js | 16 +
stock_barcodes/static/src/views.esm.js | 178 +++++++
stock_barcodes/tests/common.py | 165 +++++++
stock_barcodes/tests/test_stock_barcodes.py | 173 +------
.../tests/test_stock_barcodes_new_lot.py | 4 +-
.../tests/test_stock_barcodes_picking.py | 16 +-
stock_barcodes/views/stock_picking_views.xml | 1 -
stock_barcodes/wizard/stock_barcodes_read.py | 29 +-
.../stock_barcodes_read_inventory_views.xml | 20 +-
.../wizard/stock_barcodes_read_picking.py | 20 +-
.../stock_barcodes_read_picking_views.xml | 218 ++++-----
.../wizard/stock_barcodes_read_todo.py | 18 +-
.../wizard/stock_barcodes_read_todo_view.xml | 21 +-
.../wizard/stock_barcodes_read_views.xml | 463 +++++++++---------
stock_barcodes/wizard/stock_production_lot.py | 4 +-
37 files changed, 1125 insertions(+), 1229 deletions(-)
delete mode 100644 stock_barcodes/migrations/15.0.1.0.0/pre-migration.py
create mode 100644 stock_barcodes/static/src/barcodes_models_utils.esm.js
create mode 100644 stock_barcodes/static/src/boolean_toggle.esm.js
create mode 100644 stock_barcodes/static/src/form_view.esm.js
delete mode 100644 stock_barcodes/static/src/js/barcodes_models_mixin.js
delete mode 100644 stock_barcodes/static/src/js/basic_controller.js
delete mode 100644 stock_barcodes/static/src/js/basic_fields.js
delete mode 100644 stock_barcodes/static/src/js/form_view.js
delete mode 100644 stock_barcodes/static/src/js/kanban_renderer.js
delete mode 100644 stock_barcodes/static/src/js/numeric_step.js
create mode 100644 stock_barcodes/static/src/kanban_renderer.esm.js
create mode 100644 stock_barcodes/static/src/numeric_step.esm.js
create mode 100644 stock_barcodes/static/src/numeric_step.xml
rename stock_barcodes/static/src/{css => }/stock.css (100%)
rename stock_barcodes/static/src/{css => }/stock.scss (90%)
create mode 100644 stock_barcodes/static/src/view_button.esm.js
create mode 100644 stock_barcodes/static/src/view_button.xml
create mode 100644 stock_barcodes/static/src/view_compiler.esm.js
create mode 100644 stock_barcodes/static/src/views.esm.js
create mode 100644 stock_barcodes/tests/common.py
diff --git a/stock_barcodes/README.rst b/stock_barcodes/README.rst
index 30ff3959b7f1..b0039fe43677 100644
--- a/stock_barcodes/README.rst
+++ b/stock_barcodes/README.rst
@@ -7,7 +7,7 @@ Stock Barcodes
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:525a93165bdee89662d7cde6315ac154e43ad2bb1db6fd2b6c4f72847a16ac6d
+ !! source digest: sha256:cc8a4769a4d49396466d2fe0d4ee151e77ecf646a27d11a3700c9bb5c1855b1f
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
@@ -17,13 +17,13 @@ Stock Barcodes
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--barcode-lightgray.png?logo=github
- :target: https://github.com/OCA/stock-logistics-barcode/tree/15.0/stock_barcodes
+ :target: https://github.com/OCA/stock-logistics-barcode/tree/16.0/stock_barcodes
:alt: OCA/stock-logistics-barcode
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
- :target: https://translation.odoo-community.org/projects/stock-logistics-barcode-15-0/stock-logistics-barcode-15-0-stock_barcodes
+ :target: https://translation.odoo-community.org/projects/stock-logistics-barcode-16-0/stock-logistics-barcode-16-0-stock_barcodes
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
- :target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-barcode&target_branch=15.0
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-barcode&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -159,7 +159,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues `_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
-`feedback `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -209,6 +209,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
-This module is part of the `OCA/stock-logistics-barcode `_ project on GitHub.
+This module is part of the `OCA/stock-logistics-barcode `_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/stock_barcodes/__manifest__.py b/stock_barcodes/__manifest__.py
index 2101ea3f26a9..0002d26d8f8c 100644
--- a/stock_barcodes/__manifest__.py
+++ b/stock_barcodes/__manifest__.py
@@ -3,12 +3,12 @@
{
"name": "Stock Barcodes",
"summary": "It provides read barcode on stock operations.",
- "version": "15.0.2.0.0",
+ "version": "16.0.1.0.0",
"author": "Tecnativa, " "Odoo Community Association (OCA)",
"website": "https://github.com/OCA/stock-logistics-barcode",
"license": "AGPL-3",
"category": "Extra Tools",
- "depends": ["barcodes", "stock", "web_widget_numeric_step"],
+ "depends": ["barcodes", "stock", "web_widget_numeric_step", "web"],
"data": [
"security/ir.model.access.csv",
"views/stock_barcodes_action_view.xml",
@@ -27,13 +27,16 @@
],
"assets": {
"web.assets_backend": [
- "/stock_barcodes/static/src/js/barcodes_models_mixin.js",
- "/stock_barcodes/static/src/js/basic_controller.js",
- "/stock_barcodes/static/src/js/kanban_renderer.js",
- "/stock_barcodes/static/src/js/basic_fields.js",
- "/stock_barcodes/static/src/js/form_view.js",
- "/stock_barcodes/static/src/js/numeric_step.js",
- "/stock_barcodes/static/src/css/stock.scss",
+ "/stock_barcodes/static/src/barcodes_models_utils.esm.js",
+ "/stock_barcodes/static/src/kanban_renderer.esm.js",
+ "/stock_barcodes/static/src/boolean_toggle.esm.js",
+ "/stock_barcodes/static/src/views.esm.js",
+ "/stock_barcodes/static/src/form_view.esm.js",
+ "/stock_barcodes/static/src/numeric_step.esm.js",
+ "/stock_barcodes/static/src/view_button.esm.js",
+ "/stock_barcodes/static/src/view_compiler.esm.js",
+ "/stock_barcodes/static/src/view_button.xml",
+ "/stock_barcodes/static/src/stock.scss",
],
},
"installable": True,
diff --git a/stock_barcodes/migrations/15.0.1.0.0/pre-migration.py b/stock_barcodes/migrations/15.0.1.0.0/pre-migration.py
deleted file mode 100644
index 8df703cba910..000000000000
--- a/stock_barcodes/migrations/15.0.1.0.0/pre-migration.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright 2023 ForgeFlow
-# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-
-from openupgradelib import openupgrade
-
-
-def delete_fk_constraints(env):
- # delete obsolete model references
- openupgrade.remove_tables_fks(env.cr, ["wiz_stock_barcodes_read_inventory"])
-
-
-@openupgrade.migrate()
-def migrate(env, version):
- delete_fk_constraints(env)
diff --git a/stock_barcodes/models/stock_barcodes_read_log.py b/stock_barcodes/models/stock_barcodes_read_log.py
index fd536e288a88..448ec58c4c22 100644
--- a/stock_barcodes/models/stock_barcodes_read_log.py
+++ b/stock_barcodes/models/stock_barcodes_read_log.py
@@ -12,7 +12,7 @@ class StockBarcodesReadLog(models.Model):
res_model_id = fields.Many2one(comodel_name="ir.model", index=True)
res_id = fields.Integer(index=True)
product_id = fields.Many2one(comodel_name="product.product", index=True)
- lot_id = fields.Many2one(comodel_name="stock.production.lot", string="Lot scanned")
+ lot_id = fields.Many2one(comodel_name="stock.lot", string="Lot scanned")
location_id = fields.Many2one(comodel_name="stock.location")
packaging_id = fields.Many2one(comodel_name="product.packaging")
packaging_qty = fields.Float(string="Package Qty", digits="Product Unit of Measure")
diff --git a/stock_barcodes/models/stock_move_line.py b/stock_barcodes/models/stock_move_line.py
index 0b4d553ae686..c2f887b66ca4 100644
--- a/stock_barcodes/models/stock_move_line.py
+++ b/stock_barcodes/models/stock_move_line.py
@@ -15,10 +15,10 @@ class StockMoveLine(models.Model):
store=True,
)
- @api.depends("qty_done", "product_uom_qty")
+ @api.depends("qty_done", "reserved_uom_qty")
def _compute_barcode_scan_state(self):
for line in self:
- if line.qty_done >= line.product_uom_qty:
+ if line.qty_done >= line.reserved_uom_qty:
line.barcode_scan_state = "done"
else:
line.barcode_scan_state = "pending"
diff --git a/stock_barcodes/static/description/index.html b/stock_barcodes/static/description/index.html
index 9e72786dce66..56380bc3a4c2 100644
--- a/stock_barcodes/static/description/index.html
+++ b/stock_barcodes/static/description/index.html
@@ -367,9 +367,9 @@ Stock Barcodes
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!! source digest: sha256:525a93165bdee89662d7cde6315ac154e43ad2bb1db6fd2b6c4f72847a16ac6d
+!! source digest: sha256:cc8a4769a4d49396466d2fe0d4ee151e77ecf646a27d11a3700c9bb5c1855b1f
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-
+
This module provides a barcode reader interface for stock module.
This module contains a base wizard read barcode that can be extended by
other modules.
@@ -518,7 +518,7 @@
Bugs are tracked on GitHub Issues .
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
-feedback .
+feedback .
Do not contact contributors directly about support or help with technical issues.
diff --git a/stock_barcodes/static/src/barcodes_models_utils.esm.js b/stock_barcodes/static/src/barcodes_models_utils.esm.js
new file mode 100644
index 000000000000..1bdc048ba349
--- /dev/null
+++ b/stock_barcodes/static/src/barcodes_models_utils.esm.js
@@ -0,0 +1,19 @@
+/** @odoo-module */
+/* Copyright 2022 Tecnativa - Alexandre D. Díaz
+ * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
+
+const barcodeModels = [
+ "stock.barcodes.action",
+ "stock.picking",
+ "stock.picking.type",
+ "wiz.candidate.picking",
+ "wiz.stock.barcodes.new.lot",
+ "wiz.stock.barcodes.read",
+ "wiz.stock.barcodes.read.inventory",
+ "wiz.stock.barcodes.read.picking",
+ "wiz.stock.barcodes.read.todo",
+];
+
+export function isAllowedBarcodeModel(modelName) {
+ return barcodeModels.indexOf(modelName) !== -1;
+}
diff --git a/stock_barcodes/static/src/boolean_toggle.esm.js b/stock_barcodes/static/src/boolean_toggle.esm.js
new file mode 100644
index 000000000000..f8e063abab81
--- /dev/null
+++ b/stock_barcodes/static/src/boolean_toggle.esm.js
@@ -0,0 +1,17 @@
+/** @odoo-module */
+/* Copyright 2018-2019 Sergio Teruel .
+ * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
+
+import {BooleanToggleField} from "@web/views/fields/boolean_toggle/boolean_toggle_field";
+import {registry} from "@web/core/registry";
+
+class BarcodeBooleanToggleField extends BooleanToggleField {
+ onChange(newValue) {
+ super.onChange(newValue);
+ requestIdleCallback(() => {
+ document.activeElement.blur();
+ });
+ }
+}
+
+registry.category("fields").add("barcode_boolean_toggle", BarcodeBooleanToggleField);
diff --git a/stock_barcodes/static/src/form_view.esm.js b/stock_barcodes/static/src/form_view.esm.js
new file mode 100644
index 000000000000..fd0584082b52
--- /dev/null
+++ b/stock_barcodes/static/src/form_view.esm.js
@@ -0,0 +1,15 @@
+/** @odoo-module */
+/* Copyright 2021 Tecnativa - Alexandre D. Díaz
+ * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
+
+import {FormController} from "@web/views/form/form_controller";
+import {patch} from "@web/core/utils/patch";
+
+patch(FormController.prototype, "Allow display.controlPanel overriding", {
+ setup() {
+ this._super(...arguments);
+ if (this.props.context.control_panel_hidden) {
+ this.display.controlPanel = false;
+ }
+ },
+});
diff --git a/stock_barcodes/static/src/js/barcodes_models_mixin.js b/stock_barcodes/static/src/js/barcodes_models_mixin.js
deleted file mode 100644
index 310cdb612f97..000000000000
--- a/stock_barcodes/static/src/js/barcodes_models_mixin.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Copyright 2022 Tecnativa - Alexandre D. Díaz
- * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
-
-// eslint-disable-next-line no-unused-vars
-odoo.define("stock_barcodes.BarcodesModelsMixin", function (require) {
- "use strict";
-
- const BarcodesModelsMixin = {
- // Models allowed to have extra keybinding features
- _barcode_models: [
- "stock.barcodes.action",
- "stock.picking",
- "stock.picking.type",
- "wiz.candidate.picking",
- "wiz.stock.barcodes.new.lot",
- "wiz.stock.barcodes.read",
- "wiz.stock.barcodes.read.inventory",
- "wiz.stock.barcodes.read.picking",
- "wiz.stock.barcodes.read.todo",
- ],
-
- /**
- * Helper to know if the given model is allowed
- *
- * @private
- * @returns {Boolean}
- */
- _isAllowedBarcodeModel: function (model_name) {
- return this._barcode_models.indexOf(model_name) !== -1;
- },
- };
-
- return BarcodesModelsMixin;
-});
diff --git a/stock_barcodes/static/src/js/basic_controller.js b/stock_barcodes/static/src/js/basic_controller.js
deleted file mode 100644
index df9c4324ce6c..000000000000
--- a/stock_barcodes/static/src/js/basic_controller.js
+++ /dev/null
@@ -1,402 +0,0 @@
-/* Copyright 2018-2019 Sergio Teruel .
- * Copyright 2022 Tecnativa - Alexandre D. Díaz
- * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
-
-odoo.define("stock_barcodes.BasicController", function (require) {
- "use strict";
-
- const BasicController = require("web.BasicController");
- const BrowserDetection = require("web.BrowserDetection");
- const BarcodesModelsMixin = require("stock_barcodes.BarcodesModelsMixin");
- const ui = require("@web/core/utils/ui");
- const getVisibleElements = ui.getVisibleElements;
-
- BasicController.include(BarcodesModelsMixin);
- BasicController.include({
- /**
- * @override
- */
- init: function () {
- this._super.apply(this, arguments);
- this._is_valid_barcode_model = this._isAllowedBarcodeModel(
- this.initialState.model
- );
- if (this._is_valid_barcode_model) {
- this.BrowserDetection = new BrowserDetection();
- this._keybind_selectable_index = -1;
- this._keybind_selectable_items = [];
- this._is_browser_chrome = this.BrowserDetection.isBrowserChrome();
- const state_id = this.initialState.data.id;
- this._areAccessKeyVisible = false;
- if (state_id) {
- this._channel_barcode = `stock_barcodes-${this.initialState.data.id}`;
-
- if (this.call("bus_service", "isMasterTab")) {
- this.call("bus_service", "addChannel", this._channel_barcode);
- }
- }
- }
- },
-
- /**
- * @override
- */
- destroy: function () {
- this._super.apply(this, arguments);
- if (this._is_valid_barcode_model) {
- if (this.$sound_ok) {
- this.$sound_ok.remove();
- }
- if (this.$sound_ko) {
- this.$sound_ko.remove();
- }
- }
- },
-
- /**
- * @override
- */
- on_detach_callback: function () {
- this._super.apply(this, arguments);
- if (
- this._is_valid_barcode_model &&
- ["form", "kanban"].indexOf(this.initialState.viewType) !== -1
- ) {
- $(document).off("keydown", this._onDocumentKeyDown);
- $(document).off("keyup", this._onDocumentKeyUp);
- this.call(
- "bus_service",
- "off",
- "notification",
- this,
- this.onBusNotificationBarcode
- );
- }
- },
-
- /**
- * @override
- */
- on_attach_callback: function () {
- this._super.apply(this, arguments);
- if (
- this._is_valid_barcode_model &&
- ["form", "kanban"].indexOf(this.initialState.viewType) !== -1
- ) {
- this._appendBarcodesSounds();
- $(document).on("keydown", {controller: this}, this._onDocumentKeyDown);
- $(document).on("keyup", {controller: this}, this._onDocumentKeyUp);
- this.call(
- "bus_service",
- "on",
- "notification",
- this,
- this.onBusNotificationBarcode
- );
- this._update_selectable_items();
- }
- },
-
- /**
- * Longpolling messages
- *
- * @param {Array} notifications
- */
- onBusNotificationBarcode: function (notifications) {
- for (const notif of notifications) {
- const notif_type = notif.type;
- const message = notif.payload;
- if (notif_type === "stock_barcodes_read-" + this.initialState.data.id) {
- if (message.action === "focus") {
- setTimeout(() => {
- this.$(`[name=${message.field_name}] input`).select();
- }, 400);
- }
- } else if (
- notif_type ===
- "stock_barcodes_sound-" + this.initialState.data.id
- ) {
- if (message.sound === "ok") {
- this.$sound_ok[0].play();
- } else if (message.sound === "ko") {
- this.$sound_ko[0].play();
- }
- }
- }
- },
-
- _addHotkeyOverlays: function () {
- if (this._areAccessKeyVisible) {
- return;
- }
- for (const el of getVisibleElements(
- document,
- "[data-hotkey]:not(:disabled)"
- )) {
- const hotkey = el.dataset.hotkey;
- const overlay = document.createElement("div");
- overlay.className = "o_web_hotkey_overlay";
- overlay.appendChild(document.createTextNode(hotkey.toUpperCase()));
-
- let overlayParent = false;
- if (el.tagName.toUpperCase() === "INPUT") {
- // Special case for the search input that has an access key
- // defined. We cannot set the overlay on the input itself,
- // only on its parent.
- overlayParent = el.parentElement;
- } else {
- overlayParent = el;
- }
-
- if (overlayParent.style.position !== "absolute") {
- overlayParent.style.position = "relative";
- }
- overlayParent.appendChild(overlay);
- }
- this._areAccessKeyVisible = true;
- },
-
- _removeHotkeyOverlays: function () {
- if (!this._areAccessKeyVisible) {
- return;
- }
- for (const overlay of document.querySelectorAll(".o_web_hotkey_overlay")) {
- overlay.remove();
- }
- this._areAccessKeyVisible = false;
- },
- /**
- * Helper to toggle access keys panel visibility
- *
- * @param {Boolean} status
- */
- _toggleAccessKeys: function (status) {
- if (status) {
- this._addHotkeyOverlays();
- } else {
- this._removeHotkeyOverlays();
- }
- },
-
- /**
- * Used to manipulate fields
- *
- * @private
- */
- _postProcessFields: function () {
- // Set tabindex for readonly elements
- // this is necessary to don't block the elements chain
- const $fields = $("span.o_readonly_modifier");
- $fields.attr("tabindex", "-1");
- },
-
- /**
- * Append the audio elements to play the sounds.
- * This is here because only must exists one controller at time
- *
- * @private
- */
- _appendBarcodesSounds: function () {
- this.$sound_ok = $("", {
- src: "/stock_barcodes/static/src/sounds/bell.wav",
- preload: "auto",
- });
- this.$sound_ok.appendTo("body");
- this.$sound_ko = $("", {
- src: "/stock_barcodes/static/src/sounds/error.wav",
- preload: "auto",
- });
- this.$sound_ko.appendTo("body");
- },
-
- /**
- * Dedicated keyboard handle for chrome browser
- * @param {KeyboardEvent} ev
- */
- _onPushKeyForChrome: function (ev) {
- let prefixkey = "";
- if (ev.shift) {
- prefixkey += "shift+";
- }
- const elementWithAccessKey = document.querySelector(
- `[data-hotkey="${prefixkey}${ev.key.toLowerCase()}"], [data-hotkey="${prefixkey}${ev.key.toUpperCase()}"]`
- );
- if (elementWithAccessKey) {
- ev.preventDefault();
- elementWithAccessKey.focus();
- elementWithAccessKey.click();
- }
- },
-
- /**
- * @private
- * @param {KeyboardEvent} ev
- */
- _onDocumentKeyDown: function (ev) {
- var self = (ev.data && ev.data.controller) || this;
- if (self._is_valid_barcode_model) {
- // ACCESS KEY PANEL MANAGEMENT
- const alt = ev.altKey || ev.key === "Alt",
- newEvent = _.extend({}, ev),
- shift = ev.shiftKey || ev.key === "Shift";
- if (ev.keyCode === 113) {
- // F2
- self._toggleAccessKeys(!self._areAccessKeyVisible);
- } else if (self._areAccessKeyVisible && !shift && !alt) {
- if (self._is_browser_chrome) {
- self._onPushKeyForChrome(ev);
- } else {
- newEvent.altKey = true;
- newEvent.shiftKey = true;
- self._onKeyDown(newEvent);
- }
- }
- // Open actions directly only when menu is active
- // 1-9 Only accesskey
- if (
- self.initialState.data.display_menu &&
- ((ev.keyCode >= 50 && ev.keyCode <= 57) ||
- (ev.keyCode >= 97 && ev.keyCode <= 105))
- ) {
- self.$(
- "button[data-hotkey=" +
- String.fromCharCode(ev.keyCode) +
- "]:visible"
- ).click();
- }
-
- // VIEW ACTIONS MANAGEMENT
- if (ev.keyCode === 120) {
- // F9
- self.$("button[name='action_clean_values']").click();
- } else if (ev.keyCode === 123 || ev.keyCode === 115) {
- // F12 or F4
- return self.open_action_menu();
- // Self.$("button[name='open_actions']").click();
- } else if (ev.keyCode === $.ui.keyCode.UP) {
- // Search kanban buttons to navigate
- ev.preventDefault();
- --self._keybind_selectable_index;
- if (self._keybind_selectable_index < 0) {
- self._keybind_selectable_index =
- self._keybind_selectable_items.length - 1;
- }
- self._set_focus_on_selectable_item(
- self._keybind_selectable_items,
- self._keybind_selectable_index
- );
- } else if (ev.keyCode === $.ui.keyCode.DOWN) {
- ev.preventDefault();
- ++self._keybind_selectable_index;
- if (
- self._keybind_selectable_index >=
- self._keybind_selectable_items.length
- ) {
- self._keybind_selectable_index = 0;
- }
- self._set_focus_on_selectable_item(
- self._keybind_selectable_items,
- self._keybind_selectable_index
- );
- } else if (ev.keyCode === $.ui.keyCode.ENTER || ev.keyCode === 13) {
- if ($(".modal-dialog:visible").length) {
- // Workaround: Bootstrap don't close modal when use 'enter' so, force click event.
- const $button_confirm = $(".modal-footer .btn-primary:visible");
- if ($button_confirm.length) {
- $button_confirm.click();
- }
- } else {
- // Try to click selectable item
- const selectable_clicked = self._set_click_on_selectable_item(
- self._keybind_selectable_items,
- self._keybind_selectable_index
- );
- if (!selectable_clicked) {
- // If not, try default validation buttons
- // Only one button is visible
- const $action_confirm = self.$(
- "button[name='action_confirm']:visible"
- );
- const $action_confirm_force = self.$(
- "button[name='action_force_done']:visible"
- );
- if ($action_confirm.length) {
- $action_confirm.click();
- } else if ($action_confirm_force.length) {
- $action_confirm_force.click();
- }
- }
- }
- }
- }
- },
-
- _set_focus_on_selectable_item: function (items, index) {
- if (items && index >= 0 && index < items.length) {
- items[index].focus();
- return true;
- }
- return false;
- },
-
- _set_click_on_selectable_item: function (items, index) {
- if (items && index >= 0 && index < items.length) {
- items[index].click();
- return true;
- }
- return false;
- },
-
- _update_selectable_items: function (reset_index) {
- if (reset_index) {
- this._keybind_selectable_index = -1;
- }
- this._keybind_selectable_items = this.$(
- ".oe_kanban_action_button:visible,.oe_btn_quick_action:visible"
- );
- },
-
- /**
- * @private
- * @param {KeyboardEvent} ev
- */
- _onDocumentKeyUp: function (ev) {
- var self = (ev.data && ev.data.controller) || this;
- if (self._is_valid_barcode_model) {
- if (
- (!ev.altKey || ev.key !== "Alt") &&
- !ev.ctrlKey &&
- ev.keyCode !== 113
- ) {
- self._toggleAccessKeys(false);
- }
- }
- },
-
- /* eslint-disable no-unused-vars */
- update: function (params, options) {
- return this._super.apply(this, arguments).then((res) => {
- this._update_selectable_items(true);
- return res;
- });
- },
-
- /**
- * @override
- */
- _applyChanges: function () {
- return this._super.apply(this, arguments).then((res) => {
- this._update_selectable_items();
- return res;
- });
- },
-
- open_action_menu: function () {
- return this.do_action("stock_barcodes.action_stock_barcodes_action", {
- name: "Barcode wizard menu",
- res_model: "wiz.stock.barcodes.read.picking",
- type: "ir.actions.act_window",
- });
- },
- });
-});
diff --git a/stock_barcodes/static/src/js/basic_fields.js b/stock_barcodes/static/src/js/basic_fields.js
deleted file mode 100644
index f9648537bc56..000000000000
--- a/stock_barcodes/static/src/js/basic_fields.js
+++ /dev/null
@@ -1,59 +0,0 @@
-/* Copyright 2018-2019 Sergio Teruel .
- * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
-
-odoo.define("stock_barcodes.FieldFloatNumericMode", function (require) {
- "use strict";
-
- var basic_fields = require("web.basic_fields");
- var field_registry = require("web.field_registry");
-
- var FieldFloatNumericMode = basic_fields.FieldFloat.extend({
- events: _.extend({}, basic_fields.FieldFloat.prototype.events, {
- focusin: "_onFocusIn",
- }),
- _onFocusIn: function () {
- // Auto select all content when user enters into fields with this
- // widget.
- this.$input.select();
- },
- _prepareInput: function ($input) {
- // Set numeric mode to display numeric keyboard in mobile devices
- var $input_numeric = this._super($input);
- $input_numeric.attr({inputmode: "numeric"});
- return $input_numeric;
- },
- });
-
- var FieldBarcodeBooleanToggle = basic_fields.BooleanToggle.extend({
- /*
- This is needed because, whenever we click the checkbox to enter data
- manually, the checkbox will be focused causing that when we scan the
- barcode afterwards, it will not perform the python on_barcode_scanned
- function.
- */
- _onClick: function (event) {
- this._super(event);
- // This.getFocusableElement().blur();
- // HACK: Fails normal way
- _.defer(() => {
- this.getFocusableElement().blur();
- });
- },
-
- _render: function () {
- this._super.apply(this, arguments);
- const accesskey = this.attrs && this.attrs.accesskey;
- if (accesskey) {
- this.$el.attr("accesskey", accesskey);
- }
- },
- });
-
- field_registry.add("FieldFloatNumericMode", FieldFloatNumericMode);
- field_registry.add("FieldBarcodeBooleanToggle", FieldBarcodeBooleanToggle);
-
- return {
- FieldFloatNumericMode: FieldFloatNumericMode,
- FieldBarcodeBooleanToggle: FieldBarcodeBooleanToggle,
- };
-});
diff --git a/stock_barcodes/static/src/js/form_view.js b/stock_barcodes/static/src/js/form_view.js
deleted file mode 100644
index 1973c2c090c2..000000000000
--- a/stock_barcodes/static/src/js/form_view.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Copyright 2021 Tecnativa - Alexandre D. Díaz
- * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
-
-odoo.define("stock_barcodes.FormView", function (require) {
- "use strict";
-
- var FormView = require("web.FormView");
-
- FormView.include({
- /**
- * Adds support to define the 'control_panel_hidden' context key to
- * override 'withControlPanel' option.
- *
- * @override
- */
- _extractParamsFromAction: function (action) {
- const params = this._super.apply(this, arguments);
- if (action && action.context && action.context.control_panel_hidden) {
- params.withControlPanel = false;
- }
- return params;
- },
- });
-});
diff --git a/stock_barcodes/static/src/js/kanban_renderer.js b/stock_barcodes/static/src/js/kanban_renderer.js
deleted file mode 100644
index 0c1250230737..000000000000
--- a/stock_barcodes/static/src/js/kanban_renderer.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/* Copyright 2022 Tecnativa - Alexandre D. Díaz
- * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
-odoo.define("stock_barcodes.KanbanRenderer", function (require) {
- "use strict";
-
- const BasicController = require("web.BasicController");
- const KanbanRenderer = require("web.KanbanRenderer");
- const BarcodesModelsMixin = require("stock_barcodes.BarcodesModelsMixin");
-
- KanbanRenderer.include(BarcodesModelsMixin);
- KanbanRenderer.include({
- /**
- * @override
- */
- init: function () {
- this._super.apply(this, arguments);
- this._is_valid_barcode_model = this._isAllowedBarcodeModel(
- this.state.model
- );
- if (this._is_valid_barcode_model) {
- // Controller base is used when the renderer its initialized by a field
- this._controller_base = this.findAncestor((parent) => {
- return parent instanceof BasicController;
- });
- }
- },
-
- /**
- * Avoid intercept events in valid barcodes models.
- * This is necessary to get the event in the controller.
- *
- * @override
- */
- _onRecordKeyDown: function (ev) {
- if (this._is_valid_barcode_model) {
- if (this._controller_base) {
- ev.stopPropagation();
- this._controller_base._onDocumentKeyDown(ev);
- }
- } else {
- this._super.apply(this, arguments);
- }
- },
- });
-});
diff --git a/stock_barcodes/static/src/js/numeric_step.js b/stock_barcodes/static/src/js/numeric_step.js
deleted file mode 100644
index df95a14ec349..000000000000
--- a/stock_barcodes/static/src/js/numeric_step.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Copyright 2022 Tecnativa - Alexandre D. Díaz
- * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
-odoo.define("stock_barcodes.web_widget_numeric_step.field", function (require) {
- "use strict";
-
- const BasicController = require("web.BasicController");
- const NumericStep = require("web_widget_numeric_step.field");
- const BarcodesModelsMixin = require("stock_barcodes.BarcodesModelsMixin");
-
- NumericStep.include(BarcodesModelsMixin);
- NumericStep.include({
- /**
- * @override
- */
- init: function () {
- this._super.apply(this, arguments);
- this._is_valid_barcode_model = this._isAllowedBarcodeModel(this.model);
- if (this._is_valid_barcode_model) {
- // Controller base is used when the renderer its initialized by a field
- this._controller_base = this.findAncestor((parent) => {
- return parent instanceof BasicController;
- });
- }
- },
-
- /**
- * Avoid intercept events in valid barcodes models.
- * This is necessary to get the event in the controller.
- *
- * @override
- */
- _onKeyDown: function (ev) {
- if (this._is_valid_barcode_model && ev.keyCode === $.ui.keyCode.ENTER) {
- if (this._controller_base) {
- ev.stopPropagation();
- this._controller_base._onDocumentKeyDown(ev);
- }
- } else {
- this._super.apply(this, arguments);
- }
- },
- });
-});
diff --git a/stock_barcodes/static/src/kanban_renderer.esm.js b/stock_barcodes/static/src/kanban_renderer.esm.js
new file mode 100644
index 000000000000..e61d5ea8a394
--- /dev/null
+++ b/stock_barcodes/static/src/kanban_renderer.esm.js
@@ -0,0 +1,163 @@
+/** @odoo-module */
+/* Copyright 2022 Tecnativa - Alexandre D. Díaz
+ * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
+
+import {KanbanRenderer} from "@web/views/kanban/kanban_renderer";
+import {isAllowedBarcodeModel} from "./barcodes_models_utils.esm";
+import {patch} from "@web/core/utils/patch";
+import {useBus} from "@web/core/utils/hooks";
+import {useHotkey} from "@web/core/hotkeys/hotkey_hook";
+import {useRef} from "@odoo/owl";
+
+patch(KanbanRenderer.prototype, "add hotkey", {
+ setup() {
+ const rootRef = useRef("root");
+ useHotkey(
+ "Enter",
+ ({target}) => {
+ if (!target.classList.contains("o_kanban_record")) {
+ return;
+ }
+
+ // Open first link
+ let firstLink = null;
+ if (isAllowedBarcodeModel(this.props.list.resModel)) {
+ firstLink = target.querySelector(
+ ".oe_kanban_action_button,.oe_btn_quick_action"
+ );
+ }
+ if (!firstLink) {
+ firstLink = target.querySelector(
+ ".oe_kanban_global_click, a, button"
+ );
+ }
+ if (firstLink && firstLink instanceof HTMLElement) {
+ firstLink.click();
+ }
+ return;
+ },
+ {area: () => rootRef.el}
+ );
+
+ this._super(...arguments);
+
+ if (isAllowedBarcodeModel(this.props.list.resModel)) {
+ if (this.env.searchModel) {
+ useBus(this.env.searchModel, "focus-view", () => {
+ const {model} = this.props.list;
+ if (model.useSampleModel || !model.hasData()) {
+ return;
+ }
+ const cards = Array.from(
+ rootRef.el.querySelectorAll(".o_kanban_record")
+ );
+ const firstCard = cards.find(
+ (card) =>
+ card.querySelectorAll("button[name='action_barcode_scan']")
+ .length > 0
+ );
+ if (firstCard) {
+ // Focus first kanban card
+ firstCard.focus();
+ }
+ });
+ }
+ }
+ },
+ // eslint-disable-next-line complexity
+ focusNextCard(area, direction) {
+ const {isGrouped} = this.props.list;
+ const closestCard = document.activeElement.closest(".o_kanban_record");
+ if (!closestCard) {
+ return;
+ }
+ const groups = isGrouped
+ ? [...area.querySelectorAll(".o_kanban_group")]
+ : [area];
+ let cards = [...groups]
+ .map((group) => [...group.querySelectorAll(".o_kanban_record")])
+ .filter((group) => group.length);
+
+ if (isAllowedBarcodeModel(this.props.list.resModel)) {
+ cards = cards.map((group) => {
+ const result = group.filter((card) => {
+ return (
+ card.querySelectorAll('button[name="action_barcode_scan"]')
+ .length > 0
+ );
+ });
+ return result;
+ });
+ }
+
+ let iGroup = null;
+ let iCard = null;
+ for (iGroup = 0; iGroup < cards.length; iGroup++) {
+ const i = cards[iGroup].indexOf(closestCard);
+ if (i !== -1) {
+ iCard = i;
+ break;
+ }
+ }
+ if (iCard === undefined) {
+ iCard = 0;
+ iGroup = 0;
+ }
+ // Find next card to focus
+ let nextCard = null;
+ switch (direction) {
+ case "down":
+ nextCard = iCard < cards[iGroup].length - 1 && cards[iGroup][iCard + 1];
+ break;
+ case "up":
+ nextCard = iCard > 0 && cards[iGroup][iCard - 1];
+ break;
+ case "right":
+ if (isGrouped) {
+ nextCard = iGroup < cards.length - 1 && cards[iGroup + 1][0];
+ } else {
+ nextCard = iCard < cards[0].length - 1 && cards[0][iCard + 1];
+ }
+ break;
+ case "left":
+ if (isGrouped) {
+ nextCard = iGroup > 0 && cards[iGroup - 1][0];
+ } else {
+ nextCard = iCard > 0 && cards[0][iCard - 1];
+ }
+ break;
+ }
+
+ if (nextCard && nextCard instanceof HTMLElement) {
+ nextCard.focus();
+ return true;
+ }
+ },
+});
+
+// Class BarcodeKanbanController extends KanbanController {
+// setup() {
+// super.setup();
+// this._appendBarcodesSounds();
+// }
+//
+// /**
+// * Append the audio elements to play the sounds.
+// * This is here because only must exists one controller at time
+// *
+// * @private
+// */
+// _appendBarcodesSounds() {
+// this.$sound_ok = $("", {
+// src: "/stock_barcodes/static/src/sounds/bell.wav",
+// preload: "auto",
+// });
+// this.$sound_ok.appendTo("body");
+// this.$sound_ko = $("", {
+// src: "/stock_barcodes/static/src/sounds/error.wav",
+// preload: "auto",
+// });
+// this.$sound_ko.appendTo("body");
+// }
+//
+// }
diff --git a/stock_barcodes/static/src/numeric_step.esm.js b/stock_barcodes/static/src/numeric_step.esm.js
new file mode 100644
index 000000000000..3cc26d1210fb
--- /dev/null
+++ b/stock_barcodes/static/src/numeric_step.esm.js
@@ -0,0 +1,80 @@
+/** @odoo-module */
+/* Copyright 2022 Tecnativa - Alexandre D. Díaz
+ * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
+
+import {NumericStep} from "@web_widget_numeric_step/numeric_step.esm";
+import {isAllowedBarcodeModel} from "./barcodes_models_utils.esm";
+import {patch} from "@web/core/utils/patch";
+
+patch(NumericStep.prototype, "Adds barcode event handling and focus", {
+ _onFocus() {
+ if (isAllowedBarcodeModel(this.props.record.resModel)) {
+ this.inputRef.el.select();
+ }
+ },
+
+ _onKeyDown(ev) {
+ if (isAllowedBarcodeModel(this.props.record.resModel) && ev.keyCode === 13) {
+ const action_confirm = document.querySelector(
+ "button[name='action_confirm']"
+ );
+
+ if (action_confirm) {
+ action_confirm.click();
+ return;
+ }
+
+ const action_confirm_force = document.querySelector(
+ "button[name='action_force_done']"
+ );
+
+ if (action_confirm_force) {
+ action_confirm_force.click();
+ return;
+ }
+ }
+ this._super(...arguments);
+ },
+});
+//
+// odoo.define("stock_barcodes.web_widget_numeric_step.field", function (require) {
+// "use strict";
+//
+// const BasicController = require("web.BasicController");
+// const NumericStep = require("web_widget_numeric_step.field");
+// const BarcodesModelsMixin = require("stock_barcodes.BarcodesModelsMixin");
+//
+// NumericStep.include(BarcodesModelsMixin);
+// NumericStep.include({
+// /**
+// * @override
+// */
+// init: function () {
+// this._super.apply(this, arguments);
+// this._is_valid_barcode_model = this._isAllowedBarcodeModel(this.model);
+// if (this._is_valid_barcode_model) {
+// // Controller base is used when the renderer its initialized by a field
+// this._controller_base = this.findAncestor((parent) => {
+// return parent instanceof BasicController;
+// });
+// }
+// },
+//
+// /**
+// * Avoid intercept events in valid barcodes models.
+// * This is necessary to get the event in the controller.
+// *
+// * @override
+// */
+// _onKeyDown: function (ev) {
+// if (this._is_valid_barcode_model && ev.keyCode === $.ui.keyCode.ENTER) {
+// if (this._controller_base) {
+// ev.stopPropagation();
+// this._controller_base._onDocumentKeyDown(ev);
+// }
+// } else {
+// this._super.apply(this, arguments);
+// }
+// },
+// });
+// });
diff --git a/stock_barcodes/static/src/numeric_step.xml b/stock_barcodes/static/src/numeric_step.xml
new file mode 100644
index 000000000000..0c13f3eb2a8f
--- /dev/null
+++ b/stock_barcodes/static/src/numeric_step.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+ _onFocus
+
+
+
diff --git a/stock_barcodes/static/src/css/stock.css b/stock_barcodes/static/src/stock.css
similarity index 100%
rename from stock_barcodes/static/src/css/stock.css
rename to stock_barcodes/static/src/stock.css
diff --git a/stock_barcodes/static/src/css/stock.scss b/stock_barcodes/static/src/stock.scss
similarity index 90%
rename from stock_barcodes/static/src/css/stock.scss
rename to stock_barcodes/static/src/stock.scss
index 3d17c809ab93..199122e9319f 100644
--- a/stock_barcodes/static/src/css/stock.scss
+++ b/stock_barcodes/static/src/stock.scss
@@ -38,6 +38,13 @@
.o_kanban_record {
padding: 0.3em;
+ flex-basis: 100%;
+
+ .btn-full-width {
+ margin: -9px;
+ width: calc(100% + 18px);
+ height: calc(100% + 18px);
+ }
&.o_kanban_ghost {
display: none;
diff --git a/stock_barcodes/static/src/view_button.esm.js b/stock_barcodes/static/src/view_button.esm.js
new file mode 100644
index 000000000000..28c4b3903058
--- /dev/null
+++ b/stock_barcodes/static/src/view_button.esm.js
@@ -0,0 +1,8 @@
+/** @odoo-module */
+
+import {ViewButton} from "@web/views/view_button/view_button";
+import {patch} from "@web/core/utils/patch";
+
+patch(ViewButton, "Add hotkey to button", {
+ props: [...ViewButton.props, "hotkey?"],
+});
diff --git a/stock_barcodes/static/src/view_button.xml b/stock_barcodes/static/src/view_button.xml
new file mode 100644
index 000000000000..6a85cb5de2f2
--- /dev/null
+++ b/stock_barcodes/static/src/view_button.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ props.hotkey
+
+
+
diff --git a/stock_barcodes/static/src/view_compiler.esm.js b/stock_barcodes/static/src/view_compiler.esm.js
new file mode 100644
index 000000000000..986bd1f69283
--- /dev/null
+++ b/stock_barcodes/static/src/view_compiler.esm.js
@@ -0,0 +1,16 @@
+/** @odoo-module */
+
+import {ViewCompiler} from "@web/views/view_compiler";
+import {patch} from "@web/core/utils/patch";
+
+patch(ViewCompiler.prototype, "Add hotkey props to button tag", {
+ compileButton(el, params) {
+ const hotkey = el.getAttribute("data-hotkey");
+ el.removeAttribute("data-hotkey");
+ const button = this._super(el, params);
+ if (hotkey) {
+ button.setAttribute("hotkey", hotkey);
+ }
+ return button;
+ },
+});
diff --git a/stock_barcodes/static/src/views.esm.js b/stock_barcodes/static/src/views.esm.js
new file mode 100644
index 000000000000..30d8aee918d9
--- /dev/null
+++ b/stock_barcodes/static/src/views.esm.js
@@ -0,0 +1,178 @@
+/** @odoo-module */
+/* Copyright 2024 Akretion
+ * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
+
+import {getVisibleElements, isVisible} from "@web/core/utils/ui";
+import {FormController} from "@web/views/form/form_controller";
+import {KanbanController} from "@web/views/kanban/kanban_controller";
+import {ListController} from "@web/views/list/list_controller";
+import {isAllowedBarcodeModel} from "./barcodes_models_utils.esm";
+import {patch} from "@web/core/utils/patch";
+import {useEffect} from "@odoo/owl";
+import {useService} from "@web/core/utils/hooks";
+
+let barcodeOverlaysVisible = false;
+
+// This is necessary because the hotkey service does not make its API public for
+// some reasons
+
+export function barcodeRemoveHotkeyOverlays() {
+ for (const overlay of document.querySelectorAll(".o_barcode_web_hotkey_overlay")) {
+ overlay.remove();
+ }
+ barcodeOverlaysVisible = false;
+}
+
+// This is necessary because the hotkey service does not make its API public for
+// some reasons
+
+export function barcodeAddHotkeyOverlays(activeElement) {
+ for (const el of getVisibleElements(
+ activeElement,
+ "[data-hotkey]:not(:disabled)"
+ )) {
+ const hotkey = el.dataset.hotkey;
+ const overlay = document.createElement("div");
+ overlay.classList.add(
+ "o_barcode_web_hotkey_overlay",
+ "position-absolute",
+ "top-0",
+ "bottom-0",
+ "start-0",
+ "end-0",
+ "d-flex",
+ "justify-content-center",
+ "align-items-center",
+ "m-0",
+ "bg-black-50",
+ "h6"
+ );
+ const overlayKbd = document.createElement("kbd");
+ overlayKbd.className = "small";
+ overlayKbd.appendChild(document.createTextNode(hotkey.toUpperCase()));
+ overlay.appendChild(overlayKbd);
+
+ let overlayParent = null;
+ if (el.tagName.toUpperCase() === "INPUT") {
+ // Special case for the search input that has an access key
+ // defined. We cannot set the overlay on the input itself,
+ // only on its parent.
+ overlayParent = el.parentElement;
+ } else {
+ overlayParent = el;
+ }
+
+ if (overlayParent.style.position !== "absolute") {
+ overlayParent.style.position = "relative";
+ }
+ overlayParent.appendChild(overlay);
+ }
+ barcodeOverlaysVisible = true;
+}
+
+function setupView() {
+ const actionService = useService("action");
+ const uiService = useService("ui");
+ const busService = useService("bus_service");
+
+ const handleKeys = async (ev) => {
+ if (ev.keyCode === 113) {
+ const {activeElement} = uiService;
+
+ if (barcodeOverlaysVisible) {
+ barcodeRemoveHotkeyOverlays();
+ } else {
+ barcodeAddHotkeyOverlays(activeElement);
+ }
+ } else if (ev.keyCode === 120) {
+ const button = document.querySelector("button[name='action_clean_values']");
+ if (isVisible(button)) {
+ button.click();
+ }
+ } else if (ev.keyCode === 123 || ev.keyCode === 115) {
+ await actionService.doAction(
+ "stock_barcodes.action_stock_barcodes_action",
+ {
+ name: "Barcode wizard menu",
+ res_model: "wiz.stock.barcodes.read.picking",
+ type: "ir.actions.act_window",
+ }
+ );
+ }
+ };
+
+ const handleNotification = ({detail: notifications}) => {
+ if (notifications && notifications.length > 0) {
+ notifications.forEach((notif) => {
+ const {payload, type} = notif;
+ if (type === "stock_barcodes_sound") {
+ this.$sound_ok[0].play();
+ }
+ if (type === "stock_barcodes_focus") {
+ requestIdleCallback(() => {
+ const input = document.querySelector(
+ `[name=${payload.field_name}] input`
+ );
+ if (input) {
+ input.focus();
+ }
+ });
+ }
+ });
+ }
+ };
+
+ useEffect(() => {
+ document.body.addEventListener("keydown", handleKeys);
+
+ this.$sound_ok = $("", {
+ src: "/stock_barcodes/static/src/sounds/bell.wav",
+ preload: "auto",
+ });
+ this.$sound_ok.appendTo("body");
+ this.$sound_ko = $("", {
+ src: "/stock_barcodes/static/src/sounds/error.wav",
+ preload: "auto",
+ });
+ this.$sound_ko.appendTo("body");
+
+ busService.addChannel("barcode_scan");
+
+ busService.addEventListener("notification", handleNotification);
+
+ return () => {
+ this.$sound_ok.remove();
+ this.$sound_ko.remove();
+ document.body.removeEventListener("keydown", handleKeys);
+ busService.deleteChannel("barcode_scan");
+ busService.removeEventListener("notification", handleNotification);
+ };
+ });
+}
+
+patch(KanbanController.prototype, "add hotkeys to kanban", {
+ setup() {
+ this._super(...arguments);
+ if (isAllowedBarcodeModel(this.props.resModel)) {
+ setupView.call(this);
+ }
+ },
+});
+
+patch(FormController.prototype, "add hotkeys to form", {
+ setup() {
+ this._super(...arguments);
+ if (isAllowedBarcodeModel(this.props.resModel)) {
+ setupView.call(this);
+ }
+ },
+});
+
+patch(ListController.prototype, "add hotkeys to list", {
+ setup() {
+ this._super(...arguments);
+ if (isAllowedBarcodeModel(this.props.resModel)) {
+ setupView.call(this);
+ }
+ },
+});
diff --git a/stock_barcodes/tests/common.py b/stock_barcodes/tests/common.py
new file mode 100644
index 000000000000..f9e2fcd5c735
--- /dev/null
+++ b/stock_barcodes/tests/common.py
@@ -0,0 +1,165 @@
+# Copyright 2108-2019 Francois Poizat
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+
+from odoo.tests.common import TransactionCase
+
+
+class TestCommonStockBarcodes(TransactionCase):
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+
+ # Active group_stock_packaging and group_production_lot for user
+ group_stock_packaging = cls.env.ref("product.group_stock_packaging")
+ group_production_lot = cls.env.ref("stock.group_production_lot")
+ cls.env.user.groups_id = [
+ (4, group_stock_packaging.id),
+ (4, group_production_lot.id),
+ ]
+ # models
+ cls.StockLocation = cls.env["stock.location"]
+ cls.Product = cls.env["product.product"]
+ cls.ProductPackaging = cls.env["product.packaging"]
+ cls.WizScanReadPicking = cls.env["wiz.stock.barcodes.read.picking"]
+ cls.StockProductionLot = cls.env["stock.lot"]
+ cls.StockPicking = cls.env["stock.picking"]
+ cls.StockQuant = cls.env["stock.quant"]
+
+ cls.company = cls.env.company
+
+ # Option groups for test
+ cls.option_group = cls._create_barcode_option_group()
+
+ # warehouse and locations
+ cls.warehouse = cls.env.ref("stock.warehouse0")
+ cls.stock_location = cls.env.ref("stock.stock_location_stock")
+ cls.location_1 = cls.StockLocation.create(
+ {
+ "name": "Test location 1",
+ "usage": "internal",
+ "location_id": cls.stock_location.id,
+ "barcode": "8411322222568",
+ }
+ )
+ cls.location_2 = cls.StockLocation.create(
+ {
+ "name": "Test location 2",
+ "usage": "internal",
+ "location_id": cls.stock_location.id,
+ "barcode": "8470001809032",
+ }
+ )
+
+ # products
+ cls.product_wo_tracking = cls.Product.create(
+ {
+ "name": "Product test wo lot tracking",
+ "type": "product",
+ "tracking": "none",
+ "barcode": "8480000723208",
+ "packaging_ids": [
+ (
+ 0,
+ 0,
+ {
+ "name": "Box 10 Units",
+ "qty": 10.0,
+ "barcode": "5099206074439",
+ },
+ )
+ ],
+ }
+ )
+ cls.product_tracking = cls.Product.create(
+ {
+ "name": "Product test with lot tracking",
+ "type": "product",
+ "tracking": "lot",
+ "barcode": "8433281006850",
+ "packaging_ids": [
+ (
+ 0,
+ 0,
+ {"name": "Box 5 Units", "qty": 5.0, "barcode": "5420008510489"},
+ )
+ ],
+ }
+ )
+ cls.lot_1 = cls.StockProductionLot.create(
+ {
+ "name": "8411822222568",
+ "product_id": cls.product_tracking.id,
+ "company_id": cls.company.id,
+ }
+ )
+ cls.quant_lot_1 = cls.StockQuant.create(
+ {
+ "product_id": cls.product_tracking.id,
+ "lot_id": cls.lot_1.id,
+ "location_id": cls.stock_location.id,
+ "quantity": 100.0,
+ }
+ )
+ cls.wiz_scan = cls.WizScanReadPicking.create(
+ {"option_group_id": cls.option_group.id, "step": 1}
+ )
+
+ @classmethod
+ def _create_barcode_option_group(cls):
+ return cls.env["stock.barcodes.option.group"].create(
+ {
+ "name": "option group for tests",
+ "show_scan_log": True,
+ "create_lot": True,
+ "option_ids": [
+ (
+ 0,
+ 0,
+ {
+ "step": 1,
+ "name": "Location",
+ "field_name": "location_id",
+ "to_scan": True,
+ "required": True,
+ },
+ ),
+ (
+ 0,
+ 0,
+ {
+ "step": 2,
+ "name": "Product",
+ "field_name": "product_id",
+ "to_scan": True,
+ "required": True,
+ },
+ ),
+ (
+ 0,
+ 0,
+ {
+ "step": 2,
+ "name": "Packaging",
+ "field_name": "packaging_id",
+ "to_scan": True,
+ "required": False,
+ },
+ ),
+ (
+ 0,
+ 0,
+ {
+ "step": 2,
+ "name": "Lot / Serial",
+ "field_name": "lot_id",
+ "to_scan": True,
+ "required": True,
+ },
+ ),
+ ],
+ }
+ )
+
+ def action_barcode_scanned(self, wizard, barcode):
+ wizard._barcode_scanned = barcode
+ wizard._on_barcode_scanned()
diff --git a/stock_barcodes/tests/test_stock_barcodes.py b/stock_barcodes/tests/test_stock_barcodes.py
index b9f5a9033644..74031ac05190 100644
--- a/stock_barcodes/tests/test_stock_barcodes.py
+++ b/stock_barcodes/tests/test_stock_barcodes.py
@@ -1,170 +1,13 @@
# Copyright 2108-2019 Sergio Teruel
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-from odoo.tests.common import TransactionCase, tagged
+from odoo.tests.common import tagged
+from .common import TestCommonStockBarcodes
-@tagged("post_install", "-at_install")
-class TestStockBarcodes(TransactionCase):
- @classmethod
- def setUpClass(cls):
- super().setUpClass()
-
- # Active group_stock_packaging and group_production_lot for user
- group_stock_packaging = cls.env.ref("product.group_stock_packaging")
- group_production_lot = cls.env.ref("stock.group_production_lot")
- cls.env.user.groups_id = [
- (4, group_stock_packaging.id),
- (4, group_production_lot.id),
- ]
- # models
- cls.StockLocation = cls.env["stock.location"]
- cls.Product = cls.env["product.product"]
- cls.ProductPackaging = cls.env["product.packaging"]
- cls.WizScanReadPicking = cls.env["wiz.stock.barcodes.read.picking"]
- cls.StockProductionLot = cls.env["stock.production.lot"]
- cls.StockPicking = cls.env["stock.picking"]
- cls.StockQuant = cls.env["stock.quant"]
-
- cls.company = cls.env.company
-
- # Option groups for test
- cls.option_group = cls._create_barcode_option_group()
-
- # warehouse and locations
- cls.warehouse = cls.env.ref("stock.warehouse0")
- cls.stock_location = cls.env.ref("stock.stock_location_stock")
- cls.location_1 = cls.StockLocation.create(
- {
- "name": "Test location 1",
- "usage": "internal",
- "location_id": cls.stock_location.id,
- "barcode": "8411322222568",
- }
- )
- cls.location_2 = cls.StockLocation.create(
- {
- "name": "Test location 2",
- "usage": "internal",
- "location_id": cls.stock_location.id,
- "barcode": "8470001809032",
- }
- )
-
- # products
- cls.product_wo_tracking = cls.Product.create(
- {
- "name": "Product test wo lot tracking",
- "type": "product",
- "tracking": "none",
- "barcode": "8480000723208",
- "packaging_ids": [
- (
- 0,
- 0,
- {
- "name": "Box 10 Units",
- "qty": 10.0,
- "barcode": "5099206074439",
- },
- )
- ],
- }
- )
- cls.product_tracking = cls.Product.create(
- {
- "name": "Product test with lot tracking",
- "type": "product",
- "tracking": "lot",
- "barcode": "8433281006850",
- "packaging_ids": [
- (
- 0,
- 0,
- {"name": "Box 5 Units", "qty": 5.0, "barcode": "5420008510489"},
- )
- ],
- }
- )
- cls.lot_1 = cls.StockProductionLot.create(
- {
- "name": "8411822222568",
- "product_id": cls.product_tracking.id,
- "company_id": cls.company.id,
- }
- )
- cls.quant_lot_1 = cls.StockQuant.create(
- {
- "product_id": cls.product_tracking.id,
- "lot_id": cls.lot_1.id,
- "location_id": cls.stock_location.id,
- "quantity": 100.0,
- }
- )
- cls.wiz_scan = cls.WizScanReadPicking.create(
- {"option_group_id": cls.option_group.id, "step": 1}
- )
-
- @classmethod
- def _create_barcode_option_group(cls):
- return cls.env["stock.barcodes.option.group"].create(
- {
- "name": "option group for tests",
- "show_scan_log": True,
- "create_lot": True,
- "option_ids": [
- (
- 0,
- 0,
- {
- "step": 1,
- "name": "Location",
- "field_name": "location_id",
- "to_scan": True,
- "required": True,
- },
- ),
- (
- 0,
- 0,
- {
- "step": 2,
- "name": "Product",
- "field_name": "product_id",
- "to_scan": True,
- "required": True,
- },
- ),
- (
- 0,
- 0,
- {
- "step": 2,
- "name": "Packaging",
- "field_name": "packaging_id",
- "to_scan": True,
- "required": False,
- },
- ),
- (
- 0,
- 0,
- {
- "step": 2,
- "name": "Lot / Serial",
- "field_name": "lot_id",
- "to_scan": True,
- "required": True,
- },
- ),
- ],
- }
- )
-
- def action_barcode_scanned(self, wizard, barcode):
- wizard._barcode_scanned = barcode
- wizard._on_barcode_scanned()
+@tagged("post_install", "-at_install")
+class TestStockBarcodes(TestCommonStockBarcodes):
def test_wizard_scan_location(self):
self.action_barcode_scanned(self.wiz_scan, "8411322222568")
self.assertEqual(self.wiz_scan.location_id, self.location_1)
@@ -205,14 +48,6 @@ def test_wizard_scan_package(self):
self.assertEqual(self.wiz_scan.product_qty, 15.0)
self.wiz_scan.manual_entry = False
- # Force more than one package with the same lot
- self.product_wo_tracking.packaging_ids.barcode = "5420008510489"
- self.action_barcode_scanned(self.wiz_scan, "5420008510489")
- self.assertEqual(
- self.wiz_scan.message,
- "5420008510489 (More than one package found)",
- )
-
def test_wizard_scan_lot(self):
self.wiz_scan.location_id = self.location_1.id
self.wiz_scan.action_show_step()
diff --git a/stock_barcodes/tests/test_stock_barcodes_new_lot.py b/stock_barcodes/tests/test_stock_barcodes_new_lot.py
index afb3a2bca97d..d2bc10f01bdc 100644
--- a/stock_barcodes/tests/test_stock_barcodes_new_lot.py
+++ b/stock_barcodes/tests/test_stock_barcodes_new_lot.py
@@ -2,11 +2,11 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.tests.common import tagged
-from .test_stock_barcodes_picking import TestStockBarcodesPicking
+from .common import TestCommonStockBarcodes
@tagged("post_install", "-at_install")
-class TestStockBarcodesNewLot(TestStockBarcodesPicking):
+class TestStockBarcodesNewLot(TestCommonStockBarcodes):
@classmethod
def setUpClass(cls):
super().setUpClass()
diff --git a/stock_barcodes/tests/test_stock_barcodes_picking.py b/stock_barcodes/tests/test_stock_barcodes_picking.py
index c26449f0a611..6ed005fc0762 100644
--- a/stock_barcodes/tests/test_stock_barcodes_picking.py
+++ b/stock_barcodes/tests/test_stock_barcodes_picking.py
@@ -2,11 +2,11 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.tests.common import tagged
-from .test_stock_barcodes import TestStockBarcodes
+from .common import TestCommonStockBarcodes
@tagged("post_install", "-at_install")
-class TestStockBarcodesPicking(TestStockBarcodes):
+class TestStockBarcodesPicking(TestCommonStockBarcodes):
@classmethod
def setUpClass(cls):
super().setUpClass()
@@ -41,7 +41,7 @@ def setUpClass(cls):
"location_dest_id": cls.customer_location.id,
"partner_id": cls.partner_agrolite.id,
"picking_type_id": cls.picking_type_out.id,
- "move_lines": [
+ "move_ids": [
(
0,
0,
@@ -68,7 +68,7 @@ def setUpClass(cls):
"location_dest_id": cls.stock_location.id,
"partner_id": cls.partner_agrolite.id,
"picking_type_id": cls.picking_type_in.id,
- "move_lines": [
+ "move_ids": [
(
0,
0,
@@ -97,7 +97,7 @@ def setUpClass(cls):
0,
0,
{
- "name": cls.product_tracking.name,
+ "name": cls.product_wo_tracking.name,
"product_id": cls.product_tracking.id,
"product_uom_qty": 3,
"product_uom": cls.product_tracking.uom_id.id,
@@ -109,7 +109,7 @@ def setUpClass(cls):
0,
0,
{
- "name": cls.product_tracking.name,
+ "name": cls.product_wo_tracking.name,
"product_id": cls.product_tracking.id,
"product_uom_qty": 5,
"product_uom": cls.product_tracking.uom_id.id,
@@ -215,10 +215,10 @@ def test_barcode_from_operation(self):
wiz_barcode_id=self.wiz_scan_picking.id, picking_id=self.picking_out_01.id
)
candidate_wiz.with_context(force_create_move=True).action_lock_picking()
- self.assertEqual(self.picking_out_01.move_lines.quantity_done, 2)
+ self.assertEqual(self.picking_out_01.move_ids.quantity_done, 2)
self.wiz_scan_picking.product_qty = 2
self.wiz_scan_picking.with_context(force_create_move=True).action_confirm()
- self.assertEqual(self.picking_out_01.move_lines.quantity_done, 4)
+ self.assertEqual(self.picking_out_01.move_ids.quantity_done, 4)
# Picking out 3 is in confirmed state, so until confirmed moves has
# not been activated candidate pickings is 2
diff --git a/stock_barcodes/views/stock_picking_views.xml b/stock_barcodes/views/stock_picking_views.xml
index 761be6ac9edf..5383184368ad 100644
--- a/stock_barcodes/views/stock_picking_views.xml
+++ b/stock_barcodes/views/stock_picking_views.xml
@@ -28,7 +28,6 @@
1:
- self._set_messagge_info(
- "more_match", _("More than one package found")
- )
- return False
self.action_packaging_scaned_post(packaging)
return True
return False
@@ -623,7 +618,7 @@ def action_force_done(self):
self.visible_force_done = False
return res
- @api.model
+ @api.model_create_multi
def create(self, vals):
wiz = super().create(vals)
wiz.action_show_step()
@@ -674,7 +669,7 @@ def action_confirm(self):
record.write(self._convert_to_write(self._cache))
self = record
res = self.action_done()
- self.refresh()
+ self.invalidate_recordset()
self.play_sounds(res)
self._set_focus_on_qty_input()
return res
@@ -693,14 +688,14 @@ def process_lot_before_done(self):
def play_sounds(self, res):
if res:
self.env["bus.bus"]._sendone(
- "stock_barcodes-{}".format(self.ids[0]),
- "stock_barcodes_sound-{}".format(self.ids[0]),
+ "barcode_scan",
+ "stock_barcodes_sound",
{"sound": "ok"},
)
else:
self.env["bus.bus"]._sendone(
- "stock_barcodes-{}".format(self.ids[0]),
- "stock_barcodes_sound-{}".format(self.ids[0]),
+ "barcode_scan",
+ "stock_barcodes_sound",
{"sound": "ko"},
)
@@ -710,9 +705,9 @@ def _set_focus_on_qty_input(self, field_name=None):
if field_name == "product_qty" and self.packaging_id:
field_name = "packaging_qty"
self.env["bus.bus"]._sendone(
- "stock_barcodes-{}".format(self.ids[0]),
- "stock_barcodes_read-{}".format(self.ids[0]),
- {"action": "focus", "field_name": field_name},
+ "barcode_scan",
+ "stock_barcodes_focus",
+ {"field_name": field_name},
)
@api.onchange("product_id")
diff --git a/stock_barcodes/wizard/stock_barcodes_read_inventory_views.xml b/stock_barcodes/wizard/stock_barcodes_read_inventory_views.xml
index 42732a564bcd..efc2e93544b1 100644
--- a/stock_barcodes/wizard/stock_barcodes_read_inventory_views.xml
+++ b/stock_barcodes/wizard/stock_barcodes_read_inventory_views.xml
@@ -11,10 +11,13 @@
-
+
@@ -96,15 +100,11 @@
-
-
+ />
diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking.py b/stock_barcodes/wizard/stock_barcodes_read_picking.py
index 68998a412d5b..f705aab60080 100644
--- a/stock_barcodes/wizard/stock_barcodes_read_picking.py
+++ b/stock_barcodes/wizard/stock_barcodes_read_picking.py
@@ -72,7 +72,7 @@ def _compute_todo_line_display_ids(self):
"""Technical field to display only the first record in kanban view"""
self.todo_line_display_ids = self.todo_line_id
- @api.depends("todo_line_ids")
+ @api.depends("todo_line_ids", "_barcode_scanned")
def _compute_pending_move_ids(self):
if self.option_group_id.show_pending_moves:
self.pending_move_ids = self.todo_line_ids.filtered(
@@ -111,7 +111,7 @@ def _set_default_picking(self):
if picking_id:
self._set_candidate_pickings(self.env["stock.picking"].browse(picking_id))
- @api.model
+ @api.model_create_multi
def create(self, vals):
# When user click any view button the wizard record is create and the
# picking candidates have been lost, so we need set it.
@@ -174,7 +174,7 @@ def get_moves_or_move_lines(self):
if self.option_group_id.source_pending_moves == "move_line_ids":
return self.picking_id.move_line_ids.filtered(lambda ln: ln.move_id)
else:
- return self.picking_id.move_lines
+ return self.picking_id.move_ids
def fill_todo_records(self):
move_lines = self.get_sorted_move_lines(self.get_moves_or_move_lines())
@@ -227,7 +227,7 @@ def determine_todo_action(self, forced_todo_line=False):
else:
self.lot_id = False
if self.option_group_id.get_option_value("product_qty", "filled_default"):
- self.product_qty = move_line.product_uom_qty - move_line.qty_done
+ self.product_qty = move_line.reserved_uom_qty - move_line.qty_done
else:
if not self.visible_force_done:
self.product_qty = 0.0
@@ -463,7 +463,7 @@ def _process_stock_move_line(self): # noqa: C901
lines = candidate_lines.filtered(
lambda ln: (
ln.lot_id == self.lot_id
- and ln.product_uom_qty == 0.0
+ and ln.reserved_uom_qty == 0.0
and ln.qty_done > 0.0
)
)
@@ -490,9 +490,9 @@ def _process_stock_move_line(self): # noqa: C901
return False
move_lines_dic = {}
for line in lines:
- if line.product_uom_qty and len(lines) > 1:
+ if line.reserved_uom_qty and len(lines) > 1:
assigned_qty = min(
- max(line.product_uom_qty - line.qty_done, 0.0), available_qty
+ max(line.reserved_uom_qty - line.qty_done, 0.0), available_qty
)
else:
assigned_qty = available_qty
@@ -514,7 +514,7 @@ def _process_stock_move_line(self): # noqa: C901
elif line.result_package_id == line.package_id:
sml_vals.update({"result_package_id": False})
self._update_stock_move_line(line, sml_vals)
- if line.qty_done >= line.product_uom_qty:
+ if line.qty_done >= line.reserved_uom_qty:
line.barcode_scan_state = "done"
elif self.env.context.get("done_forced"):
line.barcode_scan_state = "done_forced"
@@ -767,9 +767,9 @@ def _compute_picking_quantity(self):
qty_demand = 0
qty_done = 0
candidate.product_qty_reserved = sum(
- candidate.picking_id.mapped("move_lines.reserved_availability")
+ candidate.picking_id.mapped("move_ids.reserved_availability")
)
- for move in candidate.picking_id.move_lines:
+ for move in candidate.picking_id.move_ids:
qty_reserved += move.reserved_availability
qty_demand += move.product_uom_qty
qty_done += move.quantity_done
diff --git a/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml b/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml
index a1599f22b392..883ba8ce8a80 100644
--- a/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml
+++ b/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml
@@ -74,7 +74,6 @@
@@ -98,6 +97,7 @@
type="object"
class="btn btn-secondary pull-left"
context="{'wiz_barcode_id': parent.id, 'picking_id': picking_id}"
+ data-hotkey="'shift+o'"
>Open
@@ -109,6 +109,7 @@
attrs="{'invisible': [('state', 'not in', ['draft', 'assigned', 'confirmed'])]}"
confirm="Are you sure to validate the picking ?"
context="{'wiz_barcode_id': parent.id, 'picking_id': picking_id}"
+ data-hotkey="'shift+v'"
>Validate
@@ -125,121 +126,116 @@
-
+
{'readonly': [('manual_entry', '=', False)], 'invisible': [('picking_type_code', '=', 'incoming')]}
-
-
-
-
- Dest. Location
-
-
-
-
-
+
+
+
+ Dest. Location
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
@@ -320,15 +317,11 @@
-
-
+ />
@@ -369,12 +362,13 @@
name="action_put_in_pack"
help="Put in pack"
type="object"
+ icon="fa-cubes"
+ title="Put in Pack"
attrs="{'invisible': ['|', ('picking_state', 'in', ('draft', 'done', 'cancel')), ('display_menu', '=', True)]}"
class="ml-auto oe_kanban_action_button btn btn-secondary btn-sm"
groups="stock.group_tracking_lot"
data-hotkey="6"
>
-
diff --git a/stock_barcodes/wizard/stock_barcodes_read_todo.py b/stock_barcodes/wizard/stock_barcodes_read_todo.py
index 9738b5d3031e..bfc8cd631dab 100644
--- a/stock_barcodes/wizard/stock_barcodes_read_todo.py
+++ b/stock_barcodes/wizard/stock_barcodes_read_todo.py
@@ -52,7 +52,7 @@ class WizStockBarcodesReadTodo(models.TransientModel):
string="Destinatino Name", related="location_dest_id.name"
)
product_id = fields.Many2one(comodel_name="product.product")
- lot_id = fields.Many2one(comodel_name="stock.production.lot")
+ lot_id = fields.Many2one(comodel_name="stock.lot")
uom_id = fields.Many2one(comodel_name="uom.uom")
package_id = fields.Many2one(comodel_name="stock.quant.package")
result_package_id = fields.Many2one(comodel_name="stock.quant.package")
@@ -76,7 +76,7 @@ def _group_key(self, wiz, line):
def _get_all_products_quantities_in_package(self, package):
res = {}
- for quant in package._get_contained_quants():
+ for quant in package.quant_ids:
if quant.product_id not in res:
res[quant.product_id] = 0
res[quant.product_id] += quant.quantity
@@ -103,7 +103,7 @@ def _prepare_fill_record_values(self, wiz_barcode, line, position):
"result_package_id": line.result_package_id.id,
"uom_id": line.product_uom_id.id,
"qty_done": line.qty_done,
- "product_qty_reserved": line.product_qty,
+ "product_qty_reserved": line.reserved_qty,
"line_ids": [(6, 0, line.ids)],
"stock_move_ids": [(6, 0, line.move_id.ids)],
"package_product_qty": package_product_dic
@@ -121,8 +121,8 @@ def _prepare_fill_record_values(self, wiz_barcode, line, position):
"uom_id": line.product_uom.id,
"qty_done": line.quantity_done,
"product_qty_reserved": line.move_line_ids
- and sum(line.move_line_ids.mapped("product_qty"))
- or line.product_uom_qty,
+ and sum(line.move_line_ids.mapped("reserved_qty"))
+ or line.reserved_uom_qty,
"line_ids": [(6, 0, line.move_line_ids.ids)],
"stock_move_ids": [(6, 0, line.ids)],
}
@@ -130,9 +130,9 @@ def _prepare_fill_record_values(self, wiz_barcode, line, position):
return vals
def _update_fill_record_values(self, wiz_barcode, line, vals):
- vals["product_uom_qty"] += line.product_uom_qty
+ vals["product_uom_qty"] += line.reserved_uom_qty
if wiz_barcode.option_group_id.source_pending_moves == "move_line_ids":
- vals["product_qty_reserved"] += line.product_qty
+ vals["product_qty_reserved"] += line.reserved_qty
vals["qty_done"] += line.qty_done
vals["line_ids"][0][2].append(line.id)
vals["stock_move_ids"][0][2].append(line.move_id.id)
@@ -140,7 +140,7 @@ def _update_fill_record_values(self, wiz_barcode, line, vals):
vals["product_qty_reserved"] += (
line.move_line_ids
and sum(line.move_line_ids.mapped("product_qty"))
- or line.product_uom_qty
+ or line.reserved_uom_qty
)
vals["qty_done"] += line.quantity_done
vals["line_ids"][0][2].extend(line.move_line_ids.ids)
@@ -200,7 +200,7 @@ def _compute_qty_done(self):
@api.depends(
"line_ids",
"line_ids.qty_done",
- "line_ids.product_uom_qty",
+ "line_ids.reserved_uom_qty",
"line_ids.barcode_scan_state",
"qty_done",
"product_uom_qty",
diff --git a/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml b/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml
index 98590b475a87..19ebc6862a5f 100644
--- a/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml
+++ b/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml
@@ -107,26 +107,23 @@
-
-
+ />
-
-
+ />
-
-
+ />
diff --git a/stock_barcodes/wizard/stock_barcodes_read_views.xml b/stock_barcodes/wizard/stock_barcodes_read_views.xml
index 65ad9def8c35..aeddeed3d2a5 100644
--- a/stock_barcodes/wizard/stock_barcodes_read_views.xml
+++ b/stock_barcodes/wizard/stock_barcodes_read_views.xml
@@ -19,7 +19,7 @@
-
+
@@ -30,13 +30,20 @@
+
+
@@ -124,209 +131,225 @@
invisible="0"
/>
-
-
- Source Location
-
-
-
-
-
-
- Source Package -> Result Package
-
-
- ->
-
-
-
-
-
-
-
-
-
-
-
- Product
-
-
-
-
-
-
+ Source Location
+
+
+
+
+ Source Package -> Result Package
+
+
+ ->
+
+
+
+
+
+
+
+
+
+ Product
+
+
+
+
+
-
- Lot S/N
-
-
-
-
-
-
-
-
Lot S/N
+
+
+
+
+
+
+
-
- Total Qty
-
-
-
-
+
Total Qty
+
+
+
-
-
+
+
+
-
-
-
+
+
+
+
+
+
+
@@ -423,25 +446,27 @@
-
-
-
+ />
-
-
+ />
-
- stock.barcodes.read.packaging.form
- wiz.stock.barcodes.read
-
-
-
-
-
-
-