Exemplo n.º 1
0
            protected override async Task Handle(UpdateOrderCommand command, CancellationToken token)
            {
                if (command is null)
                {
                    throw new ArgumentNullException(nameof(command));
                }

                var infoTrackOrderUpdateMessage = _wCADbContext.InfoTrackOrderUpdateMessageHistory.Find(command.InfoTrackOrderUpdateMessageId);

                infoTrackOrderUpdateMessage.MarkProcessInProgress();
                await _wCADbContext.SaveChangesAsync();

                var executionId = Guid.NewGuid();

                _telemetryLogger.TrackTrace(
                    $"{typeof(Handler).FullName}: Entered UpdateOrder.",
                    WCASeverityLevel.Information,
                    new Dictionary <string, string>()
                {
                    { "InfoTrackOrderId", infoTrackOrderUpdateMessage.InfoTrackOrderId.ToString(CultureInfo.InvariantCulture) },
                    { "Actionstep matter", infoTrackOrderUpdateMessage.InfoTrackClientReference },
                    { "InfoTrackOrderUpdateMessageId", command.InfoTrackOrderUpdateMessageId.ToString(CultureInfo.InvariantCulture) },
                    { "ExecutionId", executionId.ToString() }
                });

                int matterId = int.Parse(infoTrackOrderUpdateMessage.InfoTrackClientReference, CultureInfo.InvariantCulture);

                string[] retailerReference = infoTrackOrderUpdateMessage.InfoTrackRetailerReference.Split('|');
                var      orgKey            = string.Empty;
                var      wcaUserId         = string.Empty;

                if (retailerReference.Length >= 2)
                {
                    // Substring removes the "WCA_" prefix. We know the prefix is present because
                    // the message passed Validation to get here.
                    orgKey    = retailerReference[0].Substring(4);
                    wcaUserId = retailerReference[1];
                }

                if (string.IsNullOrEmpty(wcaUserId))
                {
                    throw new InvalidInfoTrackOrderUserException(
                              "InfoTrack order information was supplied without WCA user ID. " +
                              "Cannot process the order.");
                }

                var userWhoPlacedOrder = await _wCADbContext.Users.FindAsync(wcaUserId);

                if (userWhoPlacedOrder == null)
                {
                    throw new InvalidInfoTrackOrderUserException(
                              "InfoTrack order information was supplied with an invalid WCA user ID. " +
                              "Cannot process the order.");
                }

                // Update the current record for this order
                var infoTrackOrder = await _wCADbContext.InfoTrackOrders.FindAsync(infoTrackOrderUpdateMessage.InfoTrackOrderId);

                if (infoTrackOrder == null)
                {
                    infoTrackOrder = new InfoTrackOrder();
                    infoTrackOrder.DateCreatedUtc = DateTime.UtcNow;
                    infoTrackOrder.LastUpdatedUtc = infoTrackOrder.DateCreatedUtc;
                    infoTrackOrder.CreatedBy      = userWhoPlacedOrder;
                    infoTrackOrder.UpdatedBy      = userWhoPlacedOrder;
                    await _wCADbContext.AddAsync(infoTrackOrder);
                }

                var previousFileHash           = infoTrackOrder.InfoTrackFileHash;
                var previousOrderTotalFeeTotal = infoTrackOrder.InfoTrackTotalFeeTotal;

                infoTrackOrder = _mapper.Map(infoTrackOrderUpdateMessage, infoTrackOrder);
                infoTrackOrder.OrderedByWCAUser   = userWhoPlacedOrder;
                infoTrackOrder.ActionstepMatterId = matterId;
                infoTrackOrder.LastUpdatedUtc     = DateTime.UtcNow;

                var existingOrg = await _wCADbContext.ActionstepOrgs.FindAsync(orgKey);

                infoTrackOrder.ActionstepOrg = existingOrg
                                               ?? throw new InvalidOperationException(
                                                         "InfoTrack order information references an invalid Actonstep org key. " +
                                                         "WCA doesn't have any information or API credentials for the specified " +
                                                         "org key. Cannot continue.");

                // Update current record with values from history - checkpoint
                await _wCADbContext.SaveChangesAsync();

                var tokenSetQuery = new TokenSetQuery(userWhoPlacedOrder.Id, orgKey);

                var processingErrors = new StringBuilder();

                // Save InfoTrack Title Order Resources (Files) to Actionstep
                // We only want to upload documents if the filehash has changed, and if there's
                // actually a download url.
                if (string.IsNullOrEmpty(infoTrackOrderUpdateMessage.InfoTrackDownloadUrl))
                {
                    _telemetryLogger.TrackTrace(
                        $"{typeof(Handler).FullName}: InfoTrackDownloadUrl is empty. No document to upload.",
                        WCASeverityLevel.Verbose,
                        new Dictionary <string, string>()
                    {
                        { "InfoTrackOrderUpdateMessageId", command.InfoTrackOrderUpdateMessageId.ToString(CultureInfo.InvariantCulture) },
                        { "Actionstep org key", orgKey },
                        { "Actionstep matter", matterId.ToString(CultureInfo.InvariantCulture) },
                        { "ExecutionId", executionId.ToString() }
                    });

                    infoTrackOrder.LastUpdatedUtc = DateTime.UtcNow;
                    infoTrackOrder.ActionstepDocumentUploadStatusUpdatedUtc = infoTrackOrder.LastUpdatedUtc;
                    infoTrackOrder.ActionstepDocumentUploadStatus           = ActionstepDocumentUploadStatus.NotApplicable;
                    await _wCADbContext.SaveChangesAsync();
                }
                else if (previousFileHash == infoTrackOrderUpdateMessage.InfoTrackFileHash)
                {
                    _telemetryLogger.TrackTrace(
                        $"{typeof(Handler).FullName}: FileHash has not changed. Skipping document upload.",
                        WCASeverityLevel.Verbose,
                        new Dictionary <string, string>()
                    {
                        { "InfoTrackOrderUpdateMessageId", command.InfoTrackOrderUpdateMessageId.ToString(CultureInfo.InvariantCulture) },
                        { "Actionstep org key", orgKey },
                        { "Actionstep matter", matterId.ToString(CultureInfo.InvariantCulture) },
                        { "ExecutionId", executionId.ToString() }
                    });

                    // Don't update ActionstepDocumentUploadStatus here because the existing status on the record
                    // is still valid and informative.
                }
                else
                {
                    // If we're here, we have a download url, and the FileHash is new (either becaues it's changed or
                    // there was no prior FileHash).
                    try
                    {
                        infoTrackOrder.LastUpdatedUtc = DateTime.UtcNow;
                        infoTrackOrder.ActionstepDocumentUploadStatusUpdatedUtc = infoTrackOrder.LastUpdatedUtc;
                        infoTrackOrder.ActionstepDocumentUploadStatus           = ActionstepDocumentUploadStatus.UploadInProgress;
                        await _wCADbContext.SaveChangesAsync();

                        var infoTrackCredentials = await _infoTrackCredentialRepository.FindCredential(orgKey);

                        await _mediator.Send(new SaveResources.SaveResourcesCommand
                        {
                            AuthenticatedUser    = userWhoPlacedOrder,
                            MatterId             = matterId,
                            ActionstepOrgKey     = orgKey,
                            InfoTrackCredentials = infoTrackCredentials,
                            ResourceURL          = infoTrackOrderUpdateMessage.InfoTrackDownloadUrl,
                            FolderName           = "_SEARCHES",
                            FileNameWithoutExtensionIfNotAvailableFromHeader = infoTrackOrder.InfoTrackServiceName,
                            FileNameAddition = " - Updated at " + DateTime.UtcNow.ToString("dd MMM yyyy hh-mm tt", CultureInfo.InvariantCulture)
                        });

                        infoTrackOrder.LastUpdatedUtc = DateTime.UtcNow;
                        infoTrackOrder.ActionstepDocumentUploadStatusUpdatedUtc = infoTrackOrder.LastUpdatedUtc;
                        infoTrackOrder.ActionstepDocumentUploadStatus           = ActionstepDocumentUploadStatus.UploadedSuccessfully;
                        await _wCADbContext.SaveChangesAsync();
                    }
                    catch (Exception ex)
                    {
                        infoTrackOrder.LastUpdatedUtc = DateTime.UtcNow;
                        infoTrackOrder.ActionstepDocumentUploadStatusUpdatedUtc = infoTrackOrder.LastUpdatedUtc;
                        infoTrackOrder.ActionstepDocumentUploadStatus           = ActionstepDocumentUploadStatus.UploadFailed;
                        await _wCADbContext.SaveChangesAsync();

                        var message = $"Failed to upload InfoTrack document to Actionstep for ID: {command.InfoTrackOrderUpdateMessageId}";
                        _logger.LogError(ex, message, null);
                        _telemetryLogger.TrackTrace(
                            message,
                            WCASeverityLevel.Error,
                            new Dictionary <string, string>()
                        {
                            { "InfoTrackOrderUpdateMessageId", command.InfoTrackOrderUpdateMessageId.ToString(CultureInfo.InvariantCulture) },
                            { "Actionstep org key", orgKey },
                            { "Actionstep matter", matterId.ToString(CultureInfo.InvariantCulture) },
                            { "Exception Message", ex.Message },
                            { "Stack Trace", ex.StackTrace.ToString(CultureInfo.InvariantCulture) },
                            { "ExecutionId", executionId.ToString() }
                        });

                        processingErrors.Append(message + "<br/>");
                    }
                }

                if (infoTrackOrder.ActionstepDisbursementStatus == ActionstepDisbursementStatus.CreatedSuccessfully)
                {
                    _telemetryLogger.TrackTrace(
                        $"{typeof(Handler).FullName}: ActionstepDisbursementStatus is CreatedSucessfully, skipping processing of disbursements.",
                        WCASeverityLevel.Information,
                        new Dictionary <string, string>()
                    {
                        { "InfoTrackOrderUpdateMessageId", command.InfoTrackOrderUpdateMessageId.ToString(CultureInfo.InvariantCulture) },
                        { "Actionstep org key", orgKey },
                        { "Actionstep matter", matterId.ToString(CultureInfo.InvariantCulture) },
                        { "ExecutionId", executionId.ToString() }
                    });
                }
                else
                {
                    ValidateFees(infoTrackOrderUpdateMessage);

                    // Negative values are allowed as this represents a refund.
                    if (infoTrackOrderUpdateMessage.InfoTrackTotalFeeTotal != 0)
                    {
                        _telemetryLogger.TrackTrace(
                            $"{typeof(Handler).FullName}: Disbursements have not yet been created and TotalFeeTotal != 0. Processing disbursements now.",
                            WCASeverityLevel.Information,
                            new Dictionary <string, string>()
                        {
                            { "InfoTrackOrderUpdateMessageId", command.InfoTrackOrderUpdateMessageId.ToString(CultureInfo.InvariantCulture) },
                            { "Actionstep org key", orgKey },
                            { "Actionstep matter", matterId.ToString(CultureInfo.InvariantCulture) },
                            { "InfoTrackTotalFeeTotal", infoTrackOrderUpdateMessage.InfoTrackTotalFeeTotal.ToString(CultureInfo.InvariantCulture) },
                            { "ExecutionId", executionId.ToString() }
                        });

                        try
                        {
                            infoTrackOrder.LastUpdatedUtc = DateTime.UtcNow;
                            infoTrackOrder.ActionstepDisbursementStatusUpdatedUtc = infoTrackOrder.LastUpdatedUtc;
                            infoTrackOrder.ActionstepDisbursementStatus           = ActionstepDisbursementStatus.CreationInProgress;
                            await _wCADbContext.SaveChangesAsync();

                            (int?gstTaxCodeId, int?nonGstTaxCodeId) = await GetTaxCodeIds(
                                _actionstepService,
                                tokenSetQuery,
                                _telemetryLogger,
                                infoTrackOrderUpdateMessage.InfoTrackOrderId,
                                orgKey,
                                matterId,
                                executionId);

                            // Save under disbursments
                            // If GST values match, we'll create a single disbursement. If they don't match,
                            // we need to create two different disbursements to account for the differing GST amounts.
                            dynamic disbursement;

                            // If they have the same GST status, then we can combine to a single disbursement.
                            // I.e. either if they both have GST, or if neither of them have GST.
                            bool retailerHasGST     = infoTrackOrderUpdateMessage.InfoTrackRetailerFeeGST != 0;
                            bool supplierHasGST     = infoTrackOrderUpdateMessage.InfoTrackSupplierFeeGST != 0;
                            bool splitDisbursements = false;
                            if (retailerHasGST == supplierHasGST)
                            {
                                disbursement = PrepareSingleDisbursement(infoTrackOrderUpdateMessage, matterId, gstTaxCodeId, nonGstTaxCodeId);
                            }
                            else
                            {
                                splitDisbursements = true;
                                disbursement       = PrepareSplitDisbursements(infoTrackOrderUpdateMessage, matterId, gstTaxCodeId, nonGstTaxCodeId);
                            }

                            if (disbursement != null)
                            {
                                _telemetryLogger.TrackTrace(
                                    $"{typeof(Handler).FullName}: Creating disbursements.",
                                    WCASeverityLevel.Information,
                                    new Dictionary <string, string>()
                                {
                                    { "InfoTrackOrderUpdateMessageId", command.InfoTrackOrderUpdateMessageId.ToString(CultureInfo.InvariantCulture) },
                                    { "Actionstep org key", orgKey },
                                    { "Actionstep matter", matterId.ToString(CultureInfo.InvariantCulture) },
                                    { "InfoTrackTotalFeeTotal", infoTrackOrderUpdateMessage.InfoTrackTotalFeeTotal.ToString(CultureInfo.InvariantCulture) },
                                    { "SplitDisbursements", splitDisbursements.ToString(CultureInfo.InvariantCulture) },
                                    { "ExecutionId", executionId.ToString() }
                                });

                                var response = await _actionstepService.Handle <dynamic>(new GenericActionstepRequest(
                                                                                             tokenSetQuery,
                                                                                             $"/rest/disbursements",
                                                                                             HttpMethod.Post,
                                                                                             disbursement));

                                infoTrackOrder.LastUpdatedUtc = DateTime.UtcNow;
                                infoTrackOrder.ActionstepDisbursementStatusUpdatedUtc = infoTrackOrder.LastUpdatedUtc;
                                infoTrackOrder.ActionstepDisbursementStatus           = ActionstepDisbursementStatus.CreatedSuccessfully;
                                await _wCADbContext.SaveChangesAsync();
                            }
                            else
                            {
                                _telemetryLogger.TrackTrace(
                                    $"{typeof(Handler).FullName}: After processing it was determined that no disbursements need to be created.",
                                    WCASeverityLevel.Information,
                                    new Dictionary <string, string>()
                                {
                                    { "InfoTrackOrderUpdateMessageId", command.InfoTrackOrderUpdateMessageId.ToString(CultureInfo.InvariantCulture) },
                                    { "Actionstep org key", orgKey },
                                    { "Actionstep matter", matterId.ToString(CultureInfo.InvariantCulture) },
                                    { "InfoTrackTotalFeeTotal", infoTrackOrderUpdateMessage.InfoTrackTotalFeeTotal.ToString(CultureInfo.InvariantCulture) },
                                    { "ExecutionId", executionId.ToString() }
                                });

                                infoTrackOrder.LastUpdatedUtc = DateTime.UtcNow;
                                infoTrackOrder.ActionstepDisbursementStatusUpdatedUtc = infoTrackOrder.LastUpdatedUtc;
                                infoTrackOrder.ActionstepDisbursementStatus           = ActionstepDisbursementStatus.NotApplicable;
                                await _wCADbContext.SaveChangesAsync();
                            }
                        }
                        catch (Exception ex)
                        {
                            infoTrackOrder.LastUpdatedUtc = DateTime.UtcNow;
                            infoTrackOrder.ActionstepDisbursementStatusUpdatedUtc = infoTrackOrder.LastUpdatedUtc;
                            infoTrackOrder.ActionstepDisbursementStatus           = ActionstepDisbursementStatus.CreationFailed;
                            await _wCADbContext.SaveChangesAsync();

                            var message = $"Failed to process disbursements for InfoTrack order for ID: {command.InfoTrackOrderUpdateMessageId}";
                            _logger.LogError(ex, message, null);
                            _telemetryLogger.TrackTrace(
                                message,
                                WCASeverityLevel.Error,
                                new Dictionary <string, string>()
                            {
                                { "InfoTrackOrderUpdateMessageId", command.InfoTrackOrderUpdateMessageId.ToString(CultureInfo.InvariantCulture) },
                                { "Actionstep org key", orgKey },
                                { "Actionstep matter", matterId.ToString(CultureInfo.InvariantCulture) },
                                { "Exception Message", ex.Message },
                                { "Stack Trace", ex.StackTrace.ToString(CultureInfo.InvariantCulture) },
                                { "ExecutionId", executionId.ToString() }
                            });

                            processingErrors.Append(message + "<br/>");
                        }
                    }
                    else
                    {
                        _telemetryLogger.TrackTrace(
                            $"{typeof(Handler).FullName}: Disbursement amounts remain at 0. Skipping disbursements.",
                            WCASeverityLevel.Information,
                            new Dictionary <string, string>()
                        {
                            { "InfoTrackOrderUpdateMessageId", command.InfoTrackOrderUpdateMessageId.ToString(CultureInfo.InvariantCulture) },
                            { "Actionstep org key", orgKey },
                            { "Actionstep matter", matterId.ToString(CultureInfo.InvariantCulture) },
                            { "InfoTrackTotalFeeTotal", infoTrackOrderUpdateMessage.InfoTrackTotalFeeTotal.ToString(CultureInfo.InvariantCulture) },
                            { "ExecutionId", executionId.ToString() }
                        });

                        infoTrackOrder.LastUpdatedUtc = DateTime.UtcNow;
                        infoTrackOrder.ActionstepDisbursementStatusUpdatedUtc = infoTrackOrder.LastUpdatedUtc;
                        infoTrackOrder.ActionstepDisbursementStatus           = ActionstepDisbursementStatus.NotApplicable;
                        await _wCADbContext.SaveChangesAsync();
                    }
                }

                var errorMessage = processingErrors.ToString();
                var processingErrorsEncountered = !String.IsNullOrEmpty(errorMessage);

                if (processingErrorsEncountered)
                {
                    infoTrackOrderUpdateMessage.MarkProcessedWithErrors();
                }
                else
                {
                    infoTrackOrderUpdateMessage.MarkProcessed();
                }

                await _wCADbContext.SaveChangesAsync();

                _telemetryLogger.TrackTrace(
                    processingErrorsEncountered ? $"{typeof(Handler).FullName}: Finished UpdateOrder with errors." : $"{typeof(Handler).FullName}: Finished UpdateOrder.",
                    WCASeverityLevel.Information,
                    new Dictionary <string, string>()
                {
                    { "InfoTrackOrderUpdateMessageId", command.InfoTrackOrderUpdateMessageId.ToString(CultureInfo.InvariantCulture) },
                    { "Actionstep org key", orgKey },
                    { "Actionstep matter", matterId.ToString(CultureInfo.InvariantCulture) },
                    { "ExecutionId", executionId.ToString() }
                });

                if (processingErrorsEncountered)
                {
                    var ex = new ApplicationException("Errors were encountered while processing message");
                    ex.Data.Add("message", errorMessage);
                    ex.Data.Add("contentType", "text/html");
                    throw ex;
                }
            }