In Odoo, there are several ways to manage internal transfers
between warehouses. The method you choose depends on the level of control and
validation you need.
1.
Immediate Transfer (Basic but Risky)
A simple way to transfer products between warehouses is by
creating an Internal Transfer, where the source location belongs to Warehouse
A and the destination location is Warehouse B.
Pros: Quick and straightforward.
Cons: Not realistic as it updates stock immediately, leading to
potential physical discrepancies.
2.
Transfer with Validation on Receipt
i. Using an In-Transit Location
To introduce validation, you can
use an in-transit location:
- Create an Internal Transfer from Warehouse
A to In-Transit.
- Then, create another transfer from In-Transit
to Warehouse B.
Pros: Allows step-by-step
tracking.
Cons: Does not automatically create a receipt order for Warehouse B or
include delivery/shipping details.
Often, businesses require Warehouse
B to automatically generate an order for the products they are waiting for.
This method does not cover that scenario.
ii. Automated Replenishment
Odoo can automatically generate internal
transfer orders by setting up Replenishment Rules. This will create:
- A Delivery Order in Warehouse A.
- A Receipt Order in Warehouse B.
Pros: Fully automated transfer
process.
Cons: Requires proper routes and rules configuration and apply them to
products.
iii. Manual Replenishment Trigger
Similar to automated replenishment, but instead of an
automatic trigger, the user manually initiates the process by clicking the Replenish
button under Inventory > Products.
Pros:
More control over when the transfer happens.
Cons: Requires manual action and also proper routes and rules configuration
and apply them to products.
iv. Transfer Triggered by Delivery Order Validation (using Routes)
This method works by setting up Routes so that when
a Delivery Order is validated in Warehouse A, a Receipt Order
is automatically created in Warehouse B.
Pros:
Automates the process without manual intervention.
Cons: This method has the limitations mentioned from Jaideep above
and in order to surpass them you have to create a different in transit location
for each different warehouse and assigned it to different route in order to
cover all the different scenarios.
v. Automated Transfer on Delivery (using Automated Actions)
If
you want to dynamically create a Receipt Order in Warehouse B without
manually creating a delivery order first and avoiding the creation of unnecessary
locations and routes, Automated Actions is the best way.
Use Case:
An
operator in Warehouse A creates a Delivery Order without a
predefined order and selects Warehouse B as the recipient. Instead of
manually creating a receipt, an automated action triggers the creation of a Receipt
Order in Warehouse B.
Implementation Steps:
- Create
an Automated Action that triggers “On Save”.
- Update
when the record is "Created On" in the Transfer model.
- The
action should apply when:
- Status is "Done".
- Operation Type is "Delivery".
- Source Document is not set (to prevent
duplicate receipts for replenishments, as replenishments fill this with “Manual
Replenishment”).
In
this automated action a code should be executed to see if the “Delivery Address”
field belong to our company that owns Warehouse A and B. When this happens, Odoo
automatically creates send the product(s) from this delivery to the “Physical
location / Inter-Warehouse Transit” location but does not create the related
receipt. So a code like the below should be implemented.
if record.picking_type_id.code == 'outgoing':
# Identify the source and destination warehouses
source_warehouse = record.picking_type_id.warehouse_id
destination_warehouse = env['stock.warehouse'].search([
('partner_id', '=', record.partner_id.id)
], limit=1)
if destination_warehouse and source_warehouse:
source_partner = source_warehouse.partner_id
procurement_group = record.group_id
# Create the receipt picking without move lines
new_receipt = env['stock.picking'].create({
'picking_type_id': destination_warehouse.in_type_id.id,
'location_id': record.location_dest_id.id,
'location_dest_id': destination_warehouse.lot_stock_id.id,
'origin': record.name,
'partner_id': source_partner.id if source_partner else False,
'user_id': False,
'note': record.note if record.note else False,
'group_id': procurement_group.id,
'carrier_id': record.carrier_id.id if record.carrier_id else False,
'carrier_tracking_ref': record.carrier_tracking_ref if record.carrier_tracking_ref else False,
'weight': record.weight if record.weight else 0.0,
'shipping_weight': record.shipping_weight if record.shipping_weight else 0.0,
})
# Create move lines for the new receipt
move_line_vals = []
for move in record.move_ids:
for move_line in move.move_line_ids.filtered(lambda ml: ml.qty_done > 0):
new_move = env['stock.move'].create({
'product_id': move_line.product_id.id,
'product_uom_qty': move_line.qty_done,
'product_uom': move_line.product_uom_id.id,
'name': move_line.product_id.name,
'location_id': record.location_dest_id.id,
'location_dest_id': destination_warehouse.lot_stock_id.id,
'picking_id': new_receipt.id,
'procure_method': 'make_to_stock',
'origin': record.name,
'group_id': procurement_group.id,
})
move_line_vals.append((0, 0, {
'move_id': new_move.id,
'product_id': move_line.product_id.id,
'quantity': move_line.qty_done,
'product_uom_id': move_line.product_uom_id.id,
'location_id': record.location_dest_id.id,
'location_dest_id': destination_warehouse.lot_stock_id.id,
}))
if move_line_vals:
new_receipt.write({'move_line_ids': move_line_vals})
# Set the receipt moves as "Assigned"
new_receipt.move_ids.write({'state': 'assigned'})
# Update the delivery origin to match procurement group
record.write({
'origin': 'Manual Operation',
'group_id': procurement_group.id,
})
# Link moves from delivery to receipt
for move in record.move_ids:
matching_receipt_move = new_receipt.move_ids.filtered(lambda m: m.product_id == move.product_id)
if matching_receipt_move:
move.write({'move_dest_ids': [(4, matching_receipt_move.id)]})
# Ensure Odoo recomputes "show_next_pickings"
record._compute_show_next_pickings()
I have a couple questions:
- Do the warehouses share adress?
- What rules do you have in each warehouse (X step delivery?, X step recepcion?)
Hi,
"negative level" is caused because you are using immediate transfer -> which means you don't check product availability " you are sure they are there with this count.
So instead of that try to use planned transfer which will allow you to check availability of product in source destination
Hello,
I understood your problem. Yes you can get the acknowledgement from the receiver end. You have to extend the two to three points inside the internal transfer process. You can contact us for discussing in more details.
Thanks,
shivoham.odoo@gmail.com