odoo-addon-stock-barcodes 16.0.1.1.0.10__py3-none-any.whl → 16.0.2.0.0.1__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.
- odoo/addons/stock_barcodes/README.rst +217 -12
- odoo/addons/stock_barcodes/__init__.py +1 -0
- odoo/addons/stock_barcodes/__manifest__.py +8 -3
- odoo/addons/stock_barcodes/data/stock_barcodes_action.xml +8 -0
- odoo/addons/stock_barcodes/data/stock_barcodes_option.xml +99 -1
- odoo/addons/stock_barcodes/i18n/es.po +343 -104
- odoo/addons/stock_barcodes/i18n/fr.po +357 -104
- odoo/addons/stock_barcodes/i18n/it.po +386 -112
- odoo/addons/stock_barcodes/i18n/stock_barcodes.pot +268 -79
- odoo/addons/stock_barcodes/i18n/tr.po +411 -124
- odoo/addons/stock_barcodes/models/__init__.py +1 -0
- odoo/addons/stock_barcodes/models/barcode_events_mixin.py +11 -0
- odoo/addons/stock_barcodes/models/stock_barcodes_action.py +137 -3
- odoo/addons/stock_barcodes/models/stock_picking.py +17 -2
- odoo/addons/stock_barcodes/models/stock_picking_type.py +35 -0
- odoo/addons/stock_barcodes/models/stock_quant.py +51 -1
- odoo/addons/stock_barcodes/readme/CONTRIBUTORS.rst +4 -0
- odoo/addons/stock_barcodes/readme/DESCRIPTION.rst +4 -0
- odoo/addons/stock_barcodes/readme/HISTORY.rst +12 -0
- odoo/addons/stock_barcodes/readme/USAGE.rst +191 -10
- odoo/addons/stock_barcodes/reports/__init__.py +1 -0
- odoo/addons/stock_barcodes/reports/barcode_actions_report.py +16 -0
- odoo/addons/stock_barcodes/reports/barcode_actions_report.xml +25 -0
- odoo/addons/stock_barcodes/reports/reports.xml +28 -0
- odoo/addons/stock_barcodes/static/description/index.html +189 -33
- odoo/addons/stock_barcodes/static/src/docs/barcodes_actions.pdf +0 -0
- odoo/addons/stock_barcodes/static/src/docs/barcodes_demo.pdf +0 -0
- odoo/addons/stock_barcodes/static/src/img/add_product.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/apply_inventory.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/apply_inventory_reason.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/barcode_interface_picking.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/confirm_all_quantity_items_picking.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/confirm_items_picking.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/create_barcode_action.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/form_add_product.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/form_add_product_quantity.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/form_add_product_reset.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/inventory_barcode_action.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/list_action_items.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/list_items.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/list_items_picking.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/list_items_picking_edit.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/list_items_picking_quantity.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/list_items_picking_scanned.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/list_picking.png +0 -0
- odoo/addons/stock_barcodes/static/src/img/print_barcodes.png +0 -0
- odoo/addons/stock_barcodes/static/src/scss/barcode.scss +135 -0
- odoo/addons/stock_barcodes/static/src/scss/stock.scss +285 -0
- odoo/addons/stock_barcodes/static/src/utils/barcodes_models_utils.esm.js +1 -0
- odoo/addons/stock_barcodes/static/src/views/actions/stock_barcode_main_menu.esm.js +88 -0
- odoo/addons/stock_barcodes/static/src/views/actions/stock_barcode_main_menu.scss +74 -0
- odoo/addons/stock_barcodes/static/src/views/actions/stock_barcode_main_menu.xml +45 -0
- odoo/addons/stock_barcodes/static/src/views/form/form_controller.esm.js +71 -0
- odoo/addons/stock_barcodes/static/src/views/form/form_view.esm.js +14 -0
- odoo/addons/stock_barcodes/static/src/views/kanban/kanban_record.esm.js +27 -0
- odoo/addons/stock_barcodes/static/src/views/{kanban_renderer.esm.js → kanban/kanban_renderer.esm.js} +78 -27
- odoo/addons/stock_barcodes/static/src/views/kanban/kanban_view.esm.js +8 -0
- odoo/addons/stock_barcodes/static/src/views/kanban/stock_barcodes_kanban.xml +28 -0
- odoo/addons/stock_barcodes/static/src/views/views.esm.js +35 -7
- odoo/addons/stock_barcodes/static/src/widgets/boolean_toggle.esm.js +46 -0
- odoo/addons/stock_barcodes/tests/common.py +25 -0
- odoo/addons/stock_barcodes/tests/test_stock_barcodes.py +65 -1
- odoo/addons/stock_barcodes/tests/test_stock_barcodes_picking.py +97 -13
- odoo/addons/stock_barcodes/views/stock_barcodes_action_view.xml +83 -9
- odoo/addons/stock_barcodes/views/stock_barcodes_menu.xml +1 -1
- odoo/addons/stock_barcodes/views/stock_picking_views.xml +29 -10
- odoo/addons/stock_barcodes/wizard/__init__.py +1 -0
- odoo/addons/stock_barcodes/wizard/stock_barcodes_candidate_picking.py +148 -0
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read.py +148 -55
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_inventory.py +30 -2
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_inventory_views.xml +101 -92
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_picking.py +77 -141
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml +197 -219
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_todo.py +45 -0
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_views.xml +87 -170
- odoo_addon_stock_barcodes-16.0.2.0.0.1.dist-info/METADATA +435 -0
- odoo_addon_stock_barcodes-16.0.2.0.0.1.dist-info/RECORD +103 -0
- odoo/addons/stock_barcodes/static/src/css/stock.scss +0 -136
- odoo/addons/stock_barcodes/static/src/views/form_view.esm.js +0 -17
- odoo_addon_stock_barcodes-16.0.1.1.0.10.dist-info/METADATA +0 -230
- odoo_addon_stock_barcodes-16.0.1.1.0.10.dist-info/RECORD +0 -68
- {odoo_addon_stock_barcodes-16.0.1.1.0.10.dist-info → odoo_addon_stock_barcodes-16.0.2.0.0.1.dist-info}/WHEEL +0 -0
- {odoo_addon_stock_barcodes-16.0.1.1.0.10.dist-info → odoo_addon_stock_barcodes-16.0.2.0.0.1.dist-info}/top_level.txt +0 -0
odoo/addons/stock_barcodes/static/src/views/{kanban_renderer.esm.js → kanban/kanban_renderer.esm.js}
RENAMED
@@ -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 "
|
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, "
|
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
|
-
|
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,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.
|
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
|
113
|
-
(this.model.root.resId
|
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,
|
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
|
-
|
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")),
|
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")),
|
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")),
|
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(
|
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(
|
207
|
-
|
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(
|
219
|
-
|
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(
|
227
|
-
|
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(
|
232
|
-
|
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
|
+
)
|