Skip to content

m-pension Module

Complete pension lifecycle: Retirement Order → PPPO → Audit → FPPO.


class RetirementOrder(BaseDocType):
"""Retirement order (created 2 years before retirement)."""
order_number: str = Field(max_length=50, unique=True)
employee: str # FK to Employee
pension_file_number: str | None = None
# Dates
service_book_retirement_date: date # From employee master
new_retirement_date: date | None = None # If modified
forms_submission_due_date: date | None = None
retirement_type: str = "Superannuation" # Superannuation, Voluntary, Resignation, Death
status: str = "Generated" # Generated, FormsComplete, PPPOIssued, FPPOIssued
remarks: str | None = None
class Meta:
table_name = "retirement_order"
class PensionFormStatus(BaseDocType):
"""Status of pension forms required from employee."""
retirement_order: str # FK to RetirementOrder
form: str # FK to PensionFormMaster
is_received: bool = False
received_date: date | None = None
remarks: str | None = None
class Meta:
table_name = "pension_form_status"
class PensionNominee(BaseDocType):
"""Pension/Gratuity nominee details."""
employee: str # FK to Employee
serial_no: int
name: str
relationship: str
date_of_birth: date | None = None
marital_status: str | None = None
is_employed: bool = False
is_pension_eligible: bool = False
gratuity_amount: Decimal = Decimal("0")
# Address
address_line_1: str | None = None
city: str | None = None
pincode: str | None = None
state: str | None = None
phone: str | None = None
remarks: str | None = None
class Meta:
table_name = "pension_nominee"
class RecoverySummary(BaseDocType):
"""Summary of pending recoveries before pension."""
retirement_order: str # FK to RetirementOrder
recovery_type: str # NoDemandCertificate, MedicalCard, LibraryBooks, etc.
is_recovered: bool = False
recovery_date: date | None = None
remarks: str | None = None
class Meta:
table_name = "recovery_summary"
class PPPO(BaseDocType):
"""Provisional Pension Payment Order."""
pppo_number: str = Field(max_length=50, unique=True)
retirement_order: str # FK to RetirementOrder
employee: str
pension_file_number: str
order_type: str # PPPO, PFPPO (Family)
order_date: date
pension_start_date: date
pension_end_date: date | None = None
# Calculated amounts
pension_amount: Decimal = Decimal("0")
family_pension_amount: Decimal = Decimal("0")
service_gratuity: Decimal = Decimal("0")
general_gratuity: Decimal = Decimal("0")
death_gratuity: Decimal = Decimal("0")
# Service details
six_monthly_periods: int = 0
avg_basic_pay: Decimal = Decimal("0")
non_practicing_allowance: Decimal = Decimal("0")
# Flags
is_forms_received: bool = False
is_recovery_done: bool = False
status: str = "Draft" # Draft, Issued, Audited, FPPOIssued
reference_pppo: str | None = None # For PFPPO referencing PPPO
remarks: str | None = None
class Meta:
table_name = "pppo"
class CommutationApplication(BaseDocType):
"""Application for pension commutation."""
pppo: str # FK to PPPO
employee: str
application_date: date
medical_certificate_given: bool = False
reason: str | None = None
forwarded_by: str | None = None
# Calculation inputs
pension_basic: Decimal
percentage_of_pension: Decimal # Usually 40%
commutation_factor: Decimal # From factor master based on age
# Calculated
calculated_commutation_amount: Decimal = Decimal("0")
approved_amount: Decimal | None = None
restoration_date: date | None = None # When full pension restarts
monthly_deduction: Decimal = Decimal("0")
class Meta:
table_name = "commutation_application"
class AuditedPension(BaseDocType):
"""Audited pension details from CDA."""
audit_report_number: str = Field(max_length=50, unique=True)
pppo: str # FK to PPPO
pension_file_number: str
audit_report_date: date
# Audited amounts
audited_pension_amount: Decimal
audited_family_pension: Decimal
audited_commutation: Decimal = Decimal("0")
audited_service_gratuity: Decimal
audited_general_gratuity: Decimal
audited_death_gratuity: Decimal
audited_monthly_deduction: Decimal = Decimal("0")
remarks: str | None = None
class Meta:
table_name = "audited_pension"
class FPPO(BaseDocType):
"""Final Pension Payment Order."""
fppo_number: str = Field(max_length=50, unique=True)
pppo: str # FK to PPPO
audited_pension: str # FK to AuditedPension
employee: str
pension_file_number: str
fppo_date: date
# Final amounts (from audited)
final_pension_amount: Decimal
final_family_pension: Decimal
final_commutation: Decimal = Decimal("0")
final_service_gratuity: Decimal
final_general_gratuity: Decimal
final_death_gratuity: Decimal
final_monthly_deduction: Decimal = Decimal("0")
remarks: str | None = None
class Meta:
table_name = "fppo"

class PensionFormMaster(BaseDocType):
"""List of required pension forms."""
code: str = Field(max_length=20, unique=True)
name: str = Field(max_length=200)
class Meta:
table_name = "pension_form_master"
class CommutationFactor(BaseDocType):
"""Age-wise commutation factor table."""
age_next_birthday: int = Field(unique=True)
factor_value: Decimal
class Meta:
table_name = "commutation_factor"

class RetirementListGenerator:
"""Generate retirement orders for upcoming retirements."""
async def generate_for_period(
self,
start_date: date,
end_date: date,
) -> list[RetirementOrder]:
"""Generate retirement orders for employees retiring in period."""
employees = await self.employee_repo.find_retiring_between(
start_date, end_date
)
orders = []
for emp in employees:
order = RetirementOrder(
employee=emp.id,
service_book_retirement_date=emp.retirement_date,
forms_submission_due_date=emp.retirement_date - timedelta(days=180),
)
await self.repo.save(order)
orders.append(order)
return orders
class PensionCalculator:
"""Calculate pension package."""
async def calculate(self, pppo: PPPO) -> dict:
"""Calculate pension, gratuity, etc."""
# Get last 10 months pay
pay_details = await self.get_last_10_months_pay(pppo.employee)
avg_basic = sum(p.basic for p in pay_details) / len(pay_details)
# Service periods
service = await self.calculate_service_period(pppo.employee)
six_monthly = service.total_days // 182
# Pension = Avg Basic * Six Monthly Periods / 66
pension = avg_basic * six_monthly / 66
# Gratuity = Avg Basic * Six Monthly Periods / 4
gratuity = avg_basic * six_monthly / 4
return {
"pension_amount": min(pension, Decimal("27000")), # Max cap
"service_gratuity": gratuity,
...
}

Endpoint Method Description
/api/v1/retirement-order GET, POST Retirement orders
/api/v1/retirement-order/generate POST Generate for date range
/api/v1/pension-nominee/{employee} GET, POST Nominees
/api/v1/pppo GET, POST PPPO
/api/v1/pppo/{id}/calculate POST Calculate pension
/api/v1/commutation GET, POST Commutation apps
/api/v1/audited-pension GET, POST Audited details
/api/v1/fppo GET, POST Final PPO