Desactiva auto-dedup (pérdida de datos cross-device) + forward-fill en gráficas de evolution

CRÍTICO: el auto-dedup de build 68 borraba entidades padre con regla Cascade
(Account/Category). Al propagarse el delete por CloudKit, otros dispositivos
aplicaban la cascada al importar ANTES de la reasignación de hijos → wipe de
sources/snapshots. Vació iPhone y iPad. Quitadas las llamadas automáticas a
cleanupLogicalDuplicates() en processRemoteChanges y en la carga; el UUID-dedup
seguro se mantiene. Métodos quedan definidos para un rediseño CloudKit-safe.

Gráficas: forward-fill por source en evolution/allocation/period para que el
total de cada mes refleje el último valor de CADA source (no solo las
actualizadas ese mes) y el último punto coincida con el total del portfolio.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2p3gUZRNWW388rWFRjiU7
This commit is contained in:
alexandrev-tibco
2026-07-22 19:58:13 +02:00
parent 20b8a32ea3
commit 99dc855467
3 changed files with 83 additions and 50 deletions
+9 -13
View File
@@ -213,11 +213,12 @@ class CoreDataStack: ObservableObject {
}
DispatchQueue.main.async {
self?.isLoaded = true
// Collapse any logical duplicates already sitting in the local store
// (per-device default accounts/categories piled up by earlier syncs).
// Runs once at launch so an existing mess is cleaned even if no new
// remote change arrives this session.
self?.cleanupLogicalDuplicates()
// NOTE: automatic logical dedup DISABLED. Deleting cascade-parent
// entities (Account/Category) and letting those deletes propagate via
// CloudKit can trigger the Cascade delete rule on OTHER devices before
// the child-reassignment transaction imports cascade-wiping sources
// and snapshots. This caused total data loss across devices on build 68.
// Dedup must be redesigned to be CloudKit-safe before re-enabling.
MonthlyCheckInStore.migrateIfNeeded()
// Migrate device-local UserDefaults data into Core Data so it syncs via iCloud.
MonthlyContributionStore.migrateIfNeeded(context: container.viewContext)
@@ -506,14 +507,9 @@ class CoreDataStack: ObservableObject {
if removed > 0 {
print("[RemoteChanges] Removed \(removed) duplicate objects after CloudKit import")
}
// Then collapse *logical* duplicates (same entity, different UUIDs)
// e.g. per-device default accounts/categories, or a dataset that was
// seeded independently in two environments. UUID dedup above can't see
// these because the UUIDs differ.
let logical = self.cleanupLogicalDuplicates()
if logical > 0 {
print("[RemoteChanges] Removed \(logical) logical duplicate objects after CloudKit import")
}
// NOTE: automatic logical dedup DISABLED here on purpose see the load
// block. Deleting cascade-parent entities and propagating those deletes
// via CloudKit cascade-wiped children on other devices (build 68 incident).
// Notify repositories to re-fetch unconditionally.
NotificationCenter.default.post(name: .cloudKitForceReload, object: nil)
self.objectWillChange.send()