1.4.1 (build 42): contribución por snapshot, fixes Period vs Period y navegación iPad

- 151: contribución editable por snapshot en cualquier modo (quitado gate detailed)
  con diálogo de propagación al editar: solo este / adelante / atrás / todos
  (SnapshotRepository.propagateContribution, SnapshotFormViewModel.contributionChanged)
- 148: Period vs Period mostraba mal el último mes del period B — la agrupación usaba
  chartMonth(for:) que aplicaba el grace-period del check-in a snapshots históricos;
  ahora agrupa por mes calendario crudo
- 152: iPad Sources no saltaba entre fuentes — añadido .id() al SourceDetailView para
  recrear el StateObject al cambiar de selección
- 146/147: Year vs Year con forecast del año en curso (asterisco de estimado) y KPIs
  arriba / detalle debajo
- 145: card de contribución mensual en SourceDetailView
- Nuevas claves snapshot_contribution_propagate_* en los 7 idiomas

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
This commit is contained in:
alexandrev-tibco
2026-06-30 16:51:03 +02:00
parent 54dfd8a8e3
commit 7a18dd8360
48 changed files with 2854 additions and 608 deletions
+2 -2
View File
@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<string>1.4.1</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
@@ -30,7 +30,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<string>42</string>
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-1549720748100858~9632507420</string>
<key>GADDelayAppMeasurementInit</key>
@@ -311,6 +311,7 @@
"category_name_placeholder" = "z.B. Notfallfonds";
"chart_yoy_empty" = "Füge Snapshots über verschiedene Jahre hinzu, um den Vergleich zu sehen.";
"chart_yoy_title" = "Jahr für Jahr";
"chart_yoy_estimated_note" = "* geschätzt (Jahresend-Prognose)";
"checking_icloud" = "iCloud wird geprüft...";
"contributions_vs_returns_invested" = "Investiert";
"contributions_vs_returns_returns" = "Marktrendite";
@@ -354,6 +355,12 @@
"reengagement_body" = "Ein paar Minuten reichen, um deine Investitionen im Blick zu behalten.";
"reengagement_title" = "Dein Portfolio wartet";
"snapshot_duplicate_add" = "Trotzdem hinzufügen";
"snapshot_contribution_propagate_title" = "Beitrag anwenden";
"snapshot_contribution_propagate_message" = "%@ auch als Beitrag auf andere Einträge anwenden?";
"snapshot_contribution_propagate_forward" = "Auf spätere anwenden";
"snapshot_contribution_propagate_backward" = "Auf frühere anwenden";
"snapshot_contribution_propagate_all" = "Auf alle anwenden";
"snapshot_contribution_propagate_this" = "Nur dieser Eintrag";
"snapshot_duplicate_message" = "Du hast bereits einen Snapshot für diesen Monat. Möchtest du einen weiteren hinzufügen?";
"snapshot_duplicate_replace" = "Vorhandenen ersetzen";
"snapshot_duplicate_title" = "Snapshot bereits vorhanden";
@@ -384,3 +391,26 @@
"whats_new_goals_title" = "Intelligentere Ziele & Journal";
"whats_new_goals_body" = "Verbesserte Leerzustände helfen dir, schneller zu starten.";
"whats_new_continue" = "Los geht's";
"source_monthly_contribution_title" = "Monatlicher Beitrag";
"source_monthly_contribution_placeholder" = "z.B. 500";
"source_monthly_contribution_not_set" = "Nicht konfiguriert";
"source_monthly_contribution_hint" = "Wird in der Schnellaktualisierung vorausgefüllt";
"quick_update_contribution_label" = "Beitrag";
"quick_update_contribution_placeholder" = "Betrag";
"source_monthly_contribution_apply_title" = "Beitrag anwenden";
"source_monthly_contribution_apply_message" = "%@ auf alle früheren Snapshots ohne Beitrag anwenden?";
"source_monthly_contribution_apply_retroactive" = "Auf alle früheren Snapshots anwenden";
"source_monthly_contribution_apply_forward" = "Nur ab jetzt";
// MARK: - Portfolio Insights
"insights_section_title" = "Einblicke";
"insight_milestone_title" = "Meilenstein in Sicht";
"insight_milestone_value" = "%1$@ bis %2$@";
"insight_ytd_title" = "Jahresbeginn";
"insight_market_gains_title" = "Marktgewinne";
"insight_market_gains_value" = "+%@ aus Märkten";
"insight_streak_title" = "Tracking-Serie";
"insight_streak_value" = "%d Monate in Folge";
"insight_forecast_title" = "Auf dem Weg zu";
"notification_milestone_title" = "Portfolio-Meilenstein! 🎉";
"notification_milestone_body" = "Dein Portfolio hat gerade %@ überschritten. Herzlichen Glückwunsch!";
@@ -354,12 +354,19 @@
"snapshot_duplicate_message" = "You already have a snapshot for this month. Do you want to add another one?";
"snapshot_duplicate_replace" = "Replace Existing";
"snapshot_duplicate_add" = "Add Anyway";
"snapshot_contribution_propagate_title" = "Apply Contribution";
"snapshot_contribution_propagate_message" = "Apply %@ as the contribution to other snapshots too?";
"snapshot_contribution_propagate_forward" = "Apply Going Forward";
"snapshot_contribution_propagate_backward" = "Apply to Earlier";
"snapshot_contribution_propagate_all" = "Apply to All";
"snapshot_contribution_propagate_this" = "Only This Snapshot";
"contributions_vs_returns_title" = "Invested vs. Returns";
"contributions_vs_returns_invested" = "Invested";
"contributions_vs_returns_returns" = "Market Returns";
"chart_yoy_title" = "Year over Year";
"chart_yoy_estimated_note" = "* estimated (year-end forecast)";
"chart_yoy_empty" = "Add more snapshots across different years to see the comparison.";
// MARK: - Goal Delete Confirmation (1.3.2)
@@ -392,3 +399,26 @@
"whats_new_goals_title" = "Smarter Goals & Journal";
"whats_new_goals_body" = "Improved empty states help you get started faster.";
"whats_new_continue" = "Let's Go";
"source_monthly_contribution_title" = "Monthly Contribution";
"source_monthly_contribution_placeholder" = "e.g. 500";
"source_monthly_contribution_not_set" = "Not configured";
"source_monthly_contribution_hint" = "Pre-filled as contribution in Quick Update";
"quick_update_contribution_label" = "Contribution";
"quick_update_contribution_placeholder" = "Amount";
"source_monthly_contribution_apply_title" = "Apply Contribution";
"source_monthly_contribution_apply_message" = "Apply %@ to all past snapshots that don't have a contribution?";
"source_monthly_contribution_apply_retroactive" = "Apply to All Past Snapshots";
"source_monthly_contribution_apply_forward" = "Only Going Forward";
// MARK: - Portfolio Insights
"insights_section_title" = "Insights";
"insight_milestone_title" = "Milestone ahead";
"insight_milestone_value" = "%1$@ from %2$@";
"insight_ytd_title" = "Year to date";
"insight_market_gains_title" = "Market gains";
"insight_market_gains_value" = "+%@ from markets";
"insight_streak_title" = "Tracking streak";
"insight_streak_value" = "%d months in a row";
"insight_forecast_title" = "On track for";
"notification_milestone_title" = "Portfolio Milestone! 🎉";
"notification_milestone_body" = "Your portfolio just passed %@. Congrats!";
@@ -295,12 +295,19 @@
"snapshot_duplicate_message" = "Ya tienes un snapshot para este mes. ¿Quieres añadir otro?";
"snapshot_duplicate_replace" = "Reemplazar existente";
"snapshot_duplicate_add" = "Añadir de todos modos";
"snapshot_contribution_propagate_title" = "Aplicar aportación";
"snapshot_contribution_propagate_message" = "¿Aplicar %@ como aportación también a otros registros?";
"snapshot_contribution_propagate_forward" = "Aplicar hacia adelante";
"snapshot_contribution_propagate_backward" = "Aplicar hacia atrás";
"snapshot_contribution_propagate_all" = "Aplicar a todos";
"snapshot_contribution_propagate_this" = "Solo este registro";
"contributions_vs_returns_title" = "Invertido vs. Rentabilidad";
"contributions_vs_returns_invested" = "Invertido";
"contributions_vs_returns_returns" = "Rentabilidad de mercado";
"chart_yoy_title" = "Año a año";
"chart_yoy_estimated_note" = "* estimado (previsión de cierre de año)";
"chart_yoy_empty" = "Añade más snapshots en diferentes años para ver la comparación.";
"Year vs Year" = "Año a año";
@@ -336,3 +343,26 @@
"whats_new_goals_body" = "Los estados vacíos mejorados te ayudan a empezar más rápido.";
"whats_new_continue" = "¡Vamos!";
"goal_delete_confirm" = "Eliminar";
"source_monthly_contribution_title" = "Aportación Mensual";
"source_monthly_contribution_placeholder" = "ej. 500";
"source_monthly_contribution_not_set" = "No configurada";
"source_monthly_contribution_hint" = "Se rellena automáticamente en Actualización Rápida";
"quick_update_contribution_label" = "Aportación";
"quick_update_contribution_placeholder" = "Importe";
"source_monthly_contribution_apply_title" = "Aplicar Aportación";
"source_monthly_contribution_apply_message" = "¿Aplicar %@ a todos los snapshots anteriores sin aportación?";
"source_monthly_contribution_apply_retroactive" = "Aplicar a Todos los Históricos";
"source_monthly_contribution_apply_forward" = "Solo a Partir de Ahora";
// MARK: - Portfolio Insights
"insights_section_title" = "Perspectivas";
"insight_milestone_title" = "Hito cerca";
"insight_milestone_value" = "%1$@ para %2$@";
"insight_ytd_title" = "Año en curso";
"insight_market_gains_title" = "Ganancias de mercado";
"insight_market_gains_value" = "+%@ en mercados";
"insight_streak_title" = "Racha de seguimiento";
"insight_streak_value" = "%d meses consecutivos";
"insight_forecast_title" = "En camino hacia";
"notification_milestone_title" = "¡Hito de cartera! 🎉";
"notification_milestone_body" = "Tu cartera acaba de superar %@. ¡Enhorabuena!";
@@ -311,6 +311,7 @@
"category_name_placeholder" = "ex. Fonds d'urgence";
"chart_yoy_empty" = "Ajoutez plus de snapshots sur différentes années pour voir la comparaison.";
"chart_yoy_title" = "Année par année";
"chart_yoy_estimated_note" = "* estimé (prévision de fin d'année)";
"checking_icloud" = "Vérification d'iCloud...";
"contributions_vs_returns_invested" = "Investi";
"contributions_vs_returns_returns" = "Rendement du marché";
@@ -354,6 +355,12 @@
"reengagement_body" = "Quelques minutes suffisent pour rester au top de vos investissements.";
"reengagement_title" = "Votre portefeuille vous attend";
"snapshot_duplicate_add" = "Ajouter quand même";
"snapshot_contribution_propagate_title" = "Appliquer la contribution";
"snapshot_contribution_propagate_message" = "Appliquer %@ comme contribution aux autres relevés aussi ?";
"snapshot_contribution_propagate_forward" = "Appliquer aux suivants";
"snapshot_contribution_propagate_backward" = "Appliquer aux précédents";
"snapshot_contribution_propagate_all" = "Appliquer à tous";
"snapshot_contribution_propagate_this" = "Ce relevé uniquement";
"snapshot_duplicate_message" = "Vous avez déjà un snapshot pour ce mois. Voulez-vous en ajouter un autre ?";
"snapshot_duplicate_replace" = "Remplacer l'existant";
"snapshot_duplicate_title" = "Snapshot déjà existant";
@@ -384,3 +391,26 @@
"whats_new_goals_title" = "Objectifs et journal plus intelligents";
"whats_new_goals_body" = "Les états vides améliorés vous aident à démarrer plus rapidement.";
"whats_new_continue" = "C'est parti !";
"source_monthly_contribution_title" = "Apport Mensuel";
"source_monthly_contribution_placeholder" = "ex. 500";
"source_monthly_contribution_not_set" = "Non configuré";
"source_monthly_contribution_hint" = "Pré-rempli dans Mise à jour rapide";
"quick_update_contribution_label" = "Apport";
"quick_update_contribution_placeholder" = "Montant";
"source_monthly_contribution_apply_title" = "Appliquer l'apport";
"source_monthly_contribution_apply_message" = "Appliquer %@ à tous les snapshots passés sans apport ?";
"source_monthly_contribution_apply_retroactive" = "Appliquer à tous les anciens snapshots";
"source_monthly_contribution_apply_forward" = "Seulement à partir de maintenant";
// MARK: - Portfolio Insights
"insights_section_title" = "Aperçus";
"insight_milestone_title" = "Jalon en vue";
"insight_milestone_value" = "%1$@ de %2$@";
"insight_ytd_title" = "Depuis le début de l'année";
"insight_market_gains_title" = "Gains de marché";
"insight_market_gains_value" = "+%@ des marchés";
"insight_streak_title" = "Série de suivi";
"insight_streak_value" = "%d mois consécutifs";
"insight_forecast_title" = "En bonne voie pour";
"notification_milestone_title" = "Jalon de portefeuille ! 🎉";
"notification_milestone_body" = "Votre portefeuille vient de dépasser %@. Félicitations !";
@@ -311,6 +311,7 @@
"category_name_placeholder" = "es. Fondo di emergenza";
"chart_yoy_empty" = "Aggiungi più snapshot in anni diversi per vedere il confronto.";
"chart_yoy_title" = "Anno su anno";
"chart_yoy_estimated_note" = "* stimato (previsione di fine anno)";
"checking_icloud" = "Verifica iCloud...";
"contributions_vs_returns_invested" = "Investito";
"contributions_vs_returns_returns" = "Rendimento di mercato";
@@ -354,6 +355,12 @@
"reengagement_body" = "Pochi minuti bastano per tenere sotto controllo i tuoi investimenti.";
"reengagement_title" = "Il tuo portafoglio ti aspetta";
"snapshot_duplicate_add" = "Aggiungi comunque";
"snapshot_contribution_propagate_title" = "Applica contributo";
"snapshot_contribution_propagate_message" = "Applicare %@ come contributo anche agli altri snapshot?";
"snapshot_contribution_propagate_forward" = "Applica ai successivi";
"snapshot_contribution_propagate_backward" = "Applica ai precedenti";
"snapshot_contribution_propagate_all" = "Applica a tutti";
"snapshot_contribution_propagate_this" = "Solo questo snapshot";
"snapshot_duplicate_message" = "Hai già uno snapshot per questo mese. Vuoi aggiungerne un altro?";
"snapshot_duplicate_replace" = "Sostituisci esistente";
"snapshot_duplicate_title" = "Snapshot già esistente";
@@ -384,3 +391,26 @@
"whats_new_goals_title" = "Obiettivi e diario più intelligenti";
"whats_new_goals_body" = "Gli stati vuoti migliorati ti aiutano a iniziare più velocemente.";
"whats_new_continue" = "Iniziamo!";
"source_monthly_contribution_title" = "Contributo Mensile";
"source_monthly_contribution_placeholder" = "es. 500";
"source_monthly_contribution_not_set" = "Non configurato";
"source_monthly_contribution_hint" = "Pre-compilato in Aggiornamento Rapido";
"quick_update_contribution_label" = "Contributo";
"quick_update_contribution_placeholder" = "Importo";
"source_monthly_contribution_apply_title" = "Applica Contributo";
"source_monthly_contribution_apply_message" = "Applicare %@ a tutti gli snapshot passati senza contributo?";
"source_monthly_contribution_apply_retroactive" = "Applica a tutti i snapshot passati";
"source_monthly_contribution_apply_forward" = "Solo da adesso in poi";
// MARK: - Portfolio Insights
"insights_section_title" = "Approfondimenti";
"insight_milestone_title" = "Traguardo vicino";
"insight_milestone_value" = "%1$@ da %2$@";
"insight_ytd_title" = "Da inizio anno";
"insight_market_gains_title" = "Guadagni di mercato";
"insight_market_gains_value" = "+%@ dai mercati";
"insight_streak_title" = "Serie di aggiornamenti";
"insight_streak_value" = "%d mesi consecutivi";
"insight_forecast_title" = "In direzione di";
"notification_milestone_title" = "Traguardo raggiunto! 🎉";
"notification_milestone_body" = "Il tuo portafoglio ha appena superato %@. Complimenti!";
@@ -311,6 +311,7 @@
"category_name_placeholder" = "例:緊急資金";
"chart_yoy_empty" = "比較を表示するには、異なる年のスナップショットを追加してください。";
"chart_yoy_title" = "年次比較";
"chart_yoy_estimated_note" = "* 推定(年末予測)";
"checking_icloud" = "iCloudを確認中...";
"contributions_vs_returns_invested" = "投資額";
"contributions_vs_returns_returns" = "市場リターン";
@@ -354,6 +355,12 @@
"reengagement_body" = "数分で投資の状況を把握できます。";
"reengagement_title" = "ポートフォリオが待っています";
"snapshot_duplicate_add" = "とにかく追加";
"snapshot_contribution_propagate_title" = "拠出を適用";
"snapshot_contribution_propagate_message" = "%@ を他のスナップショットにも拠出として適用しますか?";
"snapshot_contribution_propagate_forward" = "以降に適用";
"snapshot_contribution_propagate_backward" = "以前に適用";
"snapshot_contribution_propagate_all" = "すべてに適用";
"snapshot_contribution_propagate_this" = "このスナップショットのみ";
"snapshot_duplicate_message" = "今月のスナップショットはすでにあります。別のスナップショットを追加しますか?";
"snapshot_duplicate_replace" = "既存を置き換え";
"snapshot_duplicate_title" = "スナップショットがすでに存在します";
@@ -384,3 +391,26 @@
"whats_new_goals_title" = "スマートな目標とジャーナル";
"whats_new_goals_body" = "改善された空の状態でより素早く始められます。";
"whats_new_continue" = "始めよう";
"source_monthly_contribution_title" = "月次積立額";
"source_monthly_contribution_placeholder" = "例: 500";
"source_monthly_contribution_not_set" = "未設定";
"source_monthly_contribution_hint" = "クイック更新で自動入力されます";
"quick_update_contribution_label" = "積立";
"quick_update_contribution_placeholder" = "金額";
"source_monthly_contribution_apply_title" = "積立の適用";
"source_monthly_contribution_apply_message" = "積立なしの過去のスナップショットすべてに%@を適用しますか?";
"source_monthly_contribution_apply_retroactive" = "過去のスナップショットにも適用";
"source_monthly_contribution_apply_forward" = "今後のみ適用";
// MARK: - Portfolio Insights
"insights_section_title" = "インサイト";
"insight_milestone_title" = "目標まであと少し";
"insight_milestone_value" = "%2$@まであと%1$@";
"insight_ytd_title" = "年初来";
"insight_market_gains_title" = "市場利益";
"insight_market_gains_value" = "市場から+%@";
"insight_streak_title" = "追跡連続記録";
"insight_streak_value" = "%dヶ月連続";
"insight_forecast_title" = "目標に向けて順調";
"notification_milestone_title" = "ポートフォリオ達成!🎉";
"notification_milestone_body" = "ポートフォリオが%@を突破しました。おめでとうございます!";
@@ -311,6 +311,7 @@
"category_name_placeholder" = "ex.: Reserva de emergência";
"chart_yoy_empty" = "Adicione mais snapshots em anos diferentes para ver a comparação.";
"chart_yoy_title" = "Ano a ano";
"chart_yoy_estimated_note" = "* estimado (previsão de fim de ano)";
"checking_icloud" = "Verificando iCloud...";
"contributions_vs_returns_invested" = "Investido";
"contributions_vs_returns_returns" = "Retorno do mercado";
@@ -354,6 +355,12 @@
"reengagement_body" = "Alguns minutos são suficientes para manter o controle dos seus investimentos.";
"reengagement_title" = "Seu portfólio está esperando";
"snapshot_duplicate_add" = "Adicionar mesmo assim";
"snapshot_contribution_propagate_title" = "Aplicar contribuição";
"snapshot_contribution_propagate_message" = "Aplicar %@ como contribuição também a outros registros?";
"snapshot_contribution_propagate_forward" = "Aplicar adiante";
"snapshot_contribution_propagate_backward" = "Aplicar para trás";
"snapshot_contribution_propagate_all" = "Aplicar a todos";
"snapshot_contribution_propagate_this" = "Apenas este registro";
"snapshot_duplicate_message" = "Você já tem um snapshot para este mês. Deseja adicionar outro?";
"snapshot_duplicate_replace" = "Substituir existente";
"snapshot_duplicate_title" = "Snapshot já existe";
@@ -384,3 +391,26 @@
"whats_new_goals_title" = "Metas e diário mais inteligentes";
"whats_new_goals_body" = "Estados vazios melhorados ajudam você a começar mais rápido.";
"whats_new_continue" = "Vamos lá!";
"source_monthly_contribution_title" = "Aporte Mensal";
"source_monthly_contribution_placeholder" = "ex. 500";
"source_monthly_contribution_not_set" = "Não configurado";
"source_monthly_contribution_hint" = "Preenchido automaticamente na Atualização Rápida";
"quick_update_contribution_label" = "Aporte";
"quick_update_contribution_placeholder" = "Valor";
"source_monthly_contribution_apply_title" = "Aplicar Aporte";
"source_monthly_contribution_apply_message" = "Aplicar %@ a todos os snapshots anteriores sem aporte?";
"source_monthly_contribution_apply_retroactive" = "Aplicar a Todos os Snapshots Anteriores";
"source_monthly_contribution_apply_forward" = "Apenas daqui para frente";
// MARK: - Portfolio Insights
"insights_section_title" = "Insights";
"insight_milestone_title" = "Meta à vista";
"insight_milestone_value" = "%1$@ para %2$@";
"insight_ytd_title" = "No ano";
"insight_market_gains_title" = "Ganhos de mercado";
"insight_market_gains_value" = "+%@ dos mercados";
"insight_streak_title" = "Sequência de atualizações";
"insight_streak_value" = "%d meses consecutivos";
"insight_forecast_title" = "Caminhando para";
"notification_milestone_title" = "Meta de portfólio! 🎉";
"notification_milestone_body" = "Seu portfólio acabou de ultrapassar %@. Parabéns!";