Odoo for Logistics and 3PL Companies#
What 3PL Companies Need from an ERP#
Third-party logistics providers manage inventory, warehousing, and transportation on behalf of multiple clients. The core requirements are:
- Multi-client inventory isolation - each client's stock separated and visible only to them
- Billing per operation - charge clients per pick, pack, pallet, storage unit, or shipment
- Real-time shipment visibility - clients want to see their stock levels and order status
- Carrier integration - connect to FedEx, UPS, DHL, and other carriers for label generation
- Proof of delivery - capture signatures and photos at delivery
- Reporting by client - stock aging, throughput, and cost reports per client
Multi-Client Inventory in Odoo#
Method 1: Multi-company
Each client is a separate Odoo company sharing infrastructure but with isolated data. Pros: full data isolation. Cons: switching between companies is slow; reporting across all clients requires enterprise Odoo Reporting.
Method 2: Owner field on stock move lines
Odoo Inventory has a Product Owner field on stock moves and quants. This assigns ownership of inventory to a partner (your client) without separating it into different databases:
Inventory → Products → [Product] → On Hand → Owner column
Filter stock by owner to see each client's inventory level.
Method 3: Lot/Serial Numbers per Client
Use lot tracking where each lot belongs to a specific client, identified by a client prefix (e.g., CLI001-LOT-2024-001). This works for warehouses handling serialized goods.
Warehouse Structure for 3PL#
Warehouse: Main 3PL Hub
Locations:
├── Input (receiving dock)
├── Quality Control
├── Storage
│ ├── Client A - Pallet Rack A1-A20
│ ├── Client B - Refrigerated B1-B10
│ └── Client C - Bulk C1-C5
├── Packing
└── Output (shipping dock)Create client-specific sub-locations to track exactly where each client's stock sits:
Inventory → Configuration → Locations → New sub-location under Storage
Receiving and Put-Away for 3PL#
When clients ship goods to the 3PL warehouse:
- Create a Receipt under the client's product source location.
- Apply put-away rules to route to the client's designated zone automatically.
- Generate a Warehouse Receipt confirmation to send to the client.
Put-away rules:
Inventory → Configuration → Put Away Rules → New:
- Product: Client A's products
- Source location: Input
- Destination: Storage / Client A
Order Fulfillment Workflow#
- Client places order in their system → sends to 3PL via EDI, email, or portal.
- 3PL creates a Delivery Order in Odoo with client's product and destination.
- Warehouse staff picks, packs, and validates the transfer.
- Carrier label generated via Odoo Delivery Carriers.
- Client sees the shipment status in the customer portal.
Billing per Operation#
3PL companies bill clients based on activity:
- Storage fee: per pallet per day
- Handling fee: per pick, per order, per line
- Label/packaging fee: per shipment
Automate billing using Odoo Service products with time/quantity tracking:
Product: 3PL Handling Fee
Type: Service
Invoicing Policy: Based on Milestones or TimesheetsOr use Timesheet-based billing if staff log time per client operation.
Automated billing trigger example:
# In stock.picking (delivery order) on validation:
def _action_done(self):
res = super()._action_done()
if self.picking_type_id.name == 'Delivery Orders':
# Create a handling fee service line for the client
so = self.sale_id or self._find_client_contract()
if so:
so.order_line.create({
'order_id': so.id,
'product_id': env.ref('my_3pl.handling_fee').id,
'product_uom_qty': len(self.move_line_ids),
'price_unit': 2.50, # per pick line
})
return resCustomer Portal for 3PL Clients#
Enable Customer Portal so clients can log in and see their:
- Current stock levels by product
- Open delivery orders and their status
- Invoices and payment history
Settings → Customers → Customer Portal → enable
Clients access the portal at https://yourodoo.com/my/ using their email login.
Carrier Integration#
Inventory → Configuration → Delivery Carriers:
Connect FedEx, UPS, DHL, Canada Post, and others. Once configured:
- On a validated delivery, click Send to Shipper.
- Odoo calls the carrier API and retrieves a tracking number + label.
- Label prints automatically on your warehouse label printer.
- Tracking number is stored on the delivery order.
Inventory Aging and Slow-Mover Reports#
Inventory → Products → Current Stock (Pivot view) → Group by: Product, Owner
Add a custom stored field days_in_stock to identify inventory that hasn't moved:
days_in_stock = fields.Integer(
compute='_compute_days_in_stock', store=True
)
@api.depends('date')
def _compute_days_in_stock(self):
for quant in self:
quant.days_in_stock = (fields.Date.today() - quant.date.date()).daysBill clients for slow-moving stock as a storage surcharge after 90 days.
Common Mistakes#
- Not isolating client locations - without per-client storage locations, picking the wrong client's stock is easy; always set up client-specific sub-locations
- Using one product per item across clients - if two clients store the same SKU, use product variants or lot tracking to maintain ownership; don't share a single stock quant
- No portal access for clients - clients calling the warehouse to check stock status is a sign the portal isn't set up; this adds avoidable manual work
- Manual billing - manually invoicing clients per operation is error-prone at scale; automate handling fees through service products linked to delivery orders
- Carrier label volume - 3PL operations generate thousands of labels; ensure your Odoo server has adequate resources and a fast label printer connected via direct IP (not USB)

