Valid Period Documentation¶
Overview¶
The Valid Period module (valid.period) provides reusable validity period templates that define time durations in days. These templates are used throughout the system, particularly in quotations and other time-sensitive documents, to standardize validity periods and make them easy to select and apply.
Model Information¶
Model Name: valid.period
Display Name: Valid Period
Name Field: name (e.g., "30 Days", "1 Year")
Features¶
- Simple template-based validity periods
- Reusable across multiple documents
- Days-based duration definition
- Name-based selection and ordering
- Lightweight utility model
Model Purpose¶
This is a utility/lookup model that serves as a template library for validity periods:
valid.period Templates
|
├─> Used in sale.quot (quotation validity)
├─> Used in contracts
├─> Used in time-limited offers
└─> Used anywhere standard periods are needed
Why This Model Exists: - Standardize common validity periods across the system - Avoid hardcoding durations in business logic - Provide user-friendly period selection in UI - Enable consistent period naming (e.g., "30 Days" vs "1 Month") - Simplify period management
Key Fields Reference¶
Basic Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
name |
Char | Yes | Descriptive name of the period (e.g., "30 Days", "6 Months") |
days |
Integer | No | Number of days in this period |
Note: The days field has a comment # XXX in the source code, suggesting it may be under review or planned for enhancement.
Common Validity Period Templates¶
Typical validity period templates you might create:
| Name | Days | Use Case |
|---|---|---|
| 7 Days | 7 | Short-term offers, urgent quotes |
| 14 Days | 14 | Standard quote validity |
| 30 Days | 30 | Monthly validity periods |
| 60 Days | 60 | Extended quote validity |
| 90 Days | 90 | Quarterly validity |
| 6 Months | 180 | Semi-annual periods |
| 1 Year | 365 | Annual contracts |
| 2 Years | 730 | Long-term agreements |
| Indefinite | 0 or NULL | No expiration |
API Methods¶
1. Create Valid Period Template¶
Method: create(vals, context)
Creates a new validity period template.
Parameters:
Returns: int - New valid period ID
Example:
# Create standard validity periods
period_id = get_model("valid.period").create({
"name": "30 Days",
"days": 30
})
2. Search Valid Periods¶
Method: search(conditions, order)
Find validity period templates.
Example:
# Get all valid periods ordered by name
all_periods = get_model("valid.period").search([], order="name")
# Get a specific period by name
thirty_days = get_model("valid.period").search([["name", "=", "30 Days"]])
Search Functions¶
Search by Name¶
# Find specific validity period
period_ids = get_model("valid.period").search([
["name", "=", "30 Days"]
])
Search by Days¶
Get All Periods (Ordered)¶
# Get all periods sorted by name, then days
all_periods = get_model("valid.period").search([], order="name, days")
Related Models¶
| Model | Relationship | Description |
|---|---|---|
sale.quot |
Referenced | Quotations use valid periods for expiration |
contract |
Referenced (potential) | Contracts may use valid periods |
| Time-limited entities | Referenced | Any model needing standard validity periods |
Common Use Cases¶
Use Case 1: Create Standard Validity Periods¶
# Setup: Create standard validity period templates
standard_periods = [
{"name": "7 Days", "days": 7},
{"name": "14 Days", "days": 14},
{"name": "30 Days", "days": 30},
{"name": "60 Days", "days": 60},
{"name": "90 Days", "days": 90},
{"name": "6 Months", "days": 180},
{"name": "1 Year", "days": 365}
]
for period in standard_periods:
get_model("valid.period").create(period)
# Now these can be selected in quotation forms, etc.
Use Case 2: Use Valid Period in Quotation¶
# Customer requests a quote valid for 30 days
# 1. Find the 30-day validity period template
period_id = get_model("valid.period").search([["name", "=", "30 Days"]])[0]
period = get_model("valid.period").browse(period_id)
# 2. Calculate expiration date
from datetime import datetime, timedelta
today = datetime.now()
expiry_date = today + timedelta(days=period.days)
# 3. Create quotation with this validity period
quote_id = get_model("sale.quot").create({
"customer_id": customer_id,
"date": today.strftime("%Y-%m-%d"),
"exp_date": expiry_date.strftime("%Y-%m-%d"),
"valid_period_id": period_id, # Reference to template
# ... other fields
})
print(f"Quote valid until: {expiry_date.strftime('%Y-%m-%d')}")
Use Case 3: Populate Dropdown in UI¶
# In UI form, populate validity period dropdown
def get_valid_period_options():
"""Get all validity periods for dropdown selection"""
period_ids = get_model("valid.period").search([], order="days")
periods = get_model("valid.period").browse(period_ids)
options = []
for period in periods:
options.append({
"value": period.id,
"label": period.name,
"days": period.days
})
return options
# Returns:
# [
# {"value": 1, "label": "7 Days", "days": 7},
# {"value": 2, "label": "14 Days", "days": 14},
# {"value": 3, "label": "30 Days", "days": 30},
# ...
# ]
Use Case 4: Calculate Expiration from Template¶
# Helper function to calculate expiration date from valid period
def calculate_expiry_date(valid_period_id, start_date=None):
"""Calculate expiration date based on valid period template"""
from datetime import datetime, timedelta
if start_date is None:
start_date = datetime.now()
elif isinstance(start_date, str):
start_date = datetime.strptime(start_date, "%Y-%m-%d")
period = get_model("valid.period").browse(valid_period_id)
if not period.days:
return None # Indefinite validity
expiry_date = start_date + timedelta(days=period.days)
return expiry_date.strftime("%Y-%m-%d")
# Usage
expiry = calculate_expiry_date(
period_id,
start_date="2026-01-15"
)
# Returns: "2026-02-14" (30 days later)
Use Case 5: Industry-Specific Validity Periods¶
# Create specialized validity periods for specific industries
# Construction industry (typical bid validity)
get_model("valid.period").create({
"name": "Bid Validity - 45 Days",
"days": 45
})
# Real estate (typical offer validity)
get_model("valid.period").create({
"name": "Offer Validity - 3 Days",
"days": 3
})
# Software licensing (annual)
get_model("valid.period").create({
"name": "Annual License",
"days": 365
})
# SaaS subscriptions (monthly)
get_model("valid.period").create({
"name": "Monthly Subscription",
"days": 30
})
Use Case 6: Extend Quotation Validity¶
# Customer requests extension of quotation validity
def extend_quotation_validity(quote_id, new_period_name):
"""Extend quotation with a different validity period"""
from datetime import datetime, timedelta
quote = get_model("sale.quot").browse(quote_id)
# Find new validity period
period_ids = get_model("valid.period").search([
["name", "=", new_period_name]
])
if not period_ids:
raise Exception(f"Validity period '{new_period_name}' not found")
period = get_model("valid.period").browse(period_ids[0])
# Calculate new expiry date from today
today = datetime.now()
new_expiry = today + timedelta(days=period.days)
# Update quotation
quote.write({
"valid_period_id": period.id,
"exp_date": new_expiry.strftime("%Y-%m-%d")
})
return new_expiry
# Usage
new_expiry = extend_quotation_validity(quote_id, "60 Days")
print(f"Quote extended until: {new_expiry.strftime('%Y-%m-%d')}")
Best Practices¶
1. Use Descriptive Names¶
# Good: Clear, consistent naming
periods = [
{"name": "7 Days", "days": 7},
{"name": "30 Days", "days": 30},
{"name": "90 Days", "days": 90},
{"name": "6 Months", "days": 180},
{"name": "1 Year", "days": 365}
]
# Less clear: Inconsistent naming
periods = [
{"name": "One Week", "days": 7},
{"name": "30d", "days": 30},
{"name": "3 months", "days": 90},
{"name": "half year", "days": 180}
]
2. Create Standard Set on System Setup¶
# Good: Create standard periods during system initialization
def initialize_valid_periods():
"""Create standard validity periods if they don't exist"""
standard_periods = [
{"name": "7 Days", "days": 7},
{"name": "14 Days", "days": 14},
{"name": "30 Days", "days": 30},
{"name": "60 Days", "days": 60},
{"name": "90 Days", "days": 90},
{"name": "6 Months", "days": 180},
{"name": "1 Year", "days": 365}
]
for period in standard_periods:
# Check if exists
exists = get_model("valid.period").search([
["name", "=", period["name"]]
])
if not exists:
get_model("valid.period").create(period)
# Run during system setup or migration
initialize_valid_periods()
3. Handle Edge Cases¶
# Good: Handle indefinite validity
# Create "Indefinite" period option
get_model("valid.period").create({
"name": "Indefinite",
"days": None # or 0
})
# When calculating expiry
def get_expiry_date(period_id, start_date):
period = get_model("valid.period").browse(period_id)
if not period.days or period.days == 0:
return None # No expiration
from datetime import datetime, timedelta
expiry = datetime.strptime(start_date, "%Y-%m-%d")
expiry += timedelta(days=period.days)
return expiry.strftime("%Y-%m-%d")
4. Use Ordering for Better UX¶
# Good: Retrieve periods in logical order
# By number of days (shortest to longest)
periods = get_model("valid.period").search([], order="days")
# By name (alphabetical)
periods = get_model("valid.period").search([], order="name")
# Custom ordering in UI
def get_ordered_periods():
all_periods = get_model("valid.period").search_browse([])
# Sort by days, then name
sorted_periods = sorted(all_periods, key=lambda p: (p.days or 0, p.name))
return sorted_periods
Performance Tips¶
1. Cache Valid Periods¶
# Valid periods rarely change - cache them
_valid_periods_cache = None
def get_valid_periods():
global _valid_periods_cache
if _valid_periods_cache is None:
_valid_periods_cache = get_model("valid.period").search_browse([])
return _valid_periods_cache
# Clear cache when periods are modified
def clear_valid_periods_cache():
global _valid_periods_cache
_valid_periods_cache = None
2. Minimize Database Queries¶
# Efficient: Load once, use many times
all_periods = get_model("valid.period").browse(
get_model("valid.period").search([])
)
period_lookup = {p.name: p for p in all_periods}
# Now lookup by name without database query
thirty_days = period_lookup.get("30 Days")
sixty_days = period_lookup.get("60 Days")
Troubleshooting¶
"Valid period not found"¶
Cause: Trying to use a period that doesn't exist in the database Solution: - Run initialization script to create standard periods - Check spelling/capitalization of period name - Use search to list available periods
Days field is None/null¶
Cause: The days field is optional
Solution:
- Check for None/null before calculating dates
- Use 0 or None for "indefinite" periods
- Always validate days value exists before date calculations
Inconsistent period names¶
Cause: Periods created manually with different naming conventions Solution: - Standardize period creation - Use initialization script - Add validation to enforce naming conventions
Database Constraints¶
No explicit constraints defined in the model, but best practices suggest:
# Recommended: Add unique constraint on name
_sql_constraints = [
("name_unique", "unique (name)", "Validity period name must be unique")
]
Integration Points¶
Internal Modules¶
- sale.quot: Quotations use valid periods for expiration dates
- sale.order: Orders may reference validity periods for delivery timelines
- contract: Contracts may use valid periods for term lengths
- Time-limited offers: Any promotional offers with validity
UI Integration¶
- Dropdown selection: Periods populate selection lists in forms
- Date calculators: Auto-calculate expiration dates based on period
- Templates: Pre-fill validity periods in document templates
Extension Ideas¶
The simple model can be extended with additional features:
1. Add Period Types¶
# Add type categorization
_fields = {
"name": fields.Char("Name", required=True),
"days": fields.Integer("Days"),
"type": fields.Selection([
["short", "Short Term"],
["medium", "Medium Term"],
["long", "Long Term"]
], "Period Type")
}
2. Add Active Flag¶
# Control which periods are active/available
_fields = {
"name": fields.Char("Name", required=True),
"days": fields.Integer("Days"),
"active": fields.Boolean("Active", default=True)
}
3. Add Description¶
# Add helpful descriptions
_fields = {
"name": fields.Char("Name", required=True),
"days": fields.Integer("Days"),
"description": fields.Text("Description")
}
# Example:
{
"name": "30 Days",
"days": 30,
"description": "Standard quote validity period. Recommended for most quotations."
}
Version History¶
Last Updated: 2026-01-05 Model Version: valid_period.py (18 lines) Framework: Netforce
Additional Resources¶
- Sale Quotation Documentation:
sale.quot - Date/Time Utilities: System utilities for date calculations
- Form Field Documentation: How to use valid periods in UI forms
Support & Feedback¶
For issues or questions about this module: 1. Verify standard periods are created in database 2. Check field naming conventions match your search queries 3. Test date calculation logic with sample periods 4. Ensure proper ordering for dropdown displays
This documentation is generated for developer onboarding and reference purposes.