m-pension Module
Complete pension lifecycle: Retirement Order → PPPO → Audit → FPPO.
DocTypes
Section titled “DocTypes”RetirementOrder
Section titled “RetirementOrder”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"PensionFormStatus
Section titled “PensionFormStatus”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"PensionNominee
Section titled “PensionNominee”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"RecoverySummary
Section titled “RecoverySummary”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"CommutationApplication
Section titled “CommutationApplication”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"AuditedPension
Section titled “AuditedPension”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"Masters
Section titled “Masters”PensionFormMaster
Section titled “PensionFormMaster”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"CommutationFactor
Section titled “CommutationFactor”class CommutationFactor(BaseDocType): """Age-wise commutation factor table."""
age_next_birthday: int = Field(unique=True) factor_value: Decimal
class Meta: table_name = "commutation_factor"Services
Section titled “Services”RetirementListGenerator
Section titled “RetirementListGenerator”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 ordersPensionCalculator
Section titled “PensionCalculator”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, ... }API Endpoints
Section titled “API Endpoints”| 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 |