Primera build enviada

This commit is contained in:
2026-01-19 14:40:43 +01:00
parent c6be398e5a
commit b03d35194f
36 changed files with 1641 additions and 561 deletions
+92 -22
View File
@@ -11,6 +11,7 @@ class ImportService {
let accountsCreated: Int
let sourcesCreated: Int
let snapshotsCreated: Int
let snapshotsUpdated: Int
let errors: [String]
}
@@ -385,11 +386,13 @@ Personal,Real Estate,Rental Property,2024-01-01,82000,80000,Estimated value
var accountsCreated = 0
var sourcesCreated = 0
var snapshotsCreated = 0
var snapshotsUpdated = 0
var errors: [String] = []
var categoryLookup = buildCategoryLookup(from: categoryRepository.categories)
let otherCategory = resolveExistingCategory(named: "Other", lookup: categoryLookup) ??
categoryRepository.createCategory(
let existingCategories = fetchCategories(in: context)
var categoryLookup = buildCategoryLookup(from: existingCategories)
let otherCategory = resolveExistingCategory(named: "Other", lookup: categoryLookup)
?? categoryRepository.createCategory(
name: "Other",
colorHex: "#64748B",
icon: "ellipsis.circle.fill"
@@ -398,17 +401,22 @@ Personal,Real Estate,Rental Property,2024-01-01,82000,80000,Estimated value
var completionDatesByMonth: [String: Date] = [:]
for importedAccount in accounts {
let existingAccount = accountRepository.accounts.first(where: { $0.name == importedAccount.name })
let account = existingAccount ?? accountRepository.createAccount(
name: importedAccount.name,
currency: importedAccount.currency,
inputMode: importedAccount.inputMode,
notificationFrequency: importedAccount.notificationFrequency,
customFrequencyMonths: importedAccount.customFrequencyMonths
)
// Build lookup of existing accounts by fetching directly from database
let existingAccountsLookup = fetchAccountsLookup(in: context)
if existingAccount == nil {
for importedAccount in accounts {
let existingAccount = existingAccountsLookup[importedAccount.name]
let account: Account
if let existing = existingAccount {
account = existing
} else {
account = accountRepository.createAccount(
name: importedAccount.name,
currency: importedAccount.currency,
inputMode: importedAccount.inputMode,
notificationFrequency: importedAccount.notificationFrequency,
customFrequencyMonths: importedAccount.customFrequencyMonths
)
accountsCreated += 1
}
@@ -431,8 +439,9 @@ Personal,Real Estate,Rental Property,2024-01-01,82000,80000,Estimated value
categoryLookup[normalizedCategoryName(category.name)] = category
for importedSource in importedCategory.sources {
let accountId = account.safeId
let existingSource = sourceRepository.sources.first(where: {
$0.name == importedSource.name && $0.account?.id == account.id
$0.name == importedSource.name && $0.account?.id == accountId
})
let source = existingSource ?? sourceRepository.createSource(
name: importedSource.name,
@@ -446,14 +455,30 @@ Personal,Real Estate,Rental Property,2024-01-01,82000,80000,Estimated value
}
for snapshot in importedSource.snapshots {
snapshotRepository.createSnapshot(
for: source,
date: snapshot.date,
value: snapshot.value,
contribution: snapshot.contribution,
notes: snapshot.notes
)
snapshotsCreated += 1
// Check if a snapshot with the same date already exists for this source
let existingSnapshot = fetchSnapshot(for: source, date: snapshot.date, in: context)
if let existing = existingSnapshot {
// Update existing snapshot instead of creating a duplicate
existing.value = NSDecimalNumber(decimal: snapshot.value)
if let contribution = snapshot.contribution {
existing.contribution = NSDecimalNumber(decimal: contribution)
}
if let notes = snapshot.notes, !notes.isEmpty {
existing.notes = notes
}
snapshotsUpdated += 1
} else {
// Create new snapshot
snapshotRepository.createSnapshot(
for: source,
date: snapshot.date,
value: snapshot.value,
contribution: snapshot.contribution,
notes: snapshot.notes
)
snapshotsCreated += 1
}
snapshotProgress?(snapshotsCreated)
let monthKey = MonthlyCheckInStore.monthKey(for: snapshot.date)
@@ -491,6 +516,7 @@ Personal,Real Estate,Rental Property,2024-01-01,82000,80000,Estimated value
accountsCreated: accountsCreated,
sourcesCreated: sourcesCreated,
snapshotsCreated: snapshotsCreated,
snapshotsUpdated: snapshotsUpdated,
errors: errors
)
}
@@ -516,6 +542,50 @@ Personal,Real Estate,Rental Property,2024-01-01,82000,80000,Estimated value
return lookup
}
private func fetchCategories(in context: NSManagedObjectContext) -> [Category] {
let request: NSFetchRequest<Category> = Category.fetchRequest()
request.sortDescriptors = [
NSSortDescriptor(keyPath: \Category.sortOrder, ascending: true),
NSSortDescriptor(keyPath: \Category.name, ascending: true)
]
return (try? context.fetch(request)) ?? []
}
private func fetchAccountsLookup(in context: NSManagedObjectContext) -> [String: Account] {
let request: NSFetchRequest<Account> = Account.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(keyPath: \Account.createdAt, ascending: true)]
let accounts = (try? context.fetch(request)) ?? []
var lookup: [String: Account] = [:]
for account in accounts {
lookup[account.name] = account
}
return lookup
}
/// Fetch a snapshot for a given source and date (same day)
private func fetchSnapshot(
for source: InvestmentSource,
date: Date,
in context: NSManagedObjectContext
) -> Snapshot? {
let request: NSFetchRequest<Snapshot> = Snapshot.fetchRequest()
// Match snapshots on the same day (ignore time component)
let calendar = Calendar.current
let startOfDay = calendar.startOfDay(for: date)
let endOfDay = calendar.date(byAdding: .day, value: 1, to: startOfDay)!
request.predicate = NSPredicate(
format: "source == %@ AND date >= %@ AND date < %@",
source,
startOfDay as NSDate,
endOfDay as NSDate
)
request.fetchLimit = 1
return try? context.fetch(request).first
}
private func canonicalCategoryName(for rawName: String) -> String? {
let normalized = normalizedCategoryName(rawName)
for mapping in categoryAliasMappings {