Files
InvestmentTrackerApp/PortfolioJournal/ViewModels/MonthlyCheckInViewModel.swift
T
alexandrev-tibco 7cb5f92cf4 Remove Add Transaction feature and clean all related code
Deleted files:
- AddTransactionView.swift
- TransactionRepository.swift
- Transaction+CoreDataClass.swift

Cleaned files:
- SourceDetailViewModel: removed transactions published property,
  showingAddTransaction flag, transactionRepository dependency,
  addTransaction() and deleteTransaction() methods
- SourceDetailView: removed transactionsSection, TransactionRow struct,
  Add Transaction button from quickActions, sheet presentation
- InvestmentSource+CoreDataClass: removed transactions NSManaged property,
  transactionsArray, totalInvested, totalDividends, totalFees computed
  properties, and all transaction Core Data accessors
- SettingsViewModel: removed "Transaction" from resetAllData entity list
- SampleDataService: removed transactionRepository and sample transaction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 23:04:03 +01:00

129 lines
4.6 KiB
Swift

import Foundation
import Combine
import CoreData
@MainActor
class MonthlyCheckInViewModel: ObservableObject {
@Published var sourcesNeedingUpdate: [InvestmentSource] = []
@Published var sources: [InvestmentSource] = []
@Published var monthlySummary: MonthlySummary = .empty
@Published var recentNotes: [Snapshot] = []
@Published var isLoading = false
@Published var selectedAccount: Account?
@Published var showAllAccounts = true
@Published var selectedRange: DateRange = .thisMonth
@Published var checkInStats: MonthlyCheckInStats = .empty
private let sourceRepository: InvestmentSourceRepository
private let snapshotRepository: SnapshotRepository
private let calculationService: CalculationService
private var cancellables = Set<AnyCancellable>()
@MainActor
init(
sourceRepository: InvestmentSourceRepository? = nil,
snapshotRepository: SnapshotRepository? = nil,
calculationService: CalculationService? = nil
) {
let context = CoreDataStack.shared.viewContext
self.sourceRepository = sourceRepository ?? InvestmentSourceRepository(context: context)
self.snapshotRepository = snapshotRepository ?? SnapshotRepository(context: context)
self.calculationService = calculationService ?? .shared
setupObservers()
refresh()
}
private func setupObservers() {
sourceRepository.$sources
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.refresh()
}
.store(in: &cancellables)
NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange)
.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)
.sink { [weak self] _ in
self?.refresh()
}
.store(in: &cancellables)
}
func refresh() {
isLoading = true
let filtered = filteredSources()
let snapshots = filteredSnapshots(for: filtered)
let accountFilter = showAllAccounts ? nil : selectedAccount
sourcesNeedingUpdate = sourceRepository.fetchSourcesNeedingUpdate(for: accountFilter)
sources = filtered
monthlySummary = calculationService.calculateMonthlySummary(
sources: filtered,
snapshots: snapshots,
range: selectedRange
)
checkInStats = MonthlyCheckInStore.stats(referenceDate: selectedRange.end)
recentNotes = snapshots
.filter { ($0.notes?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false)
&& selectedRange.contains($0.date) }
.sorted { $0.date > $1.date }
.prefix(5)
.map { $0 }
isLoading = false
}
func duplicatePreviousMonthSnapshots(referenceDate: Date) {
let targetRange = DateRange.month(containing: referenceDate)
let previousRange = DateRange.month(containing: referenceDate.adding(months: -1))
let sources = filteredSources()
guard !sources.isEmpty else { return }
let targetSnapshots = snapshotRepository.fetchSnapshots(
from: targetRange.start,
to: targetRange.end
)
let targetSourceIds = Set(targetSnapshots.compactMap { $0.source?.id })
let targetDate = Date()
for source in sources {
let sourceId = source.id
if targetSourceIds.contains(sourceId) { continue }
let previousSnapshots = snapshotRepository.fetchSnapshots(for: source)
guard let previousSnapshot = previousSnapshots.first(where: { previousRange.contains($0.date) }) else {
continue
}
snapshotRepository.createSnapshot(
for: source,
date: targetDate,
value: previousSnapshot.decimalValue,
contribution: previousSnapshot.contribution?.decimalValue,
notes: previousSnapshot.notes
)
}
}
private func filteredSources() -> [InvestmentSource] {
let selectedAccountId = selectedAccount?.safeId
if showAllAccounts || selectedAccountId == nil {
return sourceRepository.sources
}
return sourceRepository.sources.filter { $0.account?.id == selectedAccountId }
}
private func filteredSnapshots(for sources: [InvestmentSource]) -> [Snapshot] {
let sourceIds = Set(sources.compactMap { $0.id })
return snapshotRepository.fetchAllSnapshots().filter { snapshot in
let sourceId = snapshot.source?.id
return sourceId.map(sourceIds.contains) ?? false
}
}
}