diff --git a/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs b/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs index 16f5bffe..5c33547a 100644 --- a/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/SettlementDomainService.cs @@ -41,82 +41,96 @@ public async Task> ProcessSettlement(SettlementCommands.ProcessSett IAsyncPolicy> retryPolicy = PolicyFactory.CreatePolicy>(policyTag: "SettlementDomainService - ProcessSettlement"); try { - return await PolicyFactory.ExecuteWithPolicyAsync(async () => { - - Guid settlementAggregateId = Helpers.CalculateSettlementAggregateId(command.SettlementDate, command.MerchantId, command.EstateId); - - Result getSettlementResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(settlementAggregateId, ct), settlementAggregateId, cancellationToken, false); - if (getSettlementResult.IsFailed) - return ResultHelpers.CreateFailure(getSettlementResult); - - SettlementAggregate settlementAggregate = getSettlementResult.Data; - - if (settlementAggregate.IsCreated == false) { - Logger.LogInformation($"No pending settlement for {command.SettlementDate:yyyy-MM-dd}"); - // Not pending settlement for this date - return Result.Success(); - } - - Result getMerchantResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.Get(command.MerchantId, ct), command.MerchantId, cancellationToken, false); - if (getMerchantResult.IsFailed) - return ResultHelpers.CreateFailure(getMerchantResult); - - Result settlementSaveResult = Result.Success(); - MerchantAggregate merchant = getMerchantResult.Data; - if (merchant.SettlementSchedule == SettlementSchedule.Immediate) { - // Mark the settlement as completed - settlementAggregate.StartProcessing(DateTime.Now); - settlementAggregate.ManuallyComplete(); - settlementSaveResult = await this.AggregateService.Save(settlementAggregate, cancellationToken); - } - - List<(Guid transactionId, Guid merchantId, CalculatedFee calculatedFee)> feesToBeSettled = settlementAggregate.GetFeesToBeSettled(); - - if (feesToBeSettled.Any()) { - // Record the process call - Result stateResult = settlementAggregate.StartProcessing(DateTime.Now); - if (stateResult.IsFailed) - return ResultHelpers.CreateFailure(stateResult); - settlementSaveResult = await this.AggregateService.Save(settlementAggregate, cancellationToken); - } - - // Settlement updates done, now we need to update the transactions - if (settlementSaveResult.IsFailed) - return ResultHelpers.CreateFailure(settlementSaveResult); - - List failedResults = new(); - foreach ((Guid transactionId, Guid merchantId, CalculatedFee calculatedFee) feeToSettle in feesToBeSettled) { - try { - - Result getTransactionResult = await this.AggregateService.GetLatest(feeToSettle.transactionId, cancellationToken); - if (getTransactionResult.IsFailed) - return ResultHelpers.CreateFailure(getTransactionResult); - var transactionAggregate = getTransactionResult.Data; - - transactionAggregate.AddSettledFee(feeToSettle.calculatedFee, command.SettlementDate, settlementAggregateId); - - Result saveResult = await this.AggregateService.Save(transactionAggregate, cancellationToken); - if (saveResult.IsFailed) - return ResultHelpers.CreateFailure(saveResult); - } - catch (Exception ex) { - Logger.LogError($"Failed to process transaction {feeToSettle.transactionId} for settlement {settlementAggregateId}", ex); - failedResults.Add(Result.Failure($"Failed to process transaction {feeToSettle.transactionId} for settlement {settlementAggregateId}: {ex.Message}")); - } - } - - if (failedResults.Any()) { - return Result.Failure($"Not all fees were processed successfully {failedResults.Count} have failed"); - } - - return Result.Success(settlementAggregateId); - }, retryPolicy, "SettlementDomainService - ProcessSettlement"); + return await PolicyFactory.ExecuteWithPolicyAsync(() => this.ProcessSettlementInternal(command, cancellationToken), retryPolicy, "SettlementDomainService - ProcessSettlement"); } catch (Exception ex) { return Result.Failure(ex.GetExceptionMessages()); } } + private async Task> ProcessSettlementInternal(SettlementCommands.ProcessSettlementCommand command, + CancellationToken cancellationToken) { + Guid settlementAggregateId = Helpers.CalculateSettlementAggregateId(command.SettlementDate, command.MerchantId, command.EstateId); + + Result getSettlementResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.GetLatest(settlementAggregateId, ct), settlementAggregateId, cancellationToken, false); + if (getSettlementResult.IsFailed) + return ResultHelpers.CreateFailure(getSettlementResult); + + SettlementAggregate settlementAggregate = getSettlementResult.Data; + + if (settlementAggregate.IsCreated == false) { + Logger.LogInformation($"No pending settlement for {command.SettlementDate:yyyy-MM-dd}"); + return Result.Success(); + } + + Result getMerchantResult = await DomainServiceHelper.GetAggregateOrFailure(ct => this.AggregateService.Get(command.MerchantId, ct), command.MerchantId, cancellationToken, false); + if (getMerchantResult.IsFailed) + return ResultHelpers.CreateFailure(getMerchantResult); + + Result> settlementPreparationResult = await this.PrepareSettlementForProcessing(settlementAggregate, getMerchantResult.Data, cancellationToken); + if (settlementPreparationResult.IsFailed) + return ResultHelpers.CreateFailure(settlementPreparationResult); + + return await this.ProcessSettlementFees(settlementPreparationResult.Data, command.SettlementDate, settlementAggregateId, cancellationToken); + } + + private async Task>> PrepareSettlementForProcessing(SettlementAggregate settlementAggregate, + MerchantAggregate merchantAggregate, + CancellationToken cancellationToken) { + Result settlementSaveResult = Result.Success(); + if (merchantAggregate.SettlementSchedule == SettlementSchedule.Immediate) { + settlementAggregate.StartProcessing(DateTime.Now); + settlementAggregate.ManuallyComplete(); + settlementSaveResult = await this.AggregateService.Save(settlementAggregate, cancellationToken); + } + + List<(Guid transactionId, Guid merchantId, CalculatedFee calculatedFee)> feesToBeSettled = settlementAggregate.GetFeesToBeSettled(); + + if (feesToBeSettled.Any()) { + Result stateResult = settlementAggregate.StartProcessing(DateTime.Now); + if (stateResult.IsFailed) + return ResultHelpers.CreateFailure(stateResult); + + settlementSaveResult = await this.AggregateService.Save(settlementAggregate, cancellationToken); + } + + if (settlementSaveResult.IsFailed) + return ResultHelpers.CreateFailure(settlementSaveResult); + + return Result.Success(feesToBeSettled); + } + + private async Task> ProcessSettlementFees(List<(Guid transactionId, Guid merchantId, CalculatedFee calculatedFee)> feesToBeSettled, + DateTime settlementDate, + Guid settlementAggregateId, + CancellationToken cancellationToken) { + List failedResults = new(); + foreach ((Guid transactionId, Guid merchantId, CalculatedFee calculatedFee) feeToSettle in feesToBeSettled) { + try { + Result getTransactionResult = await this.AggregateService.GetLatest(feeToSettle.transactionId, cancellationToken); + if (getTransactionResult.IsFailed) + return ResultHelpers.CreateFailure(getTransactionResult); + + TransactionAggregate transactionAggregate = getTransactionResult.Data; + transactionAggregate.AddSettledFee(feeToSettle.calculatedFee, settlementDate, settlementAggregateId); + + Result saveResult = await this.AggregateService.Save(transactionAggregate, cancellationToken); + if (saveResult.IsFailed) + return ResultHelpers.CreateFailure(saveResult); + } + catch (Exception ex) { + Logger.LogError($"Failed to process transaction {feeToSettle.transactionId} for settlement {settlementAggregateId}", ex); + failedResults.Add(Result.Failure($"Failed to process transaction {feeToSettle.transactionId} for settlement {settlementAggregateId}: {ex.Message}")); + } + } + + if (failedResults.Any()) { + return Result.Failure($"Not all fees were processed successfully {failedResults.Count} have failed"); + } + + return Result.Success(settlementAggregateId); + } + public async Task AddMerchantFeePendingSettlement(SettlementCommands.AddMerchantFeePendingSettlementCommand command, CancellationToken cancellationToken) { @@ -214,4 +228,4 @@ public SettlementDomainService(Func aggregateService) this.AggregateService = aggregateService(); } } -} \ No newline at end of file +}