diff --git a/SqlServerAgentJobDeploymentTool/MainForm.cs b/SqlServerAgentJobDeploymentTool/MainForm.cs index a07328d..72f90b8 100644 --- a/SqlServerAgentJobDeploymentTool/MainForm.cs +++ b/SqlServerAgentJobDeploymentTool/MainForm.cs @@ -415,7 +415,7 @@ private async Task LoadManifestFromPathAsync(string manifestPath) private async Task RunDeploymentAsync(bool dryRun) { SetBusyState(true); - string currentOperation = "preparing deployment"; + string currentOperation = string.Empty; string? currentContext = null; try @@ -465,10 +465,11 @@ private async Task RunDeploymentAsync(bool dryRun) } catch (Exception ex) { - DeploymentErrorReporter.ReportUi(ex, _logger, AppendOutput, currentOperation, currentContext); + string operation = string.IsNullOrWhiteSpace(currentOperation) ? "preparing deployment" : currentOperation; + DeploymentErrorReporter.ReportUi(ex, _logger, AppendOutput, operation, currentContext); UpdateValidationSummary(); SetStatus("Deployment failed"); - MessageBox.Show(this, DeploymentErrorReporter.GetUserMessage(ex, currentOperation, currentContext), "Deployment failed", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(this, DeploymentErrorReporter.GetUserMessage(ex, operation, currentContext), "Deployment failed", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { diff --git a/SqlServerAgentJobDeploymentTool/SqlAgentDeploymentService.cs b/SqlServerAgentJobDeploymentTool/SqlAgentDeploymentService.cs index 17777bf..8593abc 100644 --- a/SqlServerAgentJobDeploymentTool/SqlAgentDeploymentService.cs +++ b/SqlServerAgentJobDeploymentTool/SqlAgentDeploymentService.cs @@ -6,6 +6,7 @@ namespace SqlServerAgentJobDeploymentTool; internal sealed class SqlAgentDeploymentService { + private const string JobIdParameterName = "@job_id"; private readonly SqlConnection _connection; private readonly TextWriter _writer; private readonly ILogger _logger; @@ -55,7 +56,7 @@ private async Task DeployJobAsync(JobDefinition job, string? databaseNameOverrid job.Schedules.Count); int deleteLevel = job.DeleteLevel ?? 0; - SqlParameter jobIdParameter = new("@job_id", System.Data.SqlDbType.UniqueIdentifier) + SqlParameter jobIdParameter = new(JobIdParameterName, System.Data.SqlDbType.UniqueIdentifier) { Direction = System.Data.ParameterDirection.Output }; @@ -110,7 +111,7 @@ private async Task AddJobStepAsync(Guid jobId, JobStepDefinition step, int stepI List parameters = [ - new SqlParameter("@job_id", jobId), + new SqlParameter(JobIdParameterName, jobId), new SqlParameter("@step_id", stepId), new SqlParameter("@step_name", step.Name), new SqlParameter("@subsystem", ToSubsystemName(step.Subsystem)), @@ -143,7 +144,7 @@ private async Task AddJobScheduleAsync(Guid jobId, JobScheduleDefinition schedul List parameters = [ - new SqlParameter("@job_id", jobId), + new SqlParameter(JobIdParameterName, jobId), new SqlParameter("@name", schedule.Name), new SqlParameter("@enabled", schedule.Enabled), new SqlParameter("@freq_type", (int)schedule.FrequencyType), @@ -233,7 +234,7 @@ private async Task AddJobServerAsync(Guid jobId, string? targetServerName, Cance { List parameters = [ - new SqlParameter("@job_id", jobId) + new SqlParameter(JobIdParameterName, jobId) ]; if (!string.IsNullOrWhiteSpace(targetServerName)) diff --git a/TransactionProcessing.MerchantFileProcessor/FileBuilding/TransactionFileModels.cs b/TransactionProcessing.MerchantFileProcessor/FileBuilding/TransactionFileModels.cs index 70f7d0f..1e916fe 100644 --- a/TransactionProcessing.MerchantFileProcessor/FileBuilding/TransactionFileModels.cs +++ b/TransactionProcessing.MerchantFileProcessor/FileBuilding/TransactionFileModels.cs @@ -91,6 +91,24 @@ private static string BuildMobileNumber() => public static class TransactionFileFieldResolver { + private static readonly Dictionary> FieldSourceHandlers = new(StringComparer.OrdinalIgnoreCase) + { + [TransactionFileFieldSources.MerchantId] = static (context, _) => context.Merchant.MerchantId, + [TransactionFileFieldSources.ContractId] = static (context, _) => context.Contract.ContractId, + [TransactionFileFieldSources.ContractIssuer] = static (context, _) => context.Contract.Issuer, + [TransactionFileFieldSources.ProductCode] = static (context, _) => GetTransaction(context.Transaction).ProductCode, + [TransactionFileFieldSources.Description] = static (context, _) => GetTransaction(context.Transaction).Description, + [TransactionFileFieldSources.RecipientMobileNumber] = static (context, _) => GetTransaction(context.Transaction).RecipientMobileNumber, + [TransactionFileFieldSources.Quantity] = static (context, format) => ApplyFormat(GetTransaction(context.Transaction).Quantity, format), + [TransactionFileFieldSources.UnitAmount] = static (context, format) => ApplyFormat(GetTransaction(context.Transaction).UnitAmount, format), + [TransactionFileFieldSources.TotalAmount] = static (context, format) => ApplyFormat(GetTransaction(context.Transaction).TotalAmount, format), + [TransactionFileFieldSources.Currency] = static (context, _) => GetTransaction(context.Transaction).Currency, + [TransactionFileFieldSources.TransactionDateUtc] = static (context, format) => ApplyDateFormat(GetTransaction(context.Transaction).TransactionDateUtc, format), + [TransactionFileFieldSources.ProcessingDateUtc] = static (context, format) => ApplyDateFormat(context.ProcessingTimestampUtc, format), + [TransactionFileFieldSources.RecordCount] = static (context, format) => ApplyFormat(context.RecordCount, format), + [TransactionFileFieldSources.FileTotalAmount] = static (context, format) => ApplyFormat(context.FileTotalAmount, format ?? "0.00") + }; + public static string GetTextValue(FileFieldOptions field, TransactionFileContext context) { return GetValue(field, context)?.ToString() ?? string.Empty; @@ -108,86 +126,22 @@ public static object GetValue(FileFieldOptions field, TransactionFileContext con throw new InvalidOperationException($"Field '{field.Name}' must define either a source or a literal value."); } - var transaction = context.Transaction; - - var source = field.Source.Trim(); - - if (source.Equals(TransactionFileFieldSources.MerchantId, StringComparison.OrdinalIgnoreCase)) - { - return context.Merchant.MerchantId; - } - - if (source.Equals(TransactionFileFieldSources.ContractId, StringComparison.OrdinalIgnoreCase)) - { - return context.Contract.ContractId; - } - - if (source.Equals(TransactionFileFieldSources.ContractIssuer, StringComparison.OrdinalIgnoreCase)) - { - return context.Contract.Issuer; - } - - if (source.Equals(TransactionFileFieldSources.ProductCode, StringComparison.OrdinalIgnoreCase)) - { - return GetTransaction(transaction).ProductCode; - } - - if (source.Equals(TransactionFileFieldSources.Description, StringComparison.OrdinalIgnoreCase)) - { - return GetTransaction(transaction).Description; - } - - if (source.Equals(TransactionFileFieldSources.RecipientMobileNumber, StringComparison.OrdinalIgnoreCase)) - { - return GetTransaction(transaction).RecipientMobileNumber; - } - - if (source.Equals(TransactionFileFieldSources.Quantity, StringComparison.OrdinalIgnoreCase)) - { - return ApplyFormat(GetTransaction(transaction).Quantity, field.Format); - } - - if (source.Equals(TransactionFileFieldSources.UnitAmount, StringComparison.OrdinalIgnoreCase)) - { - return ApplyFormat(GetTransaction(transaction).UnitAmount, field.Format); - } - - if (source.Equals(TransactionFileFieldSources.TotalAmount, StringComparison.OrdinalIgnoreCase)) - { - return ApplyFormat(GetTransaction(transaction).TotalAmount, field.Format); - } - - if (source.Equals(TransactionFileFieldSources.Currency, StringComparison.OrdinalIgnoreCase)) - { - return GetTransaction(transaction).Currency; - } - - if (source.Equals(TransactionFileFieldSources.TransactionDateUtc, StringComparison.OrdinalIgnoreCase)) - { - return ApplyDateFormat(GetTransaction(transaction).TransactionDateUtc, field.Format); - } - - if (source.Equals(TransactionFileFieldSources.ProcessingDateUtc, StringComparison.OrdinalIgnoreCase)) - { - return ApplyDateFormat(context.ProcessingTimestampUtc, field.Format); - } + return ResolveFieldSourceValue(field.Source.Trim(), field.Format, context); + } - if (source.Equals(TransactionFileFieldSources.RecordCount, StringComparison.OrdinalIgnoreCase)) - { - return ApplyFormat(context.RecordCount, field.Format); - } + private static GeneratedTransaction GetTransaction(GeneratedTransaction? transaction) => + transaction ?? throw new InvalidOperationException("The requested field requires a transaction record, but no transaction context was supplied."); - if (source.Equals(TransactionFileFieldSources.FileTotalAmount, StringComparison.OrdinalIgnoreCase)) + private static object ResolveFieldSourceValue(string source, string? format, TransactionFileContext context) + { + if (!FieldSourceHandlers.TryGetValue(source, out var handler)) { - return ApplyFormat(context.FileTotalAmount, field.Format ?? "0.00"); + throw new InvalidOperationException($"Unsupported field source '{source}'."); } - throw new InvalidOperationException($"Unsupported field source '{field.Source}'."); + return handler(context, format); } - private static GeneratedTransaction GetTransaction(GeneratedTransaction? transaction) => - transaction ?? throw new InvalidOperationException("The requested field requires a transaction record, but no transaction context was supplied."); - private static object ApplyFormat(T value, string? format) where T : IFormattable { diff --git a/TransactionProcessing.MerchantFileProcessor/Reporting/ConfigurationManagementEndpointRouteBuilderExtensions.cs b/TransactionProcessing.MerchantFileProcessor/Reporting/ConfigurationManagementEndpointRouteBuilderExtensions.cs index ab28692..cb8f55d 100644 --- a/TransactionProcessing.MerchantFileProcessor/Reporting/ConfigurationManagementEndpointRouteBuilderExtensions.cs +++ b/TransactionProcessing.MerchantFileProcessor/Reporting/ConfigurationManagementEndpointRouteBuilderExtensions.cs @@ -1,4 +1,3 @@ -using System.Text.Json; using Microsoft.AspNetCore.Http; using TransactionProcessing.MerchantFileProcessor.Configuration; using Microsoft.Extensions.Primitives; @@ -186,7 +185,6 @@ private static async Task HandleFileProfileFieldConfigurationAsync( string section, HttpRequest request, IMerchantProcessingConfigurationStore configurationStore, - JsonSerializerOptions jsonSerializerOptions, CancellationToken cancellationToken) { var form = await request.ReadFormAsync(cancellationToken); diff --git a/TransactionProcessing.MerchantFileProcessor/Reporting/OperationsDashboardService.cs b/TransactionProcessing.MerchantFileProcessor/Reporting/OperationsDashboardService.cs index 54f877e..40618b6 100644 --- a/TransactionProcessing.MerchantFileProcessor/Reporting/OperationsDashboardService.cs +++ b/TransactionProcessing.MerchantFileProcessor/Reporting/OperationsDashboardService.cs @@ -78,6 +78,14 @@ public sealed class OperationsDashboardService( IHostEnvironment hostEnvironment, IConfiguration configuration) : IOperationsDashboardService { + private const string SectionEnd = " "; + private const string TableStart = " "; + private const string TableEnd = "
"; + private const string TableBodyStart = " "; + private const string TableBodyEnd = " "; + private const string TableRowStart = " "; + private const string TableRowEnd = " "; + public Task GetDashboardModelAsync(CancellationToken cancellationToken) => this.BuildDashboardModelAsync(cancellationToken); @@ -100,19 +108,19 @@ public async Task RenderDashboardHtmlAsync(CancellationToken cancellatio html.AppendLine($"
Generated
{Encode(model.GeneratedUtc.ToString("u"))}
"); html.AppendLine($"
Connection
{Encode(model.ConnectionStringSummary)}
"); html.AppendLine(" "); - html.AppendLine(" "); + html.AppendLine(SectionEnd); AppendMetrics(html, model.Metrics); html.AppendLine("
"); html.AppendLine("
Merchant schedule
"); - html.AppendLine(" "); + html.AppendLine(TableStart); html.AppendLine(" "); - html.AppendLine(" "); + html.AppendLine(TableBodyStart); foreach (var merchant in model.Merchants) { - html.AppendLine(" "); + html.AppendLine(TableRowStart); html.AppendLine($" "); html.AppendLine($" "); html.AppendLine($" "); @@ -123,7 +131,7 @@ public async Task RenderDashboardHtmlAsync(CancellationToken cancellatio html.AppendLine($" "); html.AppendLine($" "); html.AppendLine($" "); - html.AppendLine(" "); + html.AppendLine(TableRowEnd); } if (model.Merchants.Count == 0) @@ -131,30 +139,30 @@ public async Task RenderDashboardHtmlAsync(CancellationToken cancellatio html.AppendLine(" "); } - html.AppendLine(" "); - html.AppendLine("
MerchantEnabledEstateRun times (UTC)Last merchant runNext runSuccessFailedPending checksStatus
{Encode(merchant.MerchantName)}
{Encode(merchant.MerchantId)}
{RenderBadge(merchant.Enabled ? "Enabled" : "Disabled", merchant.Enabled ? "good" : "neutral")}{Encode(merchant.EstateId)}{merchant.FailedFiles}{merchant.PendingStatusChecks}View status
No merchants are configured.
"); - html.AppendLine("
"); + html.AppendLine(TableBodyEnd); + html.AppendLine(TableEnd); + html.AppendLine(SectionEnd); html.AppendLine("
"); AppendFileProfilesSection(html, model.FileProfiles); AppendContractsSection(html, model.Contracts); - html.AppendLine("
"); + html.AppendLine(SectionEnd); html.AppendLine("
"); html.AppendLine("
Recent merchant runs
"); html.AppendLine(" "); html.AppendLine(" "); - html.AppendLine(" "); + html.AppendLine(TableBodyStart); foreach (var run in model.RecentRuns) { - html.AppendLine(" "); + html.AppendLine(TableRowStart); html.AppendLine($" "); html.AppendLine($" "); html.AppendLine($" "); html.AppendLine($" "); html.AppendLine($" "); - html.AppendLine(" "); + html.AppendLine(TableRowEnd); } if (model.RecentRuns.Count == 0) @@ -162,9 +170,9 @@ public async Task RenderDashboardHtmlAsync(CancellationToken cancellatio html.AppendLine(" "); } - html.AppendLine(" "); + html.AppendLine(TableBodyEnd); html.AppendLine("
MerchantScheduledCompletedStatusError
{Encode(run.MerchantName)}
{Encode(run.MerchantId)}
{Encode(run.ScheduledRunUtc.ToString("u"))}{Encode(run.CompletedUtc.ToString("u"))}{RenderBadge(run.Status, RunTone(run.Status))}{Encode(run.ErrorMessage ?? string.Empty)}
No merchant runs have been recorded yet.
"); - html.AppendLine("
"); + html.AppendLine(SectionEnd); AppendDocumentEnd(html); return html.ToString(); @@ -189,7 +197,7 @@ public async Task RenderConfigurationHtmlAsync(CancellationToken cancell html.AppendLine($"
File processing
{Encode(model.FileProcessingSummary)}
"); html.AppendLine($"
Polling
{Encode(model.PollingSummary)}
"); html.AppendLine(" "); - html.AppendLine(" "); + html.AppendLine(SectionEnd); html.AppendLine("
"); AppendSummaryPanel(html, "Runtime summary", new[] @@ -204,28 +212,28 @@ public async Task RenderConfigurationHtmlAsync(CancellationToken cancell }); AppendMerchantConfigPanel(html); - html.AppendLine("
"); + html.AppendLine(SectionEnd); html.AppendLine("
"); AppendFileProfilesSection(html, model.FileProfiles); AppendContractsSection(html, model.Contracts); - html.AppendLine("
"); + html.AppendLine(SectionEnd); html.AppendLine("
"); html.AppendLine("
Merchant definitions
"); html.AppendLine(" "); html.AppendLine(" "); - html.AppendLine(" "); + html.AppendLine(TableBodyStart); foreach (var merchant in model.Merchants) { - html.AppendLine(" "); + html.AppendLine(TableRowStart); html.AppendLine($" "); html.AppendLine($" "); html.AppendLine($" "); html.AppendLine($" "); html.AppendLine($" "); - html.AppendLine(" "); + html.AppendLine(TableRowEnd); } if (model.Merchants.Count == 0) @@ -233,9 +241,9 @@ public async Task RenderConfigurationHtmlAsync(CancellationToken cancell html.AppendLine(" "); } - html.AppendLine(" "); + html.AppendLine(TableBodyEnd); html.AppendLine("
MerchantEnabledEstateMerchant IDRun schedule
{Encode(merchant.MerchantName)}{RenderBadge(merchant.Enabled ? "Enabled" : "Disabled", merchant.Enabled ? "good" : "neutral")}{Encode(merchant.EstateId)}{Encode(merchant.MerchantId)}{Encode(merchant.RunTimesUtc)}
No merchants are configured.
"); - html.AppendLine("
"); + html.AppendLine(SectionEnd); AppendDocumentEnd(html); return html.ToString(); @@ -255,22 +263,22 @@ public async Task RenderRunsHtmlAsync(CancellationToken cancellationToke html.AppendLine("

Merchant execution audit trail

"); html.AppendLine("

Each record shows the scheduled slot, completion time, and final outcome for a merchant processing cycle.

"); html.AppendLine(" "); - html.AppendLine(" "); + html.AppendLine(SectionEnd); html.AppendLine("
"); html.AppendLine(" "); html.AppendLine(" "); - html.AppendLine(" "); + html.AppendLine(TableBodyStart); foreach (var run in model.RecentRuns) { - html.AppendLine(" "); + html.AppendLine(TableRowStart); html.AppendLine($" "); html.AppendLine($" "); html.AppendLine($" "); html.AppendLine($" "); html.AppendLine($" "); - html.AppendLine(" "); + html.AppendLine(TableRowEnd); } if (model.RecentRuns.Count == 0) @@ -278,9 +286,9 @@ public async Task RenderRunsHtmlAsync(CancellationToken cancellationToke html.AppendLine(" "); } - html.AppendLine(" "); + html.AppendLine(TableBodyEnd); html.AppendLine("
MerchantScheduledCompletedStatusError
{Encode(run.MerchantName)}
{Encode(run.MerchantId)}
{Encode(run.ScheduledRunUtc.ToString("u"))}{Encode(run.CompletedUtc.ToString("u"))}{RenderBadge(run.Status, RunTone(run.Status))}{Encode(run.ErrorMessage ?? string.Empty)}
No merchant runs are recorded yet.
"); - html.AppendLine("
"); + html.AppendLine(SectionEnd); AppendDocumentEnd(html); return html.ToString(); @@ -589,7 +597,7 @@ private static void AppendMetrics(StringBuilder html, IReadOnlyList{Encode(metric.Detail)}"); html.AppendLine(" "); } - html.AppendLine(" "); + html.AppendLine(SectionEnd); } private static void AppendSummaryPanel(StringBuilder html, string title, IEnumerable<(string Label, string Value)> items) @@ -645,8 +653,8 @@ private static void AppendFileProfilesSection(StringBuilder html, IReadOnlyList< html.AppendLine(" No file profiles are configured."); } - html.AppendLine(" "); - html.AppendLine(" "); + html.AppendLine(TableBodyEnd); + html.AppendLine(TableEnd); html.AppendLine(" "); } @@ -671,8 +679,8 @@ private static void AppendContractsSection(StringBuilder html, IReadOnlyListNo contract definitions are configured."); } - html.AppendLine(" "); - html.AppendLine(" "); + html.AppendLine(TableBodyEnd); + html.AppendLine(TableEnd); html.AppendLine(" "); } diff --git a/TransactionProcessing.MerchantFileProcessor/Worker.cs b/TransactionProcessing.MerchantFileProcessor/Worker.cs index 6e76998..b19b1e5 100644 --- a/TransactionProcessing.MerchantFileProcessor/Worker.cs +++ b/TransactionProcessing.MerchantFileProcessor/Worker.cs @@ -11,7 +11,9 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { LocalLogger.Context logger = LocalLogger.For(); logger.LogInformation("Merchant processor started"); - while (await RunIterationAsync(stoppingToken)) { + while (await RunIterationAsync(stoppingToken)) + { + continue; } } diff --git a/TransactionProcessing.MerchantPos/Runtime/ApiClient.cs b/TransactionProcessing.MerchantPos/Runtime/ApiClient.cs index 472adbb..5d63cae 100644 --- a/TransactionProcessing.MerchantPos/Runtime/ApiClient.cs +++ b/TransactionProcessing.MerchantPos/Runtime/ApiClient.cs @@ -36,6 +36,7 @@ Task> SendSale(MerchantConfig cfg, TokenResponse token, Pro } public class ApiClient : ClientProxyBase.ClientProxyBase, IApiClient { + private const string DefaultCustomerAccountNumber = "07777777705"; private readonly ISecurityServiceClient SecurityClient; private readonly ITransactionProcessorClient TransactionProcessorClient; private readonly MerchantPosSettingsStore SettingsStore; @@ -345,7 +346,7 @@ private Result> BuildMobileSaleTransactionRe TransactionNumber = transactionNumber.ToString("D4") }; - saleTransactionRequest.AdditionalRequestMetadata = new Dictionary { { "Amount", value.ToString() }, { "CustomerAccountNumber", "07777777705" } }; + saleTransactionRequest.AdditionalRequestMetadata = new Dictionary { { "Amount", value.ToString() }, { "CustomerAccountNumber", DefaultCustomerAccountNumber } }; requestMessages.Add(saleTransactionRequest); return Result.Success(requestMessages); } @@ -369,7 +370,7 @@ private Result> BuildVoucherTransactionReque saleTransactionRequest.AdditionalRequestMetadata = new Dictionary { { "Amount", value.ToString() }, - { "RecipientMobile", "07777777705" } + { "RecipientMobile", DefaultCustomerAccountNumber } }; requestMessages.Add(saleTransactionRequest); return Result.Success(requestMessages); @@ -419,7 +420,7 @@ private String BuildRequestUrl(String route) { return ResultHelpers.CreateFailure(result); } - return Result.Success((body.account_number.ToString(), body.account_name, "07777777705")); + return Result.Success((body.account_number.ToString(), body.account_name, DefaultCustomerAccountNumber)); } @@ -441,7 +442,7 @@ private String BuildRequestUrl(String route) { return ResultHelpers.CreateFailure(result); } - return Result.Success((body.meter_number.ToString(), body.customer_name, "07777777705")); + return Result.Success((body.meter_number.ToString(), body.customer_name, DefaultCustomerAccountNumber)); } } diff --git a/TransactionProcessor.SystemSetupTool/EstateSetupFunctions.cs b/TransactionProcessor.SystemSetupTool/EstateSetupFunctions.cs index 6ec477f..ab5e5ad 100644 --- a/TransactionProcessor.SystemSetupTool/EstateSetupFunctions.cs +++ b/TransactionProcessor.SystemSetupTool/EstateSetupFunctions.cs @@ -419,7 +419,7 @@ private async Task UpdateMerchant(Merchant merchant, return ResultHelpers.CreateFailure(ensureUserResult); } - var ensureOperatorsResult = await EnsureMerchantOperatorsAsync(merchant, existingMerchant, cancellationToken); + var ensureOperatorsResult = await EnsureMerchantOperatorsAsync(existingMerchant, cancellationToken); if (ensureOperatorsResult.IsFailed) { return ResultHelpers.CreateFailure(ensureOperatorsResult); @@ -470,7 +470,7 @@ private async Task EnsureMerchantUserAsync(Merchant merchant, Cancellati return createMerchantUserResult.IsFailed ? ResultHelpers.CreateFailure(createMerchantUserResult) : Result.Success(); } - private async Task EnsureMerchantOperatorsAsync(Merchant merchant, MerchantResponse existingMerchant, CancellationToken cancellationToken) + private async Task EnsureMerchantOperatorsAsync(MerchantResponse existingMerchant, CancellationToken cancellationToken) { var getEstateResult = await this.TransactionProcessorClient.GetEstate(this.TokenResponse.AccessToken, this.EstateId, cancellationToken); if (getEstateResult.IsFailed)