BaseRepository takes an ILogger via its constructor and logs the failing file path and exception in Save's catch block before rethrowing. Every concrete repository takes ILogger<ThisRepo> and passes it up so failures are categorised by repository type. Pulls Microsoft.Extensions.Logging.Abstractions into the Db project. TransactionRepositoryTestShould and AccountRepositoryTestShould pass NullLogger<T>.Instance. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
116 lines
No EOL
3.7 KiB
C#
116 lines
No EOL
3.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Microsoft.Extensions.Logging;
|
|
using Schaad.Accounting.Interfaces;
|
|
using Schaad.Accounting.Models;
|
|
|
|
namespace Schaad.Accounting.Repositories
|
|
{
|
|
public class AccountRepository : BaseRepository, IAccountRepository
|
|
{
|
|
private readonly string ACCOUNTS = "Accounts.xml";
|
|
|
|
public AccountRepository(ISettingsService settingsService, RepositoryCache cache, ILogger<AccountRepository> logger)
|
|
: base(settingsService, cache, logger)
|
|
{
|
|
EnsureAccountsFile();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load accounts
|
|
/// </summary>
|
|
public List<Account> GetAccountList() => LoadList<Account>(ACCOUNTS);
|
|
|
|
/// <summary>
|
|
/// Save an account (insert/update)
|
|
/// </summary>
|
|
public void SaveAccount(Account account)
|
|
{
|
|
var accounts = GetAccountList();
|
|
var existingAccount = accounts.FirstOrDefault(a => a.Id == account.Id);
|
|
|
|
if (existingAccount == null)
|
|
{
|
|
existingAccount = new Account();
|
|
accounts.Add(existingAccount);
|
|
account.Id = Guid.NewGuid().ToString();
|
|
}
|
|
account.Copy(existingAccount);
|
|
Save(accounts, ACCOUNTS);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get account
|
|
/// </summary>
|
|
public Account GetAccount(string id)
|
|
{
|
|
var accounts = GetAccountList();
|
|
return accounts.FirstOrDefault(t => t.Id == id);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete account
|
|
/// </summary>
|
|
public void DeleteAccount(string id)
|
|
{
|
|
var accounts = GetAccountList();
|
|
accounts.RemoveAll(t => t.Id == id);
|
|
Save(accounts, ACCOUNTS);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get account
|
|
/// </summary>
|
|
public Account GetAccountForBankAccountNumber(string bankAccountNumber)
|
|
{
|
|
var accounts = GetAccountList();
|
|
return accounts.SingleOrDefault(t => t.BankAccountNumber == bankAccountNumber);
|
|
}
|
|
|
|
public void SaveBankAccountBalance(string bankAccountNumber, decimal accountBalance)
|
|
{
|
|
var account = GetAccountForBankAccountNumber(bankAccountNumber);
|
|
account.LastBankBalance = accountBalance;
|
|
SaveAccount(account);
|
|
}
|
|
|
|
private void EnsureAccountsFile()
|
|
{
|
|
string filePath = Path.Combine(settingsService.GetDbPath(), ACCOUNTS);
|
|
if (File.Exists(filePath))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var lastYearFile = Path.Combine(settingsService.GetLastYearDbPath(), ACCOUNTS);
|
|
if (File.Exists(lastYearFile))
|
|
{
|
|
File.Copy(lastYearFile, filePath);
|
|
}
|
|
|
|
// Year rollover: seed start balances from the previous year's closing values.
|
|
// Bank accounts start at the last known bank balance; cash accounts start at zero.
|
|
var accounts = GetAccountList();
|
|
var modified = false;
|
|
foreach (var account in accounts.Where(a => a.Class == ClassIds.Activa))
|
|
{
|
|
if (account.LastBankBalance > 0)
|
|
{
|
|
account.StartBalance = account.LastBankBalance;
|
|
modified = true;
|
|
}
|
|
else if (account.Number < 1010)
|
|
{
|
|
account.StartBalance = 0;
|
|
modified = true;
|
|
}
|
|
}
|
|
if (modified)
|
|
{
|
|
Save(accounts, ACCOUNTS);
|
|
}
|
|
}
|
|
}
|
|
} |