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
@@ -30,6 +30,11 @@ class SnapshotFormViewModel: ObservableObject {
let mode: Mode
let source: InvestmentSource
/// Contribution stored on the snapshot when editing started (nil in add mode or
/// when the snapshot had no contribution). Used to detect whether the user changed
/// the contribution and should be offered to propagate it to other snapshots.
private(set) var originalContribution: Decimal?
// MARK: - Dependencies
private var cancellables = Set<AnyCancellable>()
@@ -63,6 +68,7 @@ class SnapshotFormViewModel: ObservableObject {
if let contribution = snapshot.contribution {
includeContribution = true
contributionString = formatDecimalForInput(contribution.decimalValue)
originalContribution = contribution.decimalValue
}
notes = snapshot.notes ?? ""
}
@@ -128,6 +134,12 @@ class SnapshotFormViewModel: ObservableObject {
return parseDecimal(trimmed)
}
/// True when the current contribution differs from the value the snapshot had when
/// editing started. Drives the "propagate to other snapshots" prompt.
var contributionChanged: Bool {
contribution != originalContribution
}
var formattedValue: String {
guard let value = value else { return CurrencyFormatter.format(Decimal.zero) }
return value.currencyString