diff --git a/Schaad.Accounting.Common/Datasets/TransactionDataset.cs b/Schaad.Accounting.Common/Datasets/TransactionDataset.cs
index b080b64..9e0f7fd 100644
--- a/Schaad.Accounting.Common/Datasets/TransactionDataset.cs
+++ b/Schaad.Accounting.Common/Datasets/TransactionDataset.cs
@@ -33,5 +33,15 @@ namespace Schaad.Accounting.Datasets
else
return Value;
}
+
+ public decimal GetValue(bool withFxRate, string accountId)
+ {
+ if (accountId == OriginAccountId)
+ {
+ return -1 * GetValue(withFxRate);
+ }
+
+ return GetValue(withFxRate);
+ }
}
}
\ No newline at end of file
diff --git a/Schaad.Accounting.Services/ChartService.cs b/Schaad.Accounting.Services/ChartService.cs
index 03cf130..103ad09 100644
--- a/Schaad.Accounting.Services/ChartService.cs
+++ b/Schaad.Accounting.Services/ChartService.cs
@@ -112,7 +112,7 @@ namespace Schaad.Accounting.Services
{
var groupedByMonth = grp
.GroupBy(t => t.ValueDate.Month)
- .ToDictionary(g => g.Key, g => g.Sum(t => t.GetValue(account.IsFxAccount)));
+ .ToDictionary(g => g.Key, g => g.Sum(t => t.GetValue(account.IsFxAccount, accountId)));
EnsureEntryForEveryMonth(groupedByMonth, maxMonth);
list.Add(new DataSerie(
@@ -132,7 +132,7 @@ namespace Schaad.Accounting.Services
return transactions
.GroupBy(t => t.Text)
- .Select(g => new CategoryTotal(g.Key, g.Sum(t => t.GetValue(account.IsFxAccount))))
+ .Select(g => new CategoryTotal(g.Key, g.Sum(t => t.GetValue(account.IsFxAccount, accountId))))
.Where(c => c.Value != 0)
.ToList();
}
diff --git a/Schaad.Accounting.Services/ViewService.cs b/Schaad.Accounting.Services/ViewService.cs
index b59682f..abcabce 100644
--- a/Schaad.Accounting.Services/ViewService.cs
+++ b/Schaad.Accounting.Services/ViewService.cs
@@ -132,8 +132,9 @@ namespace Schaad.Accounting.Services
}
///
- /// Get transaction list filtered to a single account, with values sign-flipped
- /// when the requested account is the origin of an activa booking.
+ /// Get transaction list filtered to a single account. Sign is not baked in here -
+ /// callers use with this
+ /// account's id to get a value that's negative when the account is paying out.
///
public async Task> GetTransactionViewListAsync(string accountId)
{
@@ -143,22 +144,10 @@ namespace Schaad.Accounting.Services
return transactionList.Select(t =>
new TransactionDataset(
- WithDisplaySign(t),
+ t,
accountsById[t.OriginAccountId],
accountsById[t.TargetAccountId]))
.ToList();
-
- Transaction WithDisplaySign(Transaction t)
- {
- var originAccount = accountsById[t.OriginAccountId];
- if (originAccount.Class == ClassIds.Activa && accountId == t.OriginAccountId)
- {
- var copy = t.Clone();
- copy.Value *= -1;
- return copy;
- }
- return t;
- }
}
///
diff --git a/Schaad.Accounting.Tests/ChartServiceTestShould.cs b/Schaad.Accounting.Tests/ChartServiceTestShould.cs
index 5e00978..1e6dcb9 100644
--- a/Schaad.Accounting.Tests/ChartServiceTestShould.cs
+++ b/Schaad.Accounting.Tests/ChartServiceTestShould.cs
@@ -186,11 +186,27 @@ public class ChartServiceTestShould
series.Count.ShouldBe(2);
var migros = series.Single(s => s.Name == "Migros");
- migros.Y[0].ShouldBe(50m);
+ migros.Y[0].ShouldBe(-50m);
migros.Y[1].ShouldBe(0m);
var coop = series.Single(s => s.Name == "Coop");
coop.Y[0].ShouldBe(0m);
- coop.Y[1].ShouldBe(20m);
+ coop.Y[1].ShouldBe(-20m);
+ }
+
+ [Fact]
+ public async Task ShowPositiveValueWhenAccountIsTargetOfBookingTextTransaction()
+ {
+ var checking = MakeAccount("chk", 1010);
+ var salary = MakeAccount("sal", 3000);
+ viewService.GetAccountViewAsync("chk").Returns(AsDataset(checking));
+ viewService.GetTransactionViewListAsync("chk").Returns(new List
+ {
+ MakeTransaction(salary, checking, value: 5000m, valueDate: new DateTime(2026, 1, 5), text: "Lohn")
+ });
+
+ var series = await BuildService().GetBookingTextExpensesPerMonthAsync("chk");
+
+ series.Single().Y.Single().ShouldBe(5000m);
}
[Fact]
@@ -206,7 +222,7 @@ public class ChartServiceTestShould
var series = await BuildService().GetBookingTextExpensesPerMonthAsync("chk");
- series.Single().Y.Single().ShouldBe(200m);
+ series.Single().Y.Single().ShouldBe(-200m);
}
[Fact]
@@ -225,8 +241,25 @@ public class ChartServiceTestShould
var totals = await BuildService().GetBookingTextTotalsAsync("chk");
totals.Count.ShouldBe(2);
- totals.ShouldContain(t => t.Name == "Migros" && t.Value == 80m);
- totals.ShouldContain(t => t.Name == "Coop" && t.Value == 20m);
+ totals.ShouldContain(t => t.Name == "Migros" && t.Value == -80m);
+ totals.ShouldContain(t => t.Name == "Coop" && t.Value == -20m);
+ }
+
+ [Fact]
+ public async Task NetOutgoingAndIncomingBookingsWithTheSameTextWhenBuildingTotalsForAccount()
+ {
+ var checking = MakeAccount("chk", 1010);
+ var groceries = MakeAccount("groc", 4200);
+ viewService.GetAccountViewAsync("chk").Returns(AsDataset(checking));
+ viewService.GetTransactionViewListAsync("chk").Returns(new List
+ {
+ MakeTransaction(checking, groceries, value: 50m, valueDate: new DateTime(2026, 1, 5), text: "Migros"),
+ MakeTransaction(groceries, checking, value: 20m, valueDate: new DateTime(2026, 1, 6), text: "Migros")
+ });
+
+ var totals = await BuildService().GetBookingTextTotalsAsync("chk");
+
+ totals.Single().Value.ShouldBe(-30m);
}
[Fact]
diff --git a/Schaad.Accounting.Tests/TransactionDatasetTestShould.cs b/Schaad.Accounting.Tests/TransactionDatasetTestShould.cs
new file mode 100644
index 0000000..db931cd
--- /dev/null
+++ b/Schaad.Accounting.Tests/TransactionDatasetTestShould.cs
@@ -0,0 +1,54 @@
+using Schaad.Accounting.Datasets;
+using Schaad.Accounting.Models;
+using Shouldly;
+
+namespace Schaad.Accounting.Tests;
+
+public class TransactionDatasetTestShould
+{
+ private static Account MakeAccount(string id) => new() { Id = id, Name = id, Currency = "CHF" };
+
+ private static TransactionDataset MakeTransaction(Account origin, Account target, decimal value, decimal? fxRate = null) =>
+ new(
+ new Transaction
+ {
+ Id = Guid.NewGuid().ToString(),
+ OriginAccountId = origin.Id,
+ TargetAccountId = target.Id,
+ Value = value,
+ FxRate = fxRate,
+ ValueDate = DateTime.Now,
+ BookingDate = DateTime.Now
+ },
+ origin, target);
+
+ [Fact]
+ public void ReturnNegativeValueWhenAccountIsOrigin()
+ {
+ var checking = MakeAccount("chk");
+ var groceries = MakeAccount("groc");
+ var transaction = MakeTransaction(checking, groceries, value: 50m);
+
+ transaction.GetValue(withFxRate: false, accountId: "chk").ShouldBe(-50m);
+ }
+
+ [Fact]
+ public void ReturnPositiveValueWhenAccountIsTarget()
+ {
+ var checking = MakeAccount("chk");
+ var groceries = MakeAccount("groc");
+ var transaction = MakeTransaction(groceries, checking, value: 50m);
+
+ transaction.GetValue(withFxRate: false, accountId: "chk").ShouldBe(50m);
+ }
+
+ [Fact]
+ public void ApplyFxConversionBeforeApplyingSignWhenAccountIsOrigin()
+ {
+ var checking = MakeAccount("chk");
+ var groceries = MakeAccount("groc");
+ var transaction = MakeTransaction(checking, groceries, value: 100m, fxRate: 0.5m);
+
+ transaction.GetValue(withFxRate: true, accountId: "chk").ShouldBe(-200m);
+ }
+}
diff --git a/Schaad.Accounting.UI/Components/Pages/Transactions.razor b/Schaad.Accounting.UI/Components/Pages/Transactions.razor
index 0d168ff..c26c95e 100644
--- a/Schaad.Accounting.UI/Components/Pages/Transactions.razor
+++ b/Schaad.Accounting.UI/Components/Pages/Transactions.razor
@@ -62,7 +62,7 @@
@GetAccountName(context)
- @context.GetValue(selectedAccount.IsFxAccount).ToString("N2") @selectedAccount.Currency
+ @context.GetValue(selectedAccount.IsFxAccount, selectedAccountId).ToString("N2") @selectedAccount.Currency