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>
122 lines
No EOL
3.7 KiB
C#
122 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)
|
|
{
|
|
if (string.IsNullOrEmpty(account.Currency))
|
|
{
|
|
account.Currency = "CHF";
|
|
}
|
|
|
|
var accounts = GetAccountList();
|
|
var idx = accounts.FindIndex(a => a.Id == account.Id);
|
|
if (idx >= 0)
|
|
{
|
|
accounts[idx] = account;
|
|
}
|
|
else
|
|
{
|
|
account.Id = Guid.NewGuid().ToString();
|
|
accounts.Add(account);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
} |