diff --git a/README.md b/README.md index 386dd19d096..6a98a929f1d 100644 --- a/README.md +++ b/README.md @@ -32,16 +32,16 @@ addon | version | maintainers | summary [shopfloor](shopfloor/) | 16.0.2.16.1 | guewen simahawk sebalix | manage warehouse operations with barcode scanners [shopfloor_base](shopfloor_base/) | 16.0.1.2.0 | guewen simahawk sebalix | Core module for creating mobile apps [shopfloor_batch_automatic_creation](shopfloor_batch_automatic_creation/) | 16.0.1.1.0 | guewen | Create batch transfers for Cluster Picking -[shopfloor_mobile](shopfloor_mobile/) | 16.0.1.4.0 | simahawk | Mobile frontend for WMS Shopfloor app -[shopfloor_mobile_base](shopfloor_mobile_base/) | 16.0.1.1.0 | simahawk | Mobile frontend for WMS Shopfloor app +[shopfloor_mobile](shopfloor_mobile/) | 16.0.1.4.1 | simahawk | Mobile frontend for WMS Shopfloor app +[shopfloor_mobile_base](shopfloor_mobile_base/) | 16.0.1.2.0 | simahawk | Mobile frontend for WMS Shopfloor app [shopfloor_mobile_base_auth_api_key](shopfloor_mobile_base_auth_api_key/) | 16.0.1.0.0 | | Provides authentication via API key to Shopfloor base mobile app -[shopfloor_reception](shopfloor_reception/) | 16.0.1.6.6 | mmequignon JuMiSanAr | Reception scenario for shopfloor +[shopfloor_reception](shopfloor_reception/) | 16.0.1.6.7 | mmequignon JuMiSanAr | Reception scenario for shopfloor [shopfloor_reception_mobile](shopfloor_reception_mobile/) | 16.0.1.1.2 | JuMiSanAr | Scenario for receiving products [shopfloor_reception_refund_return](shopfloor_reception_refund_return/) | 16.0.1.0.0 | mmequignon | Mark created return as to refund [shopfloor_rest_log](shopfloor_rest_log/) | 16.0.1.0.0 | simahawk | Integrate rest_log into Shopfloor app [shopfloor_workstation](shopfloor_workstation/) | 16.0.1.0.0 | | Manage warehouse workstation with barcode scanners [shopfloor_workstation_mobile](shopfloor_workstation_mobile/) | 16.0.1.0.0 | | Shopfloor mobile app integration for workstation -[stock_available_to_promise_release](stock_available_to_promise_release/) | 16.0.3.8.2 | | Release Operations based on available to promise +[stock_available_to_promise_release](stock_available_to_promise_release/) | 16.0.3.8.3 | | Release Operations based on available to promise [stock_available_to_promise_release_block](stock_available_to_promise_release_block/) | 16.0.1.1.1 | | Block Release of Operations [stock_available_to_promise_release_dynamic_routing](stock_available_to_promise_release_dynamic_routing/) | 16.0.1.0.0 | jbaudoux | Glue between moves release and dynamic routing [stock_available_to_promise_release_exclude_location](stock_available_to_promise_release_exclude_location/) | 16.0.1.0.0 | | Exclude locations from available stock diff --git a/shopfloor_mobile/README.rst b/shopfloor_mobile/README.rst index 988e1881b27..420cb24069e 100644 --- a/shopfloor_mobile/README.rst +++ b/shopfloor_mobile/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ================ Shopfloor mobile ================ @@ -7,13 +11,13 @@ Shopfloor mobile !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:e2e8c5dda324092d44eb40ecd4e2bfd44696c59cdc3af9ded861ef7401f7d971 + !! source digest: sha256:f07e5aafb87420fa19f88d904f3718cfd8877b4730a036b454a4feb9d70900a8 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fwms-lightgray.png?logo=github diff --git a/shopfloor_mobile/__manifest__.py b/shopfloor_mobile/__manifest__.py index cce3b38b0a1..b7aaa54e73a 100644 --- a/shopfloor_mobile/__manifest__.py +++ b/shopfloor_mobile/__manifest__.py @@ -5,7 +5,7 @@ { "name": "Shopfloor mobile", "summary": "Mobile frontend for WMS Shopfloor app", - "version": "16.0.1.4.0", + "version": "16.0.1.4.1", "depends": ["shopfloor", "shopfloor_mobile_base"], "author": "Camptocamp, BCIM, Akretion, Odoo Community Association (OCA)", "maintainers": ["simahawk"], diff --git a/shopfloor_mobile/static/description/index.html b/shopfloor_mobile/static/description/index.html index f437eee30bc..3b1e1642ee8 100644 --- a/shopfloor_mobile/static/description/index.html +++ b/shopfloor_mobile/static/description/index.html @@ -3,7 +3,7 @@ -Shopfloor mobile +README.rst -
-

Shopfloor mobile

+
+ + +Odoo Community Association + +
+

Shopfloor mobile

-

Beta License: AGPL-3 OCA/wms Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/wms Translate me on Weblate Try me on Runboat

Frontend for Shopfloor app.

The work is organized in scenario. A scenario represents a process in the warehouse (eg: receive, deliver). @@ -377,7 +382,7 @@

Shopfloor mobile

Each scenario is linked to a specific menu item which can be configured in the backend. Each scenario drives you through the work to do.

-

Tech details

+

Tech details

  • This frontend is built on top of VueJS and VuetifyJS and relies on shopfloor module that exposes REST API in Odoo @@ -404,18 +409,18 @@

    Tech details

-

Pre-requisites

+

Pre-requisites

  • Your Odoo instance is accessible via mobile device
  • You have an API Key configured
-

Start the app

+

Start the app

  • Go to “Inventory -> Configuration -> Shopfloor -> Shopfloor App”
  • In the login screen fill in your API key
  • @@ -423,7 +428,7 @@

    Start the app

-

Select a profile

+

Select a profile

Several profiles can be configured in the backend, you must choose one before starting.

    @@ -433,17 +438,17 @@

    Select a profile

    This will load all available menu items for the selected profile.

-

Change language

+

Change language

  • Go to “Settings -> Language”
  • Select a language
-

Customization

+

Customization

Please refer to shopfloor_mobile_custom_example.

-

Known issues / Roadmap

+

Known issues / Roadmap

  • Split module by scenario

  • @@ -483,14 +488,14 @@

    Known issues / Roadmap

-

Changelog

+

Changelog

-

13.0.1.0.0

+

13.0.1.0.0

First official version.

-

Bug Tracker

+

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 @@ -498,11 +503,11 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Camptocamp
  • BCIM
  • @@ -510,7 +515,7 @@

    Authors

-

Contributors

+

Contributors

-

Design

+

Design

-

Other credits

+

Other credits

Financial support

  • Cosanum
  • @@ -537,7 +542,7 @@

    Other credits

+
diff --git a/shopfloor_mobile/static/wms/src/components/detail/detail_product.js b/shopfloor_mobile/static/wms/src/components/detail/detail_product.js index fc3d1db0ca1..84736c8db47 100644 --- a/shopfloor_mobile/static/wms/src/components/detail/detail_product.js +++ b/shopfloor_mobile/static/wms/src/components/detail/detail_product.js @@ -136,9 +136,10 @@ Vue.component("detail-product", { - + Lots -Shopfloor mobile +README.rst -
-

Shopfloor mobile

+
+ + +Odoo Community Association + +
+

Shopfloor mobile

-

Beta License: LGPL-3 OCA/wms Translate me on Weblate Try me on Runboat

+

Beta License: LGPL-3 OCA/wms Translate me on Weblate Try me on Runboat

Base frontend for Shopfloor applications.

The work is organized in scenario. A scenario represents a process in the workplace (eg: for WMS -> receive, deliver). @@ -376,7 +382,7 @@

Shopfloor mobile

Each scenario is linked to a specific menu item which can be configured in the backend. Each scenario drives you through the work to do.

-

Tech details

+

Tech details

  • This frontend is built on top of VueJS and VuetifyJS and relies on shopfloor module that exposes REST API in Odoo @@ -403,18 +409,18 @@

    Tech details

-

Pre-requisites

+

Pre-requisites

  • Your Odoo instance is accessible via mobile device
  • You have an API Key configured
-

Start the app

+

Start the app

  • Go to “Inventory -> Configuration -> Shopfloor -> Shopfloor App”
  • In the login screen fill in your API key
  • @@ -422,7 +428,7 @@

    Start the app

-

Select a profile

+

Select a profile

Several profiles can be configured in the backend, you must choose one before starting.

    @@ -432,18 +438,18 @@

    Select a profile

    This will load all available menu items for the selected profile.

-

Change language

+

Change language

  • Go to “Settings -> Language”
  • Select a language
-

Customization

+

Customization

Please refer to shopfloor_mobile_custom_example.

-

Working environment

+

Working environment

You can control which running env is considerd by Odoo config or env vars.

For Odoo config: running_env or shopfloor_running_env.

For env var: RUNNING_ENV or SHOPFLOOR_RUNNING_ENV.

@@ -451,7 +457,7 @@

Working environment

Additionally, as specific key for Shopfloor is supported.

You don’t need `server_environment` module to use this feature.

-

Known issues / Roadmap

+

Known issues / Roadmap

  • Split module by scenario

  • @@ -497,14 +503,14 @@

    Known issues / Roadmap

-

Changelog

+

Changelog

-

13.0.1.0.0

+

13.0.1.0.0

First official version.

-

Bug Tracker

+

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 @@ -512,11 +518,11 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Camptocamp
  • BCIM
  • @@ -524,7 +530,7 @@

    Authors

-

Contributors

+

Contributors

-

Design

+

Design

-

Other credits

+

Other credits

Financial support

  • Cosanum
  • @@ -552,9 +558,11 @@

    Other credits

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

-Odoo Community Association + +Odoo Community Association +

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.

@@ -564,5 +572,6 @@

Maintainers

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
diff --git a/shopfloor_mobile_base/static/wms/src/components/datepicker/date_picker.js b/shopfloor_mobile_base/static/wms/src/components/datepicker/date_picker.js index 5cc16c00ec9..d968e5e3f46 100644 --- a/shopfloor_mobile_base/static/wms/src/components/datepicker/date_picker.js +++ b/shopfloor_mobile_base/static/wms/src/components/datepicker/date_picker.js @@ -3,48 +3,183 @@ * License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). */ -import event_hub from "../../services/event_hub.js"; +function maskString(input, mask, maskChar = "#") { + if (!input) return ""; + // 1. Sanitize: Remove existing separators so we only have raw data + // This prevents "2020-3" from becoming "2020--3" on double-processing + const cleanInput = input.replace(/[^a-zA-Z0-9]/g, ""); + + const characters = cleanInput.split(""); + let result = ""; + + for (const char of mask) { + if (characters.length === 0) break; + + if (char === maskChar) { + result += characters.shift(); + } else { + result += char; + } + } + + return result; +} export var DatePicker = Vue.component("date-picker-input", { - props: { - // Method passed from the parent to update the picker's date - // from outside as required. - handler_to_update_date: Function, - }, data: function () { return { - date: "", + date: "", // Iso format (YYYY-MM-DD) + dateInput: "", // Formatted display (DD/MM/YYYY or similar according to locale) + showInvalidDateInputMessage: false, + + // Control menu state manually to prevent closing during month/year navigation. + menu: false, }; }, - watch: { - date: function () { - this.$emit("date_picker_selected", this.date); + computed: { + userLocale: function () { + const lang = this.$root.user?.lang || "en-US"; + return lang.replace("_", "-").toLowerCase(); + }, + dateFormatter: function () { + return new Intl.DateTimeFormat(this.userLocale); + }, + dateFormat: function () { + const sample = new Date(2025, 10, 10); + const parts = this.dateFormatter.formatToParts(sample); + return parts + .map((p) => { + if (p.type === "day") return "dd"; + if (p.type === "month") return "MM"; + if (p.type === "year") return "yyyy"; + return p.value; + }) + .join(""); + }, + dateMask: function () { + const sample = new Date(2025, 10, 10); + return this.dateFormatter.format(sample).replace(/[0-9]/g, "#"); }, }, - mounted() { - event_hub.$on("datepicker:newdate", (data) => { - this.date = this.handler_to_update_date(data); - }); + methods: { + onDateChange(newDate) { + this.menu = false; + this.dateInput = ""; + this.showInvalidDateInputMessage = false; + this.$emit("dateChange", newDate); + }, + /** + * Forces a synchronization between the Vue component state and the native DOM input. + * * This bypasses Vue's reactivity optimization which may skip a DOM update if the + * internal data value remains unchanged after an invalid user input (e.g., typing + * past a character limit). By briefly clearing and then restoring the value + * during the next DOM update cycle, we ensure the rendered element + * accurately reflects the component's state. + * + * @private + * @returns {void} + */ + _force_dateInput_refresh() { + const backup = this.dateInput; + this.dateInput += "a"; + this.$nextTick(() => { + this.dateInput = backup; + }); + }, + onInput(newInput) { + this.showInvalidDateInputMessage = false; + + if (newInput === null) { + this.dateInput = ""; + return; + } + + const lastChar = newInput.slice(-1); + if ( + (!/[0-9]/.test(lastChar) && newInput.length > this.dateInput.length) || + newInput.length > this.dateMask.length + ) { + this._force_dateInput_refresh(); + return; + } + + const maskedValue = maskString(newInput.replace(/\D/g, ""), this.dateMask); + this.dateInput = maskedValue; + }, + validateAndSync() { + if (!this.dateInput) return; + + const sep = this.dateMask.match(/[^#]/)[0]; + + const dateParts = this.dateInput.split(sep); + const fmtParts = this.dateFormat.split(sep); + + if (dateParts.length !== fmtParts.length) { + this.showInvalidDateInputMessage = true; + return; + } + + let year, month, day; + for (let i = 0; i < dateParts.length; i++) { + switch (fmtParts[i]) { + case "dd": + day = dateParts[i]; + break; + case "MM": + month = dateParts[i]; + break; + default: + year = dateParts[i]; + } + } + + // ↓ suppose 2000s in case of < 4 digits year + year = year.padStart(4, "20"); + + const isoDate = `${year}-${month}-${day}`; + const dateTimestamp = Date.parse(isoDate); + const isDateValid = + !isNaN(dateTimestamp) && + // Ensure parts did not roll over (e.g. Feb 29 -> March 1) + new Date(dateTimestamp).toISOString().slice(0, 10) === isoDate; + + if (isDateValid) { + this.date = isoDate; + this.menu = false; + this.dateInput = ""; + this.$emit("dateChange", this.date); + } else { + this.showInvalidDateInputMessage = true; + } + }, }, template: ` - - - - + + + + `, }); diff --git a/shopfloor_reception/README.rst b/shopfloor_reception/README.rst index d6f1ddb0f96..f425cbb9070 100644 --- a/shopfloor_reception/README.rst +++ b/shopfloor_reception/README.rst @@ -11,7 +11,7 @@ Shopfloor Reception !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:f8d87b75abf2f07115fe503fe887e2266295e7bc6e9e86e9818ff85ed88bff85 + !! source digest: sha256:f4ad59cbe1f5457c033cdeb6f33a81adc81c40e2ba2e87cf1b12c9ffbc9cf3e3 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/shopfloor_reception/__manifest__.py b/shopfloor_reception/__manifest__.py index 3cd79d28e16..9949f22c53d 100644 --- a/shopfloor_reception/__manifest__.py +++ b/shopfloor_reception/__manifest__.py @@ -1,7 +1,7 @@ { "name": "Shopfloor Reception", "summary": "Reception scenario for shopfloor", - "version": "16.0.1.6.6", + "version": "16.0.1.6.7", "development_status": "Beta", "category": "Inventory", "website": "https://github.com/OCA/wms", diff --git a/shopfloor_reception/services/reception.py b/shopfloor_reception/services/reception.py index c52f778b948..d44c368cdfd 100644 --- a/shopfloor_reception/services/reception.py +++ b/shopfloor_reception/services/reception.py @@ -298,6 +298,8 @@ def _scan_line__find_or_create_line(self, picking, move, qty_done=1): """ unassigned_lines = self.env["stock.move.line"] for line in move.move_line_ids: + if line.shopfloor_unloaded: + continue if line.shopfloor_user_id.id == self.env.uid: return self._scan_line__recover(picking, line, qty_done) elif not line.shopfloor_user_id: @@ -344,7 +346,7 @@ def _select_line__filter_lines_by_packaging__return(self, lines, packaging): return_line = fields.first( lines.filtered( lambda l: not l.package_id.product_packaging_id - and not l.result_package_id + and not l.shopfloor_unloaded and l.shopfloor_user_id.id in (False, self.env.uid) ) ) @@ -361,7 +363,7 @@ def _select_line__filter_lines_by_packaging(self, lines, packaging): return fields.first( lines.filtered( lambda l: l.package_id.product_packaging_id == packaging - and not l.result_package_id + and not l.shopfloor_unloaded and l.shopfloor_user_id.id in [False, self.env.uid] ) ) @@ -555,9 +557,12 @@ def _scan_line__by_lot(self, picking, lot): """ lines = picking.move_line_ids.filtered( lambda l: ( - lot == l.lot_id - or (lot.name == l.lot_name and lot.product_id == l.product_id) - and not l.result_package_id + ( + lot == l.lot_id + or (lot.name == l.lot_name and lot.product_id == l.product_id) + ) + and not l.shopfloor_unloaded + and l.shopfloor_user_id.id in (False, self.env.uid) ) ) if not lines: @@ -579,7 +584,9 @@ def _scan_line__by_lot(self, picking, lot): def _scan_line__fallback(self, picking, barcode): # We might have lines with no lot, but with a lot_name. lines = picking.move_line_ids.filtered( - lambda l: l.lot_name == barcode and not l.result_package_id + lambda l: l.lot_name == barcode + and not l.shopfloor_unloaded + and l.shopfloor_user_id.id in (False, self.env.uid) ) if not lines: return self._response_for_select_move( @@ -1391,7 +1398,11 @@ def process_without_pack(self, picking_id, selected_line_id, quantity): return self._response_for_set_destination(picking, selected_line) def _post_line(self, selected_line): + """ + Called when the product is unloaded at destination. + """ selected_line.reserved_uom_qty = selected_line.qty_done + selected_line.shopfloor_unloaded = True if ( selected_line.picking_id.is_shopfloor_created and self.work.menu.allow_return diff --git a/shopfloor_reception/static/description/index.html b/shopfloor_reception/static/description/index.html index 6a9251b2515..5ff155b6605 100644 --- a/shopfloor_reception/static/description/index.html +++ b/shopfloor_reception/static/description/index.html @@ -372,7 +372,7 @@

Shopfloor Reception

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:f8d87b75abf2f07115fe503fe887e2266295e7bc6e9e86e9818ff85ed88bff85 +!! source digest: sha256:f4ad59cbe1f5457c033cdeb6f33a81adc81c40e2ba2e87cf1b12c9ffbc9cf3e3 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/wms Translate me on Weblate Try me on Runboat

Shopfloor implementation of the reception scenario. diff --git a/shopfloor_reception/tests/test_select_move.py b/shopfloor_reception/tests/test_select_move.py index a184443e0a4..ad0ded79d9e 100644 --- a/shopfloor_reception/tests/test_select_move.py +++ b/shopfloor_reception/tests/test_select_move.py @@ -45,6 +45,93 @@ def test_scan_product(self): }, ) + def test_scan_product_partial(self): + # Scan a line + # Set a partial quantity done + # Try to scan the product again + # The selected line should be the other one + picking = self._create_picking() + lot = self._create_lot() + self.assertFalse(picking.printed) + selected_move_line = picking.move_line_ids.filtered( + lambda l: l.product_id == self.product_a + ) + + # Activate INPUT location + selected_move_line.location_dest_id.sudo().active = True + + selected_move_line.lot_id = lot + response = self.service.dispatch( + "scan_line", + params={"picking_id": picking.id, "barcode": lot.name}, + ) + data = self.data.picking(picking) + + self.assertTrue(selected_move_line.picking_id.printed) + self.assert_response( + response, + next_state="set_quantity", + data={ + "picking": data, + "selected_move_line": self.data.move_lines(selected_move_line), + "confirmation_required": None, + }, + ) + + selected_move_line.shopfloor_user_id = self.env.uid + response = self.service.dispatch( + "set_quantity", + params={ + "picking_id": picking.id, + "selected_line_id": selected_move_line.id, + "quantity": 5.0, + }, + ) + + response = self.service.dispatch( + "process_without_pack", + params={ + "picking_id": picking.id, + "selected_line_id": selected_move_line.id, + "quantity": 5.0, + }, + ) + data = self.data.picking(picking) + self.assert_response( + response, + next_state="set_destination", + data={ + "picking": data, + "selected_move_line": self.data.move_lines(selected_move_line), + }, + ) + + response = self.service.dispatch( + "set_destination", + params={ + "picking_id": picking.id, + "selected_line_id": selected_move_line.id, + "location_name": "INPUT", + }, + ) + self.assert_response( + response, + next_state="select_move", + data=self._data_for_select_move(picking), + ) + lines = picking.move_line_ids.filtered(lambda l: l.product_id == self.product_a) + self.assertEqual(2, len(lines)) + previous_line = selected_move_line + + response = self.service.dispatch( + "scan_line", + params={"picking_id": picking.id, "barcode": lot.name}, + ) + + self.assertNotEqual( + previous_line.id, response["data"]["set_lot"]["selected_move_line"][0]["id"] + ) + def test_scan_packaging(self): picking = self._create_picking() self._add_package(picking) @@ -93,6 +180,36 @@ def test_scan_lot(self): }, ) + def test_scan_lot_concurrent(self): + """ + If 2 operators work on the same lot, the second operator + should not steal the move line of the first. + """ + picking = self._create_picking() + lot = self._create_lot() + + service_u1 = self.service + res_u1 = service_u1.dispatch( + "scan_line", + params={ + "picking_id": picking.id, + "barcode": lot.name, + }, + ) + # User 2 starts working on the same move + service_u2 = self._get_service_for_user(self.shopfloor_manager) + res_u2 = service_u2.dispatch( + "scan_line", + params={ + "picking_id": picking.id, + "barcode": lot.name, + }, + ) + self.assertNotEqual( + res_u1["data"]["set_lot"]["selected_move_line"][0]["id"], + res_u2["data"]["set_lot"]["selected_move_line"][0]["id"], + ) + def test_scan_not_tracked_product(self): self.product_a.tracking = "none" picking = self._create_picking() diff --git a/shopfloor_reception/tests/test_set_destination.py b/shopfloor_reception/tests/test_set_destination.py index d79a44681f3..e026ecc4a3b 100644 --- a/shopfloor_reception/tests/test_set_destination.py +++ b/shopfloor_reception/tests/test_set_destination.py @@ -225,7 +225,7 @@ def test_auto_posting_full_two_lines(self): # One move remaining in the picking, for product b, still to be processed self.assertEqual(picking.move_ids.product_id, self.product_b) - def test_auto_posting_concurent_work(self): + def test_auto_posting_concurrent_work(self): """Check 2 users working on the same move. With the auto post line option On. diff --git a/stock_available_to_promise_release/README.rst b/stock_available_to_promise_release/README.rst index 48b48bf8f91..0041f828642 100644 --- a/stock_available_to_promise_release/README.rst +++ b/stock_available_to_promise_release/README.rst @@ -11,7 +11,7 @@ Stock Available to Promise Release !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:a2c7d3a98fbb4bc85708fd746ece6b40695d865ff74d05fccbb4622ac019fe93 + !! source digest: sha256:543c66e12c249c9e0f83d9e4a5449c6915eea33005a226e38e78f54dedcda8da !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/stock_available_to_promise_release/__manifest__.py b/stock_available_to_promise_release/__manifest__.py index a6022e9dbf8..7dc337724e0 100644 --- a/stock_available_to_promise_release/__manifest__.py +++ b/stock_available_to_promise_release/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Stock Available to Promise Release", - "version": "16.0.3.8.2", + "version": "16.0.3.8.3", "summary": "Release Operations based on available to promise", "author": "Camptocamp, BCIM, Odoo Community Association (OCA)", "website": "https://github.com/OCA/wms", diff --git a/stock_available_to_promise_release/static/description/index.html b/stock_available_to_promise_release/static/description/index.html index 3619102b694..56ad583b27e 100644 --- a/stock_available_to_promise_release/static/description/index.html +++ b/stock_available_to_promise_release/static/description/index.html @@ -372,7 +372,7 @@

Stock Available to Promise Release

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:a2c7d3a98fbb4bc85708fd746ece6b40695d865ff74d05fccbb4622ac019fe93 +!! source digest: sha256:543c66e12c249c9e0f83d9e4a5449c6915eea33005a226e38e78f54dedcda8da !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: LGPL-3 OCA/wms Translate me on Weblate Try me on Runboat

Currently the reservation is performed by adding reserved quantities on quants, diff --git a/stock_available_to_promise_release/views/stock_picking_type_views.xml b/stock_available_to_promise_release/views/stock_picking_type_views.xml index 7ddb53aa7d7..50cded2a034 100644 --- a/stock_available_to_promise_release/views/stock_picking_type_views.xml +++ b/stock_available_to_promise_release/views/stock_picking_type_views.xml @@ -13,13 +13,11 @@ position="before" >

-
+ -
- + Need Release