Why Drop Shipping Is the Fastest Path to Capital-Light Fulfillment
Drop shipping eliminates the most expensive variable in e-commerce and wholesale distribution: inventory. Instead of purchasing goods, storing them in a warehouse, and shipping them yourself, the vendor ships directly to your customer. You never touch the product. Your warehouse never sees it. Your working capital stays in your bank account instead of sitting on a shelf.
The business model sounds simple, but the ERP configuration is where most implementations fail. The sale order needs to automatically generate a purchase order to the correct vendor. The delivery tracking from the vendor needs to flow back to the customer. The accounting entries must skip stock valuation entirely—because no goods ever entered your warehouse. And returns need a reverse flow that routes back to the vendor, not to your receiving dock.
Odoo 19 supports drop shipping natively through the stock_dropshipping module. This guide walks you through every step: enabling the route, configuring vendors, automating the SO-to-PO flow, tracking deliveries, handling the accounting without stock valuation, and managing returns.
Enabling and Configuring the Drop Shipping Route in Odoo 19
Drop shipping in Odoo is a route—a predefined logistics path that tells the system how to fulfill demand. Before you can use it, you need to install the module and understand how routes interact with your existing warehouse configuration.
Step 1: Install the Drop Shipping Module
# Install the drop shipping module
./odoo-bin -d your_database -i stock_dropshipping --stop-after-init
# Verify installation
./odoo-bin shell -d your_database << 'EOF'
module = env['ir.module.module'].search([
('name', '=', 'stock_dropshipping'),
('state', '=', 'installed'),
])
print(f"Installed: {{bool(module)}}")
EOFAlternatively, navigate to Apps, search for "Drop Shipping," and click Install. The module adds a new route called Dropship to your routing options.
Step 2: Enable Drop Shipping in Inventory Settings
After installation, go to Inventory → Configuration → Settings. Under the Warehouse section, enable Drop Shipping. This makes the route available at the product level and in sale order lines.
| Setting | Location | Effect |
|---|---|---|
| Drop Shipping | Inventory → Settings → Warehouse | Adds the "Dropship" route to product and SO line routing options |
| Multi-Step Routes | Inventory → Settings → Warehouse | Required if you mix drop shipping with standard warehouse fulfillment |
| Purchase Orders | Purchase → Settings | Must be enabled—drop shipping creates POs automatically |
Step 3: Understand the Route Mechanics
The Dropship route creates a specific stock rule: when demand is generated (from a sale order), instead of pulling from your warehouse stock, it creates a procurement to the vendor. The stock move goes from Vendor Location directly to Customer Location—bypassing your warehouse entirely.
# In Odoo shell: inspect the drop shipping stock rule
rule = env['stock.rule'].search([
('route_id.name', 'ilike', 'Dropship'),
])
for r in rule:
print(f"Rule: {{r.name}}")
print(f" Action: {{r.action}}")
print(f" Source Location: {{r.location_src_id.complete_name}}")
print(f" Destination: {{r.location_dest_id.complete_name}}")
print(f" Picking Type: {{r.picking_type_id.name}}")If a product has both a warehouse route (e.g., "Deliver in 2 steps") and the Dropship route, the route set on the sale order line takes precedence. If no route is specified on the SO line, Odoo falls back to the product's route, then the warehouse default. Always set the route explicitly on the SO line for drop-shipped items to avoid accidental warehouse picks.
Configuring Vendors for Drop Shipping: Pricelists, Lead Times, and Supplier Info
The drop shipping route is only half the equation. Each product that can be drop-shipped needs at least one vendor configured with pricing and lead time information. Without this, the automatic PO generation either fails or creates purchase orders with zero-cost lines and no delivery estimate.
Setting Up Vendor Pricelists on Products
Navigate to the product form, open the Purchase tab, and add vendor pricelist entries:
product = env['product.product'].browse(42)
# Add or update vendor info
env['product.supplierinfo'].create({
'product_tmpl_id': product.product_tmpl_id.id,
'partner_id': vendor_partner.id, # The drop-ship vendor
'min_qty': 1.0,
'price': 45.00,
'currency_id': env.ref('base.USD').id,
'delay': 5, # Lead time in days
'product_code': 'VENDOR-SKU-1234', # Vendor's internal reference
'product_name': 'Widget Pro (Vendor Name)',
})Assigning the Drop Ship Route to Products
On the product form, go to the Inventory tab. Under Routes, check Dropship. This sets the default route for this product—every SO line for this product will use drop shipping unless overridden.
# Find the dropship route
dropship_route = env['stock.route'].search([
('name', 'ilike', 'Dropship'),
], limit=1)
# Assign to product template
product_tmpl = env['product.template'].browse(42)
product_tmpl.write({
'route_ids': [(4, dropship_route.id)],
})Multi-Vendor Drop Shipping
Some products can be sourced from multiple vendors. Odoo selects the vendor based on the supplier info sequence and minimum quantity. The vendor with the lowest sequence number whose minimum quantity is met gets the PO. You can use this to set up primary and fallback vendors:
| Vendor | Sequence | Min Qty | Price | Lead Time |
|---|---|---|---|---|
| Supplier A (Primary) | 1 | 1 | $45.00 | 3 days |
| Supplier B (Bulk) | 2 | 50 | $38.00 | 7 days |
| Supplier C (Backup) | 3 | 1 | $52.00 | 5 days |
The delay field on the supplier info record feeds directly into the expected delivery date shown on the sale order and communicated to your customer. If you leave it at zero, customers see "today" as the expected delivery—which creates support tickets when the vendor ships in 5 business days. Always set realistic lead times and add a buffer day for vendor processing.
The SO-to-PO Automation: How Odoo 19 Creates Purchase Orders from Sales Orders
This is the core automation that makes drop shipping work. When a sale order is confirmed, Odoo's procurement engine evaluates the route on each SO line. For lines with the Dropship route, it creates a purchase order to the vendor instead of a warehouse pick.
The Automated Flow
Here's exactly what happens when you click Confirm on a sale order with a drop-shipped line:
- Sale order confirmed — The SO moves to "Sales Order" state and triggers procurement.
- Procurement evaluates routes — The system reads the route from the SO line (or product default) and finds the Dropship rule.
- Vendor selection — Odoo looks up the product's supplier info records, selects the best vendor based on sequence and quantity, and determines the purchase price.
- PO created (or lines added) — If an open draft PO already exists for this vendor, the line is appended. Otherwise, a new PO is created.
- Stock move created — A stock move from Vendor Location to Customer Location is linked to both the SO and PO.
- Delivery order generated — A "Dropship" transfer appears under Inventory → Operations, linked to both the SO and PO.
# After confirming a sale order, find the linked PO
sale_order = env['sale.order'].browse(123)
# Get all purchase orders linked to this SO
purchase_orders = env['purchase.order'].search([
('origin', 'ilike', sale_order.name),
])
for po in purchase_orders:
print(f"PO: {{po.name}} | Vendor: {{po.partner_id.name}}")
print(f" State: {{po.state}}")
for line in po.order_line:
print(f" Line: {{line.product_id.name}} x {{line.product_qty}}"
f" @ {{line.price_unit}}")
# Find the dropship transfer
pickings = sale_order.picking_ids.filtered(
lambda p: p.picking_type_id.code == 'dropship'
)
for pick in pickings:
print(f"Transfer: {{pick.name}} | State: {{pick.state}}")Automatic vs. Manual PO Confirmation
By default, the generated PO is in draft state. Your purchasing team must manually confirm it before the vendor receives the order. If you want full automation (confirm SO → auto-confirm PO), you can set the vendor's purchase order approval to automatic:
# Option 1: Use a server action on PO creation
# In your custom module:
class PurchaseOrder(models.Model):
_inherit = 'purchase.order'
@api.model_create_multi
def create(self, vals_list):
orders = super().create(vals_list)
for order in orders:
# Auto-confirm if all lines are drop-shipped
is_dropship = all(
line.sale_line_id and
any(r.name == 'Dropship'
for r in line.sale_line_id.route_id)
for line in order.order_line
)
if is_dropship and order.partner_id.x_auto_confirm_po:
order.button_confirm()
return orders If two sale orders for the same vendor are confirmed close together, Odoo may group the lines into a single PO. This is controlled by the group_id on the procurement. For drop shipping, you usually want one PO per SO to maintain traceability. Set the procurement group policy to "Propagate" on the Dropship route rule to keep SO-PO links clean.
Tracking and Delivery Confirmation: Closing the Loop Between Vendor and Customer
The biggest operational gap in drop shipping is visibility. Your customer ordered from you, but the package ships from your vendor. If the vendor doesn't communicate tracking information, your customer support team is flying blind. Odoo 19 provides two mechanisms for handling this.
Recording Vendor Tracking Numbers
When the vendor ships the order, you need to record the tracking number on the dropship transfer. Navigate to Inventory → Operations → Transfers, filter by operation type Dropship, and open the relevant transfer. Add the carrier and tracking reference in the Additional Info tab.
# Find the dropship transfer for a specific SO
picking = env['stock.picking'].search([
('origin', 'ilike', 'SO123'),
('picking_type_code', '=', 'dropship'),
], limit=1)
# Record tracking information
picking.write({
'carrier_id': env.ref('delivery_ups.delivery_carrier_ups').id,
'carrier_tracking_ref': '1Z999AA10123456784',
})
# Validate the transfer (marks as delivered)
picking.button_validate()Validating the Dropship Transfer
Validating the dropship transfer is the critical step that:
- Marks the sale order delivery as complete (green "Delivered" status).
- Updates the purchase order receipt status (vendor has fulfilled their obligation).
- Triggers accounting entries for the goods movement (vendor location → customer location).
- Enables invoicing—you can now invoice the customer and receive the vendor bill.
If the vendor can only ship part of the order, process a partial validation on the dropship transfer. Odoo will create a backorder for the remaining quantity. The sale order will show partial delivery status, and the customer can be invoiced for what has been shipped. The backorder keeps the remaining PO line open until the vendor ships the rest.
Accounting for Drop Shipping: No Stock Valuation, Clean P&L
This is where drop shipping diverges most from standard fulfillment. In a normal warehouse flow, receiving goods creates a stock valuation entry (debit stock, credit accounts payable). Shipping goods creates another (debit COGS, credit stock). With drop shipping, goods never enter your stock—so there is no stock valuation layer.
The Journal Entries
When the dropship transfer is validated in Odoo 19 with automated (Anglo-Saxon) accounting, the system creates the following entries:
| Event | Debit Account | Credit Account | Amount |
|---|---|---|---|
| Dropship transfer validated | Expense / COGS (cost of product) | Stock Interim (Received) / Stock Valuation | Vendor cost |
| Vendor bill registered | Stock Interim (Received) | Accounts Payable | Vendor invoice amount |
| Customer invoice posted | Accounts Receivable | Revenue | Sale price |
The key difference: there is no stock valuation account balance for drop-shipped items. The cost goes directly to COGS (or an expense account) when the transfer is validated. This means your balance sheet never shows inventory for these items, and your P&L reflects the margin immediately.
Product Category Accounting Configuration
# Check the product category accounting config
category = env['product.category'].browse(1)
print(f"Category: {{category.name}}")
print(f"Costing Method: {{category.property_cost_method}}")
print(f"Valuation: {{category.property_valuation}}")
print(f"Stock Input Account: "
f"{{category.property_stock_account_input_categ_id.code}}")
print(f"Stock Output Account: "
f"{{category.property_stock_account_output_categ_id.code}}")
print(f"Stock Valuation Account: "
f"{{category.property_stock_valuation_account_id.code}}")
print(f"Expense Account: "
f"{{category.property_account_expense_categ_id.code}}")In Continental accounting, the expense is recorded at the vendor bill—not at the transfer validation. The dropship transfer does not create journal entries. In Anglo-Saxon accounting, the transfer creates the interim entries shown above. Make sure your chart of accounts matches your accounting method, or your COGS timing will be wrong. Check Accounting → Settings → Anglo-Saxon Accounting to verify which mode you are running.
Handling Drop Shipping Returns: Reverse Flows Without a Warehouse
Returns are the hardest part of drop shipping to get right. Your customer wants to return the product, but you don't have a warehouse to receive it. The product needs to go back to the vendor, and the accounting needs to reverse cleanly. Odoo 19 handles this through reverse transfers on the dropship operation.
Creating a Drop Ship Return
Open the original dropship transfer that was validated. Click the Return button. Odoo creates a reverse transfer from Customer Location back to Vendor Location—mirroring the original flow in reverse.
# Find the original dropship transfer
original = env['stock.picking'].browse(456)
# Create return wizard
return_wizard = env['stock.return.picking'].with_context(
active_id=original.id,
active_model='stock.picking',
).create({})
# Modify quantities if partial return
for line in return_wizard.product_return_moves:
if line.product_id.id == partial_return_product_id:
line.quantity = 2.0 # Return only 2 units
# Execute the return
result = return_wizard.action_create_returns()
return_picking = env['stock.picking'].browse(result['res_id'])
# Validate the return transfer
return_picking.button_validate()
print(f"Return Transfer: {{return_picking.name}}")Accounting for Returns
When the return transfer is validated, Odoo reverses the original accounting entries:
- COGS reversal — The cost previously booked to COGS is credited back.
- Credit note to customer — You issue a credit note from the original invoice.
- Vendor refund — You request a refund from the vendor (debit note on the purchase).
Before processing the return in Odoo, confirm the return address with your vendor. Some vendors have dedicated returns centers that differ from their shipping warehouse. Include the vendor's RMA number in the origin field of the return transfer so both parties can track it. Without this coordination, products arrive at the wrong address, and the vendor disputes the refund.
3 Drop Shipping Mistakes That Create Fulfillment Chaos in Production
Mixing Drop-Shipped and Stock Products on the Same Sale Order Without Route-Level Control
A customer orders three products. Two ship from your warehouse; one is drop-shipped from a vendor. You confirm the SO. Odoo creates two delivery operations: a warehouse pick for the stock items and a dropship transfer for the vendor item. Your warehouse team processes the pick and ships two items. The customer calls asking where the third item is. Your support team checks the SO, sees "partially delivered," and has no idea the third item is coming from a different source with a different timeline.
Train your sales and support teams to check the Delivery smart button on the SO, which shows all transfers including dropship operations. Better yet, set up an automated email to the customer when a mixed order is confirmed, explaining that items may arrive in separate shipments. Use the mail.template on the dropship picking type to send vendor-specific tracking info.
Not Setting Vendor Prices and Getting Zero-Cost Purchase Orders
You enable the Dropship route on a product but forget to add a vendor pricelist entry. The SO is confirmed, and Odoo creates a PO with a unit price of $0.00. The PO gets sent to the vendor, who either ignores it or calls you confused. Meanwhile, your accounting shows zero COGS—the P&L looks fantastic until someone notices you're not recording any cost of goods sold.
Add a Python constraint on product.template that prevents saving a product with the Dropship route but no supplier info. Or use a server action that checks at SO confirmation time and blocks the order with a user-friendly warning. The five minutes of validation code saves hours of accounting corrections later.
Forgetting to Validate the Dropship Transfer and Leaving Orders in Limbo
The vendor ships the product and emails you a tracking number. Your team updates the PO status but never validates the dropship transfer in Inventory. The SO still shows "Waiting" or "Ready." The customer cannot be invoiced. The accounting entries for COGS are never created. Months later, your finance team discovers dozens of open dropship transfers—some for orders that were delivered weeks ago—and the P&L is understating costs.
Create a scheduled action that runs daily and flags dropship transfers older than the vendor's lead time that are still in "Assigned" state. Send an email digest to your operations manager with the list. For high-volume operations, integrate the vendor's shipping API to auto-validate transfers when tracking shows "delivered."
What Drop Shipping Configuration Saves Your Business
Drop shipping is not just a logistics convenience. It is a financial strategy that transforms your cost structure:
No rent, no utilities, no warehouse staff for drop-shipped SKUs. Every dollar you don't spend on storage goes directly to your bottom line or gets reinvested in growth.
You buy only what you've already sold. No dead stock, no obsolescence write-offs, no carrying costs. Your cash conversion cycle collapses from weeks to days.
Add 10,000 SKUs to your catalog without renting a bigger warehouse. Your product range is limited only by your vendor relationships, not by shelf space.
For a mid-size distributor carrying $2M in inventory with a 20% annual carrying cost, converting even 30% of SKUs to drop shipping frees up $600,000 in working capital and eliminates $120,000/year in carrying costs. The Odoo configuration takes a week. The ROI shows up in the first month's cash flow statement.
Optimization Metadata
Complete guide to configuring drop shipping in Odoo 19. Set up routes, automate SO-to-PO flows, handle accounting without stock valuation, and manage vendor-direct returns.
1. "Enabling and Configuring the Drop Shipping Route in Odoo 19"
2. "The SO-to-PO Automation: How Odoo 19 Creates Purchase Orders from Sales Orders"
3. "3 Drop Shipping Mistakes That Create Fulfillment Chaos in Production"