Shipping Rate Documentation¶
Overview¶
The Shipping Rate module (ship.rate) defines location-based and order-based pricing rules for shipping methods. Rates can be configured by country, province, district, postal code, and order criteria (minimum amount/weight) to provide flexible shipping cost calculations.
Model Information¶
Model Name: ship.rate
Display Name: Shipping Rate
Key Fields: None
Features¶
- ❌ No audit logging
- ❌ No multi-company support
- ✅ Search enabled on sequence, method, location fields, address name
- ✅ Auto-generated sequence numbers
- ✅ Cascade delete with parent shipping method
Key Fields Reference¶
Header Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
sequence |
Char | ✅ | Auto-generated unique sequence |
method_id |
Many2One | ✅ | Parent shipping method (cascade delete) |
ship_price |
Decimal | ✅ | Shipping cost amount |
Location Filter Fields¶
| Field | Type | Description |
|---|---|---|
country_id |
Many2One | Country filter (if specified, must match) |
province_id |
Many2One | Province/state filter (if specified, must match) |
district_id |
Many2One | District filter (if specified, must match) |
postal_code |
Char | Specific postal code (exact match required) |
address_name |
Char | Specific address name (exact match required) |
Order Criteria Fields¶
| Field | Type | Description |
|---|---|---|
min_amount |
Decimal | Minimum order amount (order must meet or exceed) |
min_weight |
Decimal | Minimum total weight in Kg (order must meet or exceed) |
Relationship Fields¶
| Field | Type | Description |
|---|---|---|
comments |
One2Many | Comments and notes (message) |
Default Order: sequence, method_id.name, country_id.name, province_id.name, postal_code, min_amount, min_weight, ship_price
Understanding Sequence Generation¶
Auto-Generated Sequences¶
The model uses an intelligent sequence generation system to ensure unique identifiers:
def _get_number(self, context={}):
while 1:
seq = get_model("sequence").get_number("shipping_rates")
if not seq:
return None
res = self.search([["sequence", "=", seq]])
if not res:
return seq
get_model("sequence").increment("shipping_rates")
How it works:
1. Gets next number from shipping_rates sequence
2. Checks if that sequence already exists
3. If exists, increments sequence and tries again
4. Returns first available unique sequence
Note: The sequence field is automatically populated on creation; you don't need to provide it.
API Methods¶
1. Create Rate¶
Method: create(vals, context)
Creates a new shipping rate with auto-generated sequence.
Parameters:
vals = {
"method_id": int, # Required: Shipping method ID
"ship_price": Decimal, # Required: Shipping cost
# Optional location filters (at least one recommended)
"country_id": int, # Country filter
"province_id": int, # Province filter
"district_id": int, # District filter
"postal_code": str, # Exact postal code
"address_name": str, # Exact address name
# Optional order criteria
"min_amount": Decimal, # Minimum order amount
"min_weight": Decimal # Minimum weight in Kg
}
Returns: int - New record ID
Example:
# Basic rate - country only
rate_id = get_model("ship.rate").create({
"method_id": 1,
"country_id": usa_id,
"ship_price": 25.00
})
# Specific postal code rate
rate_id = get_model("ship.rate").create({
"method_id": 1,
"postal_code": "10001",
"ship_price": 5.00
})
# Tiered rate by order amount
rate_id = get_model("ship.rate").create({
"method_id": 1,
"country_id": usa_id,
"min_amount": 100.00,
"ship_price": 0.00 # Free over $100
})
Common Use Cases¶
Use Case 1: Geographic Tiered Pricing¶
method_id = 1 # Standard shipping
# Tier 1: Urban area (low cost)
get_model("ship.rate").create({
"method_id": method_id,
"country_id": usa_id,
"province_id": california_id,
"postal_code": "90210",
"ship_price": 5.00
})
# Tier 2: Suburban area (medium cost)
get_model("ship.rate").create({
"method_id": method_id,
"country_id": usa_id,
"province_id": california_id,
"ship_price": 10.00
})
# Tier 3: State-wide (higher cost)
get_model("ship.rate").create({
"method_id": method_id,
"country_id": usa_id,
"ship_price": 20.00
})
Use Case 2: Free Shipping Promotion¶
method_id = 2 # Free shipping method
# Free shipping on orders over $100
get_model("ship.rate").create({
"method_id": method_id,
"min_amount": 100.00,
"ship_price": 0.00
})
# Free shipping on orders over $75 within state
get_model("ship.rate").create({
"method_id": method_id,
"province_id": california_id,
"min_amount": 75.00,
"ship_price": 0.00
})
Use Case 3: Weight-Based Pricing¶
method_id = 3 # Weight-based shipping
# Light packages (0-5 Kg)
get_model("ship.rate").create({
"method_id": method_id,
"country_id": usa_id,
"ship_price": 10.00
})
# Medium packages (5-20 Kg)
get_model("ship.rate").create({
"method_id": method_id,
"country_id": usa_id,
"min_weight": 5.0,
"ship_price": 25.00
})
# Heavy packages (20+ Kg)
get_model("ship.rate").create({
"method_id": method_id,
"country_id": usa_id,
"min_weight": 20.0,
"ship_price": 50.00
})
Rate Matching Logic¶
How Rates are Selected¶
When ship.method.get_ship_amount() is called, rates are filtered in this order:
- Country Match: If rate has
country_id, address must match - Province Match: If rate has
province_id, address must match - District Match: If rate has
district_id, address must match - Postal Code Match: If rate has
postal_code, address must match exactly - Address Name Match: If rate has
address_name, address must match exactly - Min Amount Check: If rate has
min_amount, order must meet/exceed it - Min Weight Check: If rate has
min_weight, order must meet/exceed it
Result: The lowest matching rate is selected.
Related Models¶
| Model | Relationship | Description |
|---|---|---|
ship.method |
Many2One | Parent shipping method (cascade delete) |
country |
Many2One | Country filter |
province |
Many2One | Province/state filter |
district |
Many2One | District filter |
message |
One2Many | Comments |
Best Practices¶
1. Rate Specificity Pyramid¶
Configure rates from most specific (lowest cost) to least specific (highest cost):
method_id = 1
# Most Specific: Exact address + postal code
get_model("ship.rate").create({
"method_id": method_id,
"address_name": "Main Warehouse",
"postal_code": "10001",
"ship_price": 0.00
})
# Very Specific: Postal code only
get_model("ship.rate").create({
"method_id": method_id,
"postal_code": "10001",
"ship_price": 5.00
})
# Medium: Province
get_model("ship.rate").create({
"method_id": method_id,
"province_id": new_york_id,
"ship_price": 12.00
})
# General: Country
get_model("ship.rate").create({
"method_id": method_id,
"country_id": usa_id,
"ship_price": 20.00
})
# Fallback: No filters
get_model("ship.rate").create({
"method_id": method_id,
"ship_price": 50.00
})
2. Always Provide Fallback Rate¶
# Good: Always have a catch-all rate
get_model("ship.rate").create({
"method_id": method_id,
"ship_price": 30.00 # Default rate
})
# Bad: No fallback rate results in None
Troubleshooting¶
"Rate not matching expected address"¶
Cause: Filter criteria too specific or address data incomplete
Solution: Check address has required fields (country, province, postal code)
"Free shipping not applying"¶
Cause: Order doesn't meet min_amount or min_weight
Solution: Verify order meets rate criteria
# Check rate requirements
rate = get_model("ship.rate").browse(rate_id)
print(f"Min Amount: ${rate.min_amount or 'None'}")
print(f"Min Weight: {rate.min_weight or 'None'} Kg")
Version History¶
Last Updated: October 2025
Model File: ship_rate.py
Framework: Netforce
Additional Resources¶
- Shipping Method Documentation:
ship.method - Address Documentation:
address - Geographic Models:
country,province,district
This documentation is generated for developer onboarding and reference purposes.