Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,22 @@ public void TransactionRequestHandler_ProcessReconciliationRequest_IsHandled()

}
}

public class SettlementRequestHanslerTests
{
[Fact]
public void TransactionRequestHandler_ProcessLogonTransactionRequest_IsHandled()
{
Mock<ITransactionDomainService> transactionDomainService = new Mock<ITransactionDomainService>();
SettlementRequestHandler handler = new SettlementRequestHandler(transactionDomainService.Object);

ProcessSettlementRequest command = TestData.ProcessSettlementRequest;

Should.NotThrow(async () =>
{
await handler.Handle(command, CancellationToken.None);
});

}
}
}
12 changes: 12 additions & 0 deletions TransactionProcessor.BusinessLogic.Tests/Requests/RequestTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,17 @@ public void ProcessReconciliationRequest_CanBeCreated_IsCreated()
processReconciliationRequest.TransactionValue.ShouldBe(TestData.ReconciliationTransactionValue);
processReconciliationRequest.TransactionId.ShouldBe(TestData.TransactionId);
}

[Fact]
public void ProcessSettlementRequest_CanBeCreated_IsCreated()
{
ProcessSettlementRequest processSettlementRequest = ProcessSettlementRequest.Create(TestData.SettlementDate,
TestData.EstateId);

processSettlementRequest.ShouldNotBeNull();
processSettlementRequest.EstateId.ShouldBe(TestData.EstateId);
processSettlementRequest.SettlementDate.ShouldBe(TestData.SettlementDate);

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,22 @@ public async Task TransactionAggregateManager_AddFee_FeeAddedToTransaction()

await transactionAggregateManager.AddFee(TestData.EstateId,
TestData.TransactionId,
TestData.CalculatedMerchantFees.First(),
TestData.CalculatedFeeServiceProviderFee,
CancellationToken.None);
}

[Fact]
public async Task TransactionAggregateManager_AddSettledFee_FeeAddedToTransaction()
{
Mock<IAggregateRepository<TransactionAggregate, DomainEventRecord.DomainEvent>> aggregateRepository = new Mock<IAggregateRepository<TransactionAggregate, DomainEventRecord.DomainEvent>>();
aggregateRepository.Setup(a => a.GetLatestVersion(It.IsAny<Guid>(), It.IsAny<CancellationToken>())).ReturnsAsync(TestData.GetCompletedAuthorisedSaleTransactionAggregate);
TransactionAggregateManager transactionAggregateManager = new TransactionAggregateManager(aggregateRepository.Object);

await transactionAggregateManager.AddSettledFee(TestData.EstateId,
TestData.TransactionId,
TestData.CalculatedFeeMerchantFee2,
TestData.TransactionFeeSettlementDueDate,
TestData.TransactionFeeSettledDateTime,
CancellationToken.None);
}
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public class TransactionDomainEventHandler : IDomainEventHandler
/// </summary>
private readonly IMessagingServiceClient MessagingServiceClient;

private readonly IAggregateRepository<PendingSettlementAggregate, DomainEventRecord.DomainEvent> PendingSettlementAggregateRepository;
private readonly IAggregateRepository<SettlementAggregate, DomainEventRecord.DomainEvent> SettlementAggregateRepository;

/// <summary>
/// The token response
Expand Down Expand Up @@ -94,15 +94,15 @@ public TransactionDomainEventHandler(ITransactionAggregateManager transactionAgg
ISecurityServiceClient securityServiceClient,
ITransactionReceiptBuilder transactionReceiptBuilder,
IMessagingServiceClient messagingServiceClient,
IAggregateRepository<PendingSettlementAggregate, DomainEventRecord.DomainEvent> pendingSettlementAggregateRepository)
IAggregateRepository<SettlementAggregate, DomainEventRecord.DomainEvent> settlementAggregateRepository)
{
this.TransactionAggregateManager = transactionAggregateManager;
this.FeeCalculationManager = feeCalculationManager;
this.EstateClient = estateClient;
this.SecurityServiceClient = securityServiceClient;
this.TransactionReceiptBuilder = transactionReceiptBuilder;
this.MessagingServiceClient = messagingServiceClient;
this.PendingSettlementAggregateRepository = pendingSettlementAggregateRepository;
this.SettlementAggregateRepository = settlementAggregateRepository;
}

#endregion
Expand Down Expand Up @@ -226,7 +226,7 @@ private async Task HandleSpecificDomainEvent(TransactionHasBeenCompletedEvent do
foreach (CalculatedFee calculatedFee in merchantFees)
{
// Add Fee to the Transaction
await this.TransactionAggregateManager.AddFee(transactionAggregate.EstateId, transactionAggregate.AggregateId, calculatedFee, cancellationToken);
await this.TransactionAggregateManager.AddSettledFee(transactionAggregate.EstateId, transactionAggregate.AggregateId, calculatedFee, DateTime.Now.Date, DateTime.Now, cancellationToken);
}
}
else if (merchant.SettlementSchedule == SettlementSchedule.NotSet)
Expand All @@ -241,7 +241,7 @@ private async Task HandleSpecificDomainEvent(TransactionHasBeenCompletedEvent do
Guid aggregateId = merchant.NextSettlementDueDate.ToGuid();

// We need to add the fees to a pending settlement stream (for today)
PendingSettlementAggregate aggregate = await this.PendingSettlementAggregateRepository.GetLatestVersion(aggregateId, cancellationToken);
SettlementAggregate aggregate = await this.SettlementAggregateRepository.GetLatestVersion(aggregateId, cancellationToken);

if (aggregate.IsCreated == false)
{
Expand All @@ -250,7 +250,7 @@ private async Task HandleSpecificDomainEvent(TransactionHasBeenCompletedEvent do

aggregate.AddFee(transactionAggregate.MerchantId, transactionAggregate.AggregateId, calculatedFee);

await this.PendingSettlementAggregateRepository.SaveChanges(aggregate, cancellationToken);
await this.SettlementAggregateRepository.SaveChanges(aggregate, cancellationToken);
}
}
}
Expand All @@ -277,6 +277,24 @@ private async Task HandleSpecificDomainEvent(CustomerEmailReceiptRequestedEvent

}

private async Task HandleSpecificDomainEvent(MerchantFeeAddedToTransactionEvent domainEvent,
CancellationToken cancellationToken)
{
if (domainEvent.SettlementDueDate == DateTime.MinValue)
{
// Old event format before settlement
return;
}

Guid aggregateId = domainEvent.SettlementDueDate.ToGuid();

SettlementAggregate pendingSettlementAggregate = await this.SettlementAggregateRepository.GetLatestVersion(aggregateId, cancellationToken);

pendingSettlementAggregate.MarkFeeAsSettled(domainEvent.MerchantId, domainEvent.TransactionId, domainEvent.FeeId);

await this.SettlementAggregateRepository.SaveChanges(pendingSettlementAggregate, cancellationToken);
}

/// <summary>
/// Sends the email message.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TransactionProcessor.BusinessLogic.RequestHandlers
{
using System.Threading;
using MediatR;
using Models;
using Requests;
using Services;

public class SettlementRequestHandler : IRequestHandler<ProcessSettlementRequest, ProcessSettlementResponse>
{
private readonly ITransactionDomainService TransactionDomainService;

public SettlementRequestHandler(ITransactionDomainService transactionDomainService)
{
this.TransactionDomainService = transactionDomainService;
}

public async Task<ProcessSettlementResponse> Handle(ProcessSettlementRequest request,
CancellationToken cancellationToken)
{
ProcessSettlementResponse processSettlementResponse = await this.TransactionDomainService.ProcessSettlement(request.SettlementDate, request.EstateId, cancellationToken);

return processSettlementResponse;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TransactionProcessor.BusinessLogic.Requests
{
using MediatR;
using Models;

public class ProcessSettlementRequest : IRequest<ProcessSettlementResponse>
{
#region Constructors

private ProcessSettlementRequest(DateTime settlementDate, Guid estateId)
{
this.EstateId = estateId;
this.SettlementDate = settlementDate;
}

#endregion

#region Properties

/// <summary>
/// Gets the estate identifier.
/// </summary>
/// <value>
/// The estate identifier.
/// </value>
public Guid EstateId { get; }


public DateTime SettlementDate { get; }

#endregion

#region Methods

/// <summary>
/// Creates the specified estate identifier.
/// </summary>
/// <param name="transactionId">The transaction identifier.</param>
/// <param name="estateId">The estate identifier.</param>
/// <param name="merchantId">The merchant identifier.</param>
/// <param name="deviceIdentifier">The device identifier.</param>
/// <param name="transactionType">Type of the transaction.</param>
/// <param name="transactionDateTime">The transaction date time.</param>
/// <param name="transactionNumber">The transaction number.</param>
/// <returns></returns>
public static ProcessSettlementRequest Create(DateTime settlementDate,
Guid estateId)
{
return new ProcessSettlementRequest(settlementDate, estateId);
}

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ Task AddFee(Guid estateId,
CalculatedFee calculatedFee,
CancellationToken cancellationToken);


Task AddSettledFee(Guid estateId,
Guid transactionId,
CalculatedFee calculatedFee,
DateTime settlementDueDate,
DateTime settledDateTime,
CancellationToken cancellationToken);

/// <summary>
/// Authorises the transaction.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ Task<ProcessReconciliationTransactionResponse> ProcessReconciliationTransaction(
Decimal transactionValue,
CancellationToken cancellationToken);

Task<ProcessSettlementResponse> ProcessSettlement(DateTime pendingSettlementDate,
Guid estateId,
CancellationToken cancellationToken);

#endregion
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,20 @@ public async Task AddFee(Guid estateId,

}

public async Task AddSettledFee(Guid estateId,
Guid transactionId,
CalculatedFee calculatedFee,
DateTime settlementDueDate,
DateTime settledDateTime,
CancellationToken cancellationToken)
{
TransactionAggregate transactionAggregate = await this.TransactionAggregateRepository.GetLatestVersion(transactionId, cancellationToken);

transactionAggregate.AddSettledFee(calculatedFee, settlementDueDate, settledDateTime);

await this.TransactionAggregateRepository.SaveChanges(transactionAggregate, cancellationToken);
}

/// <summary>
/// Authorises the transaction.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using ReconciliationAggregate;
using SecurityService.Client;
using SecurityService.DataTransferObjects.Responses;
using SettlementAggregates;
using Shared.DomainDrivenDesign.EventSourcing;
using Shared.EventStore.Aggregate;
using Shared.EventStore.EventStore;
Expand Down Expand Up @@ -61,7 +62,9 @@ public class TransactionDomainService : ITransactionDomainService
/// The reconciliation aggregate repository
/// </summary>
private readonly IAggregateRepository<ReconciliationAggregate, DomainEventRecord.DomainEvent> ReconciliationAggregateRepository;


private readonly IAggregateRepository<SettlementAggregate, DomainEventRecord.DomainEvent> SettlementAggregateRepository;

#endregion

#region Constructors
Expand All @@ -78,13 +81,15 @@ public TransactionDomainService(ITransactionAggregateManager transactionAggregat
IEstateClient estateClient,
ISecurityServiceClient securityServiceClient,
Func<String, IOperatorProxy> operatorProxyResolver,
IAggregateRepository<ReconciliationAggregate, DomainEventRecord.DomainEvent> reconciliationAggregateRepository)
IAggregateRepository<ReconciliationAggregate, DomainEventRecord.DomainEvent> reconciliationAggregateRepository,
IAggregateRepository<SettlementAggregate, DomainEventRecord.DomainEvent> settlementAggregateRepository)
{
this.TransactionAggregateManager = transactionAggregateManager;
this.EstateClient = estateClient;
this.SecurityServiceClient = securityServiceClient;
this.OperatorProxyResolver = operatorProxyResolver;
this.ReconciliationAggregateRepository = reconciliationAggregateRepository;
this.SettlementAggregateRepository = settlementAggregateRepository;
}

#endregion
Expand Down Expand Up @@ -194,12 +199,7 @@ public async Task<ProcessSaleTransactionResponse> ProcessSaleTransaction(Guid tr

(String responseMessage, TransactionResponseCode responseCode) validationResult =
await this.ValidateSaleTransaction(estateId, merchantId, deviceIdentifier, operatorIdentifier, transactionAmount, cancellationToken);

if (validationResult.responseCode == TransactionResponseCode.InvalidSaleTransactionAmount)
{
throw new InvalidDataException(validationResult.responseMessage);
}


await this.TransactionAggregateManager.StartTransaction(transactionId,
transactionDateTime,
transactionNumber,
Expand Down Expand Up @@ -396,7 +396,49 @@ public async Task<ProcessReconciliationTransactionResponse> ProcessReconciliatio
};
}


public async Task<ProcessSettlementResponse> ProcessSettlement(DateTime settlementDate,
Guid estateId,
CancellationToken cancellationToken)
{
ProcessSettlementResponse response = new ProcessSettlementResponse();

Guid aggregateId = settlementDate.ToGuid();

SettlementAggregate settlementAggregate = await this.SettlementAggregateRepository.GetLatestVersion(aggregateId, cancellationToken);

if (settlementAggregate.IsCreated == false)
{
// Not pending settlement for this date
return response;
}

var feesToBeSettled = settlementAggregate.GetFeesToBeSettled();
response.NumberOfFeesPendingSettlement = feesToBeSettled.Count;

foreach ((Guid transactionId, Guid merchantId, CalculatedFee calculatedFee) feeToSettle in feesToBeSettled)
{
try
{
await this.TransactionAggregateManager.AddSettledFee(estateId,
feeToSettle.transactionId,
feeToSettle.calculatedFee,
settlementDate,
DateTime.Now,
cancellationToken);
response.NumberOfFeesSuccessfullySettled++;
response.NumberOfFeesPendingSettlement--;
}
catch(Exception ex)
{
Logger.LogError(ex);
response.NumberOfFeesPendingSettlement--;
response.NumberOfFeesFailedToSettle++;
}

}

return response;
}

/// <summary>
/// Adds the device to merchant.
Expand Down
Loading