mirror of
https://github.com/we-promise/sure.git
synced 2026-05-31 16:29:03 +00:00
Introduces retirement and FIRE (Financial Independence, Retire Early) planning as a new top-level feature in the sidebar navigation. Key features: - RetirementConfig model: stores retirement planning parameters per family (birth year, retirement age, target income, pension system, etc.) - PensionEntry model: tracks pension statements (Renteninformation) over time with pension points, current/projected monthly pension - German GRV pension calculations: - Estimated monthly pension from Entgeltpunkte x Rentenwert - After-tax pension estimation - Monthly pension gap analysis - FIRE calculations: - FIRE number (capital needed via 4% rule, inflation-adjusted) - FIRE progress percentage from current portfolio value - Estimated FIRE date (iterative monthly projection) - Required monthly savings to close pension gap - Dashboard view with overview cards, FIRE progress bar, assumptions panel, and pension history table with add/delete entries - Setup and edit views for configuring retirement parameters - Full i18n support (English + German) - Minitest coverage for models and controller Database: 2 new tables (retirement_configs, pension_entries) with UUID PKs Routes: singular resource with setup, add/destroy pension entry actions This is an initial implementation focused on the German GRV pension system. The architecture supports extending to other pension systems (custom/other). Open to suggestions and improvements from the community - contributions for additional pension systems, visualization charts, or calculation refinements are very welcome.
25 lines
994 B
Ruby
25 lines
994 B
Ruby
class PensionEntry < ApplicationRecord
|
|
belongs_to :retirement_config
|
|
|
|
validates :recorded_at, presence: true, uniqueness: { scope: :retirement_config_id }
|
|
validates :current_points, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
|
validates :current_monthly_pension, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
|
|
validates :projected_monthly_pension, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
|
|
|
|
scope :chronological, -> { order(recorded_at: :asc) }
|
|
scope :reverse_chronological, -> { order(recorded_at: :desc) }
|
|
|
|
# Calculate the points gained since the previous entry.
|
|
# For bulk rendering, prefer RetirementConfig#pension_entries_with_gains
|
|
# to avoid N+1 queries.
|
|
def points_gained
|
|
previous = retirement_config.pension_entries
|
|
.where("recorded_at < ?", recorded_at)
|
|
.order(recorded_at: :desc)
|
|
.first
|
|
|
|
return current_points unless previous
|
|
current_points - previous.current_points
|
|
end
|
|
end
|