diff --git a/deltatech_picking_transit/README.rst b/deltatech_picking_transit/README.rst
index be330e2b5..1d4e86a6a 100644
--- a/deltatech_picking_transit/README.rst
+++ b/deltatech_picking_transit/README.rst
@@ -49,6 +49,27 @@ Features:
.. contents::
:local:
+Changelog
+=========
+
+18.0.0.0.15
+-----------
+
+- Setup on install: create a per-company transit stock location and, on
+ each warehouse, a two-step delivery operation type (with automatic
+ second transfer) and a two-step reception operation type, wired to the
+ warehouse main stock location and the transit location. Runs only at
+ install, so databases already using the module are not affected.
+- The second transfer can now be created manually only after the first
+ transfer is validated, so the goods are actually in the transit
+ location.
+- The second transfer now inherits the quantity actually moved to
+ transit, so the flow works even when the operator filled only the
+ "Quantity" field and left the "Demand" at 0.
+- Fixed the ``is_transit_transfer`` and ``sub_location_existent``
+ computes to work on multi-record sets (no more singleton errors) and
+ removed the side effect from the compute method.
+
Bug Tracker
===========
diff --git a/deltatech_picking_transit/__init__.py b/deltatech_picking_transit/__init__.py
index 9b4296142..700d6ab98 100644
--- a/deltatech_picking_transit/__init__.py
+++ b/deltatech_picking_transit/__init__.py
@@ -1,2 +1,3 @@
from . import models
from . import wizard
+from .hooks import post_init_hook
diff --git a/deltatech_picking_transit/__manifest__.py b/deltatech_picking_transit/__manifest__.py
index a85abb84c..4429b266f 100644
--- a/deltatech_picking_transit/__manifest__.py
+++ b/deltatech_picking_transit/__manifest__.py
@@ -1,6 +1,6 @@
{
"name": "Stock Auto Transfer",
- "version": "18.0.0.0.14",
+ "version": "18.0.0.0.15",
"author": "Terrabit, Voicu Stefan",
"website": "https://www.terrabit.ro",
"category": "Warehouse",
@@ -14,6 +14,7 @@
"views/stock_picking_type_view.xml",
],
"development_status": "Beta",
+ "post_init_hook": "post_init_hook",
"maintainers": ["VoicuStefan2001"],
"images": ["static/description/main_screenshot.png"],
}
diff --git a/deltatech_picking_transit/hooks.py b/deltatech_picking_transit/hooks.py
new file mode 100644
index 000000000..8bf196e9e
--- /dev/null
+++ b/deltatech_picking_transit/hooks.py
@@ -0,0 +1,81 @@
+# hooks.py
+
+from odoo import _
+
+
+def post_init_hook(env):
+ """Set up the two-step transit configuration on a fresh install.
+
+ Runs only at install time (never on module update), so databases that
+ already use this module are left untouched. For every company it creates a
+ dedicated transit stock location and, on each of the company warehouses, a
+ two-step delivery operation type (with automatic second transfer) and a
+ two-step reception operation type, wired to the warehouse main stock
+ location and the transit location.
+ """
+ for company in env["res.company"].search([]):
+ _setup_company_two_step_transit(env, company)
+
+
+def _setup_company_two_step_transit(env, company):
+ Location = env["stock.location"].with_company(company)
+ PickingType = env["stock.picking.type"].with_company(company)
+
+ parent_location = env.ref("stock.stock_location_locations", raise_if_not_found=False)
+ transit_location = Location.create(
+ {
+ "name": _("2-Step Transit"),
+ "usage": "transit",
+ "location_id": parent_location.id if parent_location else False,
+ "company_id": company.id,
+ }
+ )
+
+ warehouses = env["stock.warehouse"].search([("company_id", "=", company.id)])
+ for warehouse in warehouses:
+ stock_location = warehouse.lot_stock_id
+
+ # Two-step delivery: warehouse stock -> transit, with automatic
+ # creation of the second (reception) transfer on validation.
+ if not _has_two_step_type(PickingType, warehouse, "delivery"):
+ PickingType.create(
+ {
+ "name": _("2-Step Delivery"),
+ "code": "internal",
+ "sequence_code": "2SD",
+ "warehouse_id": warehouse.id,
+ "company_id": company.id,
+ "default_location_src_id": stock_location.id,
+ "default_location_dest_id": transit_location.id,
+ "two_step_transfer_use": "delivery",
+ "auto_second_transfer": True,
+ }
+ )
+
+ # Two-step reception: transit -> warehouse stock.
+ if not _has_two_step_type(PickingType, warehouse, "reception"):
+ PickingType.create(
+ {
+ "name": _("2-Step Reception"),
+ "code": "internal",
+ "sequence_code": "2SR",
+ "warehouse_id": warehouse.id,
+ "company_id": company.id,
+ "default_location_src_id": transit_location.id,
+ "default_location_dest_id": stock_location.id,
+ "two_step_transfer_use": "reception",
+ }
+ )
+
+
+def _has_two_step_type(PickingType, warehouse, use):
+ return bool(
+ PickingType.search(
+ [
+ ("warehouse_id", "=", warehouse.id),
+ ("code", "=", "internal"),
+ ("two_step_transfer_use", "=", use),
+ ],
+ limit=1,
+ )
+ )
diff --git a/deltatech_picking_transit/models/stock_picking.py b/deltatech_picking_transit/models/stock_picking.py
index fcc258d44..3c11d526e 100644
--- a/deltatech_picking_transit/models/stock_picking.py
+++ b/deltatech_picking_transit/models/stock_picking.py
@@ -19,8 +19,16 @@ class StockPicking(models.Model):
)
def open_transfer_wizard(self):
+ self.ensure_one()
if self.second_transfer_created:
raise UserError(_("Second transfer already created."))
+ if self.state != "done":
+ raise UserError(
+ _(
+ "Validate this transfer first. The second transfer can only be "
+ "created after the goods have arrived in the transit location."
+ )
+ )
return {
"name": "Create Transfer",
"type": "ir.actions.act_window",
@@ -48,7 +56,7 @@ def create_second_transfer_wizard(self, final_dest_location_id, picking_type_id)
new_picking.action_confirm()
# new_picking.action_assign()
# new_picking.do_unreserve()
- self.second_transfer_created = True
+ picking.second_transfer_created = True
message = _("This transfer was generated from %s.") % picking.name
new_picking.message_post(body=message)
@@ -62,15 +70,22 @@ def create_second_transfer_wizard(self, final_dest_location_id, picking_type_id)
return new_picking
def copy_move_lines(self, source_picking, target_picking):
- for move in source_picking.move_ids_without_package:
- move.sudo().copy(
- {
- "picking_id": target_picking.id,
- "location_id": source_picking.location_dest_id.id,
- "location_dest_id": target_picking.location_dest_id.id,
- "state": "draft",
- }
- )
+ moves = source_picking.move_ids_without_package
+ if not moves:
+ return
+ default = {
+ "picking_id": target_picking.id,
+ "location_id": source_picking.location_dest_id.id,
+ "location_dest_id": target_picking.location_dest_id.id,
+ "state": "draft",
+ }
+ vals_list = moves.sudo().copy_data(default)
+ for move, vals in zip(moves, vals_list):
+ # the second transfer must move what actually arrived in transit:
+ # use the done quantity so the flow still works when the operator
+ # filled only the "Quantity" field and left the "Demand" at 0
+ vals["product_uom_qty"] = move.quantity or move.product_uom_qty
+ self.env["stock.move"].sudo().create(vals_list)
# @api.model
# def create(self, vals):
@@ -80,6 +95,7 @@ def copy_move_lines(self, source_picking, target_picking):
# # res.immediate_transfer = False
# return res
+ @api.depends("picking_type_id")
def _compute_sub_location_existent(self):
for record in self:
sub_location_usage = (
@@ -87,7 +103,7 @@ def _compute_sub_location_existent(self):
.sudo()
.get_param(key="deltatech_picking_transit.use_sub_locations", default=False)
)
- if sub_location_usage and self.picking_type_id.code == "internal":
+ if sub_location_usage and record.picking_type_id.code == "internal":
record.sub_location_existent = True
else:
record.sub_location_existent = False
@@ -104,18 +120,21 @@ def reassign_location(self):
if quants:
move_line.location_id = quants[0].location_id
- @api.onchange("picking_type_id")
+ @api.depends("picking_type_id", "second_transfer_created")
def _compute_is_transit_transfer(self):
for record in self:
- if self.second_transfer_created:
- record.is_transit_transfer = False
- return
- if record.picking_type_id.code == "internal" and record.picking_type_id.two_step_transfer_use == "delivery":
- record.is_transit_transfer = True
- record.action_toggle_is_locked()
- # record.immediate_transfer = False
- else:
- record.is_transit_transfer = False
+ record.is_transit_transfer = bool(
+ not record.second_transfer_created
+ and record.picking_type_id.code == "internal"
+ and record.picking_type_id.two_step_transfer_use == "delivery"
+ )
+
+ @api.onchange("picking_type_id")
+ def _onchange_picking_type_lock_transit(self):
+ # lock the transit transfer so the move lines cannot be edited before
+ # the second transfer is generated
+ if self.is_transit_transfer:
+ self.action_toggle_is_locked()
def button_validate(self):
for picking in self:
diff --git a/deltatech_picking_transit/readme/HISTORY.md b/deltatech_picking_transit/readme/HISTORY.md
new file mode 100644
index 000000000..bf32628c9
--- /dev/null
+++ b/deltatech_picking_transit/readme/HISTORY.md
@@ -0,0 +1,15 @@
+## 18.0.0.0.15
+
+- Setup on install: create a per-company transit stock location and, on each
+ warehouse, a two-step delivery operation type (with automatic second transfer)
+ and a two-step reception operation type, wired to the warehouse main stock
+ location and the transit location. Runs only at install, so databases already
+ using the module are not affected.
+- The second transfer can now be created manually only after the first transfer
+ is validated, so the goods are actually in the transit location.
+- The second transfer now inherits the quantity actually moved to transit, so
+ the flow works even when the operator filled only the "Quantity" field and
+ left the "Demand" at 0.
+- Fixed the `is_transit_transfer` and `sub_location_existent` computes to work
+ on multi-record sets (no more singleton errors) and removed the side effect
+ from the compute method.
diff --git a/deltatech_picking_transit/static/description/index.html b/deltatech_picking_transit/static/description/index.html
index 521a41f1a..40aae76f4 100644
--- a/deltatech_picking_transit/static/description/index.html
+++ b/deltatech_picking_transit/static/description/index.html
@@ -396,31 +396,57 @@
Stock Auto Transfer
Table of contents
+
+
+
+
+
+- Setup on install: create a per-company transit stock location and, on
+each warehouse, a two-step delivery operation type (with automatic
+second transfer) and a two-step reception operation type, wired to the
+warehouse main stock location and the transit location. Runs only at
+install, so databases already using the module are not affected.
+- The second transfer can now be created manually only after the first
+transfer is validated, so the goods are actually in the transit
+location.
+- The second transfer now inherits the quantity actually moved to
+transit, so the flow works even when the operator filled only the
+“Quantity” field and left the “Demand” at 0.
+- Fixed the is_transit_transfer and sub_location_existent
+computes to work on multi-record sets (no more singleton errors) and
+removed the side effect from the compute method.
+
+
-
+
Bugs are tracked on Terrabit Issues.
In case of trouble, please check there if your issue has already been reported.
Do not contact contributors directly about support or help with technical issues.
-
+
-
+
Current maintainer:

This module is part of the dhongu/deltatech project on GitHub.
diff --git a/deltatech_picking_transit/views/stock_picking_views.xml b/deltatech_picking_transit/views/stock_picking_views.xml
index b167b4f04..eba4fe970 100644
--- a/deltatech_picking_transit/views/stock_picking_views.xml
+++ b/deltatech_picking_transit/views/stock_picking_views.xml
@@ -21,7 +21,7 @@
type="object"
string="Create Transfer"
class="btn-primary"
- invisible="is_transit_transfer==False or origin!=False or create_second_transfer_automatically==True"
+ invisible="is_transit_transfer==False or origin!=False or create_second_transfer_automatically==True or state!='done'"
/>