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,364 @@
|
|
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_cluster_picking_base import ClusterPickingCommonCase
|
5
|
+
|
6
|
+
# pylint: disable=missing-return
|
7
|
+
|
8
|
+
|
9
|
+
class ClusterPickingStockIssue(ClusterPickingCommonCase):
|
10
|
+
"""Tests covering the /stock_issue endpoint"""
|
11
|
+
|
12
|
+
@classmethod
|
13
|
+
def setUpClassBaseData(cls, *args, **kwargs):
|
14
|
+
super().setUpClassBaseData(*args, **kwargs)
|
15
|
+
# quants already existing are from demo data
|
16
|
+
loc_ids = (cls.stock_location.id, cls.shelf1.id, cls.shelf2.id)
|
17
|
+
cls.env["stock.quant"].sudo().search([("location_id", "in", loc_ids)]).unlink()
|
18
|
+
cls.batch = cls._create_picking_batch(
|
19
|
+
[
|
20
|
+
[cls.BatchProduct(product=cls.product_a, quantity=10)],
|
21
|
+
[cls.BatchProduct(product=cls.product_a, quantity=5)],
|
22
|
+
[cls.BatchProduct(product=cls.product_a, quantity=20)],
|
23
|
+
[cls.BatchProduct(product=cls.product_a, quantity=10)],
|
24
|
+
[cls.BatchProduct(product=cls.product_a, quantity=7)],
|
25
|
+
]
|
26
|
+
)
|
27
|
+
|
28
|
+
cls.moves = cls.batch.picking_ids.move_ids.sorted("id")
|
29
|
+
cls.move1, cls.move2, cls.move3, cls.move4, cls.move5 = cls.moves
|
30
|
+
cls.batch_other = cls._create_picking_batch(
|
31
|
+
[[cls.BatchProduct(product=cls.product_a, quantity=30)]]
|
32
|
+
)
|
33
|
+
cls.dest_package = cls.env["stock.quant.package"].create({})
|
34
|
+
|
35
|
+
def _stock_issue(self, line, next_line_func=None):
|
36
|
+
batch = line.picking_id.batch_id
|
37
|
+
response = self.service.dispatch(
|
38
|
+
"stock_issue",
|
39
|
+
params={"picking_batch_id": batch.id, "move_line_id": line.id},
|
40
|
+
)
|
41
|
+
# use a function/lambda to delay the read of the next line,
|
42
|
+
# when calling _stock_issue(), the move_line may not exist and
|
43
|
+
# be created during the call to the stock_issue service
|
44
|
+
if next_line_func:
|
45
|
+
self.assert_response(
|
46
|
+
response,
|
47
|
+
next_state="start_line",
|
48
|
+
data=self._line_data(next_line_func()),
|
49
|
+
)
|
50
|
+
else:
|
51
|
+
self.assert_response(
|
52
|
+
response,
|
53
|
+
next_state="unload_all",
|
54
|
+
data=self._data_for_batch(self.batch, self.packing_location),
|
55
|
+
)
|
56
|
+
return response
|
57
|
+
|
58
|
+
def assert_location_qty_and_reserved(
|
59
|
+
self, location, expected_reserved_qty, lot=None
|
60
|
+
):
|
61
|
+
quant_domain = [("location_id", "=", location.id)]
|
62
|
+
if lot:
|
63
|
+
quant_domain += [("lot_id", "=", lot.id)]
|
64
|
+
location_quants = self.env["stock.quant"].search(quant_domain)
|
65
|
+
self.assertEqual(sum(location_quants.mapped("quantity")), expected_reserved_qty)
|
66
|
+
self.assertEqual(
|
67
|
+
sum(location_quants.mapped("reserved_quantity")), expected_reserved_qty
|
68
|
+
)
|
69
|
+
|
70
|
+
def assert_stock_issue_inventories(
|
71
|
+
self, location, product, remaining_qty, lot=None
|
72
|
+
):
|
73
|
+
domain = [
|
74
|
+
("location_id", "=", location.id),
|
75
|
+
("product_id", "=", product.id),
|
76
|
+
]
|
77
|
+
if lot:
|
78
|
+
domain.append(("lot_id", "=", lot.id))
|
79
|
+
quant = self.env["stock.quant"].search(domain, order="id desc", limit=1)
|
80
|
+
self.assertEqual(quant.quantity, remaining_qty)
|
81
|
+
self.assertFalse(quant.inventory_quantity_set)
|
82
|
+
|
83
|
+
def test_stock_issue_with_other_batch(self):
|
84
|
+
self._update_qty_in_location(self.shelf1, self.product_a, 25)
|
85
|
+
# The other batch will reserve 25 in shelf 1, now empty
|
86
|
+
self.batch_other.picking_ids.action_assign()
|
87
|
+
|
88
|
+
self._update_qty_in_location(self.shelf2, self.product_a, 100)
|
89
|
+
# and then, the other batch reserves 5 in shelf 2.
|
90
|
+
# We'll want to check that even if on our batch we have a stock issue,
|
91
|
+
# we never change anything in the batch of another operator.
|
92
|
+
self._simulate_batch_selected(self.batch_other, fill_stock=False)
|
93
|
+
self.assertEqual(
|
94
|
+
set(self.batch_other.picking_ids.mapped("state")), {"assigned"}
|
95
|
+
)
|
96
|
+
|
97
|
+
# At this point, we have a remaining quantity of 0 in shelf1
|
98
|
+
# and 95 in shelf2.
|
99
|
+
|
100
|
+
# all the moves of our batch should be reserved as we have enough
|
101
|
+
# stock
|
102
|
+
self._simulate_batch_selected(self.batch, fill_stock=False)
|
103
|
+
self.assertEqual(set(self.batch.picking_ids.mapped("state")), {"assigned"})
|
104
|
+
|
105
|
+
# the operator could pick the 2 first lines of the batch
|
106
|
+
self._set_dest_package_and_done(self.move1.move_line_ids, self.dest_package)
|
107
|
+
self._set_dest_package_and_done(self.move2.move_line_ids, self.dest_package)
|
108
|
+
|
109
|
+
# on the third move, the operator can't pick anymore in shelf1
|
110
|
+
# because there is nothing inside, they declare a stock issue
|
111
|
+
self._stock_issue(self.move3.move_line_ids)
|
112
|
+
|
113
|
+
self.assertRecordValues(
|
114
|
+
self.moves,
|
115
|
+
[
|
116
|
+
{"state": "assigned"},
|
117
|
+
{"state": "assigned"},
|
118
|
+
{"state": "confirmed"},
|
119
|
+
{"state": "confirmed"},
|
120
|
+
{"state": "confirmed"},
|
121
|
+
],
|
122
|
+
)
|
123
|
+
expected_reserved_qty = (
|
124
|
+
self.move1.product_uom_qty
|
125
|
+
+ self.move2.product_uom_qty
|
126
|
+
+ sum(
|
127
|
+
self.batch_other.picking_ids.move_line_ids.filtered(
|
128
|
+
lambda l: l.location_id == self.shelf2
|
129
|
+
).mapped("reserved_uom_qty")
|
130
|
+
)
|
131
|
+
)
|
132
|
+
# we should have a quant with 20 quantity and 20 reserved
|
133
|
+
# (5 for the other batch and 15 qty_done in this batch)
|
134
|
+
self.assert_location_qty_and_reserved(self.shelf2, expected_reserved_qty)
|
135
|
+
self.assert_stock_issue_inventories(
|
136
|
+
self.shelf2,
|
137
|
+
self.move3.product_id,
|
138
|
+
expected_reserved_qty,
|
139
|
+
)
|
140
|
+
|
141
|
+
def test_stock_issue_several_move_lines(self):
|
142
|
+
self._update_qty_in_location(self.shelf1, self.product_a, 20)
|
143
|
+
# ensure these moves are reserved in shelf1
|
144
|
+
self.move1._action_assign()
|
145
|
+
self.move2._action_assign()
|
146
|
+
|
147
|
+
self._update_qty_in_location(self.shelf2, self.product_a, 100)
|
148
|
+
# reserve move3 first to ensure this one is reserved in both
|
149
|
+
# shelf1 and shelf2
|
150
|
+
self.move3._action_assign()
|
151
|
+
|
152
|
+
# all the remaining moves will be reserved in shelf2
|
153
|
+
self._simulate_batch_selected(self.batch, fill_stock=False)
|
154
|
+
self.assertEqual(set(self.batch.picking_ids.mapped("state")), {"assigned"})
|
155
|
+
# The moves of our batch are reserved as:
|
156
|
+
self.assertEqual(self.move1.move_line_ids.location_id, self.shelf1)
|
157
|
+
self.assertEqual(self.move2.move_line_ids.location_id, self.shelf1)
|
158
|
+
self.assertEqual(
|
159
|
+
self.move3.move_line_ids.mapped("location_id"), self.shelf1 | self.shelf2
|
160
|
+
)
|
161
|
+
self.assertEqual(self.move4.move_line_ids.location_id, self.shelf2)
|
162
|
+
self.assertEqual(self.move5.move_line_ids.location_id, self.shelf2)
|
163
|
+
|
164
|
+
line_shelf1 = self.move3.move_line_ids.filtered(
|
165
|
+
lambda l: l.location_id == self.shelf1
|
166
|
+
)
|
167
|
+
line_shelf2 = self.move3.move_line_ids.filtered(
|
168
|
+
lambda l: l.location_id == self.shelf2
|
169
|
+
)
|
170
|
+
|
171
|
+
# pick the first 2 moves
|
172
|
+
self._set_dest_package_and_done(self.move1.move_line_ids, self.dest_package)
|
173
|
+
self._set_dest_package_and_done(self.move2.move_line_ids, self.dest_package)
|
174
|
+
# the operator could pick the first part of move3 in shelf1
|
175
|
+
self._set_dest_package_and_done(line_shelf1, self.dest_package)
|
176
|
+
|
177
|
+
# on the third move, the operator can't pick anymore in shelf1
|
178
|
+
# because there is nothing inside, they declare a stock issue
|
179
|
+
self._stock_issue(line_shelf2)
|
180
|
+
|
181
|
+
self.assertRecordValues(
|
182
|
+
self.moves,
|
183
|
+
[
|
184
|
+
# move 1 and 2 aren't touched: they are in another location
|
185
|
+
{"state": "assigned"},
|
186
|
+
{"state": "assigned"},
|
187
|
+
{"state": "partially_available"},
|
188
|
+
{"state": "confirmed"},
|
189
|
+
{"state": "confirmed"},
|
190
|
+
],
|
191
|
+
)
|
192
|
+
self.assertRecordValues(
|
193
|
+
# check that the other move line of the move was not altered
|
194
|
+
line_shelf1,
|
195
|
+
[
|
196
|
+
{
|
197
|
+
"location_id": self.shelf1.id,
|
198
|
+
"qty_done": 5.0,
|
199
|
+
"result_package_id": self.dest_package.id,
|
200
|
+
}
|
201
|
+
],
|
202
|
+
)
|
203
|
+
self.assertFalse(line_shelf2.exists())
|
204
|
+
# the quantity in shelf1 should be the original one since we didn't have
|
205
|
+
# a stock issue here
|
206
|
+
self.assert_location_qty_and_reserved(self.shelf1, 20)
|
207
|
+
# since we declared the stock issue without picking anything, its
|
208
|
+
# quantity should be zero
|
209
|
+
self.assert_location_qty_and_reserved(self.shelf2, 0)
|
210
|
+
self.assert_stock_issue_inventories(self.shelf2, self.move3.product_id, 0)
|
211
|
+
|
212
|
+
def test_stock_issue_lot(self):
|
213
|
+
lot_a = self.env["stock.lot"].create(
|
214
|
+
{"product_id": self.product_a.id, "company_id": self.env.company.id}
|
215
|
+
)
|
216
|
+
lot_b = self.env["stock.lot"].create(
|
217
|
+
{"product_id": self.product_a.id, "company_id": self.env.company.id}
|
218
|
+
)
|
219
|
+
self._update_qty_in_location(
|
220
|
+
self.shelf2,
|
221
|
+
self.product_a,
|
222
|
+
self.move1.product_uom_qty + self.move5.product_uom_qty,
|
223
|
+
lot=lot_a,
|
224
|
+
)
|
225
|
+
# ensure that move 1 and 5 take lot_a (10 + 7 units), so all of them
|
226
|
+
self.move1._action_assign()
|
227
|
+
self.move5._action_assign()
|
228
|
+
# add stock for the rest of the moves
|
229
|
+
self._update_qty_in_location(self.shelf2, self.product_a, 100, lot=lot_b)
|
230
|
+
# reserve the remaining moves
|
231
|
+
self._simulate_batch_selected(self.batch, fill_stock=False)
|
232
|
+
self.assertEqual(set(self.batch.picking_ids.mapped("state")), {"assigned"})
|
233
|
+
|
234
|
+
# the operator could pick the 3 first lines of the batch
|
235
|
+
# move move1 with lot a
|
236
|
+
self._set_dest_package_and_done(self.move1.move_line_ids, self.dest_package)
|
237
|
+
# move move2 with lot b
|
238
|
+
self._set_dest_package_and_done(self.move2.move_line_ids, self.dest_package)
|
239
|
+
|
240
|
+
# on the third move, the operator can't pick anymore in the location,
|
241
|
+
# because there is nothing inside, they declare a stock issue
|
242
|
+
self._stock_issue(
|
243
|
+
self.move3.move_line_ids, next_line_func=lambda: self.move5.move_line_ids
|
244
|
+
)
|
245
|
+
|
246
|
+
self.assertRecordValues(
|
247
|
+
self.moves,
|
248
|
+
[
|
249
|
+
# still reserved because using lot a
|
250
|
+
{"state": "assigned"},
|
251
|
+
# still reserved because qty_done > 0
|
252
|
+
{"state": "assigned"},
|
253
|
+
# unreserved by the stock issue
|
254
|
+
{"state": "confirmed"},
|
255
|
+
# collaterally unreserved by the stock issue (same lot as the
|
256
|
+
# stock issue)
|
257
|
+
{"state": "confirmed"},
|
258
|
+
# still reserved because using lot a
|
259
|
+
{"state": "assigned"},
|
260
|
+
],
|
261
|
+
)
|
262
|
+
# check the qty including lot a and lot b
|
263
|
+
total_reserved_qty = (
|
264
|
+
self.move1.product_uom_qty
|
265
|
+
+ self.move2.product_uom_qty
|
266
|
+
+ self.move5.product_uom_qty
|
267
|
+
)
|
268
|
+
self.assert_location_qty_and_reserved(self.shelf2, total_reserved_qty)
|
269
|
+
# this is the only product reserved for lot_b
|
270
|
+
expected_reserved_qty = self.move2.product_uom_qty
|
271
|
+
self.assert_location_qty_and_reserved(
|
272
|
+
self.shelf2, expected_reserved_qty, lot=lot_b
|
273
|
+
)
|
274
|
+
self.assert_stock_issue_inventories(
|
275
|
+
self.shelf2,
|
276
|
+
self.move3.product_id,
|
277
|
+
expected_reserved_qty,
|
278
|
+
lot=lot_b,
|
279
|
+
)
|
280
|
+
|
281
|
+
def test_stock_issue_reserve_elsewhere(self):
|
282
|
+
self._update_qty_in_location(self.shelf1, self.product_a, 100)
|
283
|
+
self._simulate_batch_selected(self.batch, fill_stock=False)
|
284
|
+
# now, everything is reserved in shelf1 as we had enough stock
|
285
|
+
self.assertEqual(set(self.batch.picking_ids.mapped("state")), {"assigned"})
|
286
|
+
|
287
|
+
# put stock in shelf2, so we can test the outcome: goods should be
|
288
|
+
# reserved in shelf2 after a stock issue
|
289
|
+
self._update_qty_in_location(self.shelf2, self.product_a, 100)
|
290
|
+
|
291
|
+
# the operator picks the first line
|
292
|
+
self._set_dest_package_and_done(self.move1.move_line_ids, self.dest_package)
|
293
|
+
|
294
|
+
# and has a stock issue on the second line
|
295
|
+
# because there is nothing inside, they declare a stock issue
|
296
|
+
self._stock_issue(
|
297
|
+
self.move2.move_line_ids, next_line_func=lambda: self.move2.move_line_ids
|
298
|
+
)
|
299
|
+
|
300
|
+
# the inventory should have been done for shelf1, and all the remaining
|
301
|
+
# moves after move1 (already picked) should have been reserved in
|
302
|
+
# shelf2
|
303
|
+
self.assertEqual(set(self.batch.picking_ids.mapped("state")), {"assigned"})
|
304
|
+
self.assertEqual(self.move1.move_line_ids.location_id, self.shelf1)
|
305
|
+
# all the following moves have been reserved in shelf2 as we still have
|
306
|
+
# stock there
|
307
|
+
self.assertEqual(
|
308
|
+
(self.move2 | self.move3 | self.move4 | self.move5).mapped(
|
309
|
+
"move_line_ids.location_id"
|
310
|
+
),
|
311
|
+
self.shelf2,
|
312
|
+
)
|
313
|
+
|
314
|
+
def test_stock_issue_similar_move_with_picked_line(self):
|
315
|
+
"""Stock issue on the remaining of a line on partial move
|
316
|
+
|
317
|
+
We have a move with 10 units.
|
318
|
+
2 are reserved in a package. The remaining in another package.
|
319
|
+
We pick 1 of the first package and put it in a bin.
|
320
|
+
A new move line of 1 is created to pick in the first package: we
|
321
|
+
declare a stock out on it.
|
322
|
+
The first move line must be untouched, the second line for the remaining
|
323
|
+
should pick one more item in the other package.
|
324
|
+
"""
|
325
|
+
package1 = self.env["stock.quant.package"].create({"name": "PACKAGE_1"})
|
326
|
+
package2 = self.env["stock.quant.package"].create({"name": "PACKAGE_2"})
|
327
|
+
self._update_qty_in_location(self.shelf1, self.product_a, 2, package=package1)
|
328
|
+
self._update_qty_in_location(self.shelf1, self.product_a, 200, package=package2)
|
329
|
+
self.move1._action_assign()
|
330
|
+
self.move2._action_assign()
|
331
|
+
self.move3._action_assign()
|
332
|
+
self._simulate_batch_selected(self.batch, fill_stock=False)
|
333
|
+
self.assertEqual(set(self.batch.picking_ids.mapped("state")), {"assigned"})
|
334
|
+
|
335
|
+
pick_line1, pick_line2 = self.move1.move_line_ids
|
336
|
+
new_line, __ = pick_line1._split_qty_to_be_done(1)
|
337
|
+
self._set_dest_package_and_done(pick_line1, self.dest_package)
|
338
|
+
|
339
|
+
self.assertEqual(pick_line1.reserved_qty, 1.0)
|
340
|
+
self.assertEqual(new_line.reserved_qty, 1.0)
|
341
|
+
self.assertEqual(pick_line2.reserved_qty, 8.0)
|
342
|
+
# on the third move, the operator can't pick anymore in shelf1
|
343
|
+
# because there is nothing inside, they declare a stock issue
|
344
|
+
self._stock_issue(new_line, next_line_func=lambda: pick_line2)
|
345
|
+
|
346
|
+
self.assertRecordValues(
|
347
|
+
# check that the first move line of the move was not altered
|
348
|
+
pick_line1,
|
349
|
+
[
|
350
|
+
{
|
351
|
+
"location_id": self.shelf1.id,
|
352
|
+
"qty_done": 1.0,
|
353
|
+
"result_package_id": self.dest_package.id,
|
354
|
+
}
|
355
|
+
],
|
356
|
+
)
|
357
|
+
# the line on which we declared stock out does not exists
|
358
|
+
self.assertFalse(new_line.exists())
|
359
|
+
# the second line to pick has been raised to 9 instead of 8
|
360
|
+
# initially, to compensate the stock out
|
361
|
+
self.assertEqual(pick_line2.reserved_qty, 9.0)
|
362
|
+
|
363
|
+
# quant with stock out has been updated
|
364
|
+
self.assertEqual(package1.quant_ids.quantity, 1.0)
|