Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions deltatech_picking_transit/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,30 @@ Features:
.. contents::
:local:

Changelog
=========

19.0.0.0.10
-----------

- Port from 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.
- Also carries the ``sudo`` fix for creating the second transfer (ticket
8970).

Bug Tracker
===========

Expand Down
1 change: 1 addition & 0 deletions deltatech_picking_transit/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from . import models
from . import wizard
from .hooks import post_init_hook
3 changes: 2 additions & 1 deletion deltatech_picking_transit/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"images": ["static/description/main_screenshot.png"],
"name": "Stock Auto Transfer",
"version": "19.0.0.0.9",
"version": "19.0.0.0.10",
"author": "Terrabit, Voicu Stefan",
"website": "https://www.terrabit.ro",
"category": "Warehouse",
Expand All @@ -15,5 +15,6 @@
"views/stock_picking_type_view.xml",
],
"development_status": "Beta",
"post_init_hook": "post_init_hook",
"maintainers": ["VoicuStefan2001"],
}
79 changes: 79 additions & 0 deletions deltatech_picking_transit/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# hooks.py


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": env._("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": env._("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": env._("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,
)
)
108 changes: 75 additions & 33 deletions deltatech_picking_transit/models/stock_picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,24 @@ class StockPicking(models.Model):
sub_location_existent = fields.Boolean(default=False, compute="_compute_sub_location_existent")
second_transfer_created = fields.Boolean(default=False)
source_transfer_id = fields.Many2one("stock.picking")
destionation_transfer_id = fields.Many2one("stock.picking")
create_second_transfer_automatically = fields.Boolean(
string="Create Second Transfer Automatically",
related="picking_type_id.auto_second_transfer",
store=True,
)

def open_transfer_wizard(self):
self.ensure_one()
if self.second_transfer_created:
raise UserError(self.env._("Second transfer already created."))
if self.state != "done":
raise UserError(
self.env._(
"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",
Expand All @@ -30,24 +39,29 @@ def open_transfer_wizard(self):
}

def create_second_transfer_wizard(self, final_dest_location_id, picking_type_id):
# the operator validating the first transfer may not have access rights
# on the operation type / locations of the receiving warehouse
picking_type_id = picking_type_id.sudo()
final_dest_location_id = final_dest_location_id.sudo()
for picking in self:
if picking.picking_type_id.code == "internal":
new_picking_vals = {
"picking_type_id": picking_type_id.id,
"location_id": picking.location_dest_id.id,
"location_dest_id": final_dest_location_id.id,
"move_ids_without_package": [],
"move_ids": [],
}
new_picking = self.env["stock.picking"].create(new_picking_vals)
new_picking = self.env["stock.picking"].sudo().create(new_picking_vals)
self.copy_move_lines(picking, new_picking)
new_picking.action_confirm()
# new_picking.action_assign()
# new_picking.do_unreserve()
self.second_transfer_created = True
picking.second_transfer_created = True

message = self.env._("This transfer was generated from %s.") % picking.name
new_picking.message_post(body=message)
new_picking.source_transfer_id = picking.id
picking.destionation_transfer_id = new_picking.id
message = self.env._("Transfer %s was generated.") % new_picking.name

picking.message_post(body=message)
Expand All @@ -56,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.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
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):
Expand All @@ -74,14 +95,15 @@ 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 = (
self.env["ir.config_parameter"]
.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
Expand All @@ -98,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:
Expand All @@ -127,15 +152,21 @@ def button_validate(self):
"You must set a partner before validating the picking when you are using 2 step picking with auto create on the second transfer."
)
)
warehouse = self.env["stock.warehouse"].search([("partner_id", "=", picking.partner_id.id)], limit=1)
warehouse = (
self.env["stock.warehouse"].sudo().search([("partner_id", "=", picking.partner_id.id)], limit=1)
)
if warehouse:
next_operation = self.env["stock.picking.type"].search(
[
("warehouse_id", "=", warehouse.id),
("code", "=", "internal"),
("two_step_transfer_use", "=", "reception"),
],
limit=1,
next_operation = (
self.env["stock.picking.type"]
.sudo()
.search(
[
("warehouse_id", "=", warehouse.id),
("code", "=", "internal"),
("two_step_transfer_use", "=", "reception"),
],
limit=1,
)
)
if next_operation:
picking.create_second_transfer_wizard(next_operation.default_location_dest_id, next_operation)
Expand All @@ -144,10 +175,21 @@ def button_validate(self):
else:
raise UserError(self.env._("No warehouse found for partner %s") % picking.partner_id.name)
if picking.source_transfer_id:
for move in picking.move_ids_without_package:
other_moves = picking.source_transfer_id.move_ids_without_package.filtered(
for move in picking.move_ids:
other_moves = picking.source_transfer_id.move_ids.filtered(
lambda x: x.product_id == move.product_id
)
if not other_moves:
possible_picking = self.env["stock.picking"]
picking_now = picking.source_transfer_id
while picking_now.backorder_ids:
picking_now = picking_now.backorder_ids[0]
possible_picking |= picking_now
if possible_picking:
for backorder in possible_picking:
other_moves = backorder.move_ids.filtered(lambda x: x.product_id == move.product_id)
if other_moves:
break
if not other_moves:
raise UserError(
self.env._(
Expand Down
17 changes: 17 additions & 0 deletions deltatech_picking_transit/readme/HISTORY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## 19.0.0.0.10

- Port from 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.
- Also carries the `sudo` fix for creating the second transfer (ticket 8970).
Loading
Loading