Compare commits

..

No commits in common. "4cc08886271ded3dc03bbaba708b60b78ec505d2" and "7f284b10325019080981fb16be4ae20ce9a1ed4b" have entirely different histories.

4 changed files with 16 additions and 98 deletions

View file

@ -43,20 +43,5 @@ namespace Schaad.Accounting.Datasets
return GetValue(withFxRate);
}
/// <summary>
/// First word of <see cref="Text"/>, used to group related bookings (e.g. "Narbonne Car"
/// and "Narbonne Food" both group under "Narbonne").
/// </summary>
public string GetTextGroup()
{
if (string.IsNullOrEmpty(Text))
{
return Text;
}
var spaceIndex = Text.IndexOf(' ');
return spaceIndex < 0 ? Text : Text[..spaceIndex];
}
}
}

View file

@ -108,7 +108,7 @@ namespace Schaad.Accounting.Services
var maxMonth = transactions.Max(t => t.ValueDate.Month);
var list = new List<DataSerie>();
foreach (var grp in transactions.GroupBy(t => t.GetTextGroup()))
foreach (var grp in transactions.GroupBy(t => t.Text))
{
var groupedByMonth = grp
.GroupBy(t => t.ValueDate.Month)
@ -132,7 +132,7 @@ namespace Schaad.Accounting.Services
return transactions
.Where(t => t.TargetAccountId == accountId)
.GroupBy(t => t.GetTextGroup())
.GroupBy(t => t.Text)
.Select(g => new CategoryTotal(g.Key, g.Sum(t => t.GetValue(account.IsFxAccount, accountId))))
.Where(c => c.Value != 0)
.ToList();

View file

@ -193,24 +193,6 @@ public class ChartServiceTestShould
coop.Y[1].ShouldBe(-20m);
}
[Fact]
public async Task GroupBookingTextsByFirstWordWhenBuildingMonthlySeriesForAccount()
{
var checking = MakeAccount("chk", 1010);
var groceries = MakeAccount("groc", 4200);
viewService.GetAccountViewAsync("chk").Returns(AsDataset(checking));
viewService.GetTransactionViewListAsync("chk").Returns(new List<TransactionDataset>
{
MakeTransaction(checking, groceries, value: 50m, valueDate: new DateTime(2026, 1, 5), text: "Narbonne Car"),
MakeTransaction(checking, groceries, value: 20m, valueDate: new DateTime(2026, 1, 6), text: "Narbonne Food")
});
var series = await BuildService().GetBookingTextExpensesPerMonthAsync("chk");
var narbonne = series.Single(s => s.Name == "Narbonne");
narbonne.Y[0].ShouldBe(-70m);
}
[Fact]
public async Task ShowPositiveValueWhenAccountIsTargetOfBookingTextTransaction()
{
@ -248,54 +230,36 @@ public class ChartServiceTestShould
{
var checking = MakeAccount("chk", 1010);
var groceries = MakeAccount("groc", 4200);
viewService.GetAccountViewAsync("groc").Returns(AsDataset(groceries));
viewService.GetTransactionViewListAsync("groc").Returns(new List<TransactionDataset>
viewService.GetAccountViewAsync("chk").Returns(AsDataset(checking));
viewService.GetTransactionViewListAsync("chk").Returns(new List<TransactionDataset>
{
MakeTransaction(checking, groceries, value: 50m, valueDate: new DateTime(2026, 1, 5), text: "Migros"),
MakeTransaction(checking, groceries, value: 30m, valueDate: new DateTime(2026, 2, 3), text: "Migros"),
MakeTransaction(checking, groceries, value: 20m, valueDate: new DateTime(2026, 2, 4), text: "Coop")
});
var totals = await BuildService().GetBookingTextTotalsAsync("groc");
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 GroupBookingTextsByFirstWordWhenBuildingTotalsForAccount()
public async Task NetOutgoingAndIncomingBookingsWithTheSameTextWhenBuildingTotalsForAccount()
{
var checking = MakeAccount("chk", 1010);
var groceries = MakeAccount("groc", 4200);
viewService.GetAccountViewAsync("groc").Returns(AsDataset(groceries));
viewService.GetTransactionViewListAsync("groc").Returns(new List<TransactionDataset>
{
MakeTransaction(checking, groceries, value: 50m, valueDate: new DateTime(2026, 1, 5), text: "Narbonne Car"),
MakeTransaction(checking, groceries, value: 20m, valueDate: new DateTime(2026, 1, 6), text: "Narbonne Food")
});
var totals = await BuildService().GetBookingTextTotalsAsync("groc");
totals.Single().Name.ShouldBe("Narbonne");
totals.Single().Value.ShouldBe(70m);
}
[Fact]
public async Task ExcludeBookingsWhereAccountIsOriginWhenBuildingTotalsForAccount()
{
var checking = MakeAccount("chk", 1010);
var groceries = MakeAccount("groc", 4200);
viewService.GetAccountViewAsync("groc").Returns(AsDataset(groceries));
viewService.GetTransactionViewListAsync("groc").Returns(new List<TransactionDataset>
viewService.GetAccountViewAsync("chk").Returns(AsDataset(checking));
viewService.GetTransactionViewListAsync("chk").Returns(new List<TransactionDataset>
{
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("groc");
var totals = await BuildService().GetBookingTextTotalsAsync("chk");
totals.Single().Value.ShouldBe(50m);
totals.Single().Value.ShouldBe(-30m);
}
[Fact]
@ -303,14 +267,14 @@ public class ChartServiceTestShould
{
var checking = MakeAccount("chk", 1010);
var groceries = MakeAccount("groc", 4200);
viewService.GetAccountViewAsync("groc").Returns(AsDataset(groceries));
viewService.GetTransactionViewListAsync("groc").Returns(new List<TransactionDataset>
viewService.GetAccountViewAsync("chk").Returns(AsDataset(checking));
viewService.GetTransactionViewListAsync("chk").Returns(new List<TransactionDataset>
{
MakeTransaction(checking, groceries, value: 50m, valueDate: new DateTime(2026, 1, 5), text: "Migros"),
MakeTransaction(checking, groceries, value: -50m, valueDate: new DateTime(2026, 1, 6), text: "Migros")
});
var totals = await BuildService().GetBookingTextTotalsAsync("groc");
var totals = await BuildService().GetBookingTextTotalsAsync("chk");
totals.ShouldBeEmpty();
}

View file

@ -8,7 +8,7 @@ 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, string text = "") =>
private static TransactionDataset MakeTransaction(Account origin, Account target, decimal value, decimal? fxRate = null) =>
new(
new Transaction
{
@ -17,7 +17,6 @@ public class TransactionDatasetTestShould
TargetAccountId = target.Id,
Value = value,
FxRate = fxRate,
Text = text,
ValueDate = DateTime.Now,
BookingDate = DateTime.Now
},
@ -52,34 +51,4 @@ public class TransactionDatasetTestShould
transaction.GetValue(withFxRate: true, accountId: "chk").ShouldBe(-200m);
}
[Fact]
public void ReturnFirstWordWhenTextHasMultipleWords()
{
var checking = MakeAccount("chk");
var groceries = MakeAccount("groc");
var transaction = MakeTransaction(checking, groceries, value: 50m, text: "Narbonne Car");
transaction.GetTextGroup().ShouldBe("Narbonne");
}
[Fact]
public void ReturnWholeTextWhenTextHasNoSpace()
{
var checking = MakeAccount("chk");
var groceries = MakeAccount("groc");
var transaction = MakeTransaction(checking, groceries, value: 50m, text: "Migros");
transaction.GetTextGroup().ShouldBe("Migros");
}
[Fact]
public void ReturnEmptyStringWhenTextIsEmpty()
{
var checking = MakeAccount("chk");
var groceries = MakeAccount("groc");
var transaction = MakeTransaction(checking, groceries, value: 50m, text: "");
transaction.GetTextGroup().ShouldBe("");
}
}