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>
95 lines
No EOL
2.8 KiB
C#
95 lines
No EOL
2.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.Linq;
|
|
using System.Xml.Serialization;
|
|
|
|
namespace Schaad.Accounting.Models
|
|
{
|
|
public class Transaction
|
|
{
|
|
public string Id { get; set; }
|
|
public string BankTransactionId { get; set; }
|
|
|
|
/// <summary>
|
|
/// Needed for booking rule
|
|
/// </summary>
|
|
public string BankTransactionText { get; set; }
|
|
|
|
public string RelatedParty { get; set; }
|
|
|
|
[Display(Name = "Valuta")]
|
|
[Required]
|
|
public DateTime ValueDate { get; set; }
|
|
|
|
[Display(Name = "Buchung")]
|
|
public DateTime BookingDate { get; set; }
|
|
|
|
[Display(Name = "Buchungstext")]
|
|
[Required]
|
|
public string Text { get; set; }
|
|
|
|
[Display(Name = "Betrag")]
|
|
[Required]
|
|
public decimal Value { get; set; }
|
|
|
|
[Display(Name = "Habenkonto")]
|
|
[Required]
|
|
public string OriginAccountId { get; set; }
|
|
|
|
[Display(Name = "Sollkonto")]
|
|
[Required]
|
|
public string TargetAccountId { get; set; }
|
|
|
|
[Display(Name = "Währungskurs")]
|
|
public decimal? FxRate { get; set; }
|
|
|
|
[XmlIgnore]
|
|
public bool ReadOnly {get; set;}
|
|
|
|
public Transaction()
|
|
{
|
|
}
|
|
|
|
public Transaction(BankTransaction bankTransaction, List<Account> accounts)
|
|
{
|
|
BankTransactionId = bankTransaction.Id;
|
|
BankTransactionText = bankTransaction.Text;
|
|
Value = bankTransaction.Value;
|
|
ValueDate = bankTransaction.ValueDate;
|
|
BookingDate = bankTransaction.BookingDate;
|
|
|
|
// Depending on positiv or negative value, set the account
|
|
var bankAccount = accounts.FirstOrDefault(a => a.BankAccountNumber == bankTransaction.BankAccountNumber);
|
|
if (Value >= 0)
|
|
{
|
|
TargetAccountId = bankAccount.Id;
|
|
RelatedParty = bankTransaction.Debtor;
|
|
}
|
|
else
|
|
{
|
|
OriginAccountId = bankAccount.Id;
|
|
RelatedParty = bankTransaction.Creditor;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a shallow independent copy — used to hand out defensive copies
|
|
/// (e.g. from repository reads) without exposing the caller to later mutation.
|
|
/// </summary>
|
|
public Transaction Clone() => new()
|
|
{
|
|
Id = Id,
|
|
BankTransactionId = BankTransactionId,
|
|
BankTransactionText = BankTransactionText,
|
|
RelatedParty = RelatedParty,
|
|
OriginAccountId = OriginAccountId,
|
|
TargetAccountId = TargetAccountId,
|
|
Text = Text,
|
|
Value = Value,
|
|
ValueDate = ValueDate,
|
|
BookingDate = BookingDate,
|
|
FxRate = FxRate
|
|
};
|
|
}
|
|
} |