Commit graph

31 commits

Author SHA1 Message Date
69d3122243 PR U-7: success toasts after mutations
Add IToastService.ShowToast(ToastIntent.Success, ...) after each
successful save/delete across Accounts, BookingRules, BookingTexts,
Classes, and Transactions. Toasts fire after the list re-fetch so
the confirmation is visible even if the edited item paginated out of
view.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 09:51:38 +02:00
562338d0c4 PR U-6a: MainLayout wraps @Body in an ErrorBoundary
Add a Blazor built-in ErrorBoundary around @Body with a friendly
FluentMessageBar + retry / home buttons. Also hook
NavigationManager.LocationChanged so the boundary resets on any nav
(otherwise the error state persists across NavigateTo and "Zur
Startseite" still shows the failure banner).

U-6 split into U-6a (this) and U-6b (per-handler try/catch to route
expected exceptions to IMessageService — deferred).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 09:47:14 +02:00
58ca05f83e PR U-5: shared Loading component
Add Components/Loading.razor and swap the ad-hoc `<p>Lädt…</p>` guard
visuals for it across the four reports, three edit dialogs, and three
chart pages (the last three now show the placeholder instead of a
blank page while waiting for view data). Kept as a pure visual so
each call site changes from `<p>Lädt…</p>` to `<Loading/>` with no
other churn.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 09:44:23 +02:00
577d63c0fa PR U-4a: Home refreshes in place instead of forceLoad
SplitBankTransactionAsync ended with NavigateTo(forceLoad: true), which
tore down the SignalR circuit and re-ran every repo constructor just
to refresh the pending-bookings list. Extract data loading into a
LoadDataAsync helper called from OnInitializedAsync, SplitBank
TransactionAsync, and OnCompletedAsync — the page refreshes in-circuit,
scoped caches survive, and the NavigationManager injection can go.

Also flags the plan: U-4 is split into U-4a (this) and U-4b (persist
settings + drop MyHeader's forceLoad, touching ~15 pages).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 09:39:03 +02:00
93bfcae2a0 PR U-3: validate every row in TransactionSplitDialog
The single EditContext only wrapped the first split row; subsequent
rows added by AddTransactionAsync silently bypassed
DataAnnotationsValidator. Replace editContext.Validate() with an
explicit ValidateAllRows() loop that checks Text, TargetAccountId, and
Value != 0 for every row, and render the collected messages under the
existing FluentValidationSummary.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 08:36:21 +02:00
70f9ce0d58 PR U-2: guard TransactionDialog.SaveAsync against a cleared date
Guard SaveAsync against a null SelectedValue (previously
SelectedValue!.Value threw InvalidOperationException) and disable
the Save button while the date is empty so the user gets a visible
cue.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 08:32:34 +02:00
60f01d437c PR U-1: AccountSelector reacts to Accounts arriving late
Move the SelectedAccount = Accounts.FirstOrDefault(...) lookup from
OnInitialized to OnParametersSet so the preselection tracks the
Accounts list arriving asynchronously (every caller now passes []
first and hydrates after an await). Idempotent — the OnSelectedChanged
callback path returns the same AccountId back and the parent re-render
lookup finds the same account.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-03 19:17:41 +02:00
f9084dc074 PR P: full async I/O top to bottom (item 19)
Every repository, service, Blazor page/dialog, and test now uses
async/await. Single atomic diff; the codebase does not compile in
intermediate states.

- BaseRepository: LoadListAsync/LoadAsync/SaveAsync return Task<T>;
  per-file locks use SemaphoreSlim so waiters can await; Save
  serialises to a MemoryStream sync (XmlSerializer has no async
  form), then File.WriteAllBytesAsync + sync File.Move.
- RepositoryCache.GetOrLoadAsync takes a Func<Task<List<T>>>.
- All 7 repository interfaces + implementations async.
- All service interfaces + implementations async (except vendor
  IFxService and stateless IFxConverter / SettingsService).
- Every Blazor OnInitializedAsync switches to await base.
- Test suite fully async, 42 tests pass.

AccountRepository.EnsureAccountsFile keeps two .GetAwaiter().GetResult()
bridges because it runs from the constructor.

Null-render guard follow-up (folded in):
Blazor now renders the component once with fields at their initial
values while OnInitializedAsync awaits — so fields declared `= null!`
are actually null on that first render and things like
`accounts.GroupBy(...)` throw ArgumentNullException. Fixed across
Transactions, BalanceReport, BalanceSheetReport, ProfitLossReport,
DetailReport, Assets, Spendings, SpendingsOverTime, TransactionDialog,
TransactionSplitDialog, and BookingRuleDialog:
- Collection fields initialise to [] so first-render loops are empty.
- Single-object data fields become nullable; the razor wraps
  consumption in `@if (field is null) { <p>Lädt…</p> return; }`.
- <PlotlyChart> guarded behind a null check on config/layout/data so
  Plotly.Blazor's @bind doesn't see nulls.
- Header/footer strings initialise to "" instead of null!.

Architectural hygiene on a single-user local Blazor Server app: the
observed win is one File.ReadAllBytesAsync and one
File.WriteAllBytesAsync per Load/Save, and after PR D each file is
loaded at most once per SignalR circuit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-03 18:30:44 +02:00
786fafefa5 PR N: polish pass — ClassIds static, drop loaded flag, mandator from config
Three long-noted rough edges from the original analysis, each a
one-line touch:

- ClassIds becomes static class (was instantiable).
- Home.razor.cs drops the `loaded` bool guard (Blazor already runs
  OnInitializedAsync exactly once per component instance).
- Move the "Claudio Schaad" mandator default out of SettingsService
  into SettingsDataset.DefaultMandator, plumbed through
  appsettings.Development.json.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-02 22:07:08 +02:00
1f1575b0af PR M: log MatchOpenBankTransactions summary; drop dead injection
Add ILogger<ViewService> and log the match-count summary at the end
of MatchOpenBankTransactions: "Matched {Matched} of {Total} open bank
transactions". Completes item 18.

Also drop the ISettingsService [Inject] from Home.razor.cs that was
declared but never referenced.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-02 22:04:12 +02:00
857b312f9f PR K: hide IFxService and FixerIo key behind IFxConverter
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>
2026-07-02 21:51:44 +02:00
1c7ba7f416 PR D: per-scope repository cache
Add a Scoped RepositoryCache that memoizes deserialized XML lists,
keyed by absolute file path. Each XML file is loaded at most once per
SignalR circuit; Save invalidates the corresponding entry so
subsequent reads reload from disk.

BaseRepository gains a LoadList<T>(fileName) helper; each of the seven
repositories now defines Get*List() as a one-liner over that helper
and takes RepositoryCache via the constructor.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-02 20:52:24 +02:00
8961bb234c PR C: mechanical cleanup (typos, packages, formatting, culture)
- Consolidate decimal.ToFormattedString into Schaad.Accounting.Formatting
- Rename EnsureFileExisits -> EnsureFileExists and
  SplitPredefiniton* -> SplitPredefinition* (class, interface, files, DI reg)
- AccountRepository: single-save year-rollover start-balance seed
- Drop legacy .NET Standard packages redundant on net9.0 and the stale
  Interfaces\Extensions\ folder entry
- Set culture via RequestLocalizationOptions middleware
- Plan: item 9 (unit of work) split off into a dedicated PR D

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-02 20:46:55 +02:00
9f11f6490b PR A: correctness fixes and improvement plan
- 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>
2026-07-02 20:37:03 +02:00
Claudio Schaad
a16aedcfd8
Display transactions and balances in account currency
- Transaction amounts, totals, and sidebar badges in the Transactions page now show in the account's native currency (EUR, USD, etc.) instead of always CHF
- Fixed FX conversion in SaveTransaction/GetTransaction to fire when either the origin or target account is a foreign-currency account, not only the origin side

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 16:08:01 +02:00
2b1eb0dbb7 Add fx rate for transaction in UI 2025-09-27 21:32:10 +02:00
Claudio Schaad
37d568cbc2
Merge pull request #3
* Make line readonly after save

* Cleanup CSS

* Reorder columns
2025-06-28 11:26:34 +02:00
b88d3059e9 Hardly reload 2025-06-07 18:24:47 +02:00
496408aa70 Reload page after save 2025-06-07 18:00:47 +02:00
a45495c296 Add autocomplete for account 2025-05-15 22:01:48 +02:00
c86edfdde2 Add transaction split dialog 2025-05-14 21:45:52 +02:00
e76644a122 Add empty account 2025-05-13 21:15:04 +02:00
ff2ef4aba0 Footer css 2025-05-10 22:42:47 +02:00
0c537f8816 Show bankpayment matching 2025-05-10 22:33:35 +02:00
0828dd7691 Add bank transaction import 2025-05-07 21:50:01 +02:00
e2802dd797 Set CH culture 2025-05-06 19:19:02 +02:00
ece9618f09 Add transaction dialog and enhance transaction view 2025-05-05 22:49:09 +02:00
d21c49d486 Add mandator dropdown 2025-05-05 22:49:02 +02:00
38a1fb65c5 Add year and mandator dropdown 2025-05-05 22:48:52 +02:00
3c04220437 Delete appsettings 2025-05-04 20:42:52 +02:00
ad75c47709 Add new blazor app 2025-05-04 20:00:42 +02:00