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>
102 lines
No EOL
3.4 KiB
C#
102 lines
No EOL
3.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Microsoft.Extensions.Logging;
|
|
using Schaad.Accounting.Interfaces;
|
|
using Schaad.Accounting.Models;
|
|
|
|
namespace Schaad.Accounting.Repositories
|
|
{
|
|
public class TransactionRepository : BaseRepository, ITransactionRepository
|
|
{
|
|
private readonly IAccountRepository accountRepository;
|
|
private readonly string TRANSACTIONS = "Transactions.xml";
|
|
|
|
public TransactionRepository(ISettingsService settingsService, RepositoryCache cache, IAccountRepository accountRepository, ILogger<TransactionRepository> logger)
|
|
: base(settingsService, cache, logger)
|
|
{
|
|
this.accountRepository = accountRepository;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get transaction list
|
|
/// </summary>
|
|
public List<Transaction> GetTransactionList() => LoadList<Transaction>(TRANSACTIONS);
|
|
|
|
/// <summary>
|
|
/// Save a transaction (insert/update)
|
|
/// </summary>
|
|
public void SaveTransaction(Transaction transaction)
|
|
{
|
|
transaction.Value = Math.Abs(transaction.Value);
|
|
|
|
var originIsFx = accountRepository.GetAccount(transaction.OriginAccountId).IsFxAccount;
|
|
var targetIsFx = accountRepository.GetAccount(transaction.TargetAccountId).IsFxAccount;
|
|
var isFxAccount = originIsFx || targetIsFx;
|
|
|
|
// user enters amount in the foreign account's currency -> convert to CHF for storage
|
|
if (isFxAccount && transaction.FxRate != 0)
|
|
{
|
|
transaction.Value = transaction.Value * transaction.FxRate.Value;
|
|
}
|
|
|
|
if (!isFxAccount)
|
|
{
|
|
transaction.FxRate = null;
|
|
}
|
|
|
|
if (transaction.BookingDate <= DateTime.MinValue)
|
|
{
|
|
transaction.BookingDate = transaction.ValueDate;
|
|
}
|
|
|
|
var transactionList = GetTransactionList();
|
|
var idx = transactionList.FindIndex(t => t.Id == transaction.Id);
|
|
if (idx >= 0)
|
|
{
|
|
transactionList[idx] = transaction;
|
|
}
|
|
else
|
|
{
|
|
transaction.Id = Guid.NewGuid().ToString();
|
|
transactionList.Add(transaction);
|
|
}
|
|
Save(transactionList, TRANSACTIONS);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get transaction
|
|
/// </summary>
|
|
public Transaction GetTransaction(string id)
|
|
{
|
|
var transactions = GetTransactionList();
|
|
var stored = transactions.FirstOrDefault(t => t.Id == id);
|
|
if (stored == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var result = stored.Clone();
|
|
|
|
// value is stored in CHF -> convert back to foreign currency for display/editing
|
|
var isFxAccount = accountRepository.GetAccount(result.OriginAccountId).IsFxAccount
|
|
|| accountRepository.GetAccount(result.TargetAccountId).IsFxAccount;
|
|
if (isFxAccount && result.FxRate != 0)
|
|
{
|
|
result.Value = result.Value / result.FxRate.Value;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delete transaction
|
|
/// </summary>
|
|
public void DeleteTransaction(string id)
|
|
{
|
|
var transactions = GetTransactionList();
|
|
transactions.RemoveAll(t => t.Id == id);
|
|
Save(transactions, TRANSACTIONS);
|
|
}
|
|
}
|
|
} |