odoo-addon-shopfloor 16.0.1.0.0.24__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/shopfloor/README.rst +160 -0
- odoo/addons/shopfloor/__init__.py +4 -0
- odoo/addons/shopfloor/__manifest__.py +65 -0
- odoo/addons/shopfloor/actions/__init__.py +15 -0
- odoo/addons/shopfloor/actions/change_package_lot.py +164 -0
- odoo/addons/shopfloor/actions/completion_info.py +42 -0
- odoo/addons/shopfloor/actions/data.py +329 -0
- odoo/addons/shopfloor/actions/data_detail.py +154 -0
- odoo/addons/shopfloor/actions/inventory.py +150 -0
- odoo/addons/shopfloor/actions/location_content_transfer_sorter.py +89 -0
- odoo/addons/shopfloor/actions/message.py +846 -0
- odoo/addons/shopfloor/actions/move_line_search.py +119 -0
- odoo/addons/shopfloor/actions/packaging.py +59 -0
- odoo/addons/shopfloor/actions/savepoint.py +44 -0
- odoo/addons/shopfloor/actions/schema.py +182 -0
- odoo/addons/shopfloor/actions/schema_detail.py +98 -0
- odoo/addons/shopfloor/actions/search.py +187 -0
- odoo/addons/shopfloor/actions/stock.py +239 -0
- odoo/addons/shopfloor/actions/stock_unreserve.py +66 -0
- odoo/addons/shopfloor/components/__init__.py +5 -0
- odoo/addons/shopfloor/components/scan_handler_location.py +26 -0
- odoo/addons/shopfloor/components/scan_handler_lot.py +26 -0
- odoo/addons/shopfloor/components/scan_handler_package.py +26 -0
- odoo/addons/shopfloor/components/scan_handler_product.py +26 -0
- odoo/addons/shopfloor/components/scan_handler_transfer.py +26 -0
- odoo/addons/shopfloor/data/shopfloor_scenario_data.xml +73 -0
- odoo/addons/shopfloor/demo/shopfloor_app_demo.xml +12 -0
- odoo/addons/shopfloor/demo/shopfloor_menu_demo.xml +64 -0
- odoo/addons/shopfloor/demo/shopfloor_profile_demo.xml +8 -0
- odoo/addons/shopfloor/demo/stock_picking_type_demo.xml +93 -0
- odoo/addons/shopfloor/docs/checkout_diag_seq.plantuml +61 -0
- odoo/addons/shopfloor/docs/checkout_diag_seq.png +0 -0
- odoo/addons/shopfloor/docs/cluster_picking_diag_seq.plantuml +112 -0
- odoo/addons/shopfloor/docs/cluster_picking_diag_seq.png +0 -0
- odoo/addons/shopfloor/docs/delivery_diag_seq.plantuml +56 -0
- odoo/addons/shopfloor/docs/delivery_diag_seq.png +0 -0
- odoo/addons/shopfloor/docs/location_content_transfer_diag_seq.plantuml +66 -0
- odoo/addons/shopfloor/docs/location_content_transfer_diag_seq.png +0 -0
- odoo/addons/shopfloor/docs/oca_logo.png +0 -0
- odoo/addons/shopfloor/docs/single_pack_transfer_diag_seq.plantuml +36 -0
- odoo/addons/shopfloor/docs/single_pack_transfer_diag_seq.png +0 -0
- odoo/addons/shopfloor/docs/zone_picking_diag_seq.plantuml +85 -0
- odoo/addons/shopfloor/docs/zone_picking_diag_seq.png +0 -0
- odoo/addons/shopfloor/exceptions.py +6 -0
- odoo/addons/shopfloor/i18n/ca.po +1802 -0
- odoo/addons/shopfloor/i18n/de.po +1791 -0
- odoo/addons/shopfloor/i18n/es_AR.po +2147 -0
- odoo/addons/shopfloor/i18n/pt_BR.po +1791 -0
- odoo/addons/shopfloor/i18n/shopfloor.pot +1877 -0
- odoo/addons/shopfloor/models/__init__.py +12 -0
- odoo/addons/shopfloor/models/priority_postpone_mixin.py +41 -0
- odoo/addons/shopfloor/models/shopfloor_app.py +9 -0
- odoo/addons/shopfloor/models/shopfloor_menu.py +436 -0
- odoo/addons/shopfloor/models/stock_location.py +76 -0
- odoo/addons/shopfloor/models/stock_move.py +119 -0
- odoo/addons/shopfloor/models/stock_move_line.py +307 -0
- odoo/addons/shopfloor/models/stock_package_level.py +50 -0
- odoo/addons/shopfloor/models/stock_picking.py +118 -0
- odoo/addons/shopfloor/models/stock_picking_batch.py +41 -0
- odoo/addons/shopfloor/models/stock_picking_type.py +26 -0
- odoo/addons/shopfloor/models/stock_quant.py +31 -0
- odoo/addons/shopfloor/models/stock_quant_package.py +101 -0
- odoo/addons/shopfloor/readme/CONTRIBUTORS.rst +18 -0
- odoo/addons/shopfloor/readme/CREDITS.rst +5 -0
- odoo/addons/shopfloor/readme/DESCRIPTION.rst +17 -0
- odoo/addons/shopfloor/readme/HISTORY.rst +4 -0
- odoo/addons/shopfloor/readme/ROADMAP.rst +4 -0
- odoo/addons/shopfloor/readme/USAGE.rst +6 -0
- odoo/addons/shopfloor/security/groups.xml +17 -0
- odoo/addons/shopfloor/services/__init__.py +16 -0
- odoo/addons/shopfloor/services/checkout.py +1763 -0
- odoo/addons/shopfloor/services/cluster_picking.py +1628 -0
- odoo/addons/shopfloor/services/delivery.py +828 -0
- odoo/addons/shopfloor/services/forms/__init__.py +1 -0
- odoo/addons/shopfloor/services/forms/picking_form.py +78 -0
- odoo/addons/shopfloor/services/location_content_transfer.py +1194 -0
- odoo/addons/shopfloor/services/menu.py +60 -0
- odoo/addons/shopfloor/services/picking_batch.py +126 -0
- odoo/addons/shopfloor/services/service.py +101 -0
- odoo/addons/shopfloor/services/single_pack_transfer.py +366 -0
- odoo/addons/shopfloor/services/zone_picking.py +1938 -0
- odoo/addons/shopfloor/static/description/icon.png +0 -0
- odoo/addons/shopfloor/static/description/index.html +500 -0
- odoo/addons/shopfloor/tests/__init__.py +83 -0
- odoo/addons/shopfloor/tests/common.py +324 -0
- odoo/addons/shopfloor/tests/models.py +29 -0
- odoo/addons/shopfloor/tests/test_actions_change_package_lot.py +1175 -0
- odoo/addons/shopfloor/tests/test_actions_data.py +376 -0
- odoo/addons/shopfloor/tests/test_actions_data_base.py +244 -0
- odoo/addons/shopfloor/tests/test_actions_data_detail.py +322 -0
- odoo/addons/shopfloor/tests/test_actions_search.py +248 -0
- odoo/addons/shopfloor/tests/test_actions_stock.py +48 -0
- odoo/addons/shopfloor/tests/test_checkout_auto_post.py +67 -0
- odoo/addons/shopfloor/tests/test_checkout_base.py +81 -0
- odoo/addons/shopfloor/tests/test_checkout_cancel_line.py +154 -0
- odoo/addons/shopfloor/tests/test_checkout_change_packaging.py +184 -0
- odoo/addons/shopfloor/tests/test_checkout_done.py +133 -0
- odoo/addons/shopfloor/tests/test_checkout_list_delivery_packaging.py +131 -0
- odoo/addons/shopfloor/tests/test_checkout_list_package.py +327 -0
- odoo/addons/shopfloor/tests/test_checkout_new_package.py +88 -0
- odoo/addons/shopfloor/tests/test_checkout_no_package.py +95 -0
- odoo/addons/shopfloor/tests/test_checkout_scan.py +174 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_line.py +377 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_line_base.py +25 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_line_no_prefill_qty.py +91 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_package_action.py +451 -0
- odoo/addons/shopfloor/tests/test_checkout_scan_package_action_no_prefill_qty.py +107 -0
- odoo/addons/shopfloor/tests/test_checkout_select.py +74 -0
- odoo/addons/shopfloor/tests/test_checkout_select_line.py +130 -0
- odoo/addons/shopfloor/tests/test_checkout_select_package_base.py +64 -0
- odoo/addons/shopfloor/tests/test_checkout_set_qty.py +257 -0
- odoo/addons/shopfloor/tests/test_checkout_summary.py +69 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_base.py +83 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_batch.py +109 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_change_pack_lot.py +111 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_is_zero.py +98 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_scan_destination.py +376 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_scan_destination_no_prefill_qty.py +115 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_scan_line.py +402 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_scan_line_location_or_pack_first.py +114 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_scan_line_no_prefill_qty.py +70 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_select.py +387 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_skip.py +90 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_stock_issue.py +364 -0
- odoo/addons/shopfloor/tests/test_cluster_picking_unload.py +911 -0
- odoo/addons/shopfloor/tests/test_delivery_base.py +155 -0
- odoo/addons/shopfloor/tests/test_delivery_done.py +108 -0
- odoo/addons/shopfloor/tests/test_delivery_list_stock_picking.py +49 -0
- odoo/addons/shopfloor/tests/test_delivery_reset_qty_done_line.py +119 -0
- odoo/addons/shopfloor/tests/test_delivery_reset_qty_done_pack.py +107 -0
- odoo/addons/shopfloor/tests/test_delivery_scan_deliver.py +557 -0
- odoo/addons/shopfloor/tests/test_delivery_select.py +38 -0
- odoo/addons/shopfloor/tests/test_delivery_set_qty_done_line.py +91 -0
- odoo/addons/shopfloor/tests/test_delivery_set_qty_done_pack.py +135 -0
- odoo/addons/shopfloor/tests/test_delivery_sublocation.py +180 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_base.py +136 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_get_work.py +125 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_mix.py +509 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_putaway.py +143 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_scan_location.py +34 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_set_destination_all.py +343 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_set_destination_package_or_line.py +1074 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_single.py +748 -0
- odoo/addons/shopfloor/tests/test_location_content_transfer_start.py +359 -0
- odoo/addons/shopfloor/tests/test_menu_base.py +261 -0
- odoo/addons/shopfloor/tests/test_menu_counters.py +61 -0
- odoo/addons/shopfloor/tests/test_misc.py +25 -0
- odoo/addons/shopfloor/tests/test_move_action_assign.py +87 -0
- odoo/addons/shopfloor/tests/test_openapi.py +21 -0
- odoo/addons/shopfloor/tests/test_picking_form.py +62 -0
- odoo/addons/shopfloor/tests/test_scan_anything.py +49 -0
- odoo/addons/shopfloor/tests/test_single_pack_transfer.py +1121 -0
- odoo/addons/shopfloor/tests/test_single_pack_transfer_base.py +32 -0
- odoo/addons/shopfloor/tests/test_single_pack_transfer_putaway.py +104 -0
- odoo/addons/shopfloor/tests/test_stock_split.py +204 -0
- odoo/addons/shopfloor/tests/test_user.py +42 -0
- odoo/addons/shopfloor/tests/test_zone_picking_base.py +608 -0
- odoo/addons/shopfloor/tests/test_zone_picking_change_pack_lot.py +140 -0
- odoo/addons/shopfloor/tests/test_zone_picking_select_line.py +723 -0
- odoo/addons/shopfloor/tests/test_zone_picking_select_line_first_scan_location.py +207 -0
- odoo/addons/shopfloor/tests/test_zone_picking_select_line_first_scan_location.py.bak +202 -0
- odoo/addons/shopfloor/tests/test_zone_picking_select_line_no_prefill_qty.py +107 -0
- odoo/addons/shopfloor/tests/test_zone_picking_select_picking_type.py +26 -0
- odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination.py +643 -0
- odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination_no_prefill_qty.py +146 -0
- odoo/addons/shopfloor/tests/test_zone_picking_set_line_destination_pick_pack.py +241 -0
- odoo/addons/shopfloor/tests/test_zone_picking_start.py +206 -0
- odoo/addons/shopfloor/tests/test_zone_picking_stock_issue.py +121 -0
- odoo/addons/shopfloor/tests/test_zone_picking_unload_all.py +353 -0
- odoo/addons/shopfloor/tests/test_zone_picking_unload_buffer_lines.py +113 -0
- odoo/addons/shopfloor/tests/test_zone_picking_unload_set_destination.py +374 -0
- odoo/addons/shopfloor/tests/test_zone_picking_unload_single.py +123 -0
- odoo/addons/shopfloor/tests/test_zone_picking_zero_check.py +43 -0
- odoo/addons/shopfloor/utils.py +13 -0
- odoo/addons/shopfloor/views/shopfloor_menu.xml +167 -0
- odoo/addons/shopfloor/views/stock_location.xml +20 -0
- odoo/addons/shopfloor/views/stock_move_line.xml +52 -0
- odoo/addons/shopfloor/views/stock_picking_type.xml +19 -0
- odoo_addon_shopfloor-16.0.1.0.0.24.dist-info/METADATA +192 -0
- odoo_addon_shopfloor-16.0.1.0.0.24.dist-info/RECORD +182 -0
- odoo_addon_shopfloor-16.0.1.0.0.24.dist-info/WHEEL +5 -0
- odoo_addon_shopfloor-16.0.1.0.0.24.dist-info/top_level.txt +1 -0
@@ -0,0 +1,748 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
|
4
|
+
from .test_location_content_transfer_base import LocationContentTransferCommonCase
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class LocationContentTransferSingleCase(LocationContentTransferCommonCase):
|
10
|
+
"""Tests for endpoint used from state start_single
|
11
|
+
|
12
|
+
* /scan_package
|
13
|
+
* /scan_line
|
14
|
+
* /postpone_package
|
15
|
+
* /postpone_line
|
16
|
+
|
17
|
+
"""
|
18
|
+
|
19
|
+
# TODO common with set_destination_all?
|
20
|
+
@classmethod
|
21
|
+
def setUpClassBaseData(cls):
|
22
|
+
super().setUpClassBaseData()
|
23
|
+
products = cls.product_a + cls.product_b + cls.product_c + cls.product_d
|
24
|
+
for product in products:
|
25
|
+
cls.env["stock.putaway.rule"].sudo().create(
|
26
|
+
{
|
27
|
+
"product_id": product.id,
|
28
|
+
"location_in_id": cls.stock_location.id,
|
29
|
+
"location_out_id": cls.shelf1.id,
|
30
|
+
}
|
31
|
+
)
|
32
|
+
|
33
|
+
cls.product_d.tracking = "lot"
|
34
|
+
cls.picking1 = picking1 = cls._create_picking(
|
35
|
+
lines=[(cls.product_a, 10), (cls.product_b, 10)]
|
36
|
+
)
|
37
|
+
cls.picking2 = picking2 = cls._create_picking(
|
38
|
+
lines=[(cls.product_c, 10), (cls.product_d, 10)]
|
39
|
+
)
|
40
|
+
cls.pickings = picking1 | picking2
|
41
|
+
cls._fill_stock_for_moves(
|
42
|
+
picking1.move_ids, in_package=True, location=cls.content_loc
|
43
|
+
)
|
44
|
+
cls.product_d_lot = cls.env["stock.lot"].create(
|
45
|
+
{"product_id": cls.product_d.id, "company_id": cls.env.company.id}
|
46
|
+
)
|
47
|
+
cls._fill_stock_for_moves(picking2.move_ids[0], location=cls.content_loc)
|
48
|
+
cls._fill_stock_for_moves(
|
49
|
+
picking2.move_ids[1], location=cls.content_loc, in_lot=cls.product_d_lot
|
50
|
+
)
|
51
|
+
cls.pickings.action_assign()
|
52
|
+
cls._simulate_pickings_selected(cls.pickings)
|
53
|
+
|
54
|
+
def _test_scan_package_ok(self, barcode):
|
55
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
56
|
+
response = self.service.dispatch(
|
57
|
+
"scan_package",
|
58
|
+
params={
|
59
|
+
"location_id": self.content_loc.id,
|
60
|
+
"package_level_id": package_level.id,
|
61
|
+
"barcode": barcode,
|
62
|
+
},
|
63
|
+
)
|
64
|
+
self.assert_response_scan_destination(response, package_level)
|
65
|
+
|
66
|
+
def test_scan_package_location_not_found(self):
|
67
|
+
response = self.service.dispatch(
|
68
|
+
"scan_package",
|
69
|
+
params={
|
70
|
+
"location_id": 1234567890, # Doesn't exist
|
71
|
+
"package_level_id": 42,
|
72
|
+
"barcode": "TEST",
|
73
|
+
},
|
74
|
+
)
|
75
|
+
self.assert_response_start(
|
76
|
+
response, message=self.service.msg_store.record_not_found()
|
77
|
+
)
|
78
|
+
|
79
|
+
def test_scan_package_package_ok(self):
|
80
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
81
|
+
self._test_scan_package_ok(package_level.package_id.name)
|
82
|
+
|
83
|
+
def _scan_package_error(self, package_level, scanned, message):
|
84
|
+
response = self.service.dispatch(
|
85
|
+
"scan_package",
|
86
|
+
params={
|
87
|
+
"location_id": self.content_loc.id,
|
88
|
+
"package_level_id": package_level.id,
|
89
|
+
"barcode": scanned,
|
90
|
+
},
|
91
|
+
)
|
92
|
+
self.assert_response_start_single(response, self.pickings, message=message)
|
93
|
+
|
94
|
+
def test_scan_package_error_wrong_package(self):
|
95
|
+
"""Wrong package scanned"""
|
96
|
+
pack = self.env["stock.quant.package"].sudo().create({})
|
97
|
+
self._scan_package_error(
|
98
|
+
self.picking1.move_line_ids.package_level_id,
|
99
|
+
pack.name,
|
100
|
+
{"message_type": "error", "body": "Wrong pack."},
|
101
|
+
)
|
102
|
+
|
103
|
+
def test_scan_package_error_wrong_product(self):
|
104
|
+
"""Wrong product scanned"""
|
105
|
+
product = (
|
106
|
+
self.env["product.product"]
|
107
|
+
.sudo()
|
108
|
+
.create(
|
109
|
+
{
|
110
|
+
"name": "Wrong",
|
111
|
+
"barcode": "WRONGPRODUCT",
|
112
|
+
}
|
113
|
+
)
|
114
|
+
)
|
115
|
+
self._scan_package_error(
|
116
|
+
self.picking1.move_line_ids.package_level_id,
|
117
|
+
product.barcode,
|
118
|
+
{"message_type": "error", "body": "Wrong product."},
|
119
|
+
)
|
120
|
+
|
121
|
+
def test_scan_package_error_wrong_lot(self):
|
122
|
+
"""Wrong product scanned"""
|
123
|
+
lot = (
|
124
|
+
self.env["stock.lot"]
|
125
|
+
.sudo()
|
126
|
+
.create(
|
127
|
+
{
|
128
|
+
"name": "WRONGLOT",
|
129
|
+
"product_id": self.picking1.move_line_ids[0].product_id.id,
|
130
|
+
"company_id": self.env.company.id,
|
131
|
+
}
|
132
|
+
)
|
133
|
+
)
|
134
|
+
self._scan_package_error(
|
135
|
+
self.picking1.move_line_ids.package_level_id,
|
136
|
+
lot.name,
|
137
|
+
{"message_type": "error", "body": "Wrong lot."},
|
138
|
+
)
|
139
|
+
|
140
|
+
def test_scan_package_barcode_not_found(self):
|
141
|
+
"""Nothing found for the barcode"""
|
142
|
+
self._scan_package_error(
|
143
|
+
self.picking1.move_line_ids.package_level_id,
|
144
|
+
"NO_EXISTING_BARCODE",
|
145
|
+
{"message_type": "error", "body": "Barcode not found"},
|
146
|
+
)
|
147
|
+
|
148
|
+
def test_scan_package_product_ok(self):
|
149
|
+
# product_a is in the package and anywhere else so it's
|
150
|
+
# accepted to check we scanned the correct package
|
151
|
+
self._test_scan_package_ok(self.product_a.barcode)
|
152
|
+
|
153
|
+
def test_scan_package_product_packaging_ok(self):
|
154
|
+
# product_a is in the package and anywhere else so it's
|
155
|
+
# accepted to check we scanned the correct package
|
156
|
+
self._test_scan_package_ok(self.product_a.packaging_ids[0].barcode)
|
157
|
+
|
158
|
+
def test_scan_package_lot_ok(self):
|
159
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
160
|
+
line_product_a = package_level.move_line_ids[0]
|
161
|
+
self.product_a.tracking = "lot"
|
162
|
+
line_product_a.lot_id = self.env["stock.lot"].create(
|
163
|
+
{"product_id": self.product_a.id, "company_id": self.env.company.id}
|
164
|
+
)
|
165
|
+
# lot of product_a is in the package and anywhere else so it's
|
166
|
+
# accepted to check we scanned the correct package
|
167
|
+
self._test_scan_package_ok(line_product_a.lot_id.name)
|
168
|
+
|
169
|
+
def _test_scan_package_nok(self, pickings, barcode, message):
|
170
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
171
|
+
response = self.service.dispatch(
|
172
|
+
"scan_package",
|
173
|
+
params={
|
174
|
+
"location_id": self.content_loc.id,
|
175
|
+
"package_level_id": package_level.id,
|
176
|
+
"barcode": barcode,
|
177
|
+
},
|
178
|
+
)
|
179
|
+
self.assert_response_start_single(response, pickings, message=message)
|
180
|
+
|
181
|
+
def test_scan_package_product_nok_different_package(self):
|
182
|
+
# add another picking with a package with product a,
|
183
|
+
# if we scan product A, we can't know for which package it is
|
184
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
185
|
+
self._fill_stock_for_moves(
|
186
|
+
picking.move_ids, in_package=True, location=self.content_loc
|
187
|
+
)
|
188
|
+
picking.action_assign()
|
189
|
+
self._simulate_pickings_selected(picking)
|
190
|
+
self._test_scan_package_nok(
|
191
|
+
self.pickings | picking,
|
192
|
+
self.product_a.barcode,
|
193
|
+
{"message_type": "error", "body": "Scan the package"},
|
194
|
+
)
|
195
|
+
|
196
|
+
def test_scan_package_product_nok_different_line(self):
|
197
|
+
# add another picking with a raw line with product a,
|
198
|
+
# if we scan product A, we can't know which line/package we want
|
199
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
200
|
+
self._fill_stock_for_moves(picking.move_ids, location=self.content_loc)
|
201
|
+
picking.action_assign()
|
202
|
+
self._simulate_pickings_selected(picking)
|
203
|
+
self._test_scan_package_nok(
|
204
|
+
self.pickings | picking,
|
205
|
+
self.product_a.barcode,
|
206
|
+
{"message_type": "error", "body": "Scan the package"},
|
207
|
+
)
|
208
|
+
|
209
|
+
def test_scan_package_product_nok_product_tracked(self):
|
210
|
+
# we scan product_a's barcode but it's tracked by lot
|
211
|
+
self.product_a.tracking = "lot"
|
212
|
+
self._test_scan_package_nok(
|
213
|
+
self.pickings,
|
214
|
+
self.product_a.barcode,
|
215
|
+
{"message_type": "error", "body": "Scan the package"},
|
216
|
+
)
|
217
|
+
|
218
|
+
def test_scan_package_lot_nok_different_package(self):
|
219
|
+
# add another picking with a package with the lot used in our package,
|
220
|
+
# if we scan the lot, we can't know for which package it is
|
221
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
222
|
+
line_product_a = package_level.move_line_ids[0]
|
223
|
+
self.product_a.tracking = "lot"
|
224
|
+
line_product_a.lot_id = lot = self.env["stock.lot"].create(
|
225
|
+
{"product_id": self.product_a.id, "company_id": self.env.company.id}
|
226
|
+
)
|
227
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
228
|
+
self._fill_stock_for_moves(
|
229
|
+
picking.move_ids, in_package=True, in_lot=lot, location=self.content_loc
|
230
|
+
)
|
231
|
+
picking.action_assign()
|
232
|
+
self._simulate_pickings_selected(picking)
|
233
|
+
self._test_scan_package_nok(
|
234
|
+
self.pickings | picking,
|
235
|
+
self.product_a.barcode,
|
236
|
+
{"message_type": "error", "body": "Scan the package"},
|
237
|
+
)
|
238
|
+
|
239
|
+
def test_scan_package_lot_nok_different_line(self):
|
240
|
+
# add another picking with a raw line with a lot used in our package,
|
241
|
+
# if we scan the lot, we can't know which line/package we want
|
242
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
243
|
+
line_product_a = package_level.move_line_ids[0]
|
244
|
+
self.product_a.tracking = "lot"
|
245
|
+
line_product_a.lot_id = lot = self.env["stock.lot"].create(
|
246
|
+
{"product_id": self.product_a.id, "company_id": self.env.company.id}
|
247
|
+
)
|
248
|
+
picking = self._create_picking(lines=[(self.product_a, 10)])
|
249
|
+
self._fill_stock_for_moves(
|
250
|
+
picking.move_ids, in_lot=lot, location=self.content_loc
|
251
|
+
)
|
252
|
+
picking.action_assign()
|
253
|
+
self._simulate_pickings_selected(picking)
|
254
|
+
self._test_scan_package_nok(
|
255
|
+
self.pickings | picking,
|
256
|
+
self.product_a.barcode,
|
257
|
+
{"message_type": "error", "body": "Scan the package"},
|
258
|
+
)
|
259
|
+
|
260
|
+
def test_scan_package_package_level_not_exists(self):
|
261
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
262
|
+
package_level_id = package_level.id
|
263
|
+
package_level.unlink()
|
264
|
+
response = self.service.dispatch(
|
265
|
+
"scan_package",
|
266
|
+
params={
|
267
|
+
"location_id": self.content_loc.id,
|
268
|
+
"package_level_id": package_level_id,
|
269
|
+
"barcode": self.product_a.barcode,
|
270
|
+
},
|
271
|
+
)
|
272
|
+
self.assert_response_start_single(
|
273
|
+
response, self.pickings, message=self.service.msg_store.record_not_found()
|
274
|
+
)
|
275
|
+
|
276
|
+
def _test_scan_line_ok(self, move_line, barcode):
|
277
|
+
response = self.service.dispatch(
|
278
|
+
"scan_line",
|
279
|
+
params={
|
280
|
+
"location_id": self.content_loc.id,
|
281
|
+
"move_line_id": move_line.id,
|
282
|
+
"barcode": barcode,
|
283
|
+
},
|
284
|
+
)
|
285
|
+
self.assert_response_scan_destination(response, move_line)
|
286
|
+
|
287
|
+
def test_scan_line_package_ok(self):
|
288
|
+
move_line = self.picking2.move_line_ids[0]
|
289
|
+
package = move_line.package_id = self.env["stock.quant.package"].create({})
|
290
|
+
self._test_scan_line_ok(move_line, package.name)
|
291
|
+
|
292
|
+
def test_scan_line_product_ok(self):
|
293
|
+
move_line = self.picking2.move_line_ids[0]
|
294
|
+
# check we selected the good line
|
295
|
+
self.assertEqual(move_line.product_id, self.product_c)
|
296
|
+
self._test_scan_line_ok(move_line, self.product_c.barcode)
|
297
|
+
|
298
|
+
def test_scan_line_product_packaging_ok(self):
|
299
|
+
move_line = self.picking2.move_line_ids[0]
|
300
|
+
# check we selected the good line
|
301
|
+
self.assertEqual(move_line.product_id, self.product_c)
|
302
|
+
self._test_scan_line_ok(move_line, self.product_c.packaging_ids[0].barcode)
|
303
|
+
|
304
|
+
def test_scan_line_lot_ok(self):
|
305
|
+
move_line = self.picking2.move_line_ids[1]
|
306
|
+
# check we selected the good line (the one with a lot)
|
307
|
+
self.assertEqual(move_line.product_id, self.product_d)
|
308
|
+
self._test_scan_line_ok(move_line, self.product_d_lot.name)
|
309
|
+
|
310
|
+
def _test_scan_line_nok(self, pickings, move_line_id, barcode, message):
|
311
|
+
response = self.service.dispatch(
|
312
|
+
"scan_line",
|
313
|
+
params={
|
314
|
+
"location_id": self.content_loc.id,
|
315
|
+
"move_line_id": move_line_id,
|
316
|
+
"barcode": barcode,
|
317
|
+
},
|
318
|
+
)
|
319
|
+
self.assert_response_start_single(response, pickings, message=message)
|
320
|
+
|
321
|
+
def test_scan_line_product_nok_product_tracked(self):
|
322
|
+
# we scan product_d's barcode but it's tracked by lot
|
323
|
+
move_line = self.picking2.move_line_ids[1]
|
324
|
+
# check we selected the good line (the one with a lot)
|
325
|
+
self.assertEqual(move_line.product_id, self.product_d)
|
326
|
+
self._test_scan_line_nok(
|
327
|
+
self.pickings,
|
328
|
+
move_line.id,
|
329
|
+
self.product_d.barcode,
|
330
|
+
self.service.msg_store.scan_lot_on_product_tracked_by_lot(),
|
331
|
+
)
|
332
|
+
|
333
|
+
def test_scan_line_error_wrong_package(self):
|
334
|
+
"""Wrong package scanned"""
|
335
|
+
move_line = self.picking2.move_line_ids[0]
|
336
|
+
pack = self.env["stock.quant.package"].sudo().create({})
|
337
|
+
self._test_scan_line_nok(
|
338
|
+
self.pickings,
|
339
|
+
move_line.id,
|
340
|
+
pack.name,
|
341
|
+
{"message_type": "error", "body": "Wrong pack."},
|
342
|
+
)
|
343
|
+
|
344
|
+
def test_scan_line_error_wrong_product(self):
|
345
|
+
"""Wrong product scanned"""
|
346
|
+
move_line = self.picking2.move_line_ids[0]
|
347
|
+
product = (
|
348
|
+
self.env["product.product"]
|
349
|
+
.sudo()
|
350
|
+
.create(
|
351
|
+
{
|
352
|
+
"name": "Wrong",
|
353
|
+
"barcode": "WRONGPRODUCT",
|
354
|
+
}
|
355
|
+
)
|
356
|
+
)
|
357
|
+
self._test_scan_line_nok(
|
358
|
+
self.pickings,
|
359
|
+
move_line.id,
|
360
|
+
product.barcode,
|
361
|
+
{"message_type": "error", "body": "Wrong product."},
|
362
|
+
)
|
363
|
+
|
364
|
+
def test_scan_line_error_wrong_lot(self):
|
365
|
+
"""Wrong product scanned"""
|
366
|
+
move_line = self.picking2.move_line_ids[0]
|
367
|
+
lot = (
|
368
|
+
self.env["stock.lot"]
|
369
|
+
.sudo()
|
370
|
+
.create(
|
371
|
+
{
|
372
|
+
"name": "WRONGLOT",
|
373
|
+
"product_id": move_line.product_id.id,
|
374
|
+
"company_id": self.env.company.id,
|
375
|
+
}
|
376
|
+
)
|
377
|
+
)
|
378
|
+
self._test_scan_line_nok(
|
379
|
+
self.pickings,
|
380
|
+
move_line.id,
|
381
|
+
lot.name,
|
382
|
+
{"message_type": "error", "body": "Wrong lot."},
|
383
|
+
)
|
384
|
+
|
385
|
+
def test_scan_line_barcode_not_found(self):
|
386
|
+
move_line = self.picking2.move_line_ids[0]
|
387
|
+
self._test_scan_line_nok(
|
388
|
+
self.pickings,
|
389
|
+
move_line.id,
|
390
|
+
"NOT_FOUND",
|
391
|
+
self.service.msg_store.barcode_not_found(),
|
392
|
+
)
|
393
|
+
|
394
|
+
def test_scan_line_move_line_not_exists(self):
|
395
|
+
move_line = self.picking2.move_line_ids[0]
|
396
|
+
move_line_id = move_line.id
|
397
|
+
move_line.unlink()
|
398
|
+
self._test_scan_line_nok(
|
399
|
+
self.pickings,
|
400
|
+
move_line_id,
|
401
|
+
"NOT_FOUND",
|
402
|
+
self.service.msg_store.record_not_found(),
|
403
|
+
)
|
404
|
+
|
405
|
+
def test_postpone_package_wrong_parameters(self):
|
406
|
+
"""Wrong 'location_id' and 'package_level_id' parameters, redirect the
|
407
|
+
user to the 'start' screen.
|
408
|
+
"""
|
409
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
410
|
+
response = self.service.dispatch(
|
411
|
+
"postpone_package",
|
412
|
+
params={
|
413
|
+
"location_id": 1234567890, # Doesn't exist
|
414
|
+
"package_level_id": package_level.id,
|
415
|
+
},
|
416
|
+
)
|
417
|
+
self.assert_response_start(
|
418
|
+
response, message=self.service.msg_store.record_not_found()
|
419
|
+
)
|
420
|
+
response = self.service.dispatch(
|
421
|
+
"postpone_package",
|
422
|
+
params={
|
423
|
+
"location_id": self.content_loc.id,
|
424
|
+
"package_level_id": 1234567890, # Doesn't exist
|
425
|
+
},
|
426
|
+
)
|
427
|
+
move_lines = self.service._find_transfer_move_lines(self.content_loc)
|
428
|
+
self.assert_response_start_single(
|
429
|
+
response,
|
430
|
+
move_lines.mapped("picking_id"),
|
431
|
+
)
|
432
|
+
|
433
|
+
def test_postpone_package_ok(self):
|
434
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
435
|
+
previous_priority = package_level.shopfloor_priority
|
436
|
+
self.assertFalse(package_level.shopfloor_postponed)
|
437
|
+
response = self.service.dispatch(
|
438
|
+
"postpone_package",
|
439
|
+
params={
|
440
|
+
"location_id": self.content_loc.id,
|
441
|
+
"package_level_id": package_level.id,
|
442
|
+
},
|
443
|
+
)
|
444
|
+
self.assertTrue(package_level.shopfloor_postponed)
|
445
|
+
self.assertEqual(package_level.shopfloor_priority, previous_priority + 1)
|
446
|
+
move_lines = self.service._find_transfer_move_lines(self.content_loc)
|
447
|
+
self.assert_response_start_single(
|
448
|
+
response,
|
449
|
+
move_lines.mapped("picking_id"),
|
450
|
+
)
|
451
|
+
|
452
|
+
def test_postpone_sorter(self):
|
453
|
+
move_line = self.picking2.move_line_ids[0]
|
454
|
+
move_lines = self.service._find_transfer_move_lines(self.content_loc)
|
455
|
+
pickings = move_lines.mapped("picking_id")
|
456
|
+
sorter = self.service._actions_for("location_content_transfer.sorter")
|
457
|
+
sorter.feed_pickings(pickings)
|
458
|
+
content_sorted1 = list(sorter)
|
459
|
+
self.service.dispatch(
|
460
|
+
"postpone_line",
|
461
|
+
params={"location_id": self.content_loc.id, "move_line_id": move_line.id},
|
462
|
+
)
|
463
|
+
sorter.sort()
|
464
|
+
content_sorted2 = list(sorter)
|
465
|
+
self.assertTrue(content_sorted1 != content_sorted2)
|
466
|
+
|
467
|
+
def test_postpone_line_wrong_parameters(self):
|
468
|
+
"""Wrong 'location_id' and 'move_line_id' parameters, redirect the
|
469
|
+
user to the 'start' screen.
|
470
|
+
"""
|
471
|
+
move_line = self.picking2.move_line_ids[0]
|
472
|
+
response = self.service.dispatch(
|
473
|
+
"postpone_line",
|
474
|
+
params={
|
475
|
+
"location_id": 1234567890, # Doesn't exist
|
476
|
+
"move_line_id": move_line.id,
|
477
|
+
},
|
478
|
+
)
|
479
|
+
self.assert_response_start(
|
480
|
+
response, message=self.service.msg_store.record_not_found()
|
481
|
+
)
|
482
|
+
response = self.service.dispatch(
|
483
|
+
"postpone_line",
|
484
|
+
params={
|
485
|
+
"location_id": self.content_loc.id,
|
486
|
+
"move_line_id": 1234567890, # Doesn't exist
|
487
|
+
},
|
488
|
+
)
|
489
|
+
move_lines = self.service._find_transfer_move_lines(self.content_loc)
|
490
|
+
self.assert_response_start_single(
|
491
|
+
response,
|
492
|
+
move_lines.mapped("picking_id"),
|
493
|
+
)
|
494
|
+
|
495
|
+
def test_postpone_line_ok(self):
|
496
|
+
move_line = self.picking2.move_line_ids[0]
|
497
|
+
previous_priority = move_line.shopfloor_priority
|
498
|
+
self.assertFalse(move_line.shopfloor_postponed)
|
499
|
+
response = self.service.dispatch(
|
500
|
+
"postpone_line",
|
501
|
+
params={"location_id": self.content_loc.id, "move_line_id": move_line.id},
|
502
|
+
)
|
503
|
+
self.assertTrue(move_line.shopfloor_postponed)
|
504
|
+
self.assertEqual(move_line.shopfloor_priority, previous_priority + 1)
|
505
|
+
move_lines = self.service._find_transfer_move_lines(self.content_loc)
|
506
|
+
self.assert_response_start_single(
|
507
|
+
response,
|
508
|
+
move_lines.mapped("picking_id"),
|
509
|
+
)
|
510
|
+
|
511
|
+
def test_stock_out_package_wrong_parameters(self):
|
512
|
+
"""Wrong 'location_id' and 'package_level_id' parameters, redirect the
|
513
|
+
user to the 'start' screen.
|
514
|
+
"""
|
515
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
516
|
+
response = self.service.dispatch(
|
517
|
+
"stock_out_package",
|
518
|
+
params={
|
519
|
+
"location_id": 1234567890, # Doesn't exist
|
520
|
+
"package_level_id": package_level.id,
|
521
|
+
},
|
522
|
+
)
|
523
|
+
self.assert_response_start(
|
524
|
+
response, message=self.service.msg_store.record_not_found()
|
525
|
+
)
|
526
|
+
response = self.service.dispatch(
|
527
|
+
"stock_out_package",
|
528
|
+
params={
|
529
|
+
"location_id": self.content_loc.id,
|
530
|
+
"package_level_id": 1234567890, # Doesn't exist
|
531
|
+
},
|
532
|
+
)
|
533
|
+
move_lines = self.service._find_transfer_move_lines(self.content_loc)
|
534
|
+
self.assert_response_start_single(
|
535
|
+
response,
|
536
|
+
move_lines.mapped("picking_id"),
|
537
|
+
)
|
538
|
+
|
539
|
+
def test_stock_out_package_ok(self):
|
540
|
+
"""Declare a stock out on a package_level."""
|
541
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
542
|
+
response = self.service.dispatch(
|
543
|
+
"stock_out_package",
|
544
|
+
params={
|
545
|
+
"location_id": self.content_loc.id,
|
546
|
+
"package_level_id": package_level.id,
|
547
|
+
},
|
548
|
+
)
|
549
|
+
move_lines = self.service._find_transfer_move_lines(self.content_loc)
|
550
|
+
self.assert_response_start_single(
|
551
|
+
response,
|
552
|
+
move_lines.mapped("picking_id"),
|
553
|
+
)
|
554
|
+
|
555
|
+
def test_stock_out_line_wrong_parameters(self):
|
556
|
+
"""Wrong 'location_id' and 'move_line_id' parameters, redirect the
|
557
|
+
user to the 'start' screen.
|
558
|
+
"""
|
559
|
+
move_line = self.picking2.move_line_ids[0]
|
560
|
+
response = self.service.dispatch(
|
561
|
+
"stock_out_line",
|
562
|
+
params={
|
563
|
+
"location_id": 1234567890, # Doesn't exist
|
564
|
+
"move_line_id": move_line.id,
|
565
|
+
},
|
566
|
+
)
|
567
|
+
self.assert_response_start(
|
568
|
+
response, message=self.service.msg_store.record_not_found()
|
569
|
+
)
|
570
|
+
response = self.service.dispatch(
|
571
|
+
"stock_out_line",
|
572
|
+
params={
|
573
|
+
"location_id": self.content_loc.id,
|
574
|
+
"move_line_id": 1234567890, # Doesn't exist
|
575
|
+
},
|
576
|
+
)
|
577
|
+
move_lines = self.service._find_transfer_move_lines(self.content_loc)
|
578
|
+
self.assert_response_start_single(
|
579
|
+
response,
|
580
|
+
move_lines.mapped("picking_id"),
|
581
|
+
)
|
582
|
+
|
583
|
+
def test_dismiss_package_level_ok(self):
|
584
|
+
"""Open a package level"""
|
585
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
586
|
+
move_lines = package_level.move_line_ids
|
587
|
+
response = self.service.dispatch(
|
588
|
+
"dismiss_package_level",
|
589
|
+
params={
|
590
|
+
"location_id": self.content_loc.id,
|
591
|
+
"package_level_id": package_level.id,
|
592
|
+
},
|
593
|
+
)
|
594
|
+
self.assertFalse(package_level.exists())
|
595
|
+
self.assertFalse(move_lines.result_package_id)
|
596
|
+
self.assertFalse(move_lines.package_level_id)
|
597
|
+
self.assertEqual(move_lines.mapped("shopfloor_priority"), [1, 1])
|
598
|
+
move_lines = self.service._find_transfer_move_lines(self.content_loc)
|
599
|
+
self.assert_response_start_single(
|
600
|
+
response,
|
601
|
+
move_lines.mapped("picking_id"),
|
602
|
+
message=self.service.msg_store.package_open(),
|
603
|
+
)
|
604
|
+
|
605
|
+
def test_dismiss_package_level_error_no_package_level(self):
|
606
|
+
"""Open a package level, send unknown package level id"""
|
607
|
+
response = self.service.dispatch(
|
608
|
+
"dismiss_package_level",
|
609
|
+
params={"location_id": self.content_loc.id, "package_level_id": 0},
|
610
|
+
)
|
611
|
+
move_lines = self.service._find_transfer_move_lines(self.content_loc)
|
612
|
+
self.assert_response_start_single(
|
613
|
+
response,
|
614
|
+
move_lines.mapped("picking_id"),
|
615
|
+
message=self.service.msg_store.record_not_found(),
|
616
|
+
)
|
617
|
+
|
618
|
+
def test_dismiss_package_level_error_no_location(self):
|
619
|
+
"""Open a package level, send unknown location id"""
|
620
|
+
package_level = self.picking1.move_line_ids.package_level_id
|
621
|
+
response = self.service.dispatch(
|
622
|
+
"dismiss_package_level",
|
623
|
+
params={"location_id": 0, "package_level_id": package_level.id},
|
624
|
+
)
|
625
|
+
self.assert_response_start(
|
626
|
+
response,
|
627
|
+
message=self.service.msg_store.record_not_found(),
|
628
|
+
)
|
629
|
+
|
630
|
+
|
631
|
+
class LocationContentTransferSingleSpecialCase(LocationContentTransferCommonCase):
|
632
|
+
"""Tests for endpoint used from state start_single (special cases)
|
633
|
+
|
634
|
+
* /stock_out_package
|
635
|
+
* /stock_out_line
|
636
|
+
|
637
|
+
"""
|
638
|
+
|
639
|
+
@classmethod
|
640
|
+
def setUpClassBaseData(cls):
|
641
|
+
super().setUpClassBaseData()
|
642
|
+
products = cls.product_a | cls.product_b
|
643
|
+
for product in products:
|
644
|
+
cls.env["stock.putaway.rule"].sudo().create(
|
645
|
+
{
|
646
|
+
"product_id": product.id,
|
647
|
+
"location_in_id": cls.stock_location.id,
|
648
|
+
"location_out_id": cls.shelf1.id,
|
649
|
+
}
|
650
|
+
)
|
651
|
+
|
652
|
+
cls.picking = cls._create_picking(
|
653
|
+
lines=[(cls.product_a, 10), (cls.product_b, 10)]
|
654
|
+
)
|
655
|
+
cls.move_product_a = cls.picking.move_ids.filtered(
|
656
|
+
lambda m: m.product_id == cls.product_a
|
657
|
+
)
|
658
|
+
cls.move_product_b = cls.picking.move_ids.filtered(
|
659
|
+
lambda m: m.product_id == cls.product_b
|
660
|
+
)
|
661
|
+
# Change the initial demand of product_a to get two move lines for
|
662
|
+
# reserved qties:
|
663
|
+
# - 10 from the package
|
664
|
+
# - 5 from the qty without package
|
665
|
+
cls._fill_stock_for_moves(
|
666
|
+
cls.move_product_a, in_package=True, location=cls.content_loc
|
667
|
+
)
|
668
|
+
cls.move_product_a.product_uom_qty = 15
|
669
|
+
cls._update_qty_in_location(
|
670
|
+
cls.content_loc,
|
671
|
+
cls.product_a,
|
672
|
+
5,
|
673
|
+
)
|
674
|
+
# Put product_b quantities in two different source locations to get
|
675
|
+
# two stock move lines (6 and 4 to satisfy 10 qties)
|
676
|
+
cls._update_qty_in_location(cls.picking.location_id, cls.product_b, 6)
|
677
|
+
cls._update_qty_in_location(cls.content_loc, cls.product_b, 4)
|
678
|
+
# Reserve quantities
|
679
|
+
cls.picking.action_assign()
|
680
|
+
cls._simulate_pickings_selected(cls.picking)
|
681
|
+
|
682
|
+
def test_stock_out_package_split_move(self):
|
683
|
+
"""Declare a stock out on a package_level related to moves containing
|
684
|
+
other unrelated move lines.
|
685
|
+
"""
|
686
|
+
package_level = self.picking.move_line_ids.package_level_id
|
687
|
+
self.assertEqual(self.product_a.qty_available, 15)
|
688
|
+
response = self.service.dispatch(
|
689
|
+
"stock_out_package",
|
690
|
+
params={
|
691
|
+
"location_id": self.content_loc.id,
|
692
|
+
"package_level_id": package_level.id,
|
693
|
+
},
|
694
|
+
)
|
695
|
+
# Check the picking data
|
696
|
+
self.assertFalse(package_level.exists())
|
697
|
+
moves_product_a = self.picking.move_ids.filtered(
|
698
|
+
lambda m: m.product_id == self.product_a
|
699
|
+
)
|
700
|
+
self.assertEqual(len(moves_product_a), 2)
|
701
|
+
move_product_a = moves_product_a.filtered(
|
702
|
+
lambda m: m.state not in ("cancel", "done")
|
703
|
+
)
|
704
|
+
self.assertEqual(len(move_product_a), 1)
|
705
|
+
self.assertEqual(move_product_a.state, "assigned")
|
706
|
+
self.assertEqual(len(move_product_a.move_line_ids), 1)
|
707
|
+
self.assertEqual(self.product_a.qty_available, 5)
|
708
|
+
# Check the response
|
709
|
+
move_lines = self.service._find_transfer_move_lines(self.content_loc)
|
710
|
+
self.assert_response_start_single(
|
711
|
+
response,
|
712
|
+
move_lines.mapped("picking_id"),
|
713
|
+
)
|
714
|
+
|
715
|
+
def test_stock_out_line_split_move(self):
|
716
|
+
"""Declare a stock out on a move line related to moves containing
|
717
|
+
other move lines.
|
718
|
+
"""
|
719
|
+
self.assertEqual(len(self.picking.move_ids), 2)
|
720
|
+
self.assertEqual(len(self.move_product_b.move_line_ids), 2)
|
721
|
+
move_line = self.move_product_b.move_line_ids.filtered(
|
722
|
+
lambda ml: ml.reserved_uom_qty == 4 # 4/10 to stock out
|
723
|
+
)
|
724
|
+
self.assertEqual(self.product_b.qty_available, 10)
|
725
|
+
response = self.service.dispatch(
|
726
|
+
"stock_out_line",
|
727
|
+
params={"location_id": self.content_loc.id, "move_line_id": move_line.id},
|
728
|
+
)
|
729
|
+
# Check the picking data
|
730
|
+
self.assertFalse(move_line.exists())
|
731
|
+
moves_product_b = self.picking.move_ids.filtered(
|
732
|
+
lambda m: m.product_id == self.product_b
|
733
|
+
)
|
734
|
+
self.assertEqual(len(moves_product_b), 2)
|
735
|
+
move_product_b = moves_product_b.filtered(
|
736
|
+
lambda m: m.state not in ("cancel", "done")
|
737
|
+
)
|
738
|
+
self.assertEqual(len(move_product_b), 1)
|
739
|
+
self.assertEqual(move_product_b.state, "assigned")
|
740
|
+
self.assertEqual(len(move_product_b.move_line_ids), 1)
|
741
|
+
|
742
|
+
self.assertEqual(self.product_b.qty_available, 6)
|
743
|
+
# Check the response
|
744
|
+
move_lines = self.service._find_transfer_move_lines(self.content_loc)
|
745
|
+
self.assert_response_start_single(
|
746
|
+
response,
|
747
|
+
move_lines.mapped("picking_id"),
|
748
|
+
)
|