diff --git a/src/Duempelkas.App/Components/Accounts/AccountCard.razor b/src/Duempelkas.App/Components/Accounts/AccountCard.razor new file mode 100644 index 0000000..bcf42da --- /dev/null +++ b/src/Duempelkas.App/Components/Accounts/AccountCard.razor @@ -0,0 +1,16 @@ +@inject NavigationManager Navigation + +
+
+
@Account.Name
+
+ @($"{Account.TotalBalance:N2} €") +
+
+
+ +@code { + [Parameter] public AccountSummaryDto Account { get; set; } = default!; + + private void Navigate() => Navigation.NavigateTo($"/accounts/{Account.Id}"); +} diff --git a/src/Duempelkas.App/Components/Accounts/AccountCardList.razor b/src/Duempelkas.App/Components/Accounts/AccountCardList.razor new file mode 100644 index 0000000..39f62a4 --- /dev/null +++ b/src/Duempelkas.App/Components/Accounts/AccountCardList.razor @@ -0,0 +1,12 @@ +
+ @foreach (var account in Accounts) + { +
+ +
+ } +
+ +@code { + [Parameter] public List Accounts { get; set; } = new(); +} diff --git a/src/Duempelkas.App/Components/Accounts/AccountHeader.razor b/src/Duempelkas.App/Components/Accounts/AccountHeader.razor new file mode 100644 index 0000000..c85b72a --- /dev/null +++ b/src/Duempelkas.App/Components/Accounts/AccountHeader.razor @@ -0,0 +1 @@ +@* AccountHeader is no longer used - header is now inline in AccountDetail.razor *@ diff --git a/src/Duempelkas.App/Components/Accounts/EntryRow.razor b/src/Duempelkas.App/Components/Accounts/EntryRow.razor new file mode 100644 index 0000000..cac4432 --- /dev/null +++ b/src/Duempelkas.App/Components/Accounts/EntryRow.razor @@ -0,0 +1,50 @@ + + @Entry.DisplayId + @Entry.Date.ToString("dd.MM.yyyy") + + @Entry.Title + @if (Entry.IsTransfer && !string.IsNullOrEmpty(Entry.LinkedAccountName)) + { + + (@(Entry.Type == EntryType.Expense ? "an" : "von") @Entry.LinkedAccountName) + + } + + + @(Entry.Type == EntryType.Income ? "+" : "\u2212")@($"{Entry.Amount:N2} €") + + + @if (Entry.IsDeleted) + { + + } + else + { +
+ + +
+ } + + + +@code { + [Parameter] public EntryDto Entry { get; set; } = default!; + [Parameter] public EventCallback OnDelete { get; set; } + [Parameter] public EventCallback OnRestore { get; set; } + [Parameter] public EventCallback OnEdit { get; set; } + + private string GetRowClass() + { + var classes = new List(); + if (Entry.IsTransfer) classes.Add("entry-row-transfer"); + if (Entry.IsDeleted) classes.Add("entry-deleted"); + return string.Join(" ", classes); + } +} diff --git a/src/Duempelkas.App/Components/Accounts/EntryTable.razor b/src/Duempelkas.App/Components/Accounts/EntryTable.razor new file mode 100644 index 0000000..85def90 --- /dev/null +++ b/src/Duempelkas.App/Components/Accounts/EntryTable.razor @@ -0,0 +1,31 @@ +
+ + + + + + + + + + + + @foreach (var entry in Entries) + { + + } + +
LfdNrDatumBezeichnungBetrag
+
+ +@if (!Entries.Any()) +{ +
Keine Einträge vorhanden.
+} + +@code { + [Parameter] public List Entries { get; set; } = new(); + [Parameter] public EventCallback OnDeleteEntry { get; set; } + [Parameter] public EventCallback OnRestoreEntry { get; set; } + [Parameter] public EventCallback OnEditEntry { get; set; } +} diff --git a/src/Duempelkas.App/Components/Accounts/ExportButton.razor b/src/Duempelkas.App/Components/Accounts/ExportButton.razor new file mode 100644 index 0000000..b523270 --- /dev/null +++ b/src/Duempelkas.App/Components/Accounts/ExportButton.razor @@ -0,0 +1 @@ +@* ExportButton is no longer used - export is now inline in AccountDetail.razor *@ diff --git a/src/Duempelkas.App/Components/Accounts/YearSelector.razor b/src/Duempelkas.App/Components/Accounts/YearSelector.razor new file mode 100644 index 0000000..a145c1b --- /dev/null +++ b/src/Duempelkas.App/Components/Accounts/YearSelector.razor @@ -0,0 +1 @@ +@* YearSelector is no longer used - year selection has been removed from the data model. *@ diff --git a/src/Duempelkas.App/Components/App.razor b/src/Duempelkas.App/Components/App.razor new file mode 100644 index 0000000..338bcfc --- /dev/null +++ b/src/Duempelkas.App/Components/App.razor @@ -0,0 +1,13 @@ + + + + + + +
+

Seite nicht gefunden

+

Die angeforderte Seite konnte nicht gefunden werden.

+
+
+
+
diff --git a/src/Duempelkas.App/Components/Dialogs/AddAccountDialog.razor b/src/Duempelkas.App/Components/Dialogs/AddAccountDialog.razor new file mode 100644 index 0000000..27c1b7b --- /dev/null +++ b/src/Duempelkas.App/Components/Dialogs/AddAccountDialog.razor @@ -0,0 +1,29 @@ +
+
+
Neues Konto
+
+ + +
+
+ + +
+
+
+ +@code { + [Parameter] public EventCallback OnSave { get; set; } + [Parameter] public EventCallback OnCancel { get; set; } + + private string name = string.Empty; + + private async Task Save() + { + if (!string.IsNullOrWhiteSpace(name)) + await OnSave.InvokeAsync(name.Trim()); + } + + private async Task Cancel() => await OnCancel.InvokeAsync(); +} diff --git a/src/Duempelkas.App/Components/Dialogs/AddEntryDialog.razor b/src/Duempelkas.App/Components/Dialogs/AddEntryDialog.razor new file mode 100644 index 0000000..0737830 --- /dev/null +++ b/src/Duempelkas.App/Components/Dialogs/AddEntryDialog.razor @@ -0,0 +1,70 @@ +@inject IEntryService EntryService + +
+
+
@(EditEntry != null ? "Eintrag bearbeiten" : "Neuer Eintrag")
+ @if (EditEntry == null) + { +
+ + +
+ } +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ +@code { + [Parameter] public int AccountId { get; set; } + [Parameter] public EntryDto? EditEntry { get; set; } + [Parameter] public EventCallback OnSave { get; set; } + [Parameter] public EventCallback OnCancel { get; set; } + + private EntryType entryType = EntryType.Income; + private DateTime date = DateTime.Today; + private string title = string.Empty; + private decimal amount; + + protected override void OnParametersSet() + { + if (EditEntry != null) + { + entryType = EditEntry.Type; + date = EditEntry.Date; + title = EditEntry.Title; + amount = EditEntry.Amount; + } + } + + private async Task Save() + { + if (EditEntry != null) + await EntryService.UpdateEntryAsync(EditEntry.Id, date, title.Trim(), amount); + else + await EntryService.CreateEntryAsync(AccountId, entryType, date, title.Trim(), amount); + await OnSave.InvokeAsync(); + } + + private async Task Cancel() => await OnCancel.InvokeAsync(); +} diff --git a/src/Duempelkas.App/Components/Dialogs/AddTransferDialog.razor b/src/Duempelkas.App/Components/Dialogs/AddTransferDialog.razor new file mode 100644 index 0000000..5434e03 --- /dev/null +++ b/src/Duempelkas.App/Components/Dialogs/AddTransferDialog.razor @@ -0,0 +1,78 @@ +@inject IEntryService EntryService +@inject IAccountService AccountService + +
+
+
@(EditEntry != null ? "Umbuchung bearbeiten" : "Neue Umbuchung")
+ +
+ + +
+ +
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+
+
+ +@code { + [Parameter] public int SourceAccountId { get; set; } + [Parameter] public EntryDto? EditEntry { get; set; } + [Parameter] public EventCallback OnSave { get; set; } + [Parameter] public EventCallback OnCancel { get; set; } + + private List accounts = new(); + private int targetAccountId; + private DateTime date = DateTime.Today; + private string title = string.Empty; + private decimal amount; + + private bool CanSave => targetAccountId > 0 && !string.IsNullOrWhiteSpace(title) && amount > 0; + + protected override async Task OnParametersSetAsync() + { + if (!accounts.Any()) + accounts = await AccountService.GetAllAccountsAsync(); + + if (EditEntry != null) + { + targetAccountId = EditEntry.LinkedAccountId ?? 0; + date = EditEntry.Date; + title = EditEntry.Title; + amount = EditEntry.Amount; + } + } + + private async Task Save() + { + if (EditEntry != null) + await EntryService.UpdateTransferAsync(EditEntry.Id, targetAccountId, date, title.Trim(), amount); + else + await EntryService.CreateTransferAsync(SourceAccountId, targetAccountId, date, title.Trim(), amount); + await OnSave.InvokeAsync(); + } + + private async Task Cancel() => await OnCancel.InvokeAsync(); +} diff --git a/src/Duempelkas.App/Components/Dialogs/AddYearDialog.razor b/src/Duempelkas.App/Components/Dialogs/AddYearDialog.razor new file mode 100644 index 0000000..0a92355 --- /dev/null +++ b/src/Duempelkas.App/Components/Dialogs/AddYearDialog.razor @@ -0,0 +1 @@ +@* AddYearDialog is no longer used - year management has been removed from the data model. *@ diff --git a/src/Duempelkas.App/Components/Dialogs/ConfirmDialog.razor b/src/Duempelkas.App/Components/Dialogs/ConfirmDialog.razor new file mode 100644 index 0000000..d1f8d2e --- /dev/null +++ b/src/Duempelkas.App/Components/Dialogs/ConfirmDialog.razor @@ -0,0 +1,22 @@ +
+
+
@Title
+

@Message

+
+ + +
+
+
+ +@code { + [Parameter] public string Title { get; set; } = "Bestätigung"; + [Parameter] public string Message { get; set; } = "Sind Sie sicher?"; + [Parameter] public string ConfirmText { get; set; } = "Ja, bestätigen"; + [Parameter] public string CancelText { get; set; } = "Nein, abbrechen"; + [Parameter] public EventCallback OnConfirm { get; set; } + [Parameter] public EventCallback OnCancel { get; set; } + + private async Task Confirm() => await OnConfirm.InvokeAsync(); + private async Task Cancel() => await OnCancel.InvokeAsync(); +} diff --git a/src/Duempelkas.App/Components/Dialogs/EditCarryoverDialog.razor b/src/Duempelkas.App/Components/Dialogs/EditCarryoverDialog.razor new file mode 100644 index 0000000..46a5392 --- /dev/null +++ b/src/Duempelkas.App/Components/Dialogs/EditCarryoverDialog.razor @@ -0,0 +1,27 @@ +
+
+
Übertrag bearbeiten
+
+ + +
+
+ + +
+
+
+ +@code { + [Parameter] public decimal CurrentAmount { get; set; } + [Parameter] public EventCallback OnSave { get; set; } + [Parameter] public EventCallback OnCancel { get; set; } + + private decimal amount; + + protected override void OnParametersSet() => amount = CurrentAmount; + + private async Task Save() => await OnSave.InvokeAsync(amount); + + private async Task Cancel() => await OnCancel.InvokeAsync(); +} diff --git a/src/Duempelkas.App/Components/Dialogs/EditNameDialog.razor b/src/Duempelkas.App/Components/Dialogs/EditNameDialog.razor new file mode 100644 index 0000000..9ca4eb6 --- /dev/null +++ b/src/Duempelkas.App/Components/Dialogs/EditNameDialog.razor @@ -0,0 +1,31 @@ +
+
+
Kontoname bearbeiten
+
+ + +
+
+ + +
+
+
+ +@code { + [Parameter] public string CurrentName { get; set; } = string.Empty; + [Parameter] public EventCallback OnSave { get; set; } + [Parameter] public EventCallback OnCancel { get; set; } + + private string name = string.Empty; + + protected override void OnParametersSet() => name = CurrentName; + + private async Task Save() + { + if (!string.IsNullOrWhiteSpace(name)) + await OnSave.InvokeAsync(name.Trim()); + } + + private async Task Cancel() => await OnCancel.InvokeAsync(); +} diff --git a/src/Duempelkas.App/Components/Layout/MainLayout.razor b/src/Duempelkas.App/Components/Layout/MainLayout.razor new file mode 100644 index 0000000..56b58df --- /dev/null +++ b/src/Duempelkas.App/Components/Layout/MainLayout.razor @@ -0,0 +1,31 @@ +@inherits LayoutComponentBase + +
+ + +
+ @Body +
+ +
+ Dümpelkas © @DateTime.Now.Year · Version @AppVersion +
+
+ +@code { + private static string AppVersion + { + get + { + var now = DateTime.Now; + var yearShort = now.Year % 100; + var dayOfYear = now.DayOfYear; + return $"{yearShort}.{dayOfYear}"; + } + } +} diff --git a/src/Duempelkas.App/Components/Routes.razor b/src/Duempelkas.App/Components/Routes.razor new file mode 100644 index 0000000..55bc047 --- /dev/null +++ b/src/Duempelkas.App/Components/Routes.razor @@ -0,0 +1,10 @@ + + + + + + +

Seite nicht gefunden.

+
+
+
diff --git a/src/Duempelkas.App/Duempelkas.App.csproj b/src/Duempelkas.App/Duempelkas.App.csproj new file mode 100644 index 0000000..ff99cbd --- /dev/null +++ b/src/Duempelkas.App/Duempelkas.App.csproj @@ -0,0 +1,15 @@ + + + net10.0 + enable + enable + + + + + + + + + + diff --git a/src/Duempelkas.App/Pages/Accounts/AccountDetail.razor b/src/Duempelkas.App/Pages/Accounts/AccountDetail.razor new file mode 100644 index 0000000..8d84110 --- /dev/null +++ b/src/Duempelkas.App/Pages/Accounts/AccountDetail.razor @@ -0,0 +1,298 @@ +@page "/accounts/{AccountId:int}" +@inject IAccountService AccountService +@inject IEntryService EntryService +@inject IBalanceQueryService BalanceQueryService +@inject IPdfStatementService PdfStatementService +@inject IFileSaveService FileSaveService +@inject NavigationManager Navigation + +
+ @if (account == null) + { +
+
+
+ } + else + { +
+ +
+

+ @account.Name + +

+
+
+ + @if (balance != null) + { +
+
+
+ Saldo +
+ @FormatAmount(balance.TotalBalance) +
+
+
+ Einnahmen +
@FormatAmount(showCurrentYearOnly ? balance.CurrentYearIncome : balance.TotalIncome)
+
+
+ Ausgaben +
@FormatAmount(showCurrentYearOnly ? balance.CurrentYearExpense : balance.TotalExpense)
+
+
+
+ + @if (showCurrentYearOnly) + { +
+
+
+ Übertrag von @(DateTime.Now.Year - 1) +
+ @FormatAmount(balance.CarryoverBalance) + +
+
+
+ Umsätze @DateTime.Now.Year +
+ @FormatAmount(balance.CurrentYearIncome - balance.CurrentYearExpense) +
+
+
+
+ } + } + +
+ + + + +
+ + @if (entries != null) + { +
+ +
+ } + } +
+ +@if (showAddEntry) +{ + +} + +@if (showAddTransfer) +{ + +} + +@if (showEditName) +{ + +} + +@if (showEditCarryover) +{ + +} + +@if (confirmDeleteEntryId.HasValue) +{ + +} + +@if (confirmRestoreEntryId.HasValue) +{ + +} + +@if (editingEntry != null) +{ + +} + +@if (editingTransferEntry != null) +{ + +} + +@code { + [Parameter] public int AccountId { get; set; } + + private AccountSummaryDto? account; + private AccountBalanceDto? balance; + private List? entries; + private bool showAddEntry, showAddTransfer; + private bool showEditName, showEditCarryover; + private bool showCurrentYearOnly = true; + + private int? confirmDeleteEntryId; + private string? confirmDeleteEntryTitle; + + private int? confirmRestoreEntryId; + private string? confirmRestoreEntryTitle; + + private EntryDto? editingEntry; + private EntryDto? editingTransferEntry; + + private void NavigateBack() => Navigation.NavigateTo("/"); + + protected override async Task OnParametersSetAsync() + { + await LoadAll(); + } + + private async Task LoadAll() + { + account = await AccountService.GetAccountAsync(AccountId); + balance = await BalanceQueryService.GetAccountBalanceAsync(AccountId); + entries = await EntryService.GetEntriesAsync(AccountId, showCurrentYearOnly); + } + + private async Task ToggleYearFilter() + { + showCurrentYearOnly = !showCurrentYearOnly; + entries = await EntryService.GetEntriesAsync(AccountId, showCurrentYearOnly); + } + + private async Task HandleSaveName(string newName) + { + await AccountService.RenameAccountAsync(AccountId, newName); + showEditName = false; + await LoadAll(); + } + + private async Task HandleSaveCarryover(decimal newAmount) + { + await AccountService.UpdateCarryoverAsync(AccountId, newAmount); + showEditCarryover = false; + await LoadAll(); + } + + private async Task HandleEntryCreated() + { + showAddEntry = false; + await LoadAll(); + } + + private async Task HandleTransferCreated() + { + showAddTransfer = false; + await LoadAll(); + } + + private void RequestDeleteEntry(int entryId) + { + confirmDeleteEntryId = entryId; + confirmDeleteEntryTitle = entries?.FirstOrDefault(e => e.Id == entryId)?.Title; + } + + private void CancelDeleteConfirm() + { + confirmDeleteEntryId = null; + confirmDeleteEntryTitle = null; + } + + private async Task HandleConfirmDelete() + { + if (confirmDeleteEntryId.HasValue) + { + await EntryService.DeleteEntryAsync(confirmDeleteEntryId.Value); + confirmDeleteEntryId = null; + confirmDeleteEntryTitle = null; + await LoadAll(); + } + } + + private void RequestRestoreEntry(int entryId) + { + confirmRestoreEntryId = entryId; + confirmRestoreEntryTitle = entries?.FirstOrDefault(e => e.Id == entryId)?.Title; + } + + private void CancelRestoreConfirm() + { + confirmRestoreEntryId = null; + confirmRestoreEntryTitle = null; + } + + private async Task HandleConfirmRestore() + { + if (confirmRestoreEntryId.HasValue) + { + await EntryService.RestoreEntryAsync(confirmRestoreEntryId.Value); + confirmRestoreEntryId = null; + confirmRestoreEntryTitle = null; + await LoadAll(); + } + } + + private void RequestEditEntry(int entryId) + { + var entry = entries?.FirstOrDefault(e => e.Id == entryId); + if (entry?.IsTransfer == true) + editingTransferEntry = entry; + else + editingEntry = entry; + } + + private async Task HandleEntryEdited() + { + editingEntry = null; + editingTransferEntry = null; + await LoadAll(); + } + + private async Task HandleExport() + { + var pdf = await PdfStatementService.GenerateStatementAsync(AccountId, showCurrentYearOnly); + var suffix = showCurrentYearOnly ? $"_{DateTime.Now.Year}" : "_Gesamt"; + await FileSaveService.SaveFileAsync(pdf, $"{account?.Name}{suffix}.pdf"); + } + + private static string FormatAmount(decimal amount) => $"{amount:N2} €"; +} diff --git a/src/Duempelkas.App/Pages/Dashboard.razor b/src/Duempelkas.App/Pages/Dashboard.razor new file mode 100644 index 0000000..f246c26 --- /dev/null +++ b/src/Duempelkas.App/Pages/Dashboard.razor @@ -0,0 +1,56 @@ +@page "/" +@inject IAccountService AccountService + +
+
+

Übersicht

+ +
+ + @if (accounts == null) + { +
+
+
+ } + else if (!accounts.Any()) + { +
+

Noch keine Konten vorhanden

+

Erstelle dein erstes Konto, um loszulegen.

+
+ } + else + { + + } +
+ +@if (showAddAccount) +{ + +} + +@code { + private List? accounts; + private bool showAddAccount; + + protected override async Task OnInitializedAsync() + { + await LoadAccounts(); + } + + private async Task LoadAccounts() + { + accounts = await AccountService.GetAllAccountsAsync(); + } + + private async Task HandleAccountCreated(string name) + { + await AccountService.CreateAccountAsync(name); + showAddAccount = false; + await LoadAccounts(); + } +} diff --git a/src/Duempelkas.App/Services/IAccountService.cs b/src/Duempelkas.App/Services/IAccountService.cs new file mode 100644 index 0000000..61861b7 --- /dev/null +++ b/src/Duempelkas.App/Services/IAccountService.cs @@ -0,0 +1,13 @@ +using Duempelkas.App.Services.Models; + +namespace Duempelkas.App.Services; + +public interface IAccountService +{ + Task> GetAllAccountsAsync(); + Task GetAccountAsync(int accountId); + Task CreateAccountAsync(string name); + Task RenameAccountAsync(int accountId, string newName); + Task UpdateCarryoverAsync(int accountId, decimal carryoverBalance); + Task DeleteAccountAsync(int accountId); +} diff --git a/src/Duempelkas.App/Services/IAccountYearService.cs b/src/Duempelkas.App/Services/IAccountYearService.cs new file mode 100644 index 0000000..4495d0b --- /dev/null +++ b/src/Duempelkas.App/Services/IAccountYearService.cs @@ -0,0 +1 @@ +// This file is intentionally left empty. AccountYear has been removed from the data model. diff --git a/src/Duempelkas.App/Services/IBalanceQueryService.cs b/src/Duempelkas.App/Services/IBalanceQueryService.cs new file mode 100644 index 0000000..20b07ce --- /dev/null +++ b/src/Duempelkas.App/Services/IBalanceQueryService.cs @@ -0,0 +1,8 @@ +using Duempelkas.App.Services.Models; + +namespace Duempelkas.App.Services; + +public interface IBalanceQueryService +{ + Task GetAccountBalanceAsync(int accountId); +} diff --git a/src/Duempelkas.App/Services/IEntryService.cs b/src/Duempelkas.App/Services/IEntryService.cs new file mode 100644 index 0000000..e22f399 --- /dev/null +++ b/src/Duempelkas.App/Services/IEntryService.cs @@ -0,0 +1,15 @@ +using Duempelkas.App.Services.Models; +using Duempelkas.Domain.Enums; + +namespace Duempelkas.App.Services; + +public interface IEntryService +{ + Task> GetEntriesAsync(int accountId, bool currentYearOnly); + Task CreateEntryAsync(int accountId, EntryType type, DateTime date, string title, decimal amount); + Task CreateTransferAsync(int sourceAccountId, int targetAccountId, DateTime date, string title, decimal amount); + Task DeleteEntryAsync(int entryId); + Task RestoreEntryAsync(int entryId); + Task UpdateEntryAsync(int entryId, DateTime date, string title, decimal amount); + Task UpdateTransferAsync(int entryId, int newLinkedAccountId, DateTime date, string title, decimal amount); +} diff --git a/src/Duempelkas.App/Services/IFileSaveService.cs b/src/Duempelkas.App/Services/IFileSaveService.cs new file mode 100644 index 0000000..85112ba --- /dev/null +++ b/src/Duempelkas.App/Services/IFileSaveService.cs @@ -0,0 +1,6 @@ +namespace Duempelkas.App.Services; + +public interface IFileSaveService +{ + Task SaveFileAsync(byte[] content, string suggestedFileName); +} diff --git a/src/Duempelkas.App/Services/IPdfStatementService.cs b/src/Duempelkas.App/Services/IPdfStatementService.cs new file mode 100644 index 0000000..de69107 --- /dev/null +++ b/src/Duempelkas.App/Services/IPdfStatementService.cs @@ -0,0 +1,6 @@ +namespace Duempelkas.App.Services; + +public interface IPdfStatementService +{ + Task GenerateStatementAsync(int accountId, bool currentYearOnly); +} diff --git a/src/Duempelkas.App/Services/Models/AccountSummaryDto.cs b/src/Duempelkas.App/Services/Models/AccountSummaryDto.cs new file mode 100644 index 0000000..ea63267 --- /dev/null +++ b/src/Duempelkas.App/Services/Models/AccountSummaryDto.cs @@ -0,0 +1,3 @@ +namespace Duempelkas.App.Services.Models; + +public record AccountSummaryDto(int Id, string Name, decimal CarryoverBalance, decimal TotalBalance, DateTime CreatedUtc); diff --git a/src/Duempelkas.App/Services/Models/AccountYearDto.cs b/src/Duempelkas.App/Services/Models/AccountYearDto.cs new file mode 100644 index 0000000..4495d0b --- /dev/null +++ b/src/Duempelkas.App/Services/Models/AccountYearDto.cs @@ -0,0 +1 @@ +// This file is intentionally left empty. AccountYear has been removed from the data model. diff --git a/src/Duempelkas.App/Services/Models/EntryDto.cs b/src/Duempelkas.App/Services/Models/EntryDto.cs new file mode 100644 index 0000000..b91029f --- /dev/null +++ b/src/Duempelkas.App/Services/Models/EntryDto.cs @@ -0,0 +1,17 @@ +using Duempelkas.Domain.Enums; + +namespace Duempelkas.App.Services.Models; + +public record EntryDto( + int Id, + int AccountId, + string DisplayId, + EntryType Type, + DateTime Date, + string Title, + decimal Amount, + bool IsDeleted, + bool IsTransfer, + int? TransferLinkId, + string? LinkedAccountName, + int? LinkedAccountId); diff --git a/src/Duempelkas.App/Services/Models/YearlySummaryDto.cs b/src/Duempelkas.App/Services/Models/YearlySummaryDto.cs new file mode 100644 index 0000000..c7fc709 --- /dev/null +++ b/src/Duempelkas.App/Services/Models/YearlySummaryDto.cs @@ -0,0 +1,9 @@ +namespace Duempelkas.App.Services.Models; + +public record AccountBalanceDto( + decimal CarryoverBalance, + decimal TotalIncome, + decimal TotalExpense, + decimal CurrentYearIncome, + decimal CurrentYearExpense, + decimal TotalBalance); diff --git a/src/Duempelkas.App/_Imports.razor b/src/Duempelkas.App/_Imports.razor new file mode 100644 index 0000000..ac56038 --- /dev/null +++ b/src/Duempelkas.App/_Imports.razor @@ -0,0 +1,14 @@ +@using System.Net.Http +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.JSInterop +@using Duempelkas.App +@using Duempelkas.App.Components +@using Duempelkas.App.Components.Layout +@using Duempelkas.App.Components.Accounts +@using Duempelkas.App.Components.Dialogs +@using Duempelkas.App.Services +@using Duempelkas.App.Services.Models +@using Duempelkas.Domain.Entities +@using Duempelkas.Domain.Enums