odoo-addon-stock-barcodes 16.0.1.1.0.8__py3-none-any.whl → 16.0.2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. odoo/addons/stock_barcodes/README.rst +217 -12
  2. odoo/addons/stock_barcodes/__init__.py +1 -0
  3. odoo/addons/stock_barcodes/__manifest__.py +8 -3
  4. odoo/addons/stock_barcodes/data/stock_barcodes_action.xml +8 -0
  5. odoo/addons/stock_barcodes/data/stock_barcodes_option.xml +99 -1
  6. odoo/addons/stock_barcodes/i18n/stock_barcodes.pot +268 -79
  7. odoo/addons/stock_barcodes/i18n/tr.po +326 -276
  8. odoo/addons/stock_barcodes/models/__init__.py +1 -0
  9. odoo/addons/stock_barcodes/models/barcode_events_mixin.py +11 -0
  10. odoo/addons/stock_barcodes/models/stock_barcodes_action.py +137 -3
  11. odoo/addons/stock_barcodes/models/stock_picking.py +17 -2
  12. odoo/addons/stock_barcodes/models/stock_picking_type.py +35 -0
  13. odoo/addons/stock_barcodes/models/stock_quant.py +51 -1
  14. odoo/addons/stock_barcodes/readme/CONTRIBUTORS.rst +4 -0
  15. odoo/addons/stock_barcodes/readme/DESCRIPTION.rst +4 -0
  16. odoo/addons/stock_barcodes/readme/HISTORY.rst +12 -0
  17. odoo/addons/stock_barcodes/readme/USAGE.rst +191 -10
  18. odoo/addons/stock_barcodes/reports/__init__.py +1 -0
  19. odoo/addons/stock_barcodes/reports/barcode_actions_report.py +16 -0
  20. odoo/addons/stock_barcodes/reports/barcode_actions_report.xml +25 -0
  21. odoo/addons/stock_barcodes/reports/reports.xml +28 -0
  22. odoo/addons/stock_barcodes/static/description/index.html +189 -33
  23. odoo/addons/stock_barcodes/static/src/docs/barcodes_actions.pdf +0 -0
  24. odoo/addons/stock_barcodes/static/src/docs/barcodes_demo.pdf +0 -0
  25. odoo/addons/stock_barcodes/static/src/img/add_product.png +0 -0
  26. odoo/addons/stock_barcodes/static/src/img/apply_inventory.png +0 -0
  27. odoo/addons/stock_barcodes/static/src/img/apply_inventory_reason.png +0 -0
  28. odoo/addons/stock_barcodes/static/src/img/barcode_interface_picking.png +0 -0
  29. odoo/addons/stock_barcodes/static/src/img/confirm_all_quantity_items_picking.png +0 -0
  30. odoo/addons/stock_barcodes/static/src/img/confirm_items_picking.png +0 -0
  31. odoo/addons/stock_barcodes/static/src/img/create_barcode_action.png +0 -0
  32. odoo/addons/stock_barcodes/static/src/img/form_add_product.png +0 -0
  33. odoo/addons/stock_barcodes/static/src/img/form_add_product_quantity.png +0 -0
  34. odoo/addons/stock_barcodes/static/src/img/form_add_product_reset.png +0 -0
  35. odoo/addons/stock_barcodes/static/src/img/inventory_barcode_action.png +0 -0
  36. odoo/addons/stock_barcodes/static/src/img/list_action_items.png +0 -0
  37. odoo/addons/stock_barcodes/static/src/img/list_items.png +0 -0
  38. odoo/addons/stock_barcodes/static/src/img/list_items_picking.png +0 -0
  39. odoo/addons/stock_barcodes/static/src/img/list_items_picking_edit.png +0 -0
  40. odoo/addons/stock_barcodes/static/src/img/list_items_picking_quantity.png +0 -0
  41. odoo/addons/stock_barcodes/static/src/img/list_items_picking_scanned.png +0 -0
  42. odoo/addons/stock_barcodes/static/src/img/list_picking.png +0 -0
  43. odoo/addons/stock_barcodes/static/src/img/print_barcodes.png +0 -0
  44. odoo/addons/stock_barcodes/static/src/scss/barcode.scss +135 -0
  45. odoo/addons/stock_barcodes/static/src/scss/stock.scss +285 -0
  46. odoo/addons/stock_barcodes/static/src/utils/barcodes_models_utils.esm.js +1 -0
  47. odoo/addons/stock_barcodes/static/src/views/actions/stock_barcode_main_menu.esm.js +88 -0
  48. odoo/addons/stock_barcodes/static/src/views/actions/stock_barcode_main_menu.scss +74 -0
  49. odoo/addons/stock_barcodes/static/src/views/actions/stock_barcode_main_menu.xml +45 -0
  50. odoo/addons/stock_barcodes/static/src/views/form/form_controller.esm.js +71 -0
  51. odoo/addons/stock_barcodes/static/src/views/form/form_view.esm.js +14 -0
  52. odoo/addons/stock_barcodes/static/src/views/kanban/kanban_record.esm.js +27 -0
  53. odoo/addons/stock_barcodes/static/src/views/{kanban_renderer.esm.js → kanban/kanban_renderer.esm.js} +78 -27
  54. odoo/addons/stock_barcodes/static/src/views/kanban/kanban_view.esm.js +8 -0
  55. odoo/addons/stock_barcodes/static/src/views/kanban/stock_barcodes_kanban.xml +28 -0
  56. odoo/addons/stock_barcodes/static/src/views/views.esm.js +35 -7
  57. odoo/addons/stock_barcodes/static/src/widgets/boolean_toggle.esm.js +46 -0
  58. odoo/addons/stock_barcodes/tests/common.py +25 -0
  59. odoo/addons/stock_barcodes/tests/test_stock_barcodes.py +65 -1
  60. odoo/addons/stock_barcodes/tests/test_stock_barcodes_picking.py +97 -13
  61. odoo/addons/stock_barcodes/views/stock_barcodes_action_view.xml +83 -9
  62. odoo/addons/stock_barcodes/views/stock_barcodes_menu.xml +1 -1
  63. odoo/addons/stock_barcodes/views/stock_picking_views.xml +29 -10
  64. odoo/addons/stock_barcodes/wizard/__init__.py +1 -0
  65. odoo/addons/stock_barcodes/wizard/stock_barcodes_candidate_picking.py +148 -0
  66. odoo/addons/stock_barcodes/wizard/stock_barcodes_read.py +148 -55
  67. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_inventory.py +30 -2
  68. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_inventory_views.xml +101 -92
  69. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_picking.py +77 -141
  70. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml +197 -219
  71. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_todo.py +45 -0
  72. odoo/addons/stock_barcodes/wizard/stock_barcodes_read_views.xml +87 -170
  73. odoo_addon_stock_barcodes-16.0.2.0.0.dist-info/METADATA +435 -0
  74. odoo_addon_stock_barcodes-16.0.2.0.0.dist-info/RECORD +103 -0
  75. odoo/addons/stock_barcodes/static/src/css/stock.scss +0 -136
  76. odoo/addons/stock_barcodes/static/src/views/form_view.esm.js +0 -17
  77. odoo_addon_stock_barcodes-16.0.1.1.0.8.dist-info/METADATA +0 -230
  78. odoo_addon_stock_barcodes-16.0.1.1.0.8.dist-info/RECORD +0 -68
  79. {odoo_addon_stock_barcodes-16.0.1.1.0.8.dist-info → odoo_addon_stock_barcodes-16.0.2.0.0.dist-info}/WHEEL +0 -0
  80. {odoo_addon_stock_barcodes-16.0.1.1.0.8.dist-info → odoo_addon_stock_barcodes-16.0.2.0.0.dist-info}/top_level.txt +0 -0
@@ -2,14 +2,14 @@
2
2
  /* Copyright 2022 Tecnativa - Alexandre D. Díaz
3
3
  * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
4
4
 
5
+ import {onPatched, useEffect, useRef} from "@odoo/owl";
6
+ import {useBus, useService} from "@web/core/utils/hooks";
5
7
  import {KanbanRenderer} from "@web/views/kanban/kanban_renderer";
6
- import {isAllowedBarcodeModel} from "../utils/barcodes_models_utils.esm";
8
+ import {isAllowedBarcodeModel} from "../../utils/barcodes_models_utils.esm";
7
9
  import {patch} from "@web/core/utils/patch";
8
- import {useBus} from "@web/core/utils/hooks";
9
10
  import {useHotkey} from "@web/core/hotkeys/hotkey_hook";
10
- import {useRef} from "@odoo/owl";
11
11
 
12
- patch(KanbanRenderer.prototype, "add hotkey", {
12
+ patch(KanbanRenderer.prototype, "stock_barcodes.KanbanRenderer", {
13
13
  setup() {
14
14
  const rootRef = useRef("root");
15
15
  useHotkey(
@@ -40,6 +40,34 @@ patch(KanbanRenderer.prototype, "add hotkey", {
40
40
  );
41
41
 
42
42
  this._super(...arguments);
43
+ this.ormService = useService("orm");
44
+ this.action = useService("action");
45
+ const busService = useService("bus_service");
46
+ this.enableCurrentOperation = 0;
47
+ const handleNotification = ({detail: notifications}) => {
48
+ if (notifications && notifications.length > 0) {
49
+ notifications.forEach((notif) => {
50
+ const {payload, type} = notif;
51
+ if (type === "enable_operations" && payload) {
52
+ this.enableCurrentOperation = payload.id;
53
+ }
54
+ });
55
+ }
56
+ };
57
+ useEffect(() => {
58
+ busService.addChannel("stock_barcodes_kanban_update");
59
+ busService.addEventListener("notification", handleNotification);
60
+ return () => {
61
+ busService.deleteChannel("stock_barcodes_kanban_update");
62
+ busService.removeEventListener("notification", handleNotification);
63
+ };
64
+ });
65
+
66
+ onPatched(() => {
67
+ $("div.oe_kanban_operations-" + this.enableCurrentOperation).removeClass(
68
+ "d-none"
69
+ );
70
+ });
43
71
 
44
72
  if (isAllowedBarcodeModel(this.props.list.resModel)) {
45
73
  if (this.env.searchModel) {
@@ -63,6 +91,36 @@ patch(KanbanRenderer.prototype, "add hotkey", {
63
91
  });
64
92
  }
65
93
  }
94
+
95
+ this.showMessageScanProductPackage =
96
+ this.props.list.resModel === "stock.picking";
97
+ },
98
+
99
+ getNextCard(direction, iCard, cards, iGroup, isGrouped) {
100
+ let nextCard = null;
101
+ switch (direction) {
102
+ case "down":
103
+ nextCard = iCard < cards[iGroup].length - 1 && cards[iGroup][iCard + 1];
104
+ break;
105
+ case "up":
106
+ nextCard = iCard > 0 && cards[iGroup][iCard - 1];
107
+ break;
108
+ case "right":
109
+ if (isGrouped) {
110
+ nextCard = iGroup < cards.length - 1 && cards[iGroup + 1][0];
111
+ } else {
112
+ nextCard = iCard < cards[0].length - 1 && cards[0][iCard + 1];
113
+ }
114
+ break;
115
+ case "left":
116
+ if (isGrouped) {
117
+ nextCard = iGroup > 0 && cards[iGroup - 1][0];
118
+ } else {
119
+ nextCard = iCard > 0 && cards[0][iCard - 1];
120
+ }
121
+ break;
122
+ }
123
+ return nextCard;
66
124
  },
67
125
 
68
126
  // eslint-disable-next-line complexity
@@ -77,6 +135,8 @@ patch(KanbanRenderer.prototype, "add hotkey", {
77
135
  *
78
136
  * @param {Node} area
79
137
  * @param {String} direction
138
+ *
139
+ * @returns {String/Boolean}
80
140
  */
81
141
  focusNextCard(area, direction) {
82
142
  const {isGrouped} = this.props.list;
@@ -117,33 +177,24 @@ patch(KanbanRenderer.prototype, "add hotkey", {
117
177
  iGroup = 0;
118
178
  }
119
179
  // Find next card to focus
120
- let nextCard = null;
121
- switch (direction) {
122
- case "down":
123
- nextCard = iCard < cards[iGroup].length - 1 && cards[iGroup][iCard + 1];
124
- break;
125
- case "up":
126
- nextCard = iCard > 0 && cards[iGroup][iCard - 1];
127
- break;
128
- case "right":
129
- if (isGrouped) {
130
- nextCard = iGroup < cards.length - 1 && cards[iGroup + 1][0];
131
- } else {
132
- nextCard = iCard < cards[0].length - 1 && cards[0][iCard + 1];
133
- }
134
- break;
135
- case "left":
136
- if (isGrouped) {
137
- nextCard = iGroup > 0 && cards[iGroup - 1][0];
138
- } else {
139
- nextCard = iCard > 0 && cards[0][iCard - 1];
140
- }
141
- break;
142
- }
180
+ const nextCard = this.getNextCard(direction, iCard, cards, iGroup, isGrouped);
143
181
 
144
182
  if (nextCard && nextCard instanceof HTMLElement) {
145
183
  nextCard.focus();
146
184
  return true;
147
185
  }
148
186
  },
187
+
188
+ async openBarcodeScanner() {
189
+ if (this.showMessageScanProductPackage) {
190
+ const action = await this.ormService.call(
191
+ "stock.picking",
192
+ "action_barcode_scan",
193
+ [false, false]
194
+ );
195
+ this.action.doAction(action);
196
+ }
197
+ },
149
198
  });
199
+
200
+ KanbanRenderer.template = "stock_barcodes.BarcodeKanbanRenderer";
@@ -0,0 +1,8 @@
1
+ /** @odoo-module */
2
+
3
+ import {kanbanView} from "@web/views/kanban/kanban_view";
4
+ import {registry} from "@web/core/registry";
5
+
6
+ registry.category("views").add("stock_barcodes_kanban", {
7
+ ...kanbanView,
8
+ });
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <templates xml:space="preserve">
3
+
4
+ <t
5
+ t-name="stock_barcodes.BarcodeKanbanRenderer"
6
+ t-inherit="web.KanbanRenderer"
7
+ owl="1"
8
+ >
9
+ <xpath expr="//div[hasclass('o_kanban_renderer')]" position="before">
10
+ <div
11
+ t-if="showMessageScanProductPackage"
12
+ class="o_kanban_stock_barcodes text-white w-100 mt-1 mb-1 d-flex align-items-center justify-content-center bg-dark"
13
+ >
14
+ <span t-if="packageEnabled">Scan a <b>transfer</b>, a <b
15
+ >product</b> or a <b>lot</b> to filter your records</span>
16
+ <span t-else="">Scan a <b>transfer</b> or a <b
17
+ >product</b> to filter your records</span>
18
+ <button
19
+ class="o_stock_mobile_barcode btn"
20
+ t-on-click="openBarcodeScanner"
21
+ >
22
+ <i class="fa fa-barcode" />
23
+ </button>
24
+ </div>
25
+ </xpath>
26
+ </t>
27
+
28
+ </templates>
@@ -7,6 +7,7 @@ import {getVisibleElements, isVisible} from "@web/core/utils/ui";
7
7
  import {FormController} from "@web/views/form/form_controller";
8
8
  import {KanbanController} from "@web/views/kanban/kanban_controller";
9
9
  import {ListController} from "@web/views/list/list_controller";
10
+ import {_t} from "@web/core/l10n/translation";
10
11
  import {isAllowedBarcodeModel} from "../utils/barcodes_models_utils.esm";
11
12
  import {patch} from "@web/core/utils/patch";
12
13
  import {useEffect} from "@odoo/owl";
@@ -94,7 +95,7 @@ function setupView() {
94
95
  } else if (ev.keyCode === 123 || ev.keyCode === 115) {
95
96
  // F12 or F4
96
97
  await actionService.doAction(
97
- "stock_barcodes.action_stock_barcodes_action",
98
+ "stock_barcodes.action_stock_barcodes_action_client",
98
99
  {
99
100
  name: "Barcode wizard menu",
100
101
  res_model: "wiz.stock.barcodes.read.picking",
@@ -109,8 +110,8 @@ function setupView() {
109
110
  notifications.forEach((notif) => {
110
111
  const {payload, type} = notif;
111
112
  if (
112
- (this.model.root.resModel == payload.res_model) &
113
- (this.model.root.resId == payload.res_id)
113
+ (this.model.root.resModel === payload.res_model) &
114
+ (this.model.root.resId === payload.res_id)
114
115
  ) {
115
116
  if (type === "stock_barcodes_sound") {
116
117
  if (payload.sound === "ko") {
@@ -118,8 +119,7 @@ function setupView() {
118
119
  } else {
119
120
  this.$sound_ok[0].play();
120
121
  }
121
- }
122
- if (type === "stock_barcodes_focus") {
122
+ } else if (type === "stock_barcodes_focus") {
123
123
  requestIdleCallback(() => {
124
124
  const input = document.querySelector(
125
125
  `[name=${payload.field_name}] input`
@@ -128,8 +128,7 @@ function setupView() {
128
128
  input.focus();
129
129
  }
130
130
  });
131
- }
132
- if (type === "stock_barcodes_notify") {
131
+ } else if (type === "stock_barcodes_notify") {
133
132
  notification.add(notif.payload.message, {
134
133
  title: notif.payload.title,
135
134
  type: notif.payload.type,
@@ -137,6 +136,35 @@ function setupView() {
137
136
  });
138
137
  }
139
138
  }
139
+
140
+ if (type === "stock_barcodes_edit_manual") {
141
+ if (payload.manual_entry) {
142
+ this.env.bus.trigger("enableFormEditBarcode");
143
+ } else if (!payload.manual_entry) {
144
+ this.env.bus.trigger("disableFormEditBarcode");
145
+ }
146
+ } else if (type === "actions_barcode") {
147
+ if (payload.valid_picking) {
148
+ notification.add(_t("The transfer has been validated"), {
149
+ type: "success",
150
+ });
151
+ } else if (payload.apply_inventory) {
152
+ actionService.doAction(
153
+ "stock_barcodes.action_stock_barcodes_action_client"
154
+ );
155
+ notification.add(
156
+ _t("The inventory adjustment has been validated"),
157
+ {
158
+ type: "success",
159
+ }
160
+ );
161
+ }
162
+ } else if (type === "actions_barcode_notification") {
163
+ notification.add(_t(payload.message), {
164
+ type: payload.message_type,
165
+ sticky: payload.sticky,
166
+ });
167
+ }
140
168
  });
141
169
  }
142
170
  };
@@ -3,9 +3,25 @@
3
3
  * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
4
4
 
5
5
  import {BooleanToggleField} from "@web/views/fields/boolean_toggle/boolean_toggle_field";
6
+ import {onMounted} from "@odoo/owl";
6
7
  import {registry} from "@web/core/registry";
8
+ import {useBus} from "@web/core/utils/hooks";
7
9
 
8
10
  class BarcodeBooleanToggleField extends BooleanToggleField {
11
+ setup() {
12
+ super.setup();
13
+ onMounted(() => {
14
+ this.enableFormEdit(this.props.value, true);
15
+ });
16
+
17
+ useBus(this.env.bus, "enableFormEditBarcode", () =>
18
+ this.enableFormEdit(true, true)
19
+ );
20
+ useBus(this.env.bus, "disableFormEditBarcode", () =>
21
+ this.enableFormEdit(false, true)
22
+ );
23
+ }
24
+
9
25
  /*
10
26
  This is needed because, whenever we click the checkbox to enter data
11
27
  manually, the checkbox will be focused causing that when we scan the
@@ -20,6 +36,36 @@ class BarcodeBooleanToggleField extends BooleanToggleField {
20
36
  requestIdleCallback(() => {
21
37
  document.activeElement.blur();
22
38
  });
39
+ this.enableFormEdit(newValue);
40
+ }
41
+
42
+ enableFormEdit(newValue, editAction = false) {
43
+ // Enable edit form
44
+ if (this.props.name === "manual_entry" || editAction) {
45
+ const $form_edit = $("div.oe_stock_barcordes_content > div.scan_fields");
46
+ const $div_inventory_quant_ids = $("div[name='inventory_quant_ids']").find(
47
+ "div.o_kanban_renderer"
48
+ );
49
+ if ($form_edit.length > 0) {
50
+ if (newValue) {
51
+ $form_edit.removeClass("d-none");
52
+ $div_inventory_quant_ids.addClass("inventory_quant_ids_with_form");
53
+ $div_inventory_quant_ids.removeClass(
54
+ "inventory_quant_ids_without_form"
55
+ );
56
+ } else {
57
+ $form_edit.addClass("d-none");
58
+ $div_inventory_quant_ids.removeClass(
59
+ "inventory_quant_ids_with_form"
60
+ );
61
+ $div_inventory_quant_ids.addClass(
62
+ "inventory_quant_ids_without_form"
63
+ );
64
+ }
65
+ } else {
66
+ $div_inventory_quant_ids.addClass("inventory_quant_ids_without_form");
67
+ }
68
+ }
23
69
  }
24
70
  }
25
71
 
@@ -21,9 +21,12 @@ class TestCommonStockBarcodes(TransactionCase):
21
21
  cls.Product = cls.env["product.product"]
22
22
  cls.ProductPackaging = cls.env["product.packaging"]
23
23
  cls.WizScanReadPicking = cls.env["wiz.stock.barcodes.read.picking"]
24
+ cls.WizScanReadInventory = cls.env["wiz.stock.barcodes.read.inventory"]
25
+ cls.WizCandidatePicking = cls.env["wiz.candidate.picking"]
24
26
  cls.StockProductionLot = cls.env["stock.lot"]
25
27
  cls.StockPicking = cls.env["stock.picking"]
26
28
  cls.StockQuant = cls.env["stock.quant"]
29
+ cls.StockBarcodeAction = cls.env["stock.barcodes.action"]
27
30
 
28
31
  cls.company = cls.env.company
29
32
 
@@ -103,6 +106,28 @@ class TestCommonStockBarcodes(TransactionCase):
103
106
  cls.wiz_scan = cls.WizScanReadPicking.create(
104
107
  {"option_group_id": cls.option_group.id, "step": 1}
105
108
  )
109
+ cls.wiz_scan_read_inventory = cls.WizScanReadInventory.create(
110
+ {"option_group_id": cls.option_group.id, "step": 1}
111
+ )
112
+
113
+ cls.wiz_scan_candidate_picking = cls.WizCandidatePicking.create(
114
+ {"wiz_barcode_id": cls.wiz_scan.id}
115
+ )
116
+
117
+ # Barcode actions
118
+ cls.barcode_action_valid = cls.StockBarcodeAction.create(
119
+ {
120
+ "name": "Barcode action valid",
121
+ "action_window_id": cls.env.ref("stock.stock_picking_type_action").id,
122
+ "context": "{'search_default_barcode_options': 1}",
123
+ }
124
+ )
125
+
126
+ cls.barcode_action_invalid = cls.StockBarcodeAction.create(
127
+ {
128
+ "name": "Barcode action valid",
129
+ }
130
+ )
106
131
 
107
132
  @classmethod
108
133
  def _create_barcode_option_group(cls):
@@ -1,8 +1,13 @@
1
1
  # Copyright 2108-2019 Sergio Teruel <sergio.teruel@tecnativa.com>
2
2
  # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3
3
 
4
+ import re
5
+
6
+ from odoo.exceptions import ValidationError
4
7
  from odoo.tests.common import tagged
5
8
 
9
+ from odoo.addons.stock_barcodes.models.stock_barcodes_action import FIELDS_NAME, REGEX
10
+
6
11
  from .common import TestCommonStockBarcodes
7
12
 
8
13
 
@@ -42,7 +47,7 @@ class TestStockBarcodes(TestCommonStockBarcodes):
42
47
  self.wiz_scan.manual_entry = True
43
48
  self.wiz_scan.action_clean_values()
44
49
  self.action_barcode_scanned(self.wiz_scan, "5420008510489")
45
- self.assertEqual(self.wiz_scan.packaging_qty, 0.0)
50
+ self.assertEqual(self.wiz_scan.packaging_qty, 1.0)
46
51
  self.wiz_scan.packaging_qty = 3.0
47
52
  self.wiz_scan.onchange_packaging_qty()
48
53
  self.assertEqual(self.wiz_scan.product_qty, 15.0)
@@ -75,3 +80,62 @@ class TestStockBarcodes(TestCommonStockBarcodes):
75
80
  self.action_barcode_scanned(self.wiz_scan, "8411822222568")
76
81
  self.wiz_scan.action_clean_lot()
77
82
  self.assertFalse(self.wiz_scan.lot_id)
83
+
84
+ def test_barcode_action(self):
85
+ self.assertTrue(self.barcode_action_valid.action_window_id)
86
+ self.assertEqual(bool(self.barcode_action_invalid.action_window_id), False)
87
+
88
+ def test_action_back(self):
89
+ result = self.wiz_scan.action_back()
90
+ self.assertIn("name", result)
91
+ self.assertIn("type", result)
92
+ self.assertIn("res_model", result)
93
+ self.assertEqual(result["type"], "ir.actions.act_window")
94
+
95
+ def test_barcode_context_action(self):
96
+ context = self.barcode_action_valid.context
97
+ self.assertTrue(bool(re.match(REGEX.get("context", ""), context)))
98
+ self.assertGreater(len(context), 0)
99
+ context = context.strip("{}").split(",")
100
+ field_values = context[0].split(":")
101
+ self.assertGreater(len(field_values), 1)
102
+ field_name = field_values[0].split("search_default_")
103
+ self.assertGreater(len(field_name), 1)
104
+ field_value_format = field_values[1].replace("'", "").strip()
105
+ self.assertTrue(field_value_format.isdigit())
106
+ self.assertEqual(field_values[0].strip("'"), "search_default_barcode_options")
107
+ self.assertTrue(len(field_values[0].split("search_default_")), 2)
108
+ self.assertEqual(self.barcode_action_invalid._count_elements(), 0)
109
+ self.barcode_action_invalid.context = False
110
+ with self.assertRaises(TypeError):
111
+ self.barcode_action_invalid._compute_count_elements()
112
+ self.barcode_action_invalid.context = "{}"
113
+ self.assertFalse("search_default_" in self.barcode_action_invalid.context)
114
+
115
+ self.assertEqual(self.barcode_action_invalid._count_elements(), 0)
116
+ self.barcode_action_valid.context = "{'search_default_code': 1}"
117
+ self.assertEqual(self.barcode_action_valid._count_elements(), 6)
118
+ field_value_name = (
119
+ self.barcode_action_valid.context.strip("{}").split(",")[0].split(":")
120
+ )
121
+ field_name = field_value_name[0].split("search_default_")[1].strip("'")
122
+ self.assertTrue("search_default_" in self.barcode_action_valid.context)
123
+ self.assertFalse(
124
+ hasattr(
125
+ self.barcode_action_valid.action_window_id.res_model,
126
+ FIELDS_NAME.get(field_name, field_name),
127
+ )
128
+ )
129
+ field_values = field_value_name[1].strip()
130
+ self.assertTrue(field_values.isdigit())
131
+
132
+ with self.assertRaises(IndexError):
133
+ self.barcode_action_invalid.context = "{'search_default_'}"
134
+ self.assertEqual(self.barcode_action_invalid._count_elements(), 0)
135
+ with self.assertRaises(ValidationError):
136
+ self.StockBarcodeAction.create(
137
+ {
138
+ "name": "Barcode action invalid with space",
139
+ "context": "{'search_default_code': 'incoming'} ",
140
+ }
141
+ )
@@ -1,5 +1,6 @@
1
1
  # Copyright 2108-2019 Sergio Teruel <sergio.teruel@tecnativa.com>
2
2
  # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
3
+ from odoo.exceptions import MissingError, UserError
3
4
  from odoo.tests.common import tagged
4
5
 
5
6
  from .common import TestCommonStockBarcodes
@@ -140,7 +141,10 @@ class TestStockBarcodesPicking(TestCommonStockBarcodes):
140
141
  )
141
142
 
142
143
  def test_picking_wizard_scan_product(self):
143
- wiz_scan_picking = self.wiz_scan_picking.with_context(force_create_move=True)
144
+ # self.wiz_scan_picking.manual_entry = True
145
+ wiz_scan_picking = self.wiz_scan_picking.with_context(
146
+ force_create_move=True, no_increase_qty_done=True
147
+ )
144
148
  self.action_barcode_scanned(wiz_scan_picking, "8480000723208")
145
149
  sml = self.picking_in_01.move_line_ids.filtered(
146
150
  lambda x: x.product_id == self.product_wo_tracking
@@ -166,9 +170,9 @@ class TestStockBarcodesPicking(TestCommonStockBarcodes):
166
170
  self.assertEqual(sml.qty_done, 1.0)
167
171
  self.action_barcode_scanned(wiz_scan_picking, "8433281006850")
168
172
  stock_move = sml.move_id
169
- self.assertEqual(sum(stock_move.move_line_ids.mapped("qty_done")), 2.0)
173
+ self.assertEqual(sum(stock_move.move_line_ids.mapped("qty_done")), 1.0)
170
174
  self.action_barcode_scanned(wiz_scan_picking, "8411822222568")
171
- self.assertEqual(sum(stock_move.move_line_ids.mapped("qty_done")), 3.0)
175
+ self.assertEqual(sum(stock_move.move_line_ids.mapped("qty_done")), 1.0)
172
176
  self.assertEqual(
173
177
  self.wiz_scan_picking.message,
174
178
  "8411822222568 (Scan Product, Packaging, Lot / Serial)",
@@ -176,10 +180,12 @@ class TestStockBarcodesPicking(TestCommonStockBarcodes):
176
180
  # Scan a package
177
181
  self.action_barcode_scanned(wiz_scan_picking, "5420008510489")
178
182
  # Package of 5 product units. Already three unit exists
179
- self.assertEqual(sum(stock_move.move_line_ids.mapped("qty_done")), 8.0)
183
+ self.assertEqual(sum(stock_move.move_line_ids.mapped("qty_done")), 5.0)
180
184
 
181
185
  def test_picking_wizard_scan_product_manual_entry(self):
182
- wiz_scan_picking = self.wiz_scan_picking.with_context(force_create_move=True)
186
+ wiz_scan_picking = self.wiz_scan_picking.with_context(
187
+ force_create_move=True, no_increase_qty_done=True
188
+ )
183
189
  wiz_scan_picking.manual_entry = True
184
190
  self.action_barcode_scanned(wiz_scan_picking, "8480000723208")
185
191
  sml = self.picking_in_01.move_line_ids.filtered(
@@ -203,8 +209,10 @@ class TestStockBarcodesPicking(TestCommonStockBarcodes):
203
209
  self.wiz_scan_picking.lot_id = self.lot_1
204
210
  self.wiz_scan_picking.product_qty = 2
205
211
 
206
- self.wiz_scan_picking.with_context(force_create_move=True).action_confirm()
207
- self.assertEqual(len(self.wiz_scan_picking.candidate_picking_ids), 2)
212
+ self.wiz_scan_picking.with_context(
213
+ force_create_move=True, no_increase_qty_done=True
214
+ ).action_confirm()
215
+ self.assertEqual(len(self.wiz_scan_picking.candidate_picking_ids[0:2]), 2)
208
216
  # Lock first picking
209
217
  candidate = self.wiz_scan_picking.candidate_picking_ids.filtered(
210
218
  lambda c: c.picking_id == self.picking_out_01
@@ -215,21 +223,27 @@ class TestStockBarcodesPicking(TestCommonStockBarcodes):
215
223
  candidate_wiz.with_context(force_create_move=True).action_lock_picking()
216
224
  self.assertEqual(self.picking_out_01.move_ids.quantity_done, 2)
217
225
  self.wiz_scan_picking.product_qty = 2
218
- self.wiz_scan_picking.with_context(force_create_move=True).action_confirm()
219
- self.assertEqual(self.picking_out_01.move_ids.quantity_done, 4)
226
+ self.wiz_scan_picking.with_context(
227
+ force_create_move=True, no_increase_qty_done=True
228
+ ).action_confirm()
229
+ self.assertEqual(self.picking_out_01.move_ids.quantity_done, 2)
220
230
 
221
231
  # Picking out 3 is in confirmed state, so until confirmed moves has
222
232
  # not been activated candidate pickings is 2
223
233
  picking_out_3.action_confirm()
224
234
  candidate_wiz.action_unlock_picking()
225
235
  self.wiz_scan_picking.product_qty = 2
226
- self.wiz_scan_picking.with_context(force_create_move=True).action_confirm()
227
- self.assertEqual(len(self.wiz_scan_picking.candidate_picking_ids), 2)
236
+ self.wiz_scan_picking.with_context(
237
+ force_create_move=True, no_increase_qty_done=True
238
+ ).action_confirm()
239
+ self.assertEqual(len(self.wiz_scan_picking.candidate_picking_ids[0:2]), 2)
228
240
  candidate_wiz.action_unlock_picking()
229
241
  self.wiz_scan_picking.product_qty = 2
230
242
  self.wiz_scan_picking.option_group_id.confirmed_moves = True
231
- self.wiz_scan_picking.with_context(force_create_move=True).action_confirm()
232
- self.assertEqual(len(self.wiz_scan_picking.candidate_picking_ids), 3)
243
+ self.wiz_scan_picking.with_context(
244
+ force_create_move=True, no_increase_qty_done=True
245
+ ).action_confirm()
246
+ self.assertEqual(len(self.wiz_scan_picking.candidate_picking_ids[0:3]), 3)
233
247
 
234
248
  def test_picking_wizard_scan_product_auto_lot(self):
235
249
  # Prepare more data
@@ -453,3 +467,73 @@ class TestStockBarcodesPicking(TestCommonStockBarcodes):
453
467
  ],
454
468
  }
455
469
  )
470
+
471
+ def test_stock_picking_validate(self):
472
+ self.picking_in_01.state = False
473
+ with self.assertRaises(UserError):
474
+ self.picking_in_01.with_context(
475
+ stock_barcodes_validate_picking=True
476
+ ).button_validate()
477
+
478
+ def test_barcode_read_picking(self):
479
+ self.picking_in_01.state = "done"
480
+ self.wiz_scan_picking._compute_enable_add_product()
481
+ self.assertFalse(self.wiz_scan_picking.enable_add_product)
482
+
483
+ self.wiz_scan_picking.show_detailed_operations = False
484
+ self.wiz_scan_picking.action_show_detailed_operations()
485
+ self.assertTrue(self.wiz_scan_picking.action_show_detailed_operations)
486
+
487
+ self.wiz_scan_picking.action_show_detailed_operations()
488
+ self.assertFalse(self.wiz_scan_picking.show_detailed_operations)
489
+
490
+ def test_barcode_read_inventory(self):
491
+ context = {
492
+ "params": {
493
+ "model": "wiz.stock.barcodes.read.inventory",
494
+ "id": self.quant_lot_1.id,
495
+ }
496
+ }
497
+ with self.assertRaises(MissingError):
498
+ self.quant_lot_1.with_context(
499
+ **context
500
+ ).action_barcode_inventory_quant_unlink()
501
+ context = {
502
+ "params": {
503
+ "model": self.wiz_scan_read_inventory._name,
504
+ "id": self.wiz_scan_read_inventory.id,
505
+ }
506
+ }
507
+ self.quant_lot_1.with_context(**context).action_barcode_inventory_quant_unlink()
508
+ self.assertIsNone(
509
+ self.quant_lot_1.with_context(
510
+ **context
511
+ ).action_barcode_inventory_quant_unlink()
512
+ )
513
+ self.assertIsNone(self.quant_lot_1.enable_current_operations())
514
+ self.assertIsNone(self.quant_lot_1.action_barcode_inventory_quant_edit())
515
+ with self.assertRaises(ValueError):
516
+ self.quant_lot_1.write({"inventory_quantity": "test"})
517
+ self.quant_lot_1.operation_quantities_rest()
518
+ self.quant_lot_1.operation_quantities()
519
+ self.assertEqual(
520
+ type(self.picking_in_01.picking_type_id.get_action_picking_tree_ready()),
521
+ dict,
522
+ )
523
+ self.assertEqual(
524
+ type(
525
+ self.picking_in_01.picking_type_id.with_context(
526
+ **{"operations_mode": True}
527
+ ).get_action_picking_tree_ready()
528
+ ),
529
+ dict,
530
+ )
531
+ self.assertIsNone(self.wiz_scan_candidate_picking._compute_picking_quantity())
532
+ self.assertIsNone(self.wiz_scan_candidate_picking._compute_is_pending())
533
+ self.assertEqual(
534
+ self.wiz_scan_candidate_picking._get_picking_to_validate()._name,
535
+ self.picking_in_01._name,
536
+ )
537
+ self.assertEqual(
538
+ type(self.wiz_scan_candidate_picking.action_validate_picking()), tuple
539
+ )