odoo-addon-stock-barcodes 15.0.3.1.6__py3-none-any.whl → 16.0.1.0.0.19__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 +6 -6
- odoo/addons/stock_barcodes/__manifest__.py +8 -7
- odoo/addons/stock_barcodes/data/stock_barcodes_option.xml +1 -2
- odoo/addons/stock_barcodes/hooks.py +15 -13
- odoo/addons/stock_barcodes/i18n/es.po +305 -189
- odoo/addons/stock_barcodes/i18n/it.po +282 -180
- odoo/addons/stock_barcodes/i18n/stock_barcodes.pot +190 -160
- odoo/addons/stock_barcodes/models/__init__.py +1 -1
- odoo/addons/stock_barcodes/models/stock_barcodes_action.py +2 -1
- odoo/addons/stock_barcodes/models/stock_barcodes_option.py +2 -4
- odoo/addons/stock_barcodes/models/stock_move.py +37 -0
- odoo/addons/stock_barcodes/models/stock_move_line.py +8 -14
- odoo/addons/stock_barcodes/models/stock_picking.py +9 -36
- odoo/addons/stock_barcodes/models/stock_picking_type.py +5 -6
- odoo/addons/stock_barcodes/models/stock_quant.py +3 -4
- odoo/addons/stock_barcodes/security/ir.model.access.csv +0 -2
- odoo/addons/stock_barcodes/static/description/index.html +9 -11
- odoo/addons/stock_barcodes/static/src/css/stock.scss +41 -5
- odoo/addons/stock_barcodes/static/src/utils/barcode_handler_field.esm.js +36 -0
- odoo/addons/stock_barcodes/static/src/utils/barcodes_models_utils.esm.js +25 -0
- odoo/addons/stock_barcodes/static/src/views/form_view.esm.js +17 -0
- odoo/addons/stock_barcodes/static/src/views/kanban_renderer.esm.js +149 -0
- odoo/addons/stock_barcodes/static/src/views/view_compiler.esm.js +16 -0
- odoo/addons/stock_barcodes/static/src/views/views.esm.js +193 -0
- odoo/addons/stock_barcodes/static/src/widgets/boolean_toggle.esm.js +26 -0
- odoo/addons/stock_barcodes/static/src/widgets/numeric_step.esm.js +40 -0
- odoo/addons/stock_barcodes/static/src/widgets/numeric_step.xml +17 -0
- odoo/addons/stock_barcodes/static/src/widgets/view_button.esm.js +8 -0
- odoo/addons/stock_barcodes/static/src/widgets/view_button.xml +14 -0
- odoo/addons/stock_barcodes/tests/common.py +167 -0
- odoo/addons/stock_barcodes/tests/test_stock_barcodes.py +4 -172
- odoo/addons/stock_barcodes/tests/test_stock_barcodes_new_lot.py +2 -2
- odoo/addons/stock_barcodes/tests/test_stock_barcodes_picking.py +8 -8
- odoo/addons/stock_barcodes/views/stock_barcodes_option_view.xml +1 -1
- odoo/addons/stock_barcodes/views/stock_picking_views.xml +4 -3
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read.py +45 -91
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_inventory.py +1 -4
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_inventory_views.xml +14 -15
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_picking.py +256 -92
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_picking_views.xml +59 -37
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_todo.py +33 -107
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_todo_view.xml +70 -28
- odoo/addons/stock_barcodes/wizard/stock_barcodes_read_views.xml +172 -189
- odoo/addons/stock_barcodes/wizard/stock_production_lot.py +2 -2
- odoo/addons/stock_barcodes/wizard/stock_production_lot_views.xml +6 -8
- {odoo_addon_stock_barcodes-15.0.3.1.6.dist-info → odoo_addon_stock_barcodes-16.0.1.0.0.19.dist-info}/METADATA +11 -11
- odoo_addon_stock_barcodes-16.0.1.0.0.19.dist-info/RECORD +66 -0
- {odoo_addon_stock_barcodes-15.0.3.1.6.dist-info → odoo_addon_stock_barcodes-16.0.1.0.0.19.dist-info}/WHEEL +1 -1
- odoo/addons/stock_barcodes/migrations/15.0.1.0.0/pre-migration.py +0 -14
- odoo/addons/stock_barcodes/models/stock_barcodes_read_log.py +0 -51
- odoo/addons/stock_barcodes/static/src/css/stock.css +0 -5
- odoo/addons/stock_barcodes/static/src/js/barcodes_models_mixin.js +0 -34
- odoo/addons/stock_barcodes/static/src/js/basic_controller.js +0 -412
- odoo/addons/stock_barcodes/static/src/js/basic_fields.js +0 -59
- odoo/addons/stock_barcodes/static/src/js/form_view.js +0 -24
- odoo/addons/stock_barcodes/static/src/js/kanban_renderer.js +0 -47
- odoo/addons/stock_barcodes/static/src/js/numeric_step.js +0 -43
- odoo_addon_stock_barcodes-15.0.3.1.6.dist-info/RECORD +0 -62
- {odoo_addon_stock_barcodes-15.0.3.1.6.dist-info → odoo_addon_stock_barcodes-16.0.1.0.0.19.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
/** @odoo-module */
|
2
|
+
|
3
|
+
import {ViewCompiler} from "@web/views/view_compiler";
|
4
|
+
import {patch} from "@web/core/utils/patch";
|
5
|
+
|
6
|
+
patch(ViewCompiler.prototype, "Add hotkey props to button tag", {
|
7
|
+
compileButton(el, params) {
|
8
|
+
const hotkey = el.getAttribute("data-hotkey");
|
9
|
+
el.removeAttribute("data-hotkey");
|
10
|
+
const button = this._super(el, params);
|
11
|
+
if (hotkey) {
|
12
|
+
button.setAttribute("hotkey", hotkey);
|
13
|
+
}
|
14
|
+
return button;
|
15
|
+
},
|
16
|
+
});
|
@@ -0,0 +1,193 @@
|
|
1
|
+
/** @odoo-module */
|
2
|
+
/* Copyright 2024 Akretion
|
3
|
+
/* Copyright 2024 Tecnativa
|
4
|
+
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
5
|
+
|
6
|
+
import {getVisibleElements, isVisible} from "@web/core/utils/ui";
|
7
|
+
import {FormController} from "@web/views/form/form_controller";
|
8
|
+
import {KanbanController} from "@web/views/kanban/kanban_controller";
|
9
|
+
import {ListController} from "@web/views/list/list_controller";
|
10
|
+
import {isAllowedBarcodeModel} from "../utils/barcodes_models_utils.esm";
|
11
|
+
import {patch} from "@web/core/utils/patch";
|
12
|
+
import {useEffect} from "@odoo/owl";
|
13
|
+
import {useService} from "@web/core/utils/hooks";
|
14
|
+
|
15
|
+
let barcodeOverlaysVisible = false;
|
16
|
+
|
17
|
+
// This is necessary because the hotkey service does not make its API public for
|
18
|
+
// some reasons
|
19
|
+
export function barcodeRemoveHotkeyOverlays() {
|
20
|
+
for (const overlay of document.querySelectorAll(".o_barcode_web_hotkey_overlay")) {
|
21
|
+
overlay.remove();
|
22
|
+
}
|
23
|
+
barcodeOverlaysVisible = false;
|
24
|
+
}
|
25
|
+
|
26
|
+
// This is necessary because the hotkey service does not make its API public for
|
27
|
+
// some reasons
|
28
|
+
export function barcodeAddHotkeyOverlays(activeElement) {
|
29
|
+
for (const el of getVisibleElements(
|
30
|
+
activeElement,
|
31
|
+
"[data-hotkey]:not(:disabled)"
|
32
|
+
)) {
|
33
|
+
const hotkey = el.dataset.hotkey;
|
34
|
+
const overlay = document.createElement("div");
|
35
|
+
overlay.classList.add(
|
36
|
+
"o_barcode_web_hotkey_overlay",
|
37
|
+
"position-absolute",
|
38
|
+
"top-0",
|
39
|
+
"bottom-0",
|
40
|
+
"start-0",
|
41
|
+
"end-0",
|
42
|
+
"d-flex",
|
43
|
+
"justify-content-center",
|
44
|
+
"align-items-center",
|
45
|
+
"m-0",
|
46
|
+
"bg-black-50",
|
47
|
+
"h6"
|
48
|
+
);
|
49
|
+
const overlayKbd = document.createElement("kbd");
|
50
|
+
overlayKbd.className = "small";
|
51
|
+
overlayKbd.appendChild(document.createTextNode(hotkey.toUpperCase()));
|
52
|
+
overlay.appendChild(overlayKbd);
|
53
|
+
|
54
|
+
let overlayParent = null;
|
55
|
+
if (el.tagName.toUpperCase() === "INPUT") {
|
56
|
+
// Special case for the search input that has an access key
|
57
|
+
// defined. We cannot set the overlay on the input itself,
|
58
|
+
// only on its parent.
|
59
|
+
overlayParent = el.parentElement;
|
60
|
+
} else {
|
61
|
+
overlayParent = el;
|
62
|
+
}
|
63
|
+
|
64
|
+
if (overlayParent.style.position !== "absolute") {
|
65
|
+
overlayParent.style.position = "relative";
|
66
|
+
}
|
67
|
+
overlayParent.appendChild(overlay);
|
68
|
+
}
|
69
|
+
barcodeOverlaysVisible = true;
|
70
|
+
}
|
71
|
+
|
72
|
+
function setupView() {
|
73
|
+
const actionService = useService("action");
|
74
|
+
const uiService = useService("ui");
|
75
|
+
const busService = useService("bus_service");
|
76
|
+
const notification = useService("notification");
|
77
|
+
|
78
|
+
const handleKeys = async (ev) => {
|
79
|
+
if (ev.keyCode === 113) {
|
80
|
+
// F2
|
81
|
+
const {activeElement} = uiService;
|
82
|
+
|
83
|
+
if (barcodeOverlaysVisible) {
|
84
|
+
barcodeRemoveHotkeyOverlays();
|
85
|
+
} else {
|
86
|
+
barcodeAddHotkeyOverlays(activeElement);
|
87
|
+
}
|
88
|
+
} else if (ev.keyCode === 120) {
|
89
|
+
// F9
|
90
|
+
const button = document.querySelector("button[name='action_clean_values']");
|
91
|
+
if (isVisible(button)) {
|
92
|
+
button.click();
|
93
|
+
}
|
94
|
+
} else if (ev.keyCode === 123 || ev.keyCode === 115) {
|
95
|
+
// F12 or F4
|
96
|
+
await actionService.doAction(
|
97
|
+
"stock_barcodes.action_stock_barcodes_action",
|
98
|
+
{
|
99
|
+
name: "Barcode wizard menu",
|
100
|
+
res_model: "wiz.stock.barcodes.read.picking",
|
101
|
+
type: "ir.actions.act_window",
|
102
|
+
}
|
103
|
+
);
|
104
|
+
}
|
105
|
+
};
|
106
|
+
|
107
|
+
const handleNotification = ({detail: notifications}) => {
|
108
|
+
if (notifications && notifications.length > 0) {
|
109
|
+
notifications.forEach((notif) => {
|
110
|
+
const {payload, type} = notif;
|
111
|
+
if (
|
112
|
+
(this.model.root.resModel == payload.res_model) &
|
113
|
+
(this.model.root.resId == payload.res_id)
|
114
|
+
) {
|
115
|
+
if (type === "stock_barcodes_sound") {
|
116
|
+
this.$sound_ok[0].play();
|
117
|
+
}
|
118
|
+
if (type === "stock_barcodes_focus") {
|
119
|
+
requestIdleCallback(() => {
|
120
|
+
const input = document.querySelector(
|
121
|
+
`[name=${payload.field_name}] input`
|
122
|
+
);
|
123
|
+
if (input) {
|
124
|
+
input.focus();
|
125
|
+
}
|
126
|
+
});
|
127
|
+
}
|
128
|
+
if (type === "stock_barcodes_notify") {
|
129
|
+
notification.add(notif.payload.message, {
|
130
|
+
title: notif.payload.title,
|
131
|
+
type: notif.payload.type,
|
132
|
+
sticky: notif.payload.sticky,
|
133
|
+
});
|
134
|
+
}
|
135
|
+
}
|
136
|
+
});
|
137
|
+
}
|
138
|
+
};
|
139
|
+
|
140
|
+
useEffect(() => {
|
141
|
+
document.body.addEventListener("keydown", handleKeys);
|
142
|
+
|
143
|
+
this.$sound_ok = $("<audio>", {
|
144
|
+
src: "/stock_barcodes/static/src/sounds/bell.wav",
|
145
|
+
preload: "auto",
|
146
|
+
});
|
147
|
+
this.$sound_ok.appendTo("body");
|
148
|
+
this.$sound_ko = $("<audio>", {
|
149
|
+
src: "/stock_barcodes/static/src/sounds/error.wav",
|
150
|
+
preload: "auto",
|
151
|
+
});
|
152
|
+
this.$sound_ko.appendTo("body");
|
153
|
+
|
154
|
+
busService.addChannel("stock_barcodes_scan");
|
155
|
+
|
156
|
+
busService.addEventListener("notification", handleNotification);
|
157
|
+
|
158
|
+
return () => {
|
159
|
+
this.$sound_ok.remove();
|
160
|
+
this.$sound_ko.remove();
|
161
|
+
document.body.removeEventListener("keydown", handleKeys);
|
162
|
+
busService.deleteChannel("stock_barcodes_scan");
|
163
|
+
busService.removeEventListener("notification", handleNotification);
|
164
|
+
};
|
165
|
+
});
|
166
|
+
}
|
167
|
+
|
168
|
+
patch(KanbanController.prototype, "add hotkeys to kanban", {
|
169
|
+
setup() {
|
170
|
+
this._super(...arguments);
|
171
|
+
if (isAllowedBarcodeModel(this.props.resModel)) {
|
172
|
+
setupView.call(this);
|
173
|
+
}
|
174
|
+
},
|
175
|
+
});
|
176
|
+
|
177
|
+
patch(FormController.prototype, "add hotkeys to form", {
|
178
|
+
setup() {
|
179
|
+
this._super(...arguments);
|
180
|
+
if (isAllowedBarcodeModel(this.props.resModel)) {
|
181
|
+
setupView.call(this);
|
182
|
+
}
|
183
|
+
},
|
184
|
+
});
|
185
|
+
|
186
|
+
patch(ListController.prototype, "add hotkeys to list", {
|
187
|
+
setup() {
|
188
|
+
this._super(...arguments);
|
189
|
+
if (isAllowedBarcodeModel(this.props.resModel)) {
|
190
|
+
setupView.call(this);
|
191
|
+
}
|
192
|
+
},
|
193
|
+
});
|
@@ -0,0 +1,26 @@
|
|
1
|
+
/** @odoo-module */
|
2
|
+
/* Copyright 2018-2019 Sergio Teruel <sergio.teruel@tecnativa.com>.
|
3
|
+
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
4
|
+
|
5
|
+
import {BooleanToggleField} from "@web/views/fields/boolean_toggle/boolean_toggle_field";
|
6
|
+
import {registry} from "@web/core/registry";
|
7
|
+
|
8
|
+
class BarcodeBooleanToggleField extends BooleanToggleField {
|
9
|
+
/*
|
10
|
+
This is needed because, whenever we click the checkbox to enter data
|
11
|
+
manually, the checkbox will be focused causing that when we scan the
|
12
|
+
barcode afterwards, it will not perform the python on_barcode_scanned
|
13
|
+
function.
|
14
|
+
*/
|
15
|
+
onChange(newValue) {
|
16
|
+
super.onChange(newValue);
|
17
|
+
// We can't blur an element on its onchange event
|
18
|
+
// we need to wait for the event to finish (thus
|
19
|
+
// requestIdleCallback)
|
20
|
+
requestIdleCallback(() => {
|
21
|
+
document.activeElement.blur();
|
22
|
+
});
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
registry.category("fields").add("barcode_boolean_toggle", BarcodeBooleanToggleField);
|
@@ -0,0 +1,40 @@
|
|
1
|
+
/** @odoo-module */
|
2
|
+
/* Copyright 2022 Tecnativa - Alexandre D. Díaz
|
3
|
+
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
4
|
+
|
5
|
+
import {NumericStep} from "@web_widget_numeric_step/numeric_step.esm";
|
6
|
+
import {isAllowedBarcodeModel} from "../utils/barcodes_models_utils.esm";
|
7
|
+
import {patch} from "@web/core/utils/patch";
|
8
|
+
|
9
|
+
patch(NumericStep.prototype, "Adds barcode event handling and focus", {
|
10
|
+
_onFocus() {
|
11
|
+
if (isAllowedBarcodeModel(this.props.record.resModel)) {
|
12
|
+
// Auto select all content when user enters into fields with this
|
13
|
+
// widget.
|
14
|
+
this.inputRef.el.select();
|
15
|
+
}
|
16
|
+
},
|
17
|
+
|
18
|
+
_onKeyDown(ev) {
|
19
|
+
if (isAllowedBarcodeModel(this.props.record.resModel) && ev.keyCode === 13) {
|
20
|
+
const action_confirm = document.querySelector(
|
21
|
+
"button[name='action_confirm']"
|
22
|
+
);
|
23
|
+
|
24
|
+
if (action_confirm) {
|
25
|
+
action_confirm.click();
|
26
|
+
return;
|
27
|
+
}
|
28
|
+
|
29
|
+
const action_confirm_force = document.querySelector(
|
30
|
+
"button[name='action_force_done']"
|
31
|
+
);
|
32
|
+
|
33
|
+
if (action_confirm_force) {
|
34
|
+
action_confirm_force.click();
|
35
|
+
return;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
this._super(...arguments);
|
39
|
+
},
|
40
|
+
});
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?>
|
2
|
+
<!--
|
3
|
+
Copyright 2024 Akretion
|
4
|
+
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
5
|
+
-->
|
6
|
+
<template>
|
7
|
+
<t
|
8
|
+
t-name="barcode_web_widget_numeric_step"
|
9
|
+
t-inherit="web_widget_numeric_step.web_widget_numeric_step"
|
10
|
+
t-inherit-mode="extension"
|
11
|
+
owl="1"
|
12
|
+
>
|
13
|
+
<xpath expr="//input" position="attributes">
|
14
|
+
<attribute name="t-on-focus">_onFocus</attribute>
|
15
|
+
</xpath>
|
16
|
+
</t>
|
17
|
+
</template>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?>
|
2
|
+
<templates xml:space="preserve">
|
3
|
+
|
4
|
+
<t
|
5
|
+
t-name="views.ViewButton"
|
6
|
+
t-inherit="web.views.ViewButton"
|
7
|
+
t-inherit-mode="extension"
|
8
|
+
owl="1"
|
9
|
+
>
|
10
|
+
<xpath expr="//t[@t-tag]" position="attributes">
|
11
|
+
<attribute name="t-att-data-hotkey">props.hotkey</attribute>
|
12
|
+
</xpath>
|
13
|
+
</t>
|
14
|
+
</templates>
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# Copyright 2108-2019 Francois Poizat <francois.poizat@gmail.com>
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
3
|
+
|
4
|
+
from odoo.tests.common import TransactionCase
|
5
|
+
|
6
|
+
|
7
|
+
class TestCommonStockBarcodes(TransactionCase):
|
8
|
+
@classmethod
|
9
|
+
def setUpClass(cls):
|
10
|
+
super().setUpClass()
|
11
|
+
|
12
|
+
# Active group_stock_packaging and group_production_lot for user
|
13
|
+
group_stock_packaging = cls.env.ref("product.group_stock_packaging")
|
14
|
+
group_production_lot = cls.env.ref("stock.group_production_lot")
|
15
|
+
cls.env.user.groups_id = [
|
16
|
+
(4, group_stock_packaging.id),
|
17
|
+
(4, group_production_lot.id),
|
18
|
+
]
|
19
|
+
# models
|
20
|
+
cls.StockLocation = cls.env["stock.location"]
|
21
|
+
cls.Product = cls.env["product.product"]
|
22
|
+
cls.ProductPackaging = cls.env["product.packaging"]
|
23
|
+
cls.WizScanReadPicking = cls.env["wiz.stock.barcodes.read.picking"]
|
24
|
+
cls.StockProductionLot = cls.env["stock.lot"]
|
25
|
+
cls.StockPicking = cls.env["stock.picking"]
|
26
|
+
cls.StockQuant = cls.env["stock.quant"]
|
27
|
+
|
28
|
+
cls.company = cls.env.company
|
29
|
+
|
30
|
+
# Option groups for test
|
31
|
+
cls.option_group = cls._create_barcode_option_group()
|
32
|
+
|
33
|
+
# warehouse and locations
|
34
|
+
cls.warehouse = cls.env.ref("stock.warehouse0")
|
35
|
+
cls.stock_location = cls.env.ref("stock.stock_location_stock")
|
36
|
+
cls.location_1 = cls.StockLocation.create(
|
37
|
+
{
|
38
|
+
"name": "Test location 1",
|
39
|
+
"usage": "internal",
|
40
|
+
"location_id": cls.stock_location.id,
|
41
|
+
"barcode": "8411322222568",
|
42
|
+
}
|
43
|
+
)
|
44
|
+
cls.location_2 = cls.StockLocation.create(
|
45
|
+
{
|
46
|
+
"name": "Test location 2",
|
47
|
+
"usage": "internal",
|
48
|
+
"location_id": cls.stock_location.id,
|
49
|
+
"barcode": "8470001809032",
|
50
|
+
}
|
51
|
+
)
|
52
|
+
|
53
|
+
# products
|
54
|
+
cls.product_wo_tracking = cls.Product.create(
|
55
|
+
{
|
56
|
+
"name": "Product test wo lot tracking",
|
57
|
+
"type": "product",
|
58
|
+
"tracking": "none",
|
59
|
+
"barcode": "8480000723208",
|
60
|
+
"packaging_ids": [
|
61
|
+
(
|
62
|
+
0,
|
63
|
+
0,
|
64
|
+
{
|
65
|
+
"name": "Box 10 Units",
|
66
|
+
"qty": 10.0,
|
67
|
+
"barcode": "5099206074439",
|
68
|
+
},
|
69
|
+
)
|
70
|
+
],
|
71
|
+
}
|
72
|
+
)
|
73
|
+
cls.product_tracking = cls.Product.create(
|
74
|
+
{
|
75
|
+
"name": "Product test with lot tracking",
|
76
|
+
"type": "product",
|
77
|
+
"tracking": "lot",
|
78
|
+
"barcode": "8433281006850",
|
79
|
+
"packaging_ids": [
|
80
|
+
(
|
81
|
+
0,
|
82
|
+
0,
|
83
|
+
{"name": "Box 5 Units", "qty": 5.0, "barcode": "5420008510489"},
|
84
|
+
)
|
85
|
+
],
|
86
|
+
}
|
87
|
+
)
|
88
|
+
cls.lot_1 = cls.StockProductionLot.create(
|
89
|
+
{
|
90
|
+
"name": "8411822222568",
|
91
|
+
"product_id": cls.product_tracking.id,
|
92
|
+
"company_id": cls.company.id,
|
93
|
+
}
|
94
|
+
)
|
95
|
+
cls.quant_lot_1 = cls.StockQuant.create(
|
96
|
+
{
|
97
|
+
"product_id": cls.product_tracking.id,
|
98
|
+
"lot_id": cls.lot_1.id,
|
99
|
+
"location_id": cls.stock_location.id,
|
100
|
+
"quantity": 100.0,
|
101
|
+
}
|
102
|
+
)
|
103
|
+
cls.wiz_scan = cls.WizScanReadPicking.create(
|
104
|
+
{"option_group_id": cls.option_group.id, "step": 1}
|
105
|
+
)
|
106
|
+
|
107
|
+
@classmethod
|
108
|
+
def _create_barcode_option_group(cls):
|
109
|
+
return cls.env["stock.barcodes.option.group"].create(
|
110
|
+
{
|
111
|
+
"name": "option group for tests",
|
112
|
+
"create_lot": True,
|
113
|
+
"option_ids": [
|
114
|
+
(
|
115
|
+
0,
|
116
|
+
0,
|
117
|
+
{
|
118
|
+
"step": 1,
|
119
|
+
"name": "Location",
|
120
|
+
"field_name": "location_id",
|
121
|
+
"to_scan": True,
|
122
|
+
"required": True,
|
123
|
+
},
|
124
|
+
),
|
125
|
+
(
|
126
|
+
0,
|
127
|
+
0,
|
128
|
+
{
|
129
|
+
"step": 2,
|
130
|
+
"name": "Product",
|
131
|
+
"field_name": "product_id",
|
132
|
+
"to_scan": True,
|
133
|
+
"required": True,
|
134
|
+
},
|
135
|
+
),
|
136
|
+
(
|
137
|
+
0,
|
138
|
+
0,
|
139
|
+
{
|
140
|
+
"step": 2,
|
141
|
+
"name": "Packaging",
|
142
|
+
"field_name": "packaging_id",
|
143
|
+
"to_scan": True,
|
144
|
+
"required": False,
|
145
|
+
},
|
146
|
+
),
|
147
|
+
(
|
148
|
+
0,
|
149
|
+
0,
|
150
|
+
{
|
151
|
+
"step": 2,
|
152
|
+
"name": "Lot / Serial",
|
153
|
+
"field_name": "lot_id",
|
154
|
+
"to_scan": True,
|
155
|
+
"required": True,
|
156
|
+
},
|
157
|
+
),
|
158
|
+
],
|
159
|
+
}
|
160
|
+
)
|
161
|
+
|
162
|
+
def action_barcode_scanned(self, wizard, barcode):
|
163
|
+
wizard._barcode_scanned = barcode
|
164
|
+
wizard._on_barcode_scanned()
|
165
|
+
# Method to call all methods outside of onchange environment for pickings read
|
166
|
+
if wizard._name != "wiz.stock.barcodes.new.lot":
|
167
|
+
wizard.dummy_on_barcode_scanned()
|
@@ -1,170 +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
|
-
from odoo.tests.common import
|
4
|
+
from odoo.tests.common import tagged
|
5
5
|
|
6
|
+
from .common import TestCommonStockBarcodes
|
6
7
|
|
7
|
-
@tagged("post_install", "-at_install")
|
8
|
-
class TestStockBarcodes(TransactionCase):
|
9
|
-
@classmethod
|
10
|
-
def setUpClass(cls):
|
11
|
-
super().setUpClass()
|
12
|
-
|
13
|
-
# Active group_stock_packaging and group_production_lot for user
|
14
|
-
group_stock_packaging = cls.env.ref("product.group_stock_packaging")
|
15
|
-
group_production_lot = cls.env.ref("stock.group_production_lot")
|
16
|
-
cls.env.user.groups_id = [
|
17
|
-
(4, group_stock_packaging.id),
|
18
|
-
(4, group_production_lot.id),
|
19
|
-
]
|
20
|
-
# models
|
21
|
-
cls.StockLocation = cls.env["stock.location"]
|
22
|
-
cls.Product = cls.env["product.product"]
|
23
|
-
cls.ProductPackaging = cls.env["product.packaging"]
|
24
|
-
cls.WizScanReadPicking = cls.env["wiz.stock.barcodes.read.picking"]
|
25
|
-
cls.StockProductionLot = cls.env["stock.production.lot"]
|
26
|
-
cls.StockPicking = cls.env["stock.picking"]
|
27
|
-
cls.StockQuant = cls.env["stock.quant"]
|
28
|
-
|
29
|
-
cls.company = cls.env.company
|
30
|
-
|
31
|
-
# Option groups for test
|
32
|
-
cls.option_group = cls._create_barcode_option_group()
|
33
|
-
|
34
|
-
# warehouse and locations
|
35
|
-
cls.warehouse = cls.env.ref("stock.warehouse0")
|
36
|
-
cls.stock_location = cls.env.ref("stock.stock_location_stock")
|
37
|
-
cls.location_1 = cls.StockLocation.create(
|
38
|
-
{
|
39
|
-
"name": "Test location 1",
|
40
|
-
"usage": "internal",
|
41
|
-
"location_id": cls.stock_location.id,
|
42
|
-
"barcode": "8411322222568",
|
43
|
-
}
|
44
|
-
)
|
45
|
-
cls.location_2 = cls.StockLocation.create(
|
46
|
-
{
|
47
|
-
"name": "Test location 2",
|
48
|
-
"usage": "internal",
|
49
|
-
"location_id": cls.stock_location.id,
|
50
|
-
"barcode": "8470001809032",
|
51
|
-
}
|
52
|
-
)
|
53
|
-
|
54
|
-
# products
|
55
|
-
cls.product_wo_tracking = cls.Product.create(
|
56
|
-
{
|
57
|
-
"name": "Product test wo lot tracking",
|
58
|
-
"type": "product",
|
59
|
-
"tracking": "none",
|
60
|
-
"barcode": "8480000723208",
|
61
|
-
"packaging_ids": [
|
62
|
-
(
|
63
|
-
0,
|
64
|
-
0,
|
65
|
-
{
|
66
|
-
"name": "Box 10 Units",
|
67
|
-
"qty": 10.0,
|
68
|
-
"barcode": "5099206074439",
|
69
|
-
},
|
70
|
-
)
|
71
|
-
],
|
72
|
-
}
|
73
|
-
)
|
74
|
-
cls.product_tracking = cls.Product.create(
|
75
|
-
{
|
76
|
-
"name": "Product test with lot tracking",
|
77
|
-
"type": "product",
|
78
|
-
"tracking": "lot",
|
79
|
-
"barcode": "8433281006850",
|
80
|
-
"packaging_ids": [
|
81
|
-
(
|
82
|
-
0,
|
83
|
-
0,
|
84
|
-
{"name": "Box 5 Units", "qty": 5.0, "barcode": "5420008510489"},
|
85
|
-
)
|
86
|
-
],
|
87
|
-
}
|
88
|
-
)
|
89
|
-
cls.lot_1 = cls.StockProductionLot.create(
|
90
|
-
{
|
91
|
-
"name": "8411822222568",
|
92
|
-
"product_id": cls.product_tracking.id,
|
93
|
-
"company_id": cls.company.id,
|
94
|
-
}
|
95
|
-
)
|
96
|
-
cls.quant_lot_1 = cls.StockQuant.create(
|
97
|
-
{
|
98
|
-
"product_id": cls.product_tracking.id,
|
99
|
-
"lot_id": cls.lot_1.id,
|
100
|
-
"location_id": cls.stock_location.id,
|
101
|
-
"quantity": 100.0,
|
102
|
-
}
|
103
|
-
)
|
104
|
-
cls.wiz_scan = cls.WizScanReadPicking.create(
|
105
|
-
{"option_group_id": cls.option_group.id, "step": 1}
|
106
|
-
)
|
107
|
-
|
108
|
-
@classmethod
|
109
|
-
def _create_barcode_option_group(cls):
|
110
|
-
return cls.env["stock.barcodes.option.group"].create(
|
111
|
-
{
|
112
|
-
"name": "option group for tests",
|
113
|
-
"show_scan_log": True,
|
114
|
-
"create_lot": True,
|
115
|
-
"option_ids": [
|
116
|
-
(
|
117
|
-
0,
|
118
|
-
0,
|
119
|
-
{
|
120
|
-
"step": 1,
|
121
|
-
"name": "Location",
|
122
|
-
"field_name": "location_id",
|
123
|
-
"to_scan": True,
|
124
|
-
"required": True,
|
125
|
-
},
|
126
|
-
),
|
127
|
-
(
|
128
|
-
0,
|
129
|
-
0,
|
130
|
-
{
|
131
|
-
"step": 2,
|
132
|
-
"name": "Product",
|
133
|
-
"field_name": "product_id",
|
134
|
-
"to_scan": True,
|
135
|
-
"required": True,
|
136
|
-
},
|
137
|
-
),
|
138
|
-
(
|
139
|
-
0,
|
140
|
-
0,
|
141
|
-
{
|
142
|
-
"step": 2,
|
143
|
-
"name": "Packaging",
|
144
|
-
"field_name": "packaging_id",
|
145
|
-
"to_scan": True,
|
146
|
-
"required": False,
|
147
|
-
},
|
148
|
-
),
|
149
|
-
(
|
150
|
-
0,
|
151
|
-
0,
|
152
|
-
{
|
153
|
-
"step": 2,
|
154
|
-
"name": "Lot / Serial",
|
155
|
-
"field_name": "lot_id",
|
156
|
-
"to_scan": True,
|
157
|
-
"required": True,
|
158
|
-
},
|
159
|
-
),
|
160
|
-
],
|
161
|
-
}
|
162
|
-
)
|
163
|
-
|
164
|
-
def action_barcode_scanned(self, wizard, barcode):
|
165
|
-
wizard._barcode_scanned = barcode
|
166
|
-
wizard._on_barcode_scanned()
|
167
8
|
|
9
|
+
@tagged("post_install", "-at_install")
|
10
|
+
class TestStockBarcodes(TestCommonStockBarcodes):
|
168
11
|
def test_wizard_scan_location(self):
|
169
12
|
self.action_barcode_scanned(self.wiz_scan, "8411322222568")
|
170
13
|
self.assertEqual(self.wiz_scan.location_id, self.location_1)
|
@@ -205,14 +48,6 @@ class TestStockBarcodes(TransactionCase):
|
|
205
48
|
self.assertEqual(self.wiz_scan.product_qty, 15.0)
|
206
49
|
self.wiz_scan.manual_entry = False
|
207
50
|
|
208
|
-
# Force more than one package with the same lot
|
209
|
-
self.product_wo_tracking.packaging_ids.barcode = "5420008510489"
|
210
|
-
self.action_barcode_scanned(self.wiz_scan, "5420008510489")
|
211
|
-
self.assertEqual(
|
212
|
-
self.wiz_scan.message,
|
213
|
-
"5420008510489 (More than one package found)",
|
214
|
-
)
|
215
|
-
|
216
51
|
def test_wizard_scan_lot(self):
|
217
52
|
self.wiz_scan.location_id = self.location_1.id
|
218
53
|
self.wiz_scan.action_show_step()
|
@@ -233,9 +68,6 @@ class TestStockBarcodes(TransactionCase):
|
|
233
68
|
"84118xxx22568 (Barcode not found with this screen values)",
|
234
69
|
)
|
235
70
|
|
236
|
-
def test_wizard_remove_last_scan(self):
|
237
|
-
self.assertTrue(self.wiz_scan.action_undo_last_scan())
|
238
|
-
|
239
71
|
def test_wiz_clean_lot(self):
|
240
72
|
self.wiz_scan.location_id = self.location_1.id
|
241
73
|
self.wiz_scan.action_show_step()
|