Skip to content

Commit 26647e4

Browse files
Add deposits and withdrawals to Merchant Statement system
This commit introduces new features and enhancements to the Merchant Statement processing system. Key changes include: - Added tests in MerchantStatementForDateAggregateTests.cs for validating creation, transaction addition, and handling of deposits and withdrawals. - Modified AddDailySummaryRecord in MerchantStatementAggregate.cs to support additional parameters for deposits and withdrawals. - Introduced new methods in MerchantStatementForDateAggregate.cs for handling deposits and withdrawals, including event handling. - Updated MerchantStatementDomainService.cs to include methods for adding deposits and withdrawals, accommodating new data types. - Added new command records in MerchantStatementCommands.cs for deposits and withdrawals. - Enhanced tests in MerchantStatementDomainEventHandlerTests.cs to ensure correct processing of deposit and withdrawal events. - Updated TestData.cs to include new entities for deposits and withdrawals. - Modified MediatorRegistry.cs to register handlers for new deposit and withdrawal commands. - Updated appsettings.json to include new event types for deposits and withdrawals. - Conducted general code cleanup for clarity and maintainability, utilizing the Shouldly library for assertions in tests.
1 parent b38b507 commit 26647e4

File tree

18 files changed

+829
-582
lines changed

18 files changed

+829
-582
lines changed

TransactionProcessor.Aggregates.Tests/MerchantStatementAggregateTests.cs

Lines changed: 0 additions & 306 deletions
Large diffs are not rendered by default.
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
using Shouldly;
2+
using TransactionProcessor.Models.Merchant;
3+
using TransactionProcessor.Testing;
4+
5+
namespace TransactionProcessor.Aggregates.Tests;
6+
7+
public class MerchantStatementForDateAggregateTests
8+
{
9+
[Fact]
10+
public void MerchantStatementForDateAggregate_CanBeCreated_IsCreated()
11+
{
12+
MerchantStatementForDateAggregate merchantStatementForDateAggregate = MerchantStatementForDateAggregate.Create(TestData.MerchantStatementForDateId1);
13+
14+
merchantStatementForDateAggregate.ShouldNotBeNull();
15+
merchantStatementForDateAggregate.AggregateId.ShouldBe(TestData.MerchantStatementForDateId1);
16+
}
17+
18+
[Fact]
19+
public void MerchantStatementForDateAggregate_AddTransactionToStatement_TransactionAddedToStatement()
20+
{
21+
MerchantStatementForDateAggregate merchantStatementForDateAggregate = MerchantStatementForDateAggregate.Create(TestData.MerchantStatementForDateId1);
22+
merchantStatementForDateAggregate.AddTransactionToStatement(TestData.MerchantStatementId,
23+
TestData.StatementDate,
24+
TestData.EventId1,
25+
TestData.EstateId,
26+
TestData.MerchantId, TestData.Transaction1);
27+
28+
MerchantStatementForDate merchantStatementForDate = merchantStatementForDateAggregate.GetStatement(true);
29+
List<MerchantStatementLine>? statementLines = merchantStatementForDate.GetStatementLines();
30+
statementLines.ShouldNotBeNull();
31+
statementLines.ShouldNotBeEmpty();
32+
statementLines.Count.ShouldBe(1);
33+
}
34+
35+
[Fact]
36+
public void MerchantStatementForDateAggregate_AddTransactionToStatement_DuplicateTransaction_SilentlyHandled()
37+
{
38+
MerchantStatementForDateAggregate merchantStatementForDateAggregate = MerchantStatementForDateAggregate.Create(TestData.MerchantStatementForDateId1);
39+
merchantStatementForDateAggregate.AddTransactionToStatement(TestData.MerchantStatementId,
40+
TestData.StatementDate,
41+
TestData.EventId1,
42+
TestData.EstateId,
43+
TestData.MerchantId, TestData.Transaction1);
44+
45+
merchantStatementForDateAggregate.AddTransactionToStatement(TestData.MerchantStatementId,
46+
TestData.StatementDate,
47+
TestData.EventId1,
48+
TestData.EstateId,
49+
TestData.MerchantId, TestData.Transaction1);
50+
51+
MerchantStatementForDate merchantStatementForDate = merchantStatementForDateAggregate.GetStatement(true);
52+
List<MerchantStatementLine>? statementLines = merchantStatementForDate.GetStatementLines();
53+
statementLines.ShouldNotBeNull();
54+
statementLines.ShouldNotBeEmpty();
55+
statementLines.Count.ShouldBe(1);
56+
}
57+
58+
[Fact]
59+
public void MerchantStatementForDateAggregate_AddSettledFeeToStatement_FeeAddedToStatement()
60+
{
61+
MerchantStatementForDateAggregate merchantStatementForDateAggregate = MerchantStatementForDateAggregate.Create(TestData.MerchantStatementForDateId1);
62+
merchantStatementForDateAggregate.AddSettledFeeToStatement(TestData.MerchantStatementId, TestData.StatementDate, TestData.EventId1, TestData.EstateId, TestData.MerchantId, TestData.SettledFee1);
63+
64+
MerchantStatementForDate merchantStatementForDate = merchantStatementForDateAggregate.GetStatement(true);
65+
List<MerchantStatementLine>? statementLines = merchantStatementForDate.GetStatementLines();
66+
statementLines.ShouldNotBeNull();
67+
statementLines.ShouldNotBeEmpty();
68+
statementLines.Count.ShouldBe(1);
69+
}
70+
71+
[Fact]
72+
public void MerchantStatementForDateAggregate_AddSettledFeeToStatement_DuplicateFee_Silentlyhandled()
73+
{
74+
MerchantStatementForDateAggregate merchantStatementForDateAggregate = MerchantStatementForDateAggregate.Create(TestData.MerchantStatementForDateId1);
75+
merchantStatementForDateAggregate.AddSettledFeeToStatement(TestData.MerchantStatementId, TestData.StatementDate, TestData.EventId1, TestData.EstateId, TestData.MerchantId, TestData.SettledFee1);
76+
merchantStatementForDateAggregate.AddSettledFeeToStatement(TestData.MerchantStatementId, TestData.StatementDate, TestData.EventId1, TestData.EstateId, TestData.MerchantId, TestData.SettledFee1);
77+
78+
MerchantStatementForDate merchantStatementForDate = merchantStatementForDateAggregate.GetStatement(true);
79+
List<MerchantStatementLine>? statementLines = merchantStatementForDate.GetStatementLines();
80+
statementLines.ShouldNotBeNull();
81+
statementLines.ShouldNotBeEmpty();
82+
statementLines.Count.ShouldBe(1);
83+
}
84+
85+
[Theory]
86+
[InlineData(MerchantDepositSource.Manual)]
87+
[InlineData(MerchantDepositSource.Automatic)]
88+
public void MerchantStatementForDateAggregate_AddDepositToStatement_DepositAddedToStatement(MerchantDepositSource source)
89+
{
90+
MerchantStatementForDateAggregate merchantStatementForDateAggregate = MerchantStatementForDateAggregate.Create(TestData.MerchantStatementForDateId1);
91+
merchantStatementForDateAggregate.AddDepositToStatement(TestData.MerchantStatementId,
92+
TestData.StatementDate,
93+
TestData.EventId1,
94+
TestData.EstateId,
95+
TestData.MerchantId, new Deposit {
96+
DepositDateTime = TestData.DepositDateTime,
97+
Amount = TestData.DepositAmount.Value,
98+
DepositId = TestData.DepositId,
99+
Reference = TestData.DepositReference,
100+
Source = source
101+
});
102+
103+
MerchantStatementForDate merchantStatementForDate = merchantStatementForDateAggregate.GetStatement(true);
104+
List<MerchantStatementLine>? statementLines = merchantStatementForDate.GetStatementLines();
105+
statementLines.ShouldNotBeNull();
106+
statementLines.ShouldNotBeEmpty();
107+
statementLines.Count.ShouldBe(1);
108+
}
109+
110+
[Fact]
111+
public void MerchantStatementForDateAggregate_AddWithdrawalToStatement_WithdrawalAddedToStatement()
112+
{
113+
MerchantStatementForDateAggregate merchantStatementForDateAggregate = MerchantStatementForDateAggregate.Create(TestData.MerchantStatementForDateId1);
114+
merchantStatementForDateAggregate.AddWithdrawalToStatement(TestData.MerchantStatementId,
115+
TestData.StatementDate,
116+
TestData.EventId1,
117+
TestData.EstateId,
118+
TestData.MerchantId, new Withdrawal()
119+
{
120+
WithdrawalDateTime = TestData.WithdrawalDateTime,
121+
Amount = TestData.WithdrawalAmount.Value,
122+
WithdrawalId = TestData.WithdrawalId
123+
});
124+
125+
MerchantStatementForDate merchantStatementForDate = merchantStatementForDateAggregate.GetStatement(true);
126+
List<MerchantStatementLine>? statementLines = merchantStatementForDate.GetStatementLines();
127+
statementLines.ShouldNotBeNull();
128+
statementLines.ShouldNotBeEmpty();
129+
statementLines.Count.ShouldBe(1);
130+
}
131+
132+
133+
[Fact]
134+
public void MerchantStatementAggregate_AddDailySummaryRecord_RecordIsAdded() {
135+
MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId);
136+
Should.NotThrow(() => { merchantStatementAggregate.AddDailySummaryRecord(TestData.TransactionDateTime.Date, 1, 100.00m, 1, 0.10m,
137+
1, 1000, 1, 200); });
138+
}
139+
140+
[Fact]
141+
public void MerchantStatementAggregate_AddDailySummaryRecord_DuplicateAdd_ExceptionIsThrown()
142+
{
143+
MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId);
144+
merchantStatementAggregate.AddDailySummaryRecord(TestData.TransactionDateTime.Date, 1, 100.00m, 1, 0.10m,
145+
1, 1000, 1, 200);
146+
Should.Throw<InvalidOperationException>(() => { merchantStatementAggregate.AddDailySummaryRecord(TestData.TransactionDateTime.Date, 1, 100.00m, 1, 0.10m,
147+
1, 1000, 1, 200); });
148+
}
149+
150+
151+
[Fact]
152+
public void MerchantStatementAggregate_GenerateStatement_StatementIsGenerated()
153+
{
154+
MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId);
155+
merchantStatementAggregate.AddDailySummaryRecord(TestData.TransactionDateTime.Date, 1, 100.00m, 1, 0.10m,
156+
1, 1000, 1, 200);
157+
merchantStatementAggregate.GenerateStatement(TestData.StatementGeneratedDate);
158+
159+
MerchantStatement merchantStatement = merchantStatementAggregate.GetStatement();
160+
merchantStatement.IsGenerated.ShouldBeTrue();
161+
}
162+
163+
[Fact]
164+
public void MerchantStatementAggregate_GenerateStatement_StatementIsAlreadyGenerated_ExceptionThrown()
165+
{
166+
MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId);
167+
merchantStatementAggregate.AddDailySummaryRecord(TestData.TransactionDateTime.Date, 1, 100.00m, 1, 0.10m, 1, 1000, 1, 200);
168+
merchantStatementAggregate.GenerateStatement(TestData.StatementGeneratedDate);
169+
Should.Throw<InvalidOperationException>(() => {
170+
merchantStatementAggregate.GenerateStatement(TestData.StatementGeneratedDate);
171+
});
172+
}
173+
174+
[Fact]
175+
public void MerchantStatementAggregate_GenerateStatement_NoSummaries_ExceptionThrown()
176+
{
177+
MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId);
178+
179+
Should.Throw<InvalidOperationException>(() => {
180+
merchantStatementAggregate.GenerateStatement(TestData.StatementGeneratedDate);
181+
});
182+
}
183+
184+
[Fact]
185+
public void MerchantStatementAggregate_BuildStatement_StatementIsBuilt()
186+
{
187+
MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId);
188+
merchantStatementAggregate.AddDailySummaryRecord(TestData.TransactionDateTime.Date, 1, 100.00m, 1, 0.10m, 1, 1000, 1, 200);
189+
merchantStatementAggregate.GenerateStatement(TestData.StatementGeneratedDate);
190+
merchantStatementAggregate.BuildStatement(TestData.StatementBuiltDate,TestData.StatementData);
191+
192+
MerchantStatement merchantStatement = merchantStatementAggregate.GetStatement();
193+
merchantStatement.BuiltDateTime.ShouldBe(TestData.StatementBuiltDate);
194+
}
195+
196+
[Fact]
197+
public void MerchantStatementAggregate_BuildStatement_StatementIsAlreadyBuilt_ExceptionThrown()
198+
{
199+
MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId);
200+
merchantStatementAggregate.AddDailySummaryRecord(TestData.TransactionDateTime.Date, 1, 100.00m, 1, 0.10m, 1, 1000, 1, 200);
201+
merchantStatementAggregate.GenerateStatement(TestData.StatementGeneratedDate);
202+
merchantStatementAggregate.BuildStatement(TestData.StatementBuiltDate, TestData.StatementData);
203+
204+
Should.Throw<InvalidOperationException>(() => {
205+
merchantStatementAggregate.BuildStatement(TestData.StatementBuiltDate, TestData.StatementData);
206+
});
207+
}
208+
209+
[Fact]
210+
public void MerchantStatementAggregate_BuildStatement_StatementIsNotGenerated_ExceptionThrown()
211+
{
212+
MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId);
213+
merchantStatementAggregate.AddDailySummaryRecord(TestData.TransactionDateTime.Date, 1, 100.00m, 1, 0.10m, 1, 1000, 1, 200);
214+
215+
Should.Throw<InvalidOperationException>(() => {
216+
merchantStatementAggregate.BuildStatement(TestData.StatementBuiltDate, TestData.StatementData);
217+
});
218+
}
219+
220+
[Fact]
221+
public void MerchantStatementAggregate_EmailStatement_StatementIsEmailed()
222+
{
223+
MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId);
224+
merchantStatementAggregate.AddDailySummaryRecord(TestData.TransactionDateTime.Date, 1, 100.00m, 1, 0.10m, 1, 1000, 1, 200);
225+
merchantStatementAggregate.GenerateStatement(TestData.StatementGeneratedDate);
226+
merchantStatementAggregate.BuildStatement(TestData.StatementBuiltDate, TestData.StatementData);
227+
228+
merchantStatementAggregate.EmailStatement(TestData.StatementEmailedDate, TestData.MessageId);
229+
230+
MerchantStatement merchantStatement = merchantStatementAggregate.GetStatement();
231+
merchantStatement.HasBeenEmailed.ShouldBeTrue();
232+
}
233+
234+
[Fact]
235+
public void MerchantStatementAggregate_EmailStatement_StatementIsAlreadyEmailed_ExceptionThrown()
236+
{
237+
MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId);
238+
merchantStatementAggregate.AddDailySummaryRecord(TestData.TransactionDateTime.Date, 1, 100.00m, 1, 0.10m, 1, 1000, 1, 200);
239+
merchantStatementAggregate.GenerateStatement(TestData.StatementGeneratedDate);
240+
merchantStatementAggregate.BuildStatement(TestData.StatementBuiltDate, TestData.StatementData);
241+
merchantStatementAggregate.EmailStatement(TestData.StatementEmailedDate, TestData.MessageId);
242+
243+
Should.Throw<InvalidOperationException>(() => {
244+
merchantStatementAggregate.EmailStatement(TestData.StatementEmailedDate, TestData.MessageId);
245+
});
246+
}
247+
248+
[Fact]
249+
public void MerchantStatementAggregate_EmailStatement_StatementIsNotBuilt_ExceptionThrown()
250+
{
251+
MerchantStatementAggregate merchantStatementAggregate = MerchantStatementAggregate.Create(TestData.MerchantStatementId);
252+
merchantStatementAggregate.AddDailySummaryRecord(TestData.TransactionDateTime.Date, 1, 100.00m, 1, 0.10m, 1, 1000, 1, 200);
253+
merchantStatementAggregate.GenerateStatement(TestData.StatementGeneratedDate);
254+
255+
Should.Throw<InvalidOperationException>(() => {
256+
merchantStatementAggregate.EmailStatement(TestData.StatementEmailedDate, TestData.MessageId);
257+
});
258+
}
259+
}

TransactionProcessor.Aggregates/MerchantStatementAggregate.cs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -213,14 +213,6 @@ public static void GenerateStatement(this MerchantStatementAggregate aggregate,
213213
aggregate.ApplyAndAppend(statementGeneratedEvent);
214214
}
215215

216-
private static void EnsureStatementHasBeenCreated(this MerchantStatementAggregate aggregate)
217-
{
218-
if (aggregate.IsCreated == false)
219-
{
220-
throw new InvalidOperationException("Statement header has not been created");
221-
}
222-
}
223-
224216
private static void EnsureStatementHasBeenGenerated(this MerchantStatementAggregate aggregate)
225217
{
226218
if (aggregate.IsGenerated == false)
@@ -261,15 +253,19 @@ private static void EnsureStatementHasNotAlreadyBeenEmailed(this MerchantStateme
261253
}
262254
}
263255

264-
public static void AddDailySummaryRecord(this MerchantStatementAggregate aggregate, DateTime activityDate, Int32 numberOfTransactions, Decimal valueOfTransactions, Int32 numberOfSettledFees, Decimal valueOfSettledFees) {
256+
public static void AddDailySummaryRecord(this MerchantStatementAggregate aggregate, DateTime activityDate,
257+
Int32 numberOfTransactions, Decimal valueOfTransactions,
258+
Int32 numberOfSettledFees, Decimal valueOfSettledFees,
259+
Int32 numberOfDepoits, Decimal valueOfDepoits,
260+
Int32 numberOfWithdrawals, Decimal valueOfWithdrawals) {
265261
if (aggregate.MerchantStatementSummaries.Any(s => s.ActivityDate == activityDate)) {
266262
throw new InvalidOperationException($"Summary Data for Activity Date {activityDate:yyyy-MM-dd} already exists");
267263
}
268264

269265
// TODO: should this check the date has been added to the statement, before allowing the summary?
270266

271267
MerchantStatementDomainEvents.StatementSummaryForDateEvent statementSummaryForDateEvent = new(aggregate.AggregateId, aggregate.EstateId, aggregate.MerchantId, activityDate,aggregate.MerchantStatementSummaries.Count +1
272-
,numberOfTransactions, valueOfTransactions, numberOfSettledFees, valueOfSettledFees);
268+
,numberOfTransactions, valueOfTransactions, numberOfSettledFees, valueOfSettledFees, numberOfDepoits, valueOfDepoits, numberOfWithdrawals, valueOfWithdrawals);
273269
aggregate.ApplyAndAppend(statementSummaryForDateEvent);
274270
}
275271
}

0 commit comments

Comments
 (0)