Port Destination Documentation¶
Overview¶
The Port Destination module (port.destination) tracks destination ports for import shipments. Each port has a unique code and supports formatted name display for easy identification in logistics operations.
Model Information¶
Model Name: port.destination
Display Name: Port Destination
Key Fields: None
Features¶
- ❌ No audit logging
- ❌ No multi-company support
- ✅ Search enabled on name, code
- ✅ Unique code constraint
- ✅ Custom name_get formatting
Understanding Key Fields¶
Unique Code Constraint¶
Each port destination must have a globally unique code across the entire system. This ensures no duplicate port codes exist.
Examples:
Key Fields Reference¶
Header Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
name |
Char | ✅ | Port name (searchable) |
code |
Char | ✅ | Unique port code (searchable, unique) |
description |
Text | ❌ | Additional details or notes |
country_id |
Many2One | ❌ | Country location |
Default Order: Ordered by code
API Methods¶
1. name_get¶
Method: name_get(ids, context)
Returns formatted display names with code prefix: "[CODE] Name"
Parameters:
- ids (list): Record IDs to format
Returns: List of tuples [(id, formatted_name, image), ...]
Behavior:
- If code exists: Returns "[CODE] Name"
- If no code: Returns "Name"
- Always includes image field (typically None)
Example:
# Get formatted names
port_ids = [1, 2, 3]
names = get_model("port.destination").name_get(port_ids)
# Example output:
# [(1, "[VNSGN] Ho Chi Minh Port", None),
# (2, "[THLCH] Laem Chabang", None),
# (3, "[SGSIN] Port of Singapore", None)]
for port_id, display_name, image in names:
print(f"ID {port_id}: {display_name}")
2. Create Port Destination¶
Method: create(vals, context)
Creates a new port destination with unique code.
Parameters:
vals = {
"name": str, # Required: Port name
"code": str, # Required: Unique code
"description": str, # Optional: Additional details
"country_id": int, # Optional: Country ID
}
Returns: int - New record ID
Example:
# Create port destination
port_id = get_model("port.destination").create({
"code": "VNSGN",
"name": "Ho Chi Minh Port",
"description": "Major port in southern Vietnam",
"country_id": vietnam_id
})
Common Use Cases¶
Use Case 1: Setup Asian Destination Ports¶
# Setup major Asian import destinations
asian_ports = [
{
"code": "VNSGN",
"name": "Ho Chi Minh Port",
"description": "Saigon Port - Vietnam's largest port",
"country_id": vietnam_id
},
{
"code": "THLCH",
"name": "Laem Chabang",
"description": "Thailand's primary deep-sea port",
"country_id": thailand_id
},
{
"code": "SGSIN",
"name": "Port of Singapore",
"description": "World's busiest transshipment hub",
"country_id": singapore_id
},
{
"code": "MYPKG",
"name": "Port Klang",
"description": "Malaysia's busiest port",
"country_id": malaysia_id
},
{
"code": "IDTPP",
"name": "Tanjung Priok",
"description": "Indonesia's main port in Jakarta",
"country_id": indonesia_id
}
]
created_ports = []
for port_data in asian_ports:
try:
port_id = get_model("port.destination").create(port_data)
created_ports.append(port_id)
print(f"✓ Created: [{port_data['code']}] {port_data['name']}")
except Exception as e:
print(f"✗ Failed: [{port_data['code']}] - {str(e)}")
print(f"\nSuccessfully created {len(created_ports)} destination ports")
Use Case 2: Import Port Reference Data¶
def import_port_destinations_from_csv(csv_file):
"""
Import port destinations from CSV file
CSV format: code,name,description,country_code
"""
import csv
created = 0
skipped = 0
errors = []
with open(csv_file, 'r') as f:
reader = csv.DictReader(f)
for row in reader:
# Check if port already exists
existing = get_model("port.destination").search([
["code", "=", row['code']]
])
if existing:
skipped += 1
continue
# Get country ID
country = get_model("country").search([
["code", "=", row['country_code']]
])
country_id = country[0] if country else None
try:
get_model("port.destination").create({
"code": row['code'],
"name": row['name'],
"description": row.get('description', ''),
"country_id": country_id
})
created += 1
except Exception as e:
errors.append(f"{row['code']}: {str(e)}")
print(f"Import complete:")
print(f" Created: {created}")
print(f" Skipped (existing): {skipped}")
if errors:
print(f" Errors: {len(errors)}")
for err in errors:
print(f" - {err}")
# Usage
import_port_destinations_from_csv('destination_ports.csv')
Use Case 3: List Ports by Country¶
def list_destination_ports_by_country(country_id):
"""List all destination ports for a country"""
ports = get_model("port.destination").search_browse([
["country_id", "=", country_id]
])
country = get_model("country").browse(country_id)
print(f"Destination Ports in {country.name}:")
print("-" * 50)
for port in ports:
print(f"[{port.code}] {port.name}")
if port.description:
print(f" → {port.description}")
# Usage
list_destination_ports_by_country(thailand_id)
Use Case 4: Validate and Update Port Data¶
def validate_port_destinations():
"""Check for data quality issues"""
issues = []
# Check for missing countries
ports = get_model("port.destination").search_browse([
["country_id", "=", False]
])
if ports:
issues.append({
"type": "missing_country",
"count": len(ports),
"ports": [p.code for p in ports]
})
# Check for missing descriptions
ports = get_model("port.destination").search_browse([
["description", "=", False]
])
if ports:
issues.append({
"type": "missing_description",
"count": len(ports),
"ports": [p.code for p in ports]
})
# Report issues
if issues:
print("Port Destination Data Quality Issues:")
for issue in issues:
print(f"\n{issue['type']}: {issue['count']} ports")
for code in issue['ports'][:5]: # Show first 5
print(f" - {code}")
if issue['count'] > 5:
print(f" ... and {issue['count'] - 5} more")
else:
print("✓ All port destinations have complete data")
return issues
# Run validation
validate_port_destinations()
Search Functions¶
Search by Code¶
# Exact code match
condition = [["code", "=", "VNSGN"]]
port = get_model("port.destination").search_browse(condition)
Search by Name Pattern¶
# Case-insensitive partial match
condition = [["name", "ilike", "%singapore%"]]
ports = get_model("port.destination").search_browse(condition)
Search by Country¶
# All ports in a country
condition = [["country_id", "=", thailand_id]]
ports = get_model("port.destination").search_browse(condition)
Search Ports Without Country¶
# Find ports missing country assignment
condition = [["country_id", "=", False]]
incomplete_ports = get_model("port.destination").search_browse(condition)
Related Models¶
| Model | Relationship | Description |
|---|---|---|
country |
Many2One | Port location country |
port.loading |
Related | Origin ports for shipments |
sale.order |
Referenced by | Import orders specify destination |
purchase.order |
Referenced by | Import purchases track destination |
Best Practices¶
1. Use Standard Port Codes¶
# Good: Use UN/LOCODE or standard port codes
get_model("port.destination").create({
"code": "VNSGN", # UN/LOCODE format
"name": "Ho Chi Minh Port"
})
# Avoid: Custom or non-standard codes
get_model("port.destination").create({
"code": "HCM-PORT-01", # Non-standard
"name": "Ho Chi Minh Port"
})
2. Always Include Country¶
# Good: Country specified for clarity
get_model("port.destination").create({
"code": "VNSGN",
"name": "Ho Chi Minh Port",
"country_id": vietnam_id
})
# Bad: No country makes location unclear
get_model("port.destination").create({
"code": "VNSGN",
"name": "Ho Chi Minh Port"
# Missing country_id
})
3. Provide Descriptive Information¶
# Good: Helpful description
get_model("port.destination").create({
"code": "THLCH",
"name": "Laem Chabang",
"description": "Thailand's primary deep-sea port, located 130km "
"southeast of Bangkok. Handles 70% of Thailand's trade.",
"country_id": thailand_id
})
# Minimal: Just required fields
get_model("port.destination").create({
"code": "THLCH",
"name": "Laem Chabang",
"country_id": thailand_id
})
4. Check Before Creating¶
# Good: Prevent duplicate creation
def create_or_get_destination_port(code, name, country_id, description=None):
# Check by code (unique)
existing = get_model("port.destination").search([
["code", "=", code]
])
if existing:
print(f"Port {code} already exists")
return existing[0]
# Create new
port_id = get_model("port.destination").create({
"code": code,
"name": name,
"country_id": country_id,
"description": description
})
print(f"Created port {code}")
return port_id
# Usage
port_id = create_or_get_destination_port(
"VNSGN",
"Ho Chi Minh Port",
vietnam_id,
"Major port in southern Vietnam"
)
Performance Tips¶
1. Cache Port Lookups¶
# Good: Cache frequently accessed ports
_dest_port_cache = {}
def get_destination_port_by_code(code):
if code not in _dest_port_cache:
port = get_model("port.destination").search([
["code", "=", code]
])
if port:
_dest_port_cache[code] = port[0]
return _dest_port_cache.get(code)
# Usage
port_id = get_destination_port_by_code("VNSGN")
2. Bulk Load for Selections¶
# Good: Load all ports once
all_ports = get_model("port.destination").search_browse([])
port_options = [(p.id, f"[{p.code}] {p.name}") for p in all_ports]
# Use cached list for dropdown/selections
Troubleshooting¶
"Code must be unique"¶
Cause: Attempting to create port with duplicate code
Solution: Check existing ports or use different code
# Check if code exists
code = "VNSGN"
existing = get_model("port.destination").search([
["code", "=", code]
])
if existing:
port = get_model("port.destination").browse(existing[0])
print(f"Port code '{code}' already exists:")
print(f" ID: {port.id}")
print(f" Name: {port.name}")
print(f" Country: {port.country_id.name if port.country_id else 'N/A'}")
else:
# Safe to create
get_model("port.destination").create({
"code": code,
"name": "New Port",
"country_id": country_id
})
"Port code format issues"¶
Cause: Non-standard code format
Solution: Use standard 5-character UN/LOCODE format
# Good: Standard format (2-letter country + 3-letter location)
valid_codes = ["VNSGN", "THLCH", "SGSIN", "MYPKG"]
# Bad: Non-standard formats
invalid_codes = ["Vietnam-SGN", "THAILAND-LCH", "SG"]
Testing Examples¶
Unit Test: Create and Retrieve¶
def test_port_destination_crud():
# Create
port_id = get_model("port.destination").create({
"code": "TEST1",
"name": "Test Port",
"description": "Test destination port",
"country_id": usa_id
})
# Verify creation
assert port_id is not None
# Read back
port = get_model("port.destination").browse(port_id)
assert port.code == "TEST1"
assert port.name == "Test Port"
# Test name_get formatting
names = get_model("port.destination").name_get([port_id])
assert names[0][1] == "[TEST1] Test Port"
# Cleanup
get_model("port.destination").delete([port_id])
Unit Test: Unique Code Constraint¶
def test_unique_code_constraint():
# Create first port
port1_id = get_model("port.destination").create({
"code": "TEST2",
"name": "Test Port 1",
"country_id": usa_id
})
# Try to create duplicate code
try:
port2_id = get_model("port.destination").create({
"code": "TEST2", # Duplicate!
"name": "Test Port 2",
"country_id": canada_id
})
assert False, "Should have raised exception for duplicate code"
except Exception as e:
assert "unique" in str(e).lower()
# Cleanup
get_model("port.destination").delete([port1_id])
Security Considerations¶
Permission Model¶
- View: Users with logistics/import access
- Create/Modify: Admin or logistics manager
- Delete: Admin only (rarely needed)
Data Access¶
- Public reference data
- No sensitive information
- No multi-company isolation
Version History¶
Last Updated: October 2025
Model File: port_destination.py
Framework: Netforce
Additional Resources¶
- Port Loading Documentation:
port.loading - Shipping Port Documentation:
ship.port - Country Documentation:
country - UN/LOCODE Standard Reference
This documentation is generated for developer onboarding and reference purposes.