Replace the hand-rolled model.Copy(target) pattern in every SaveXxx
with FindIndex -> in-place replace (or Add on new). Move the two
hidden defaults out of the deleted Copy bodies: Currency = "CHF"
default now lives in AccountRepository.SaveAccount; BookingDate =
ValueDate default now lives in TransactionRepository.SaveTransaction.
Delete Copy from Account, BookingRule, BookingText, SubClass, and
SplitPredefinition. Keep Transaction.Clone() (renamed from Copy, and
now includes RelatedParty) for the defensive copy in GetTransaction
and ViewService.WithDisplaySign.
Fixes a pre-existing bug where Transaction.Copy silently dropped
RelatedParty on every update save.
42 tests total, all passing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New IFxConverter.ConvertToChf(amount, fromCurrency) and FxConverter
wrap the vendor IFxService and the FixerIo API key so callers stop
threading the key through every conversion. ViewService drops
IFxService and ISettingsService from its constructor and takes
IFxConverter instead; GetAccountViewList and GetBalanceSheetView no
longer read settingsService.GetSettings() per method.
FxConverterTestShould locks in the target-currency + API-key routing.
40 tests total, all passing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Eliminate O(N*M) linear scans in view assembly.
GetTransactionViewList / GetBookingRuleViewList: precompute
accountsById and use dictionary lookups instead of accountList.Single
per row. GetAccountViewList: group transactions by
TargetAccountId/OriginAccountId once; per-account balance is now O(k)
instead of a full scan. Fold GetBalanceInAccountCurrency and
GetCurrentBalanceInCHF into a single ComputeBalance helper so the
account-currency balance is computed once per account instead of
twice.
Defer item 19 (async I/O): after PR D each XML file is loaded at most
once per circuit, and this app is single-user local Blazor Server, so
async would be ~40 files of churn for negligible benefit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add IMPROVEMENT_PLAN.md with phased plan for follow-up work
- Consolidate DI registrations into a single AddAccounting() extension;
drop the duplicate service registrations and the PdfParsingService
self-registration
- TrySetYear: capture this.year before overwriting so rollback actually
restores the previous value
- DummyFxService: check toCurrency (was checking fromCurrency twice)
- TransactionRepository.GetTransaction: return a copy instead of mutating
the loaded entity, and guard against unknown ids
- ViewService.GetTransactionViewList(accountId): flip the sign on a copy
rather than mutating the entity returned by the repository
- FileService.GetTransactionListCsv: same treatment; use a local
signedValue instead of mutating trx.Value
- ProfitLossReport: use ClassIds.Income/Expenses instead of magic 3/4
- CLAUDE.md: correct the ClassIds documentation (1/2/3/4, not
1000/2000/3000/4000)
SettingsService lifetime is intentionally left as Singleton for now;
making it Scoped requires persisting year/mandator selection across
page reloads first (tracked in the plan).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>