Skip to content

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:

vals = {
    "name": "30 Days",      # Required: descriptive name
    "days": 30              # Optional: number of days
}

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

# Find periods with 30 days
period_ids = get_model("valid.period").search([
    ["days", "=", 30]
])

Get All Periods (Ordered)

# Get all periods sorted by name, then days
all_periods = get_model("valid.period").search([], order="name, days")

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.