Skip to content

Commit 63d1882

Browse files
Merge pull request #158 from TransactionProcessing/task/#156_pendingsettlement
Pending Settlement Processing added
2 parents ffc5ac2 + 940fe87 commit 63d1882

31 files changed

+1818
-196
lines changed

TransactionProcessor.BusinessLogic.Tests/DomainEventHandlers/TransactionDomainEventHandlerTests.cs

Lines changed: 264 additions & 128 deletions
Large diffs are not rendered by default.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace TransactionProcessor.BusinessLogic.Tests.Extensions
8+
{
9+
using Common;
10+
using Shared.Exceptions;
11+
using Shouldly;
12+
using Xunit;
13+
14+
public class ExtensionsTests
15+
{
16+
[Fact]
17+
public void GuidExtensions_ToDateTime_DateTimeIsReturned()
18+
{
19+
Guid guid = Guid.Parse("c150c000-7c92-08d9-0000-000000000000");
20+
DateTime dateTime = guid.ToDateTime();
21+
dateTime.ShouldBe(new DateTime(2021, 9, 21));
22+
}
23+
24+
[Fact]
25+
public void DateExtensions_ToGuid_GuidIsReturned()
26+
{
27+
DateTime dateTime = new DateTime(2021, 9, 21);
28+
Guid guid = dateTime.ToGuid();
29+
guid.ShouldBe(Guid.Parse("c150c000-7c92-08d9-0000-000000000000"));
30+
}
31+
}
32+
}

TransactionProcessor.BusinessLogic.Tests/TransactionProcessor.BusinessLogic.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFramework>net5.0</TargetFramework>
5-
<DebugType>Full</DebugType>
5+
<DebugType>None</DebugType>
66
<IsPackable>false</IsPackable>
77
</PropertyGroup>
88

TransactionProcessor.BusinessLogic/Common/Extensions.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ public static class Extensions
1818
/// <param name="additionalTransactionMetadata">The additional transaction metadata.</param>
1919
/// <returns></returns>
2020
[ExcludeFromCodeCoverage]
21-
public static T ExtractFieldFromMetadata<T>(this Dictionary<String, String> additionalTransactionMetadata, String fieldName)
21+
public static T ExtractFieldFromMetadata<T>(this Dictionary<String, String> additionalTransactionMetadata,
22+
String fieldName)
2223
{
2324
// Create a case insensitive version of the dictionary
2425
Dictionary<String, String> caseInsensitiveDictionary = new Dictionary<String, String>(StringComparer.InvariantCultureIgnoreCase);
@@ -37,5 +38,23 @@ public static T ExtractFieldFromMetadata<T>(this Dictionary<String, String> addi
3738
return default(T);
3839
}
3940
}
41+
42+
public static Guid ToGuid(this DateTime dt)
43+
{
44+
var bytes = BitConverter.GetBytes(dt.Ticks);
45+
46+
Array.Resize(ref bytes, 16);
47+
48+
return new Guid(bytes);
49+
}
50+
51+
public static DateTime ToDateTime(this Guid guid)
52+
{
53+
var bytes = guid.ToByteArray();
54+
55+
Array.Resize(ref bytes, 8);
56+
57+
return new DateTime(BitConverter.ToInt64(bytes));
58+
}
4059
}
4160
}

TransactionProcessor.BusinessLogic/EventHandling/TransactionDomainEventHandler.cs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,31 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Diagnostics.CodeAnalysis;
6+
using System.Linq;
67
using System.Threading;
78
using System.Threading.Tasks;
9+
using Common;
810
using EstateManagement.Client;
11+
using EstateManagement.DataTransferObjects;
912
using EstateManagement.DataTransferObjects.Responses;
1013
using Manager;
1114
using MessagingService.Client;
1215
using MessagingService.DataTransferObjects;
1316
using Models;
17+
using Newtonsoft.Json;
1418
using SecurityService.Client;
1519
using SecurityService.DataTransferObjects.Responses;
1620
using Services;
21+
using SettlementAggregates;
1722
using Shared.DomainDrivenDesign.EventSourcing;
23+
using Shared.EventStore.Aggregate;
1824
using Shared.EventStore.EventHandling;
1925
using Shared.General;
2026
using Shared.Logger;
2127
using Transaction.DomainEvents;
2228
using TransactionAggregate;
29+
using CalculationType = Models.CalculationType;
30+
using FeeType = Models.FeeType;
2331

2432
/// <summary>
2533
///
@@ -55,6 +63,8 @@ public class TransactionDomainEventHandler : IDomainEventHandler
5563
/// </summary>
5664
private readonly IMessagingServiceClient MessagingServiceClient;
5765

66+
private readonly IAggregateRepository<PendingSettlementAggregate, DomainEventRecord.DomainEvent> PendingSettlementAggregateRepository;
67+
5868
/// <summary>
5969
/// The token response
6070
/// </summary>
@@ -83,14 +93,16 @@ public TransactionDomainEventHandler(ITransactionAggregateManager transactionAgg
8393
IEstateClient estateClient,
8494
ISecurityServiceClient securityServiceClient,
8595
ITransactionReceiptBuilder transactionReceiptBuilder,
86-
IMessagingServiceClient messagingServiceClient)
96+
IMessagingServiceClient messagingServiceClient,
97+
IAggregateRepository<PendingSettlementAggregate, DomainEventRecord.DomainEvent> pendingSettlementAggregateRepository)
8798
{
8899
this.TransactionAggregateManager = transactionAggregateManager;
89100
this.FeeCalculationManager = feeCalculationManager;
90101
this.EstateClient = estateClient;
91102
this.SecurityServiceClient = securityServiceClient;
92103
this.TransactionReceiptBuilder = transactionReceiptBuilder;
93104
this.MessagingServiceClient = messagingServiceClient;
105+
this.PendingSettlementAggregateRepository = pendingSettlementAggregateRepository;
94106
}
95107

96108
#endregion
@@ -165,6 +177,7 @@ private async Task HandleSpecificDomainEvent(TransactionHasBeenCompletedEvent do
165177
}
166178

167179
this.TokenResponse = await this.GetToken(cancellationToken);
180+
168181
// Ok we should have filtered out the not applicable transactions
169182
// Get the fees to be calculated
170183
List<ContractProductTransactionFee> feesForProduct = await this.EstateClient.GetTransactionFeesForProduct(this.TokenResponse.AccessToken,
@@ -191,11 +204,51 @@ private async Task HandleSpecificDomainEvent(TransactionHasBeenCompletedEvent do
191204
// Do the fee calculation
192205
List<CalculatedFee> resultFees = this.FeeCalculationManager.CalculateFees(feesForCalculation, transactionAggregate.TransactionAmount.Value);
193206

194-
foreach (CalculatedFee calculatedFee in resultFees)
207+
// Process the non merchant fees
208+
IEnumerable<CalculatedFee> nonMerchantFees = resultFees.Where(f => f.FeeType == FeeType.ServiceProvider);
209+
210+
foreach (CalculatedFee calculatedFee in nonMerchantFees)
195211
{
196212
// Add Fee to the Transaction
197213
await this.TransactionAggregateManager.AddFee(transactionAggregate.EstateId, transactionAggregate.AggregateId, calculatedFee, cancellationToken);
198214
}
215+
216+
// Now deal with merchant fees
217+
IEnumerable<CalculatedFee> merchantFees = resultFees.Where(f => f.FeeType == FeeType.Merchant);
218+
219+
// get the merchant now to see the settlement schedule
220+
this.TokenResponse = await this.GetToken(cancellationToken);
221+
MerchantResponse merchant = await this.EstateClient.GetMerchant(this.TokenResponse.AccessToken, domainEvent.EstateId, domainEvent.MerchantId, cancellationToken);
222+
223+
// Add fees to transaction now if settlement is immediate
224+
if (merchant.SettlementSchedule == SettlementSchedule.Immediate)
225+
{
226+
foreach (CalculatedFee calculatedFee in merchantFees)
227+
{
228+
// Add Fee to the Transaction
229+
await this.TransactionAggregateManager.AddFee(transactionAggregate.EstateId, transactionAggregate.AggregateId, calculatedFee, cancellationToken);
230+
}
231+
}
232+
else
233+
{
234+
foreach (CalculatedFee calculatedFee in merchantFees)
235+
{
236+
// Determine when the fee should be applied
237+
Guid aggregateId = merchant.NextSettlementDueDate.ToGuid();
238+
239+
// We need to add the fees to a pending settlement stream (for today)
240+
PendingSettlementAggregate aggregate = await this.PendingSettlementAggregateRepository.GetLatestVersion(aggregateId, cancellationToken);
241+
242+
if (aggregate.IsCreated == false)
243+
{
244+
aggregate.Create(transactionAggregate.EstateId, merchant.NextSettlementDueDate);
245+
}
246+
247+
aggregate.AddFee(transactionAggregate.MerchantId, transactionAggregate.AggregateId, calculatedFee);
248+
249+
await this.PendingSettlementAggregateRepository.SaveChanges(aggregate, cancellationToken);
250+
}
251+
}
199252
}
200253

201254
/// <summary>

TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public class TransactionDomainService : ITransactionDomainService
6161
/// The reconciliation aggregate repository
6262
/// </summary>
6363
private readonly IAggregateRepository<ReconciliationAggregate, DomainEventRecord.DomainEvent> ReconciliationAggregateRepository;
64-
64+
6565
#endregion
6666

6767
#region Constructors

TransactionProcessor.BusinessLogic/TransactionProcessor.BusinessLogic.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<PackageReference Include="EstateManagement.Client" Version="1.0.10.2" />
8+
<PackageReference Include="EstateManagement.Client" Version="1.0.15-build74" />
99
<PackageReference Include="MessagingService.Client" Version="1.0.10.2" />
1010
<PackageReference Include="SecurityService.Client" Version="1.0.6.2" />
1111
<PackageReference Include="Shared.DomainDrivenDesign" Version="1.0.12" />
@@ -18,6 +18,7 @@
1818
<ItemGroup>
1919
<ProjectReference Include="..\TransactionProcessor.Models\TransactionProcessor.Models.csproj" />
2020
<ProjectReference Include="..\TransactionProcessor.ReconciliationAggregate\TransactionProcessor.ReconciliationAggregate.csproj" />
21+
<ProjectReference Include="..\TransactionProcessor.SettlementAggregates\TransactionProcessor.SettlementAggregates.csproj" />
2122
<ProjectReference Include="..\TransactionProcessor.TransactionAgrgegate\TransactionProcessor.TransactionAggregate.csproj" />
2223
</ItemGroup>
2324

TransactionProcessor.Client/ITransactionProcessorClient.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ Task<SerialisedMessage> PerformTransaction(String accessToken,
1919
SerialisedMessage transactionRequest,
2020
CancellationToken cancellationToken);
2121

22+
Task<PendingSettlementResponse> GetPendingSettlementByDate(String accessToken,
23+
DateTime pendingSettlementDate,
24+
Guid estateId,
25+
CancellationToken cancellationToken);
26+
2227
#endregion
2328
}
2429
}

TransactionProcessor.Client/TransactionProcessorClient.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,40 @@ public async Task<SerialisedMessage> PerformTransaction(String accessToken,
9090
return response;
9191
}
9292

93+
public async Task<PendingSettlementResponse> GetPendingSettlementByDate(String accessToken,
94+
DateTime pendingSettlementDate,
95+
Guid estateId,
96+
CancellationToken cancellationToken)
97+
{
98+
PendingSettlementResponse response = null;
99+
100+
String requestUri = $"{this.BaseAddress}/api/settlements/{pendingSettlementDate.Date:yyyy-MM-dd}/estates/{estateId}/pending";
101+
102+
try
103+
{
104+
// Add the access token to the client headers
105+
this.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
106+
107+
// Make the Http Call here
108+
HttpResponseMessage httpResponse = await this.HttpClient.GetAsync(requestUri, cancellationToken);
109+
110+
// Process the response
111+
String content = await this.HandleResponse(httpResponse, cancellationToken);
112+
113+
// call was successful so now deserialise the body to the response object
114+
response = JsonConvert.DeserializeObject<PendingSettlementResponse>(content);
115+
}
116+
catch (Exception ex)
117+
{
118+
// An exception has occurred, add some additional information to the message
119+
Exception exception = new Exception("Error getting pending settlment.", ex);
120+
121+
throw exception;
122+
}
123+
124+
return response;
125+
}
126+
93127
#endregion
94128
}
95129
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace TransactionProcessor.DataTransferObjects
2+
{
3+
using System;
4+
5+
public class PendingSettlementResponse
6+
{
7+
public Guid EstateId { get; set; }
8+
9+
public DateTime SettlementDate { get; set; }
10+
11+
public Int32 NumberOfFeesPendingSettlement { get; set; }
12+
13+
public Int32 NumberOfFeesSettled { get; set; }
14+
15+
}
16+
}

0 commit comments

Comments
 (0)