+ <%# German GRV fields %>
+
-
- <%= f.number_field :expected_annual_points, step: 0.01, min: 0, class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
-
<%= t(".expected_annual_points_hint") %>
+
+ <%= f.number_field "pension_params[expected_annual_points]", step: 0.01, min: 0,
+ value: f.object&.pension_params&.dig("expected_annual_points"),
+ class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
+
<%= t(".de_expected_annual_points_hint") %>
-
- <%= f.number_field :rentenwert, step: 0.01, min: 0, class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
-
<%= t(".rentenwert_hint") %>
+
+ <%= f.number_field "pension_params[rentenwert]", step: 0.01, min: 0,
+ value: f.object&.pension_params&.dig("rentenwert"),
+ class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
+
<%= t(".de_rentenwert_hint") %>
-
- <%= f.number_field :contribution_start_year, min: 1960, max: Date.current.year, class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
+
+ <%= f.number_field "pension_params[contribution_start_year]", min: 1960, max: Date.current.year,
+ value: f.object&.pension_params&.dig("contribution_start_year"),
+ class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
+
+ <%# US Social Security fields %>
+
+
+
+ <%= f.number_field "pension_params[estimated_monthly_benefit]", step: 0.01, min: 0,
+ value: f.object&.pension_params&.dig("estimated_monthly_benefit"),
+ class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
+
<%= t(".us_estimated_monthly_benefit_hint") %>
+
+
+
+ <%# UK State Pension fields %>
+
+
+
+ <%= f.number_field "pension_params[qualifying_years]", step: 1, min: 0, max: 35,
+ value: f.object&.pension_params&.dig("qualifying_years"),
+ class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
+
<%= t(".uk_qualifying_years_hint") %>
+
+
+
+ <%= f.number_field "pension_params[full_weekly_rate]", step: 0.01, min: 0,
+ value: f.object&.pension_params&.dig("full_weekly_rate"),
+ class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
+
<%= t(".uk_full_weekly_rate_hint") %>
+
+
+
+ <%# French Régime Général fields %>
+
+
+
+ <%= f.number_field "pension_params[estimated_monthly_pension]", step: 0.01, min: 0,
+ value: f.object&.pension_params&.dig("estimated_monthly_pension"),
+ class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
+
<%= t(".fr_estimated_monthly_pension_hint") %>
+
+
+
+ <%= f.number_field "pension_params[trimestres]", step: 1, min: 0,
+ value: f.object&.pension_params&.dig("trimestres"),
+ class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
+
+
+
+ <%# Spanish Social Security fields %>
+
+
+
+ <%= f.number_field "pension_params[estimated_monthly_pension]", step: 0.01, min: 0,
+ value: f.object&.pension_params&.dig("estimated_monthly_pension"),
+ class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
+
<%= t(".es_estimated_monthly_pension_hint") %>
+
+
+
+ <%= f.number_field "pension_params[contribution_years]", step: 1, min: 0,
+ value: f.object&.pension_params&.dig("contribution_years"),
+ class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
+
+
+
+ <%# Custom / Other — just use estimated pension from entries %>
+
+
<%= t(".custom_hint") %>
+
<%# Assumptions %>
diff --git a/app/views/retirement/show.html.erb b/app/views/retirement/show.html.erb
index 8020a2884..089c6dab1 100644
--- a/app/views/retirement/show.html.erb
+++ b/app/views/retirement/show.html.erb
@@ -24,9 +24,7 @@
<%# Estimated Pension %>
-
- <%= @retirement_config.pension_system == "de_grv" ? t(".estimated_pension_grv") : t(".estimated_pension") %>
-
+
<%= t(".estimated_pension") %>
<%= number_to_currency(@retirement_config.estimated_monthly_pension, unit: retirement_currency_unit(@retirement_config)) %>
@@ -154,10 +152,12 @@
<%= f.label :recorded_at, t(".date"), class: "block text-sm font-medium text-secondary mb-1" %>
<%= f.date_field :recorded_at, value: Date.current, class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2", required: true %>
-
- <%= f.label :current_points, t(".pension_points"), class: "block text-sm font-medium text-secondary mb-1" %>
- <%= f.number_field :current_points, step: 0.0001, min: 0, class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2", required: true %>
-
+ <% if @retirement_config.points_based? %>
+
+ <%= f.label :current_points, t(".pension_points"), class: "block text-sm font-medium text-secondary mb-1" %>
+ <%= f.number_field :current_points, step: 0.0001, min: 0, class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
+
+ <% end %>
<%= f.label :current_monthly_pension, t(".current_pension"), class: "block text-sm font-medium text-secondary mb-1" %>
<%= f.number_field :current_monthly_pension, step: 0.01, min: 0, class: "form-input w-full rounded-lg border border-secondary bg-container text-sm text-primary px-3 py-2" %>
@@ -185,8 +185,10 @@
| <%= t(".date") %> |
- <%= t(".pension_points") %> |
- <%= t(".points_gained") %> |
+ <% if @retirement_config.points_based? %>
+ <%= t(".pension_points") %> |
+ <%= t(".points_gained") %> |
+ <% end %>
<%= t(".current_pension") %> |
<%= t(".projected_pension") %> |
<%= t(".notes") %> |
@@ -197,12 +199,24 @@
<% @pension_entries.each do |entry| %>
| <%= l(entry.recorded_at) %> |
- <%= number_with_precision(entry.current_points, precision: 4) %> |
-
-
- <%= entry.points_gained >= 0 ? '+' : '' %><%= number_with_precision(entry.points_gained, precision: 4) %>
-
- |
+ <% if @retirement_config.points_based? %>
+
+ <% if entry.current_points %>
+ <%= number_with_precision(entry.current_points, precision: 4) %>
+ <% else %>
+ —
+ <% end %>
+ |
+
+ <% if entry.points_gained %>
+
+ <%= entry.points_gained >= 0 ? '+' : '' %><%= number_with_precision(entry.points_gained, precision: 4) %>
+
+ <% else %>
+ —
+ <% end %>
+ |
+ <% end %>
<% if entry.current_monthly_pension %>
<%= number_to_currency(entry.current_monthly_pension, unit: retirement_currency_unit(@retirement_config)) %>
diff --git a/config/locales/views/retirement/de.yml b/config/locales/views/retirement/de.yml
index 198a79e65..272b69662 100644
--- a/config/locales/views/retirement/de.yml
+++ b/config/locales/views/retirement/de.yml
@@ -9,7 +9,6 @@ de:
one: "%{count} Jahr bis zur Rente"
other: "%{count} Jahre bis zur Rente"
estimated_pension: Geschätzte monatliche Rente
- estimated_pension_grv: Geschätzte GRV-Rente
after_tax: Nach Steuern
pension_gap: Monatliche Rentenlücke
target: "Ziel"
@@ -28,7 +27,7 @@ de:
inflation: Inflationsrate
tax_rate: Steuersatz
monthly_savings: Monatliche Sparrate
- pension_history: Rentenhistorie (Renteninformation)
+ pension_history: Rentenhistorie
add_entry: Neuen Eintrag hinzufügen
date: Datum
pension_points: Entgeltpunkte
@@ -53,11 +52,30 @@ de:
pension_section: Rentensystem
pension_system: Rentensystem
country: Land
- expected_annual_points: Erwartete jährliche Entgeltpunkte
- expected_annual_points_hint: Durchschnittsgehalt = ~1,0 Punkte/Jahr
- rentenwert: Aktueller Rentenwert
- rentenwert_hint: Aktueller Wert in EUR (2025 ~ 39,32)
- contribution_start_year: Beitragsbeginn (Jahr)
+ # German GRV
+ de_expected_annual_points: Erwartete jährliche Entgeltpunkte
+ de_expected_annual_points_hint: Durchschnittsgehalt = ~1,0 Punkte/Jahr
+ de_rentenwert: Aktueller Rentenwert
+ de_rentenwert_hint: Aktueller Wert in EUR (2025 ~ 39,32)
+ de_contribution_start_year: Beitragsbeginn (Jahr)
+ # US Social Security
+ us_estimated_monthly_benefit: Geschätzte monatliche Leistung
+ us_estimated_monthly_benefit_hint: Aus deiner SSA-Bescheinigung (ssa.gov)
+ # UK State Pension
+ uk_qualifying_years: Anspruchsjahre (NI)
+ uk_qualifying_years_hint: "35 Jahre für volle State Pension"
+ uk_full_weekly_rate: Voller Wochensatz (£)
+ uk_full_weekly_rate_hint: "2024/25: £221,20/Woche"
+ # France Régime Général
+ fr_estimated_monthly_pension: Geschätzte monatliche Rente
+ fr_estimated_monthly_pension_hint: Aus deinem relevé de situation individuelle
+ fr_trimestres: Validierte Trimester
+ # Spain Social Security
+ es_estimated_monthly_pension: Geschätzte monatliche Rente
+ es_estimated_monthly_pension_hint: Aus deinem informe de vida laboral
+ es_contribution_years: Beitragsjahre
+ # Custom
+ custom_hint: Füge Renteneinträge manuell hinzu, um deine geschätzte Rente zu verfolgen.
assumptions_section: Annahmen
expected_return: Erwartete jährliche Rendite (%)
inflation: Erwartete Inflation (%)
@@ -82,4 +100,8 @@ de:
pension_entry_removed: Renteneintrag entfernt
pension_systems:
de_grv: Deutsche Gesetzliche Rentenversicherung (GRV)
+ us_ss: US-Sozialversicherung
+ uk_sp: UK State Pension
+ fr_regime: Französisches Régime Général
+ es_ss: Spanische Sozialversicherung
custom: Benutzerdefiniert / Andere
diff --git a/config/locales/views/retirement/en.yml b/config/locales/views/retirement/en.yml
index fa2f8c196..e83aa4aa7 100644
--- a/config/locales/views/retirement/en.yml
+++ b/config/locales/views/retirement/en.yml
@@ -9,7 +9,6 @@ en:
one: "%{count} year to retirement"
other: "%{count} years to retirement"
estimated_pension: Estimated Monthly Pension
- estimated_pension_grv: Estimated GRV Pension
after_tax: After tax
pension_gap: Monthly Pension Gap
target: "Target"
@@ -28,7 +27,7 @@ en:
inflation: Inflation Rate
tax_rate: Tax Rate
monthly_savings: Monthly Savings
- pension_history: Pension History (Renteninformation)
+ pension_history: Pension History
add_entry: Add new pension entry
date: Date
pension_points: Pension Points
@@ -36,11 +35,11 @@ en:
current_pension: Current Pension
projected_pension: Projected Pension
notes: Notes
- notes_placeholder: "e.g. Annual Renteninformation 2024"
+ notes_placeholder: "e.g. Annual pension statement 2024"
save_entry: Save Entry
confirm_delete: Are you sure you want to delete this entry?
no_entries: No pension entries yet
- no_entries_hint: Add entries from your Renteninformation to track your pension progress
+ no_entries_hint: Add entries from your pension statements to track your progress
setup_required: Please set up retirement planning first
form_fields: &retirement_form_fields_en
personal_section: Personal Information
@@ -53,11 +52,30 @@ en:
pension_section: Pension System
pension_system: Pension System
country: Country
- expected_annual_points: Expected Annual Points
- expected_annual_points_hint: Average salary = ~1.0 points/year
- rentenwert: Current Pension Value (Rentenwert)
- rentenwert_hint: Current value in EUR (2025 ~ 39.32)
- contribution_start_year: Contribution Start Year
+ # German GRV
+ de_expected_annual_points: Expected Annual Points
+ de_expected_annual_points_hint: Average salary = ~1.0 points/year
+ de_rentenwert: Current Pension Value (Rentenwert)
+ de_rentenwert_hint: Current value in EUR (2025 ~ 39.32)
+ de_contribution_start_year: Contribution Start Year
+ # US Social Security
+ us_estimated_monthly_benefit: Estimated Monthly Benefit
+ us_estimated_monthly_benefit_hint: From your SSA statement (ssa.gov)
+ # UK State Pension
+ uk_qualifying_years: Qualifying Years (NI)
+ uk_qualifying_years_hint: "35 years for full new State Pension"
+ uk_full_weekly_rate: Full Weekly Rate (£)
+ uk_full_weekly_rate_hint: "2024/25: £221.20/week"
+ # France Régime Général
+ fr_estimated_monthly_pension: Estimated Monthly Pension
+ fr_estimated_monthly_pension_hint: From your relevé de situation individuelle
+ fr_trimestres: Trimestres Validated
+ # Spain Social Security
+ es_estimated_monthly_pension: Estimated Monthly Pension
+ es_estimated_monthly_pension_hint: From your informe de vida laboral
+ es_contribution_years: Contribution Years
+ # Custom
+ custom_hint: Add pension entries manually to track your estimated pension.
assumptions_section: Assumptions
expected_return: Expected Annual Return (%)
inflation: Expected Inflation (%)
@@ -82,4 +100,8 @@ en:
pension_entry_removed: Pension entry removed
pension_systems:
de_grv: German Statutory Pension (GRV)
+ us_ss: US Social Security
+ uk_sp: UK State Pension
+ fr_regime: French Régime Général
+ es_ss: Spanish Social Security
custom: Custom / Other
diff --git a/config/locales/views/retirement/es.yml b/config/locales/views/retirement/es.yml
new file mode 100644
index 000000000..04a434ba3
--- /dev/null
+++ b/config/locales/views/retirement/es.yml
@@ -0,0 +1,101 @@
+---
+es:
+ retirement:
+ show:
+ page_title: Planificación de Jubilación
+ subtitle: Sigue tu camino hacia la independencia financiera y la preparación para la jubilación
+ current_age: Edad Actual
+ years_to_retirement:
+ one: "%{count} año para la jubilación"
+ other: "%{count} años para la jubilación"
+ estimated_pension: Pensión Mensual Estimada
+ after_tax: Después de impuestos
+ pension_gap: Brecha Mensual de Pensión
+ target: "Objetivo"
+ required_savings: Ahorro Mensual Necesario
+ per_month: al mes para cerrar la brecha de pensión
+ fire_progress: Progreso FIRE
+ current_portfolio: Cartera Actual
+ fire_number: Número FIRE
+ progress: Progreso
+ estimated_fire_date: Fecha FIRE Estimada
+ assumptions: Supuestos
+ edit: Editar
+ birth_year: Año de Nacimiento
+ retirement_age: Edad de Jubilación
+ expected_return: Rendimiento Esperado
+ inflation: Tasa de Inflación
+ tax_rate: Tasa Impositiva
+ monthly_savings: Ahorro Mensual
+ pension_history: Historial de Pensión
+ add_entry: Añadir nueva entrada de pensión
+ date: Fecha
+ pension_points: Puntos de Pensión
+ points_gained: Puntos Ganados
+ current_pension: Pensión Actual
+ projected_pension: Pensión Proyectada
+ notes: Notas
+ notes_placeholder: "ej. Informe de vida laboral 2024"
+ save_entry: Guardar Entrada
+ confirm_delete: "¿Estás seguro de querer eliminar esta entrada?"
+ no_entries: Aún no hay entradas de pensión
+ no_entries_hint: Añade entradas de tus informes de pensión para seguir tu progreso
+ setup_required: Por favor configura primero la planificación de jubilación
+ form_fields: &retirement_form_fields_es
+ personal_section: Información Personal
+ birth_year: Año de Nacimiento
+ retirement_age: Edad de Jubilación Deseada
+ financial_section: Objetivos Financieros
+ target_monthly_income: Ingreso Mensual Objetivo (Jubilación)
+ current_monthly_savings: Ahorro Mensual Actual
+ currency: Moneda
+ pension_section: Sistema de Pensiones
+ pension_system: Sistema de Pensiones
+ country: País
+ de_expected_annual_points: Puntos Anuales Esperados
+ de_expected_annual_points_hint: Salario promedio = ~1,0 puntos/año
+ de_rentenwert: Valor Actual de Pensión (Rentenwert)
+ de_rentenwert_hint: Valor actual en EUR (2025 ~ 39,32)
+ de_contribution_start_year: Año de Inicio de Contribuciones
+ us_estimated_monthly_benefit: Beneficio Mensual Estimado
+ us_estimated_monthly_benefit_hint: De tu declaración SSA (ssa.gov)
+ uk_qualifying_years: Años Calificados (NI)
+ uk_qualifying_years_hint: "35 años para la State Pension completa"
+ uk_full_weekly_rate: Tarifa Semanal Completa (£)
+ uk_full_weekly_rate_hint: "2024/25: £221,20/semana"
+ fr_estimated_monthly_pension: Pensión Mensual Estimada
+ fr_estimated_monthly_pension_hint: De tu relevé de situation individuelle
+ fr_trimestres: Trimestres Validados
+ es_estimated_monthly_pension: Pensión Mensual Estimada
+ es_estimated_monthly_pension_hint: De tu informe de vida laboral
+ es_contribution_years: Años de Cotización
+ custom_hint: Añade entradas de pensión manualmente para seguir tu pensión estimada.
+ assumptions_section: Supuestos
+ expected_return: Rendimiento Anual Esperado (%)
+ inflation: Inflación Esperada (%)
+ tax_rate: Tasa Impositiva en Jubilación (%)
+ setup:
+ page_title: Configurar Planificación de Jubilación
+ subtitle: Configura tus parámetros de planificación de jubilación y FIRE
+ cancel: Cancelar
+ save: Guardar y Continuar
+ edit:
+ page_title: Editar Configuración de Jubilación
+ subtitle: Actualiza tus parámetros de planificación de jubilación
+ cancel: Cancelar
+ save: Guardar Cambios
+ create:
+ created: La planificación de jubilación se ha configurado correctamente
+ update:
+ updated: Configuración de jubilación actualizada correctamente
+ add_pension_entry:
+ pension_entry_added: Entrada de pensión añadida correctamente
+ destroy_pension_entry:
+ pension_entry_removed: Entrada de pensión eliminada
+ pension_systems:
+ de_grv: Pensión Estatal Alemana (GRV)
+ us_ss: Seguridad Social de EE.UU.
+ uk_sp: Pensión Estatal del Reino Unido
+ fr_regime: Régimen General Francés
+ es_ss: Seguridad Social Española
+ custom: Personalizado / Otro
diff --git a/config/locales/views/retirement/fr.yml b/config/locales/views/retirement/fr.yml
new file mode 100644
index 000000000..42b2e29ea
--- /dev/null
+++ b/config/locales/views/retirement/fr.yml
@@ -0,0 +1,101 @@
+---
+fr:
+ retirement:
+ show:
+ page_title: Planification de la Retraite
+ subtitle: Suivez votre chemin vers l'indépendance financière et la préparation à la retraite
+ current_age: Âge Actuel
+ years_to_retirement:
+ one: "%{count} an avant la retraite"
+ other: "%{count} ans avant la retraite"
+ estimated_pension: Pension Mensuelle Estimée
+ after_tax: Après impôts
+ pension_gap: Écart Mensuel de Pension
+ target: "Objectif"
+ required_savings: Épargne Mensuelle Nécessaire
+ per_month: par mois pour combler l'écart de pension
+ fire_progress: Progression FIRE
+ current_portfolio: Portefeuille Actuel
+ fire_number: Nombre FIRE
+ progress: Progression
+ estimated_fire_date: Date FIRE Estimée
+ assumptions: Hypothèses
+ edit: Modifier
+ birth_year: Année de Naissance
+ retirement_age: Âge de Retraite
+ expected_return: Rendement Attendu
+ inflation: Taux d'Inflation
+ tax_rate: Taux d'Imposition
+ monthly_savings: Épargne Mensuelle
+ pension_history: Historique de Pension
+ add_entry: Ajouter une entrée de pension
+ date: Date
+ pension_points: Points de Pension
+ points_gained: Points Gagnés
+ current_pension: Pension Actuelle
+ projected_pension: Pension Projetée
+ notes: Notes
+ notes_placeholder: "ex. Relevé de situation individuelle 2024"
+ save_entry: Enregistrer
+ confirm_delete: "Êtes-vous sûr de vouloir supprimer cette entrée ?"
+ no_entries: Aucune entrée de pension
+ no_entries_hint: Ajoutez des entrées de vos relevés de pension pour suivre votre progression
+ setup_required: Veuillez d'abord configurer la planification de retraite
+ form_fields: &retirement_form_fields_fr
+ personal_section: Informations Personnelles
+ birth_year: Année de Naissance
+ retirement_age: Âge de Retraite Souhaité
+ financial_section: Objectifs Financiers
+ target_monthly_income: Revenu Mensuel Cible (Retraite)
+ current_monthly_savings: Épargne Mensuelle Actuelle
+ currency: Devise
+ pension_section: Système de Retraite
+ pension_system: Système de Retraite
+ country: Pays
+ de_expected_annual_points: Points Annuels Attendus
+ de_expected_annual_points_hint: Salaire moyen = ~1,0 point/an
+ de_rentenwert: Valeur Actuelle de la Pension (Rentenwert)
+ de_rentenwert_hint: Valeur actuelle en EUR (2025 ~ 39,32)
+ de_contribution_start_year: Année de Début des Cotisations
+ us_estimated_monthly_benefit: Prestation Mensuelle Estimée
+ us_estimated_monthly_benefit_hint: De votre relevé SSA (ssa.gov)
+ uk_qualifying_years: Années de Qualification (NI)
+ uk_qualifying_years_hint: "35 ans pour la State Pension complète"
+ uk_full_weekly_rate: Taux Hebdomadaire Complet (£)
+ uk_full_weekly_rate_hint: "2024/25 : £221,20/semaine"
+ fr_estimated_monthly_pension: Pension Mensuelle Estimée
+ fr_estimated_monthly_pension_hint: De votre relevé de situation individuelle
+ fr_trimestres: Trimestres Validés
+ es_estimated_monthly_pension: Pension Mensuelle Estimée
+ es_estimated_monthly_pension_hint: De votre informe de vida laboral
+ es_contribution_years: Années de Cotisation
+ custom_hint: Ajoutez des entrées de pension manuellement pour suivre votre pension estimée.
+ assumptions_section: Hypothèses
+ expected_return: Rendement Annuel Attendu (%)
+ inflation: Inflation Attendue (%)
+ tax_rate: Taux d'Imposition à la Retraite (%)
+ setup:
+ page_title: Configurer la Planification de Retraite
+ subtitle: Configurez vos paramètres de planification de retraite et FIRE
+ cancel: Annuler
+ save: Enregistrer et Continuer
+ edit:
+ page_title: Modifier les Paramètres de Retraite
+ subtitle: Mettez à jour vos paramètres de planification de retraite
+ cancel: Annuler
+ save: Enregistrer les Modifications
+ create:
+ created: La planification de la retraite a été configurée avec succès
+ update:
+ updated: Paramètres de retraite mis à jour avec succès
+ add_pension_entry:
+ pension_entry_added: Entrée de pension ajoutée avec succès
+ destroy_pension_entry:
+ pension_entry_removed: Entrée de pension supprimée
+ pension_systems:
+ de_grv: Pension Légale Allemande (GRV)
+ us_ss: Sécurité Sociale Américaine
+ uk_sp: Pension d'État Britannique
+ fr_regime: Régime Général Français
+ es_ss: Sécurité Sociale Espagnole
+ custom: Personnalisé / Autre
diff --git a/db/migrate/20260409100000_generalize_pension_systems.rb b/db/migrate/20260409100000_generalize_pension_systems.rb
new file mode 100644
index 000000000..bcc12793f
--- /dev/null
+++ b/db/migrate/20260409100000_generalize_pension_systems.rb
@@ -0,0 +1,32 @@
+class GeneralizePensionSystems < ActiveRecord::Migration[7.2]
+ def change
+ # Add JSONB column for system-specific parameters (replaces rentenwert, expected_annual_points)
+ add_column :retirement_configs, :pension_params, :jsonb, null: false, default: {}
+
+ # Add JSONB column for system-specific entry data
+ add_column :pension_entries, :data, :jsonb, null: false, default: {}
+
+ # Make current_points nullable (only used by points-based systems like DE)
+ change_column_null :pension_entries, :current_points, true
+
+ # Migrate existing data: move rentenwert and expected_annual_points into pension_params
+ reversible do |dir|
+ dir.up do
+ execute <<-SQL.squish
+ UPDATE retirement_configs
+ SET pension_params = jsonb_build_object(
+ 'rentenwert', COALESCE(rentenwert, 39.32),
+ 'expected_annual_points', COALESCE(expected_annual_points, 1.0),
+ 'contribution_start_year', contribution_start_year
+ )
+ WHERE pension_system = 'de_grv'
+ SQL
+ end
+ end
+
+ # Remove old German-specific columns
+ remove_column :retirement_configs, :rentenwert, :decimal, precision: 8, scale: 2
+ remove_column :retirement_configs, :expected_annual_points, :decimal, precision: 5, scale: 2
+ remove_column :retirement_configs, :contribution_start_year, :integer
+ end
+end
diff --git a/test/controllers/retirement_controller_test.rb b/test/controllers/retirement_controller_test.rb
index e521c2a08..eeb200f27 100644
--- a/test/controllers/retirement_controller_test.rb
+++ b/test/controllers/retirement_controller_test.rb
@@ -44,7 +44,12 @@ class RetirementControllerTest < ActionDispatch::IntegrationTest
country: "DE",
expected_return_pct: 7.0,
inflation_pct: 2.0,
- tax_rate_pct: 26.38
+ tax_rate_pct: 26.38,
+ pension_params: {
+ expected_annual_points: 1.0,
+ rentenwert: 39.32,
+ contribution_start_year: 2015
+ }
}
}
end
diff --git a/test/fixtures/retirement_configs.yml b/test/fixtures/retirement_configs.yml
index 6573978ea..b713e5a93 100644
--- a/test/fixtures/retirement_configs.yml
+++ b/test/fixtures/retirement_configs.yml
@@ -10,9 +10,10 @@ dylan_retirement:
inflation_pct: 2.0
tax_rate_pct: 26.38
current_monthly_savings: 500.0
- contribution_start_year: 2015
- expected_annual_points: 1.0
- rentenwert: 39.32
+ pension_params:
+ expected_annual_points: 1.0
+ rentenwert: 39.32
+ contribution_start_year: 2015
custom_retirement:
family: empty
@@ -26,3 +27,18 @@ custom_retirement:
inflation_pct: 2.5
tax_rate_pct: 22.0
current_monthly_savings: 800.0
+
+us_retirement:
+ family: inactive_trial
+ country: US
+ pension_system: us_ss
+ birth_year: 1980
+ retirement_age: 67
+ target_monthly_income: 5000.0
+ currency: USD
+ expected_return_pct: 7.0
+ inflation_pct: 2.5
+ tax_rate_pct: 24.0
+ current_monthly_savings: 1000.0
+ pension_params:
+ estimated_monthly_benefit: 2800.0
diff --git a/test/models/pension_entry_test.rb b/test/models/pension_entry_test.rb
index e9ae4b1c0..21a2a49e1 100644
--- a/test/models/pension_entry_test.rb
+++ b/test/models/pension_entry_test.rb
@@ -15,9 +15,9 @@ class PensionEntryTest < ActiveSupport::TestCase
assert_not @entry_2024.valid?
end
- test "requires current_points" do
+ test "current_points is optional" do
@entry_2024.current_points = nil
- assert_not @entry_2024.valid?
+ assert @entry_2024.valid?
end
test "current_points must be non-negative" do
|