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,12 @@
|
|
1
|
+
from . import priority_postpone_mixin
|
2
|
+
from . import shopfloor_menu
|
3
|
+
from . import shopfloor_app
|
4
|
+
from . import stock_picking_type
|
5
|
+
from . import stock_location
|
6
|
+
from . import stock_move
|
7
|
+
from . import stock_move_line
|
8
|
+
from . import stock_package_level
|
9
|
+
from . import stock_picking
|
10
|
+
from . import stock_picking_batch
|
11
|
+
from . import stock_quant
|
12
|
+
from . import stock_quant_package
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
from odoo import fields, models
|
4
|
+
|
5
|
+
|
6
|
+
class PriorityPostponeMixin(models.AbstractModel):
|
7
|
+
_name = "shopfloor.priority.postpone.mixin"
|
8
|
+
_description = "Adds shopfloor priority/postpone fields"
|
9
|
+
|
10
|
+
_SF_PRIORITY_DEFAULT = 10
|
11
|
+
|
12
|
+
shopfloor_priority = fields.Integer(
|
13
|
+
default=lambda self: self._SF_PRIORITY_DEFAULT,
|
14
|
+
copy=False,
|
15
|
+
help="Technical field. Overrides operation priority in barcode scenario.",
|
16
|
+
)
|
17
|
+
shopfloor_postponed = fields.Boolean(
|
18
|
+
copy=False,
|
19
|
+
help="Technical field. "
|
20
|
+
"Indicates if the operation has been postponed in a barcode scenario.",
|
21
|
+
)
|
22
|
+
|
23
|
+
def _get_max_shopfloor_priority(self, records):
|
24
|
+
self.ensure_one()
|
25
|
+
if not records:
|
26
|
+
return 0
|
27
|
+
return max(rec.shopfloor_priority for rec in records)
|
28
|
+
|
29
|
+
def shopfloor_postpone(self, *recordsets):
|
30
|
+
"""Postpone the record and update its priority based on other records.
|
31
|
+
|
32
|
+
The method accepts several recordsets as parameter (to be able to get
|
33
|
+
the current max priority from different types of records).
|
34
|
+
"""
|
35
|
+
self.ensure_one()
|
36
|
+
# Set the max priority from sibling records + 1
|
37
|
+
max_priority = max(
|
38
|
+
self._get_max_shopfloor_priority(records) for records in recordsets
|
39
|
+
)
|
40
|
+
self.shopfloor_priority = max_priority + 1
|
41
|
+
self.shopfloor_postponed = True
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Copyright 2021 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
from odoo import fields, models
|
4
|
+
|
5
|
+
|
6
|
+
class ShopfloorApp(models.Model):
|
7
|
+
_inherit = "shopfloor.app"
|
8
|
+
|
9
|
+
category = fields.Selection(selection_add=[("wms", "WMS")])
|
@@ -0,0 +1,436 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
from odoo import _, api, exceptions, fields, models
|
4
|
+
|
5
|
+
PICK_PACK_SAME_TIME_HELP = """
|
6
|
+
If you tick this box, while picking goods from a location
|
7
|
+
(eg: zone picking) set destination will work as follow:
|
8
|
+
|
9
|
+
* if a location is scanned, a new delivery package is created;
|
10
|
+
* if a package is scanned, the package is validated against the carrier
|
11
|
+
* in both cases, if the picking has no carrier the operation fails.",
|
12
|
+
"""
|
13
|
+
|
14
|
+
UNLOAD_PACK_AT_DEST_HELP = """
|
15
|
+
With this option, the lines you process by putting on a package during the
|
16
|
+
picking process will be put as bulk products at the final destination location.
|
17
|
+
|
18
|
+
This is useful if your picking device is emptied at the destination location or
|
19
|
+
if you want to provide bulk products to the next operation.
|
20
|
+
|
21
|
+
Incompatible with: "Pick and pack at the same time"
|
22
|
+
"""
|
23
|
+
|
24
|
+
MULTIPLE_MOVE_SINGLE_PACK_HELP = """
|
25
|
+
When picking a move,
|
26
|
+
allow to set a destination package that was already used for the other lines.
|
27
|
+
"""
|
28
|
+
|
29
|
+
NO_PREFILL_QTY_HELP = """
|
30
|
+
We assume the picker will take the suggested quantities.
|
31
|
+
With this option, the operator will have to enter the quantity manually or
|
32
|
+
by scanning a product or product packaging EAN to increase the quantity
|
33
|
+
(i.e. +1 Unit or +1 Box)
|
34
|
+
"""
|
35
|
+
|
36
|
+
AUTO_POST_LINE = """
|
37
|
+
When setting result pack & destination,
|
38
|
+
automatically post the corresponding line
|
39
|
+
if this option is checked.
|
40
|
+
"""
|
41
|
+
|
42
|
+
RETURN_HELP = """
|
43
|
+
When enabled, you can receive unplanned products that are returned
|
44
|
+
from an existing delivery matched on the origin (SO name).
|
45
|
+
A new move will be added as a return of the delivery,
|
46
|
+
decreasing the delivered quantity of the related SO line.
|
47
|
+
"""
|
48
|
+
|
49
|
+
|
50
|
+
class ShopfloorMenu(models.Model):
|
51
|
+
_inherit = "shopfloor.menu"
|
52
|
+
|
53
|
+
picking_type_ids = fields.Many2many(
|
54
|
+
comodel_name="stock.picking.type", string="Operation Types", required=True
|
55
|
+
)
|
56
|
+
move_create_is_possible = fields.Boolean(compute="_compute_move_create_is_possible")
|
57
|
+
# only available for some scenarios, move_create_is_possible defines if the option
|
58
|
+
# can be used or not
|
59
|
+
allow_move_create = fields.Boolean(
|
60
|
+
string="Allow Move Creation",
|
61
|
+
default=False,
|
62
|
+
help="Some scenario may create move(s) when a product or package is"
|
63
|
+
" scanned and no move already exists. Any new move is created in the"
|
64
|
+
" selected operation type, so it can be active only when one type is selected.",
|
65
|
+
)
|
66
|
+
unreserve_other_moves_is_possible = fields.Boolean(
|
67
|
+
compute="_compute_unreserve_other_moves_is_possible"
|
68
|
+
)
|
69
|
+
allow_unreserve_other_moves = fields.Boolean(
|
70
|
+
string="Allow to process reserved quantities",
|
71
|
+
default=False,
|
72
|
+
help="If you tick this box, this scenario will allow operator to move"
|
73
|
+
" goods even if a reservation is made by a different operation type.",
|
74
|
+
)
|
75
|
+
ignore_no_putaway_available_is_possible = fields.Boolean(
|
76
|
+
compute="_compute_ignore_no_putaway_available_is_possible"
|
77
|
+
)
|
78
|
+
ignore_no_putaway_available = fields.Boolean(
|
79
|
+
string="Ignore transfers when no put-away is available",
|
80
|
+
default=False,
|
81
|
+
help="If you tick this box, the transfer is reserved only "
|
82
|
+
"if the put-away can find a sublocation (when putaway destination "
|
83
|
+
"is different from the operation type's destination).",
|
84
|
+
)
|
85
|
+
prepackaged_product_is_possible = fields.Boolean(
|
86
|
+
compute="_compute_prepackaged_product_is_possible"
|
87
|
+
)
|
88
|
+
allow_prepackaged_product = fields.Boolean(
|
89
|
+
string="Process as pre-packaged",
|
90
|
+
default=False,
|
91
|
+
help=(
|
92
|
+
"When active, what you scan (typically a product packaging EAN) "
|
93
|
+
"will be ship 'as-is' and the operation will be validated "
|
94
|
+
"triggering a backorder creation with the remaining lines."
|
95
|
+
),
|
96
|
+
)
|
97
|
+
# TODO: refactor handling of these options.
|
98
|
+
# Possible solution:
|
99
|
+
# * field should stay on the scenario and get stored in options
|
100
|
+
# * field should use `sf_scenario` (eg: sf_scenario=("zone_picking", ))
|
101
|
+
# to control for which scenario it will be available
|
102
|
+
# * on the menu form, display a button to edit configurations
|
103
|
+
# and display a summary
|
104
|
+
pick_pack_same_time = fields.Boolean(
|
105
|
+
string="Pick and pack at the same time",
|
106
|
+
default=False,
|
107
|
+
help=PICK_PACK_SAME_TIME_HELP,
|
108
|
+
)
|
109
|
+
pick_pack_same_time_is_possible = fields.Boolean(
|
110
|
+
compute="_compute_pick_pack_same_time_is_possible"
|
111
|
+
)
|
112
|
+
multiple_move_single_pack_is_possible = fields.Boolean(
|
113
|
+
compute="_compute_multiple_move_single_pack_is_possible"
|
114
|
+
)
|
115
|
+
multiple_move_single_pack = fields.Boolean(
|
116
|
+
string="Collect multiple moves on a same destination package",
|
117
|
+
default=False,
|
118
|
+
help=MULTIPLE_MOVE_SINGLE_PACK_HELP,
|
119
|
+
)
|
120
|
+
unload_package_at_destination_is_possible = fields.Boolean(
|
121
|
+
compute="_compute_unload_package_at_dest_is_possible"
|
122
|
+
)
|
123
|
+
unload_package_at_destination = fields.Boolean(
|
124
|
+
string="Unload package at destination",
|
125
|
+
default=False,
|
126
|
+
help=UNLOAD_PACK_AT_DEST_HELP,
|
127
|
+
)
|
128
|
+
|
129
|
+
disable_full_bin_action_is_possible = fields.Boolean(
|
130
|
+
compute="_compute_disable_full_bin_action_is_possible"
|
131
|
+
)
|
132
|
+
disable_full_bin_action = fields.Boolean(
|
133
|
+
string="Disable full bin action",
|
134
|
+
default=False,
|
135
|
+
# TODO: improve this desc w/ usecases.
|
136
|
+
help=("When picking, prevent unloading the whole bin when full."),
|
137
|
+
)
|
138
|
+
|
139
|
+
allow_force_reservation = fields.Boolean(
|
140
|
+
string="Force stock reservation",
|
141
|
+
default=False,
|
142
|
+
)
|
143
|
+
allow_force_reservation_is_possible = fields.Boolean(
|
144
|
+
compute="_compute_allow_force_reservation_is_possible"
|
145
|
+
)
|
146
|
+
|
147
|
+
allow_get_work = fields.Boolean(
|
148
|
+
string="Show Get Work on start",
|
149
|
+
default=False,
|
150
|
+
help=(
|
151
|
+
"When enabled the user will have the option to ask "
|
152
|
+
"for a task to work on."
|
153
|
+
),
|
154
|
+
)
|
155
|
+
allow_get_work_is_possible = fields.Boolean(
|
156
|
+
compute="_compute_allow_get_work_is_possible"
|
157
|
+
)
|
158
|
+
no_prefill_qty = fields.Boolean(
|
159
|
+
string="Do not pre-fill quantity to pick",
|
160
|
+
help=NO_PREFILL_QTY_HELP,
|
161
|
+
default=False,
|
162
|
+
)
|
163
|
+
no_prefill_qty_is_possible = fields.Boolean(
|
164
|
+
compute="_compute_no_prefill_qty_is_possible"
|
165
|
+
)
|
166
|
+
show_oneline_package_content = fields.Boolean(
|
167
|
+
string="Show one-line package content",
|
168
|
+
help="Display the content of package if it contains 1 line only",
|
169
|
+
default=False,
|
170
|
+
)
|
171
|
+
show_oneline_package_content_is_possible = fields.Boolean(
|
172
|
+
compute="_compute_show_oneline_package_content_is_possible"
|
173
|
+
)
|
174
|
+
scan_location_or_pack_first = fields.Boolean(
|
175
|
+
string="Scan first location or pack",
|
176
|
+
help=(
|
177
|
+
"When selecting work, force the user to first scan a location or pack,"
|
178
|
+
"then the product or lot."
|
179
|
+
),
|
180
|
+
)
|
181
|
+
scan_location_or_pack_first_is_possible = fields.Boolean(
|
182
|
+
compute="_compute_scan_location_or_pack_first_is_possible"
|
183
|
+
)
|
184
|
+
allow_alternative_destination = fields.Boolean(
|
185
|
+
string="Allow to scan alternative destination locations",
|
186
|
+
help=(
|
187
|
+
"When enabled the user will have the option to scan "
|
188
|
+
"destination locations other than the expected ones "
|
189
|
+
"(ask for confirmation)."
|
190
|
+
),
|
191
|
+
default=False,
|
192
|
+
)
|
193
|
+
allow_alternative_destination_is_possible = fields.Boolean(
|
194
|
+
compute="_compute_allow_alternative_destination_is_possible"
|
195
|
+
)
|
196
|
+
allow_return_is_possible = fields.Boolean(
|
197
|
+
compute="_compute_allow_return_is_possible"
|
198
|
+
)
|
199
|
+
allow_return = fields.Boolean(
|
200
|
+
string="Allow create returns",
|
201
|
+
default=False,
|
202
|
+
help=RETURN_HELP,
|
203
|
+
)
|
204
|
+
|
205
|
+
auto_post_line = fields.Boolean(
|
206
|
+
string="Automatically post line",
|
207
|
+
default=False,
|
208
|
+
help=AUTO_POST_LINE,
|
209
|
+
)
|
210
|
+
auto_post_line_is_possible = fields.Boolean(
|
211
|
+
compute="_compute_auto_post_line_is_possible"
|
212
|
+
)
|
213
|
+
|
214
|
+
@api.onchange("unload_package_at_destination")
|
215
|
+
def _onchange_unload_package_at_destination(self):
|
216
|
+
# Uncheck pick_pack_same_time when unload_package_at_destination is set to True
|
217
|
+
for record in self:
|
218
|
+
if record.unload_package_at_destination:
|
219
|
+
record.pick_pack_same_time = False
|
220
|
+
|
221
|
+
@api.onchange("pick_pack_same_time")
|
222
|
+
def _onchange_pick_pack_same_time(self):
|
223
|
+
# pick_pack_same_time is incompatible with multiple_move_single_pack and
|
224
|
+
# multiple_move_single_pack
|
225
|
+
for record in self:
|
226
|
+
if record.pick_pack_same_time:
|
227
|
+
record.unload_package_at_destination = False
|
228
|
+
record.multiple_move_single_pack = False
|
229
|
+
|
230
|
+
@api.onchange("multiple_move_single_pack")
|
231
|
+
def _onchange_multiple_move_single_pack(self):
|
232
|
+
# multiple_move_single_pack is incompatible with pick_pack_same_time,
|
233
|
+
for record in self:
|
234
|
+
if record.multiple_move_single_pack:
|
235
|
+
record.pick_pack_same_time = False
|
236
|
+
|
237
|
+
@api.constrains(
|
238
|
+
"unload_package_at_destination",
|
239
|
+
"pick_pack_same_time",
|
240
|
+
"multiple_move_single_pack",
|
241
|
+
)
|
242
|
+
def _check_options(self):
|
243
|
+
if self.pick_pack_same_time and self.unload_package_at_destination:
|
244
|
+
raise exceptions.UserError(
|
245
|
+
_(
|
246
|
+
"'Pick and pack at the same time' is incompatible with "
|
247
|
+
"'Unload package at destination'."
|
248
|
+
)
|
249
|
+
)
|
250
|
+
elif self.pick_pack_same_time and self.multiple_move_single_pack:
|
251
|
+
raise exceptions.UserError(
|
252
|
+
_(
|
253
|
+
"'Pick and pack at the same time' is incompatible with "
|
254
|
+
"'Multiple moves same destination package'."
|
255
|
+
)
|
256
|
+
)
|
257
|
+
|
258
|
+
@api.depends("scenario_id", "picking_type_ids")
|
259
|
+
def _compute_move_create_is_possible(self):
|
260
|
+
for menu in self:
|
261
|
+
menu.move_create_is_possible = bool(
|
262
|
+
menu.scenario_id.has_option("allow_create_moves")
|
263
|
+
and len(menu.picking_type_ids) == 1
|
264
|
+
)
|
265
|
+
|
266
|
+
@api.onchange("move_create_is_possible")
|
267
|
+
def onchange_move_create_is_possible(self):
|
268
|
+
self.allow_move_create = self.move_create_is_possible
|
269
|
+
|
270
|
+
@api.constrains("scenario_id", "picking_type_ids", "allow_move_create")
|
271
|
+
def _check_allow_move_create(self):
|
272
|
+
for menu in self:
|
273
|
+
if menu.allow_move_create and not menu.move_create_is_possible:
|
274
|
+
raise exceptions.ValidationError(
|
275
|
+
_("Creation of moves is not allowed for menu {}.").format(menu.name)
|
276
|
+
)
|
277
|
+
|
278
|
+
@api.depends("scenario_id")
|
279
|
+
def _compute_unreserve_other_moves_is_possible(self):
|
280
|
+
for menu in self:
|
281
|
+
menu.unreserve_other_moves_is_possible = menu.scenario_id.has_option(
|
282
|
+
"allow_unreserve_other_moves"
|
283
|
+
)
|
284
|
+
|
285
|
+
@api.depends("scenario_id")
|
286
|
+
def _compute_pick_pack_same_time_is_possible(self):
|
287
|
+
for menu in self:
|
288
|
+
menu.pick_pack_same_time_is_possible = menu.scenario_id.has_option(
|
289
|
+
"pick_pack_same_time"
|
290
|
+
)
|
291
|
+
|
292
|
+
@api.depends("scenario_id")
|
293
|
+
def _compute_unload_package_at_dest_is_possible(self):
|
294
|
+
for menu in self:
|
295
|
+
menu.unload_package_at_destination_is_possible = (
|
296
|
+
menu.scenario_id.has_option("unload_package_at_destination")
|
297
|
+
)
|
298
|
+
|
299
|
+
@api.depends("scenario_id")
|
300
|
+
def _compute_multiple_move_single_pack_is_possible(self):
|
301
|
+
for menu in self:
|
302
|
+
menu.multiple_move_single_pack_is_possible = menu.scenario_id.has_option(
|
303
|
+
"multiple_move_single_pack"
|
304
|
+
)
|
305
|
+
|
306
|
+
@api.onchange("unreserve_other_moves_is_possible")
|
307
|
+
def onchange_unreserve_other_moves_is_possible(self):
|
308
|
+
self.allow_unreserve_other_moves = self.unreserve_other_moves_is_possible
|
309
|
+
|
310
|
+
@api.depends("scenario_id")
|
311
|
+
def _compute_disable_full_bin_action_is_possible(self):
|
312
|
+
for menu in self:
|
313
|
+
menu.disable_full_bin_action_is_possible = menu.scenario_id.has_option(
|
314
|
+
"disable_full_bin_action"
|
315
|
+
)
|
316
|
+
|
317
|
+
@api.depends("scenario_id")
|
318
|
+
def _compute_ignore_no_putaway_available_is_possible(self):
|
319
|
+
for menu in self:
|
320
|
+
menu.ignore_no_putaway_available_is_possible = menu.scenario_id.has_option(
|
321
|
+
"allow_ignore_no_putaway_available"
|
322
|
+
)
|
323
|
+
|
324
|
+
@api.onchange("ignore_no_putaway_available_is_possible")
|
325
|
+
def onchange_ignore_no_putaway_available_is_possible(self):
|
326
|
+
self.ignore_no_putaway_available = self.ignore_no_putaway_available_is_possible
|
327
|
+
|
328
|
+
@api.depends("scenario_id")
|
329
|
+
def _compute_prepackaged_product_is_possible(self):
|
330
|
+
for menu in self:
|
331
|
+
menu.prepackaged_product_is_possible = menu.scenario_id.has_option(
|
332
|
+
"allow_prepackaged_product"
|
333
|
+
)
|
334
|
+
|
335
|
+
@api.constrains("scenario_id", "picking_type_ids", "ignore_no_putaway_available")
|
336
|
+
def _check_ignore_no_putaway_available(self):
|
337
|
+
for menu in self:
|
338
|
+
if (
|
339
|
+
menu.ignore_no_putaway_available
|
340
|
+
and not menu.ignore_no_putaway_available_is_possible
|
341
|
+
):
|
342
|
+
raise exceptions.ValidationError(
|
343
|
+
_("Ignoring not found putaway is not allowed for menu {}.").format(
|
344
|
+
menu.name
|
345
|
+
)
|
346
|
+
)
|
347
|
+
|
348
|
+
@api.constrains("scenario_id", "picking_type_ids", "allow_unreserve_other_moves")
|
349
|
+
def _check_allow_unreserve_other_moves(self):
|
350
|
+
for menu in self:
|
351
|
+
if (
|
352
|
+
menu.allow_unreserve_other_moves
|
353
|
+
and not menu.unreserve_other_moves_is_possible
|
354
|
+
):
|
355
|
+
raise exceptions.ValidationError(
|
356
|
+
_(
|
357
|
+
"Processing reserved quantities is" " not allowed for menu {}."
|
358
|
+
).format(menu.name)
|
359
|
+
)
|
360
|
+
|
361
|
+
@api.constrains("scenario_id", "picking_type_ids")
|
362
|
+
def _check_move_entire_packages(self):
|
363
|
+
for menu in self:
|
364
|
+
# TODO: these kind of checks should be provided by the scenario itself.
|
365
|
+
bad_picking_types = [
|
366
|
+
x.name for x in menu.picking_type_ids if not x.show_entire_packs
|
367
|
+
]
|
368
|
+
if (
|
369
|
+
menu.scenario_id.has_option("must_move_entire_pack")
|
370
|
+
and bad_picking_types
|
371
|
+
):
|
372
|
+
scenario_name = menu.scenario_id.name
|
373
|
+
raise exceptions.ValidationError(
|
374
|
+
_(
|
375
|
+
"Scenario `%(scenario_name)s` require(s) "
|
376
|
+
"'Move Entire Packages' to be enabled.\n"
|
377
|
+
"These type(s) do not satisfy this constraint: "
|
378
|
+
"\n%(bad_picking_types)s.\n"
|
379
|
+
"Please, adjust your configuration.",
|
380
|
+
scenario_name=scenario_name,
|
381
|
+
bad_picking_types="\n- ".join(bad_picking_types),
|
382
|
+
)
|
383
|
+
)
|
384
|
+
|
385
|
+
@api.depends("scenario_id")
|
386
|
+
def _compute_allow_force_reservation_is_possible(self):
|
387
|
+
for menu in self:
|
388
|
+
menu.allow_force_reservation_is_possible = menu.scenario_id.has_option(
|
389
|
+
"allow_force_reservation"
|
390
|
+
)
|
391
|
+
|
392
|
+
@api.depends("scenario_id")
|
393
|
+
def _compute_allow_get_work_is_possible(self):
|
394
|
+
for menu in self:
|
395
|
+
menu.allow_get_work_is_possible = menu.scenario_id.has_option(
|
396
|
+
"allow_get_work"
|
397
|
+
)
|
398
|
+
|
399
|
+
@api.depends("scenario_id")
|
400
|
+
def _compute_no_prefill_qty_is_possible(self):
|
401
|
+
for menu in self:
|
402
|
+
menu.no_prefill_qty_is_possible = menu.scenario_id.has_option(
|
403
|
+
"no_prefill_qty"
|
404
|
+
)
|
405
|
+
|
406
|
+
@api.depends("scenario_id")
|
407
|
+
def _compute_show_oneline_package_content_is_possible(self):
|
408
|
+
for menu in self:
|
409
|
+
menu.show_oneline_package_content_is_possible = menu.scenario_id.has_option(
|
410
|
+
"show_oneline_package_content"
|
411
|
+
)
|
412
|
+
|
413
|
+
@api.depends("scenario_id")
|
414
|
+
def _compute_scan_location_or_pack_first_is_possible(self):
|
415
|
+
for menu in self:
|
416
|
+
menu.scan_location_or_pack_first_is_possible = menu.scenario_id.has_option(
|
417
|
+
"scan_location_or_pack_first"
|
418
|
+
)
|
419
|
+
|
420
|
+
@api.depends("scenario_id")
|
421
|
+
def _compute_auto_post_line_is_possible(self):
|
422
|
+
for menu in self:
|
423
|
+
menu.auto_post_line_is_possible = menu.scenario_id.has_option(
|
424
|
+
"auto_post_line"
|
425
|
+
)
|
426
|
+
|
427
|
+
def _compute_allow_alternative_destination_is_possible(self):
|
428
|
+
for menu in self:
|
429
|
+
menu.allow_alternative_destination_is_possible = (
|
430
|
+
menu.scenario_id.has_option("allow_alternative_destination")
|
431
|
+
)
|
432
|
+
|
433
|
+
@api.depends("scenario_id")
|
434
|
+
def _compute_allow_return_is_possible(self):
|
435
|
+
for menu in self:
|
436
|
+
menu.allow_return_is_possible = menu.scenario_id.has_option("allow_return")
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
3
|
+
from odoo import fields, models
|
4
|
+
from odoo.tools.float_utils import float_compare
|
5
|
+
|
6
|
+
|
7
|
+
class StockLocation(models.Model):
|
8
|
+
_inherit = "stock.location"
|
9
|
+
|
10
|
+
shopfloor_picking_sequence = fields.Char(
|
11
|
+
help="The picking done in Shopfloor scenarios will respect this order. "
|
12
|
+
"The sequence is a char so it can be composed of fields such as "
|
13
|
+
"'corridor-rack-side-level'. Pay attention to the padding "
|
14
|
+
"('09' is before '19', '9' is not). It is recommended to use an"
|
15
|
+
" Export then an Import to populate this field using a spreadsheet.",
|
16
|
+
)
|
17
|
+
source_move_line_ids = fields.One2many(
|
18
|
+
comodel_name="stock.move.line", inverse_name="location_id", readonly=True
|
19
|
+
)
|
20
|
+
reserved_move_line_ids = fields.One2many(
|
21
|
+
comodel_name="stock.move.line",
|
22
|
+
compute="_compute_reserved_move_lines",
|
23
|
+
)
|
24
|
+
|
25
|
+
def _get_reserved_move_lines(self):
|
26
|
+
return self.env["stock.move.line"].search(
|
27
|
+
[
|
28
|
+
("location_id", "child_of", self.id),
|
29
|
+
("reserved_uom_qty", ">", 0),
|
30
|
+
("state", "not in", ("done", "cancel")),
|
31
|
+
]
|
32
|
+
)
|
33
|
+
|
34
|
+
def _compute_reserved_move_lines(self):
|
35
|
+
for rec in self:
|
36
|
+
rec.update({"reserved_move_line_ids": rec._get_reserved_move_lines()})
|
37
|
+
|
38
|
+
def planned_qty_in_location_is_empty(self, move_lines=None):
|
39
|
+
"""Return if a location will be empty when move lines will be confirmed
|
40
|
+
|
41
|
+
Used for the "zero check". We need to know if a location is empty, but since
|
42
|
+
we set the move lines to "done" only at the end of the unload workflow, we
|
43
|
+
have to look at the qty_done of the move lines from this location.
|
44
|
+
|
45
|
+
With `move_lines` we can force the use of the given move lines for the check.
|
46
|
+
This allows to know that the location will be empty if we process only
|
47
|
+
these move lines.
|
48
|
+
"""
|
49
|
+
self.ensure_one()
|
50
|
+
quants = self.env["stock.quant"].search(
|
51
|
+
[("quantity", ">", 0), ("location_id", "=", self.id)]
|
52
|
+
)
|
53
|
+
remaining = sum(quants.mapped("quantity"))
|
54
|
+
move_line_qty_field = "qty_done"
|
55
|
+
if move_lines:
|
56
|
+
move_lines = move_lines.filtered(
|
57
|
+
lambda m: m.state not in ("cancel", "done")
|
58
|
+
)
|
59
|
+
move_line_qty_field = "reserved_uom_qty"
|
60
|
+
else:
|
61
|
+
move_lines = self.env["stock.move.line"].search(
|
62
|
+
[
|
63
|
+
("state", "not in", ("cancel", "done")),
|
64
|
+
("location_id", "=", self.id),
|
65
|
+
("qty_done", ">", 0),
|
66
|
+
]
|
67
|
+
)
|
68
|
+
planned = remaining - sum(move_lines.mapped(move_line_qty_field))
|
69
|
+
compare = float_compare(planned, 0, precision_rounding=0.01)
|
70
|
+
return compare <= 0
|
71
|
+
|
72
|
+
def should_bypass_reservation(self):
|
73
|
+
self.ensure_one()
|
74
|
+
if self.env.context.get("force_reservation"):
|
75
|
+
return False
|
76
|
+
return super().should_bypass_reservation()
|