180 lines
7.0 KiB
C#
180 lines
7.0 KiB
C#
using Duempelkas.App.Services;
|
|
using Duempelkas.App.Services.Models;
|
|
using Duempelkas.Domain.Entities;
|
|
using Duempelkas.Domain.Enums;
|
|
using Duempelkas.Infrastructure.Persistence;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace Duempelkas.Infrastructure.Services;
|
|
|
|
public class AccountService : IAccountService
|
|
{
|
|
private readonly IDbContextFactory<FinanceDbContext> _dbFactory;
|
|
|
|
private readonly DatabaseLocation? _location;
|
|
|
|
public AccountService(IDbContextFactory<FinanceDbContext> dbFactory, DatabaseLocation? location = null)
|
|
{
|
|
_dbFactory = dbFactory;
|
|
_location = location;
|
|
}
|
|
|
|
public Task<List<AccountSummaryDto>> GetAllAccountsAsync()
|
|
{
|
|
return GetAllAccountsAsync(null);
|
|
}
|
|
|
|
public async Task<List<AccountSummaryDto>> GetAllAccountsAsync(int? year)
|
|
{
|
|
using var lease = await DatabaseLease.AcquireAsync(_location);
|
|
await using var db = await _dbFactory.CreateDbContextAsync();
|
|
|
|
var accounts = await db.Accounts
|
|
.OrderBy(a => a.Name)
|
|
.ToListAsync();
|
|
|
|
var entries = db.Entries.Where(e => !e.IsDeleted);
|
|
|
|
if (year.HasValue)
|
|
{
|
|
var movementBeforeYearByAccountId = await entries
|
|
.Where(e => e.Date.Year < year.Value)
|
|
.GroupBy(e => e.AccountId)
|
|
.ToDictionaryAsync(
|
|
g => g.Key,
|
|
g => g.Sum(e => e.Type == EntryType.Income ? e.Amount : -e.Amount));
|
|
|
|
var movementInYearByAccountId = await entries
|
|
.Where(e => e.Date.Year == year.Value)
|
|
.GroupBy(e => e.AccountId)
|
|
.ToDictionaryAsync(
|
|
g => g.Key,
|
|
g => g.Sum(e => e.Type == EntryType.Income ? e.Amount : -e.Amount));
|
|
|
|
return accounts
|
|
.Select(account =>
|
|
{
|
|
var carryoverForYear = account.CarryoverBalance + movementBeforeYearByAccountId.GetValueOrDefault(account.Id);
|
|
var totalBalance = carryoverForYear + movementInYearByAccountId.GetValueOrDefault(account.Id);
|
|
return new AccountSummaryDto(account.Id, account.Name, carryoverForYear, totalBalance, account.CreatedUtc);
|
|
})
|
|
.ToList();
|
|
}
|
|
|
|
var movementAllByAccountId = await entries
|
|
.GroupBy(e => e.AccountId)
|
|
.ToDictionaryAsync(
|
|
g => g.Key,
|
|
g => g.Sum(e => e.Type == EntryType.Income ? e.Amount : -e.Amount));
|
|
|
|
return accounts
|
|
.Select(account =>
|
|
{
|
|
var totalBalance = account.CarryoverBalance + movementAllByAccountId.GetValueOrDefault(account.Id);
|
|
return new AccountSummaryDto(account.Id, account.Name, account.CarryoverBalance, totalBalance, account.CreatedUtc);
|
|
})
|
|
.ToList();
|
|
}
|
|
|
|
public async Task<AccountSummaryDto> GetAccountAsync(int accountId)
|
|
{
|
|
using var lease = await DatabaseLease.AcquireAsync(_location);
|
|
await using var db = await _dbFactory.CreateDbContextAsync();
|
|
|
|
var account = await db.Accounts
|
|
.Include(a => a.Entries)
|
|
.FirstOrDefaultAsync(a => a.Id == accountId)
|
|
?? throw new InvalidOperationException($"Account {accountId} not found.");
|
|
|
|
var totalIncome = account.Entries.Where(e => !e.IsDeleted && e.Type == EntryType.Income).Sum(e => e.Amount);
|
|
var totalExpense = account.Entries.Where(e => !e.IsDeleted && e.Type == EntryType.Expense).Sum(e => e.Amount);
|
|
var totalBalance = account.CarryoverBalance + totalIncome - totalExpense;
|
|
|
|
return new AccountSummaryDto(account.Id, account.Name, account.CarryoverBalance, totalBalance, account.CreatedUtc);
|
|
}
|
|
|
|
public async Task<AccountSummaryDto> CreateAccountAsync(string name)
|
|
{
|
|
using var lease = await DatabaseLease.AcquireAsync(_location);
|
|
await using var db = await _dbFactory.CreateDbContextAsync();
|
|
|
|
var account = new Account { Name = name };
|
|
db.Accounts.Add(account);
|
|
await db.SaveChangesAsync();
|
|
return new AccountSummaryDto(account.Id, account.Name, 0m, 0m, account.CreatedUtc);
|
|
}
|
|
|
|
public async Task RenameAccountAsync(int accountId, string newName)
|
|
{
|
|
using var lease = await DatabaseLease.AcquireAsync(_location);
|
|
await using var db = await _dbFactory.CreateDbContextAsync();
|
|
|
|
var account = await db.Accounts.FindAsync(accountId)
|
|
?? throw new InvalidOperationException($"Account {accountId} not found.");
|
|
account.Name = newName;
|
|
await db.SaveChangesAsync();
|
|
}
|
|
|
|
public async Task UpdateCarryoverAsync(int accountId, decimal carryoverBalance)
|
|
{
|
|
using var lease = await DatabaseLease.AcquireAsync(_location);
|
|
await using var db = await _dbFactory.CreateDbContextAsync();
|
|
|
|
var account = await db.Accounts.FindAsync(accountId)
|
|
?? throw new InvalidOperationException($"Account {accountId} not found.");
|
|
account.CarryoverBalance = carryoverBalance;
|
|
await db.SaveChangesAsync();
|
|
}
|
|
|
|
public async Task UpdateCarryoverAsync(int accountId, decimal carryoverBalance, int? year)
|
|
{
|
|
if (!year.HasValue)
|
|
{
|
|
await UpdateCarryoverAsync(accountId, carryoverBalance);
|
|
return;
|
|
}
|
|
|
|
using var lease = await DatabaseLease.AcquireAsync(_location);
|
|
await using var db = await _dbFactory.CreateDbContextAsync();
|
|
|
|
var account = await db.Accounts.FindAsync(accountId)
|
|
?? throw new InvalidOperationException($"Account {accountId} not found.");
|
|
|
|
var movementBeforeYear = await db.Entries
|
|
.Where(e => e.AccountId == accountId && !e.IsDeleted && e.Date.Year < year.Value)
|
|
.SumAsync(e => e.Type == EntryType.Income ? e.Amount : -e.Amount);
|
|
|
|
account.CarryoverBalance = carryoverBalance - movementBeforeYear;
|
|
await db.SaveChangesAsync();
|
|
}
|
|
|
|
public async Task DeleteAccountAsync(int accountId)
|
|
{
|
|
using var lease = await DatabaseLease.AcquireAsync(_location);
|
|
await using var db = await _dbFactory.CreateDbContextAsync();
|
|
|
|
var account = await db.Accounts
|
|
.Include(a => a.Entries)
|
|
.FirstOrDefaultAsync(a => a.Id == accountId)
|
|
?? throw new InvalidOperationException($"Account {accountId} not found.");
|
|
|
|
var entryIds = account.Entries.Select(e => e.Id).ToList();
|
|
var transferLinks = await db.TransferLinks
|
|
.Where(tl => entryIds.Contains(tl.SourceEntryId) || entryIds.Contains(tl.TargetEntryId))
|
|
.ToListAsync();
|
|
|
|
foreach (var link in transferLinks)
|
|
{
|
|
var otherEntryId = entryIds.Contains(link.SourceEntryId) ? link.TargetEntryId : link.SourceEntryId;
|
|
var otherEntry = await db.Entries.FindAsync(otherEntryId);
|
|
if (otherEntry != null)
|
|
db.Entries.Remove(otherEntry);
|
|
}
|
|
db.TransferLinks.RemoveRange(transferLinks);
|
|
|
|
db.Accounts.Remove(account);
|
|
await db.SaveChangesAsync();
|
|
}
|
|
|
|
}
|