diff --git a/TransactionProcessor.Aggregates/TransactionAggregate.cs b/TransactionProcessor.Aggregates/TransactionAggregate.cs index 9e09179a..8daa408c 100644 --- a/TransactionProcessor.Aggregates/TransactionAggregate.cs +++ b/TransactionProcessor.Aggregates/TransactionAggregate.cs @@ -277,6 +277,23 @@ public static void CompleteTransaction(this TransactionAggregate aggregate) aggregate.ApplyAndAppend(transactionHasBeenCompletedEvent); } + public static void RecordTransactionTimings(this TransactionAggregate aggregate, + DateTime TransactionStartedDateTime, + DateTime? OperatorCommunicationsStartedEvent, + DateTime? OperatorCommunicationsCompletedEvent, + DateTime TransactionCompletedDateTime) { + TransactionDomainEvents.TransactionTimingsAddedToTransactionEvent transactionTimingsAddedToTransactionEvent = + new (aggregate.AggregateId, + aggregate.EstateId, + aggregate.MerchantId, + TransactionStartedDateTime, + OperatorCommunicationsStartedEvent, + OperatorCommunicationsCompletedEvent, + TransactionCompletedDateTime); + + aggregate.ApplyAndAppend(transactionTimingsAddedToTransactionEvent); + } + public static void RecordAdditionalRequestData(this TransactionAggregate aggregate, Guid operatorId, Dictionary additionalTransactionRequestMetadata) @@ -661,6 +678,11 @@ public static void PlayEvent(this TransactionAggregate aggregate, TransactionDom FeeCalculationType = (CalculationType)domainEvent.FeeCalculationType }); } + + public static void PlayEvent(this TransactionAggregate aggregate, + TransactionDomainEvents.TransactionTimingsAddedToTransactionEvent domainEvent) { + // Nothing to do here, just a marker for the event + } } public record TransactionAggregate : Aggregate diff --git a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs index 946988f4..602b6e21 100644 --- a/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs +++ b/TransactionProcessor.BusinessLogic.Tests/Services/TransactionDomainServiceTests.cs @@ -93,7 +93,7 @@ public async Task TransactionDomainService_ProcessLogonTransaction_DeviceNeedsAd TestData.DeviceIdentifier, TestData.TransactionTypeLogon.ToString(), TestData.TransactionDateTime, - TestData.TransactionNumber); + TestData.TransactionNumber, TestData.TransactionReceivedDateTime); var result = await this.TransactionDomainService.ProcessLogonTransaction(command, CancellationToken.None); @@ -122,7 +122,7 @@ public async Task TransactionDomainService_ProcessLogonTransaction_TransactionIs TestData.DeviceIdentifier, TestData.TransactionTypeLogon.ToString(), TestData.TransactionDateTime, - TestData.TransactionNumber); + TestData.TransactionNumber, TestData.TransactionReceivedDateTime); var result = await this.TransactionDomainService.ProcessLogonTransaction(command, CancellationToken.None); @@ -153,7 +153,7 @@ public async Task TransactionDomainService_ProcessLogonTransaction_ValidationFai TestData.DeviceIdentifier, TestData.TransactionTypeLogon.ToString(), TestData.TransactionDateTime, - TestData.TransactionNumber); + TestData.TransactionNumber, TestData.TransactionReceivedDateTime); Result result = await this.TransactionDomainService.ProcessLogonTransaction(command, CancellationToken.None); @@ -259,7 +259,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_DeclinedByOper TestData.MerchantId, TestData.DeviceIdentifier, TestData.TransactionTypeSale.ToString(), TestData.TransactionDateTime, TestData.TransactionNumber, TestData.OperatorId, TestData.CustomerEmailAddress, TestData.AdditionalTransactionMetaDataForMobileTopup(), - TestData.ContractId, TestData.ProductId, TestData.TransactionSource); + TestData.ContractId, TestData.ProductId, TestData.TransactionSource, TestData.TransactionReceivedDateTime); ProcessSaleTransactionResponse response = await this.TransactionDomainService.ProcessSaleTransaction(command, CancellationToken.None); @@ -300,7 +300,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_OperatorProxyT TestData.MerchantId, TestData.DeviceIdentifier, TestData.TransactionTypeSale.ToString(), TestData.TransactionDateTime, TestData.TransactionNumber, TestData.OperatorId, TestData.CustomerEmailAddress, TestData.AdditionalTransactionMetaDataForMobileTopup(), - TestData.ContractId, TestData.ProductId, TestData.TransactionSource); + TestData.ContractId, TestData.ProductId, TestData.TransactionSource, TestData.TransactionReceivedDateTime); ProcessSaleTransactionResponse response = await this.TransactionDomainService.ProcessSaleTransaction(command, CancellationToken.None); @@ -358,7 +358,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_TransactionIsP TestData.MerchantId, TestData.DeviceIdentifier, TestData.TransactionTypeSale.ToString(), TestData.TransactionDateTime, TestData.TransactionNumber, TestData.OperatorId, TestData.CustomerEmailAddress, TestData.AdditionalTransactionMetaDataForMobileTopup(), - TestData.ContractId, TestData.ProductId, TestData.TransactionSource); + TestData.ContractId, TestData.ProductId, TestData.TransactionSource, TestData.TransactionReceivedDateTime); ProcessSaleTransactionResponse response = await this.TransactionDomainService.ProcessSaleTransaction(command, CancellationToken.None); @@ -420,7 +420,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_NoFloatFound_T TestData.MerchantId, TestData.DeviceIdentifier, TestData.TransactionTypeSale.ToString(), TestData.TransactionDateTime, TestData.TransactionNumber, TestData.OperatorId, TestData.CustomerEmailAddress, TestData.AdditionalTransactionMetaDataForMobileTopup(), - TestData.ContractId, TestData.ProductId, TestData.TransactionSource); + TestData.ContractId, TestData.ProductId, TestData.TransactionSource, TestData.TransactionReceivedDateTime); ProcessSaleTransactionResponse response = await this.TransactionDomainService.ProcessSaleTransaction(command, CancellationToken.None); @@ -462,7 +462,7 @@ public async Task TransactionDomainService_ProcessSaleTransaction_ValidationFail TestData.MerchantId, TestData.DeviceIdentifier, TestData.TransactionTypeSale.ToString(), TestData.TransactionDateTime, TestData.TransactionNumber, TestData.OperatorId, TestData.CustomerEmailAddress, TestData.AdditionalTransactionMetaDataForMobileTopup(), - TestData.ContractId, TestData.ProductId, TestData.TransactionSource); + TestData.ContractId, TestData.ProductId, TestData.TransactionSource, TestData.TransactionReceivedDateTime); ProcessSaleTransactionResponse response = await this.TransactionDomainService.ProcessSaleTransaction(command, CancellationToken.None); diff --git a/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs b/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs index ceaba3bd..2eb3baac 100644 --- a/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs +++ b/TransactionProcessor.BusinessLogic/EventHandling/ReadModelDomainEventHandler.cs @@ -71,6 +71,7 @@ public async Task Handle(IDomainEvent domainEvent, TransactionDomainEvents.TransactionSourceAddedToTransactionEvent de => this.EstateReportingRepository.AddSourceDetailsToTransaction(de, cancellationToken), TransactionDomainEvents.ProductDetailsAddedToTransactionEvent de => this.EstateReportingRepository.AddProductDetailsToTransaction(de, cancellationToken), TransactionDomainEvents.TransactionHasBeenCompletedEvent de => this.EstateReportingRepository.CompleteTransaction(de, cancellationToken), + TransactionDomainEvents.TransactionTimingsAddedToTransactionEvent de => this.EstateReportingRepository.RecordTransactionTimings(de, cancellationToken), ReconciliationDomainEvents.ReconciliationHasStartedEvent de => this.EstateReportingRepository.StartReconciliation(de, cancellationToken), ReconciliationDomainEvents.OverallTotalsRecordedEvent de => this.EstateReportingRepository.UpdateReconciliationOverallTotals(de, cancellationToken), ReconciliationDomainEvents.ReconciliationHasBeenLocallyAuthorisedEvent de => this.EstateReportingRepository.UpdateReconciliationStatus(de, cancellationToken), diff --git a/TransactionProcessor.BusinessLogic/Requests/TransactionCommands.cs b/TransactionProcessor.BusinessLogic/Requests/TransactionCommands.cs index 5c05b238..479de742 100644 --- a/TransactionProcessor.BusinessLogic/Requests/TransactionCommands.cs +++ b/TransactionProcessor.BusinessLogic/Requests/TransactionCommands.cs @@ -16,7 +16,8 @@ public record ProcessLogonTransactionCommand(Guid TransactionId, String DeviceIdentifier, String TransactionType, DateTime TransactionDateTime, - String TransactionNumber) + String TransactionNumber, + DateTime TransactionReceivedDateTime) : IRequest>; public record ProcessReconciliationCommand(Guid TransactionId, @@ -40,7 +41,8 @@ public record ProcessSaleTransactionCommand(Guid TransactionId, Dictionary AdditionalTransactionMetadata, Guid ContractId, Guid ProductId, - Int32 TransactionSource) + Int32 TransactionSource, + DateTime TransactionReceivedDateTime) : IRequest>; public record ResendTransactionReceiptCommand(Guid TransactionId, Guid EstateId) : IRequest; diff --git a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs index 27956a21..a7b1a098 100644 --- a/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs +++ b/TransactionProcessor.BusinessLogic/Services/TransactionDomainService.cs @@ -203,9 +203,11 @@ public async Task> ProcessLogonTransacti // Record the failure transactionAggregate.DeclineTransactionLocally(((Int32)validationResult.Data.ResponseCode).ToString().PadLeft(4, '0'), validationResult.Data.ResponseMessage); } - + transactionAggregate.CompleteTransaction(); + transactionAggregate.RecordTransactionTimings(command.TransactionReceivedDateTime, null, null,DateTime.Now); + return Result.Success(new ProcessLogonTransactionResponse { ResponseMessage = transactionAggregate.ResponseMessage, ResponseCode = transactionAggregate.ResponseCode, @@ -237,7 +239,7 @@ public async Task> ProcessRecon // Record the failure reconciliationAggregate.Decline(((Int32)validationResult.Data.ResponseCode).ToString().PadLeft(4, '0'), validationResult.Data.ResponseMessage); } - + reconciliationAggregate.CompleteReconciliation(); return Result.Success(new ProcessReconciliationTransactionResponse { @@ -292,7 +294,8 @@ public async Task> ProcessSaleTransaction // Add the transaction source transactionAggregate.AddTransactionSource(transactionSourceValue); - + DateTime? operatorStartDateTime = null; + DateTime? operatorEndDateTime = null; if (validationResult.Data.ResponseCode == TransactionResponseCode.Success) { // Record any additional request metadata transactionAggregate.RecordAdditionalRequestData(command.OperatorId, command.AdditionalTransactionMetadata); @@ -301,8 +304,9 @@ public async Task> ProcessSaleTransaction Result merchantResult = await this.GetMerchant(command.MerchantId, cancellationToken); if (merchantResult.IsFailed) return ResultHelpers.CreateFailure(merchantResult); + operatorStartDateTime =DateTime.Now; Result operatorResult = await this.ProcessMessageWithOperator(merchantResult.Data, command.TransactionId, command.TransactionDateTime, command.OperatorId, command.AdditionalTransactionMetadata, transactionReference, cancellationToken); - + operatorEndDateTime = DateTime.Now; // Act on the operator response // TODO: see if we still need this case... //if (operatorResult.IsFailed) { @@ -338,6 +342,8 @@ public async Task> ProcessSaleTransaction transactionAggregate.DeclineTransactionLocally(((Int32)validationResult.Data.ResponseCode).ToString().PadLeft(4, '0'), validationResult.Data.ResponseMessage); } + transactionAggregate.RecordTransactionTimings(command.TransactionReceivedDateTime, operatorStartDateTime, operatorEndDateTime, DateTime.Now);; + transactionAggregate.CompleteTransaction(); // Determine if the email receipt is required diff --git a/TransactionProcessor.Database/Contexts/EstateManagementGenericContext.cs b/TransactionProcessor.Database/Contexts/EstateManagementGenericContext.cs index 1707d0ec..05fd4913 100644 --- a/TransactionProcessor.Database/Contexts/EstateManagementGenericContext.cs +++ b/TransactionProcessor.Database/Contexts/EstateManagementGenericContext.cs @@ -112,6 +112,8 @@ public EstateManagementGenericContext(DbContextOptions dbContextOptions) : base( public DbSet TodayTransactions { get; set; } public DbSet TransactionHistory { get; set; } + public DbSet TransactionTimings { get; set; } + public DbSet Events { get; set; } #endregion @@ -266,7 +268,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .SetupOperator() .SetupSettlementSummary() .SetupTransactionHistory() - .SetupTodaysTransactions(); + .SetupTodaysTransactions() + .SetupTransactionTimings(); modelBuilder.SetupViewEntities(); diff --git a/TransactionProcessor.Database/Contexts/Extensions.cs b/TransactionProcessor.Database/Contexts/Extensions.cs index f6a4fc66..00d835be 100644 --- a/TransactionProcessor.Database/Contexts/Extensions.cs +++ b/TransactionProcessor.Database/Contexts/Extensions.cs @@ -254,6 +254,14 @@ public static ModelBuilder SetupMerchantSettlementFee(this ModelBuilder modelBui return modelBuilder; } + public static ModelBuilder SetupTransactionTimings(this ModelBuilder modelBuilder) { + modelBuilder.Entity().HasKey(t => new { + t.TransactionId + }).IsClustered(false); + + return modelBuilder; + } + public static ModelBuilder SetupTransaction(this ModelBuilder modelBuilder){ modelBuilder.Entity().HasKey(t => new { t.TransactionReportingId, diff --git a/TransactionProcessor.Database/Entities/TransactionTimings.cs b/TransactionProcessor.Database/Entities/TransactionTimings.cs new file mode 100644 index 00000000..bcd052e2 --- /dev/null +++ b/TransactionProcessor.Database/Entities/TransactionTimings.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace TransactionProcessor.Database.Entities; + +[Table("transactiontimings")] +public class TransactionTimings { + public Guid TransactionId { get; set; } + public DateTime TransactionStartedDateTime { get; set; } + public DateTime? OperatorCommunicationsStartedDateTime { get; set; } + public DateTime? OperatorCommunicationsCompletedDateTime { get; set; } + public DateTime TransactionCompletedDateTime { get; set; } + public Double TotalTransactionInMilliseconds { get; set; } + public Double OperatorCommunicationsDurationInMilliseconds { get; set; } + public Double TransactionProcessingDurationInMilliseconds { get; set; } +} \ No newline at end of file diff --git a/TransactionProcessor.Database/Migrations/MySql/20250505085317_LoadTransactionTimings.Designer.cs b/TransactionProcessor.Database/Migrations/MySql/20250505085317_LoadTransactionTimings.Designer.cs new file mode 100644 index 00000000..4fb0b21d --- /dev/null +++ b/TransactionProcessor.Database/Migrations/MySql/20250505085317_LoadTransactionTimings.Designer.cs @@ -0,0 +1,1504 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TransactionProcessor.Database.Contexts; + +#nullable disable + +namespace TransactionProcessor.Database.Migrations.MySql +{ + [DbContext(typeof(EstateManagementMySqlContext))] + [Migration("20250505085317_LoadTransactionTimings")] + partial class LoadTransactionTimings + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Calendar", b => + { + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("DayOfWeek") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DayOfWeekNumber") + .HasColumnType("int"); + + b.Property("DayOfWeekShort") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MonthNameLong") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MonthNameShort") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MonthNumber") + .HasColumnType("int"); + + b.Property("WeekNumber") + .HasColumnType("int"); + + b.Property("WeekNumberString") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Year") + .HasColumnType("int"); + + b.Property("YearWeekNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Date"); + + b.ToTable("calendar"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Contract", b => + { + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("OperatorId") + .HasColumnType("char(36)"); + + b.Property("ContractId") + .HasColumnType("char(36)"); + + b.Property("ContractReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ContractReportingId")); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("EstateId", "OperatorId", "ContractId"); + + b.ToTable("contract"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.ContractProduct", b => + { + b.Property("ContractProductReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ContractProductReportingId")); + + b.Property("ContractId") + .HasColumnType("char(36)"); + + b.Property("ContractProductId") + .HasColumnType("char(36)"); + + b.Property("DisplayText") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.Property("Value") + .HasColumnType("decimal(65,30)"); + + b.HasKey("ContractProductReportingId"); + + b.HasIndex("ContractProductId", "ContractId") + .IsUnique(); + + b.ToTable("contractproduct"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.ContractProductTransactionFee", b => + { + b.Property("ContractProductTransactionFeeReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ContractProductTransactionFeeReportingId")); + + b.Property("CalculationType") + .HasColumnType("int"); + + b.Property("ContractProductId") + .HasColumnType("char(36)"); + + b.Property("ContractProductTransactionFeeId") + .HasColumnType("char(36)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FeeType") + .HasColumnType("int"); + + b.Property("IsEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("Value") + .HasColumnType("decimal(18,4)"); + + b.HasKey("ContractProductTransactionFeeReportingId"); + + b.HasIndex("ContractProductTransactionFeeId", "ContractProductId") + .IsUnique(); + + b.ToTable("contractproducttransactionfee"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Estate", b => + { + b.Property("EstateReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("EstateReportingId")); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Reference") + .HasColumnType("longtext"); + + b.HasKey("EstateReportingId"); + + b.HasIndex("EstateId") + .IsUnique(); + + b.ToTable("estate"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.EstateSecurityUser", b => + { + b.Property("SecurityUserId") + .HasColumnType("char(36)"); + + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("EmailAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("SecurityUserId", "EstateId"); + + b.ToTable("estatesecurityuser"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.File", b => + { + b.Property("FileReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("FileReportingId")); + + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("FileId") + .HasColumnType("char(36)"); + + b.Property("FileImportLogId") + .HasColumnType("char(36)"); + + b.Property("FileLocation") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FileProfileId") + .HasColumnType("char(36)"); + + b.Property("FileReceivedDate") + .HasColumnType("date"); + + b.Property("FileReceivedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("IsCompleted") + .HasColumnType("tinyint(1)"); + + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("FileReportingId"); + + b.HasIndex("FileId") + .IsUnique(); + + b.ToTable("file"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.FileImportLog", b => + { + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("FileImportLogReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("FileImportLogReportingId")); + + b.Property("FileImportLogId") + .HasColumnType("char(36)"); + + b.Property("ImportLogDate") + .HasColumnType("date"); + + b.Property("ImportLogDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("EstateId", "FileImportLogReportingId"); + + b.HasIndex("EstateId", "FileImportLogId") + .IsUnique(); + + b.ToTable("fileimportlog"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.FileImportLogFile", b => + { + b.Property("FileImportLogId") + .HasColumnType("char(36)"); + + b.Property("FileId") + .HasColumnType("char(36)"); + + b.Property("FilePath") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FileProfileId") + .HasColumnType("char(36)"); + + b.Property("FileUploadedDate") + .HasColumnType("date"); + + b.Property("FileUploadedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("OriginalFileName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("FileImportLogId", "FileId"); + + b.ToTable("fileimportlogfile"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.FileLine", b => + { + b.Property("FileId") + .HasColumnType("char(36)"); + + b.Property("LineNumber") + .HasColumnType("int"); + + b.Property("FileLineData") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Status") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TransactionId") + .HasColumnType("char(36)"); + + b.HasKey("FileId", "LineNumber") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("fileline"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Float", b => + { + b.Property("FloatId") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ContractId") + .HasColumnType("char(36)"); + + b.Property("CreatedDate") + .HasColumnType("date"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("ProductId") + .HasColumnType("char(36)"); + + b.HasKey("FloatId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("CreatedDate") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("Floats"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.FloatActivity", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ActivityDate") + .HasColumnType("date"); + + b.Property("ActivityDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CostPrice") + .HasColumnType("decimal(65,30)"); + + b.Property("CreditOrDebit") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FloatId") + .HasColumnType("char(36)"); + + b.HasKey("EventId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("ActivityDate") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("FloatActivity"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Merchant", b => + { + b.Property("MerchantReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("MerchantReportingId")); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("LastSaleDate") + .HasColumnType("date"); + + b.Property("LastSaleDateTime") + .HasColumnType("datetime(6)"); + + b.Property("LastStatementGenerated") + .HasColumnType("datetime(6)"); + + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Reference") + .HasColumnType("longtext"); + + b.Property("SettlementSchedule") + .HasColumnType("int"); + + b.HasKey("MerchantReportingId"); + + b.HasIndex("EstateId", "MerchantId") + .IsUnique(); + + b.ToTable("merchant"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantAddress", b => + { + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("AddressId") + .HasColumnType("char(36)"); + + b.Property("AddressLine1") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AddressLine2") + .HasColumnType("longtext"); + + b.Property("AddressLine3") + .HasColumnType("longtext"); + + b.Property("AddressLine4") + .HasColumnType("longtext"); + + b.Property("Country") + .HasColumnType("longtext"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("PostalCode") + .HasColumnType("longtext"); + + b.Property("Region") + .HasColumnType("longtext"); + + b.Property("Town") + .HasColumnType("longtext"); + + b.HasKey("MerchantId", "AddressId"); + + b.ToTable("merchantaddress"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantContact", b => + { + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("ContactId") + .HasColumnType("char(36)"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("EmailAddress") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.HasKey("MerchantId", "ContactId"); + + b.ToTable("merchantcontact"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantContract", b => + { + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("ContractId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.HasKey("MerchantId", "ContractId"); + + b.ToTable("MerchantContracts"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantDevice", b => + { + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("DeviceId") + .HasColumnType("char(36)"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("DeviceIdentifier") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("MerchantId", "DeviceId"); + + b.ToTable("merchantdevice"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantOperator", b => + { + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("OperatorId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("MerchantNumber") + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TerminalNumber") + .HasColumnType("longtext"); + + b.HasKey("MerchantId", "OperatorId"); + + b.ToTable("merchantoperator"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantSecurityUser", b => + { + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("SecurityUserId") + .HasColumnType("char(36)"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("EmailAddress") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("MerchantId", "SecurityUserId"); + + b.ToTable("merchantsecurityuser"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantSettlementFee", b => + { + b.Property("SettlementId") + .HasColumnType("char(36)"); + + b.Property("TransactionId") + .HasColumnType("char(36)"); + + b.Property("ContractProductTransactionFeeId") + .HasColumnType("char(36)"); + + b.Property("CalculatedValue") + .HasColumnType("decimal(65,30)"); + + b.Property("FeeCalculatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("FeeValue") + .HasColumnType("decimal(65,30)"); + + b.Property("IsSettled") + .HasColumnType("tinyint(1)"); + + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.HasKey("SettlementId", "TransactionId", "ContractProductTransactionFeeId"); + + b.ToTable("merchantsettlementfee"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Operator", b => + { + b.Property("OperatorReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("OperatorReportingId")); + + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OperatorId") + .HasColumnType("char(36)"); + + b.Property("RequireCustomMerchantNumber") + .HasColumnType("tinyint(1)"); + + b.Property("RequireCustomTerminalNumber") + .HasColumnType("tinyint(1)"); + + b.HasKey("OperatorReportingId"); + + b.HasIndex("OperatorId") + .IsUnique(); + + b.ToTable("operator"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Reconciliation", b => + { + b.Property("TransactionReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransactionReportingId")); + + b.Property("DeviceIdentifier") + .HasColumnType("longtext"); + + b.Property("IsAuthorised") + .HasColumnType("tinyint(1)"); + + b.Property("IsCompleted") + .HasColumnType("tinyint(1)"); + + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("ResponseCode") + .HasColumnType("longtext"); + + b.Property("ResponseMessage") + .HasColumnType("longtext"); + + b.Property("TransactionCount") + .HasColumnType("int"); + + b.Property("TransactionDate") + .HasColumnType("date"); + + b.Property("TransactionDateTime") + .HasColumnType("datetime(6)"); + + b.Property("TransactionId") + .HasColumnType("char(36)"); + + b.Property("TransactionTime") + .HasColumnType("time(6)"); + + b.Property("TransactionValue") + .HasColumnType("decimal(65,30)"); + + b.HasKey("TransactionReportingId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("TransactionDate") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("TransactionId", "MerchantId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("reconciliation"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.ResponseCodes", b => + { + b.Property("ResponseCode") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("ResponseCode")); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("ResponseCode"); + + b.ToTable("responsecodes"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Settlement", b => + { + b.Property("SettlementReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("SettlementReportingId")); + + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("IsCompleted") + .HasColumnType("tinyint(1)"); + + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("ProcessingStarted") + .HasColumnType("tinyint(1)"); + + b.Property("ProcessingStartedDateTIme") + .HasColumnType("datetime(6)"); + + b.Property("SettlementDate") + .HasColumnType("date"); + + b.Property("SettlementId") + .HasColumnType("char(36)"); + + b.HasKey("SettlementReportingId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("SettlementDate") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("EstateId", "SettlementId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("settlement"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.StatementHeader", b => + { + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("StatementId") + .HasColumnType("char(36)"); + + b.Property("StatementCreatedDate") + .HasColumnType("date"); + + b.Property("StatementCreatedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("StatementGeneratedDate") + .HasColumnType("date"); + + b.Property("StatementGeneratedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("StatementReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("StatementReportingId")); + + b.HasKey("MerchantId", "StatementId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("StatementGeneratedDate") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("statementheader"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.StatementLine", b => + { + b.Property("StatementId") + .HasColumnType("char(36)"); + + b.Property("TransactionId") + .HasColumnType("char(36)"); + + b.Property("ActivityDateTime") + .HasColumnType("datetime(6)"); + + b.Property("ActivityType") + .HasColumnType("int"); + + b.Property("ActivityDate") + .HasColumnType("date"); + + b.Property("ActivityDescription") + .HasColumnType("longtext"); + + b.Property("InAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("OutAmount") + .HasColumnType("decimal(65,30)"); + + b.HasKey("StatementId", "TransactionId", "ActivityDateTime", "ActivityType"); + + b.ToTable("statementline"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Summary.SettlementSummary", b => + { + b.Property("ContractProductReportingId") + .HasColumnType("int"); + + b.Property("FeeCount") + .HasColumnType("int"); + + b.Property("FeeValue") + .HasColumnType("decimal(65,30)"); + + b.Property("IsCompleted") + .HasColumnType("tinyint(1)"); + + b.Property("IsSettled") + .HasColumnType("tinyint(1)"); + + b.Property("MerchantReportingId") + .HasColumnType("int"); + + b.Property("OperatorReportingId") + .HasColumnType("int"); + + b.Property("SalesCount") + .HasColumnType("int"); + + b.Property("SalesValue") + .HasColumnType("decimal(65,30)"); + + b.Property("SettlementDate") + .HasColumnType("date"); + + b.HasIndex("SettlementDate") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("SettlementDate", "MerchantReportingId", "OperatorReportingId", "ContractProductReportingId", "IsCompleted", "IsSettled") + .IsUnique(); + + b.ToTable("SettlementSummary"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Summary.TodayTransaction", b => + { + b.Property("AuthorisationCode") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ContractProductReportingId") + .HasColumnType("int"); + + b.Property("ContractReportingId") + .HasColumnType("int"); + + b.Property("DeviceIdentifier") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Hour") + .HasColumnType("int"); + + b.Property("IsAuthorised") + .HasColumnType("tinyint(1)"); + + b.Property("IsCompleted") + .HasColumnType("tinyint(1)"); + + b.Property("MerchantReportingId") + .HasColumnType("int"); + + b.Property("OperatorReportingId") + .HasColumnType("int"); + + b.Property("ResponseCode") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ResponseMessage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TransactionAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("TransactionDate") + .HasColumnType("date"); + + b.Property("TransactionDateTime") + .HasColumnType("datetime(6)"); + + b.Property("TransactionId") + .HasColumnType("char(36)"); + + b.Property("TransactionNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TransactionReference") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TransactionReportingId") + .HasColumnType("int"); + + b.Property("TransactionSource") + .HasColumnType("int"); + + b.Property("TransactionTime") + .HasColumnType("time(6)"); + + b.Property("TransactionType") + .IsRequired() + .HasColumnType("longtext"); + + b.HasIndex("TransactionDate") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("TransactionId") + .IsUnique(); + + b.ToTable("TodayTransactions"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Summary.TransactionHistory", b => + { + b.Property("AuthorisationCode") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ContractProductReportingId") + .HasColumnType("int"); + + b.Property("ContractReportingId") + .HasColumnType("int"); + + b.Property("DeviceIdentifier") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Hour") + .HasColumnType("int"); + + b.Property("IsAuthorised") + .HasColumnType("tinyint(1)"); + + b.Property("IsCompleted") + .HasColumnType("tinyint(1)"); + + b.Property("MerchantReportingId") + .HasColumnType("int"); + + b.Property("OperatorReportingId") + .HasColumnType("int"); + + b.Property("ResponseCode") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ResponseMessage") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TransactionAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("TransactionDate") + .HasColumnType("date"); + + b.Property("TransactionDateTime") + .HasColumnType("datetime(6)"); + + b.Property("TransactionId") + .HasColumnType("char(36)"); + + b.Property("TransactionNumber") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TransactionReference") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TransactionReportingId") + .HasColumnType("int"); + + b.Property("TransactionSource") + .HasColumnType("int"); + + b.Property("TransactionTime") + .HasColumnType("time(6)"); + + b.Property("TransactionType") + .IsRequired() + .HasColumnType("longtext"); + + b.HasIndex("TransactionDate") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("TransactionId") + .IsUnique(); + + b.ToTable("TransactionHistory"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Transaction", b => + { + b.Property("TransactionReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("TransactionReportingId")); + + b.Property("AuthorisationCode") + .HasColumnType("longtext"); + + b.Property("ContractId") + .HasColumnType("char(36)"); + + b.Property("ContractProductId") + .HasColumnType("char(36)"); + + b.Property("DeviceIdentifier") + .HasColumnType("longtext"); + + b.Property("IsAuthorised") + .HasColumnType("tinyint(1)"); + + b.Property("IsCompleted") + .HasColumnType("tinyint(1)"); + + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("OperatorId") + .HasColumnType("char(36)"); + + b.Property("ResponseCode") + .HasColumnType("longtext"); + + b.Property("ResponseMessage") + .HasColumnType("longtext"); + + b.Property("TransactionAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("TransactionDate") + .HasColumnType("date"); + + b.Property("TransactionDateTime") + .HasColumnType("datetime(6)"); + + b.Property("TransactionId") + .HasColumnType("char(36)"); + + b.Property("TransactionNumber") + .HasColumnType("longtext"); + + b.Property("TransactionReference") + .HasColumnType("longtext"); + + b.Property("TransactionSource") + .HasColumnType("int"); + + b.Property("TransactionTime") + .HasColumnType("time(6)"); + + b.Property("TransactionType") + .HasColumnType("longtext"); + + b.HasKey("TransactionReportingId") + .HasAnnotation("SqlServer:Clustered", false); + + b.HasIndex("TransactionDate") + .HasAnnotation("SqlServer:Clustered", true); + + b.HasIndex("TransactionId") + .IsUnique() + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("transaction"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.TransactionAdditionalRequestData", b => + { + b.Property("TransactionId") + .HasColumnType("char(36)"); + + b.Property("Amount") + .HasColumnType("longtext"); + + b.Property("CustomerAccountNumber") + .HasColumnType("longtext"); + + b.HasKey("TransactionId"); + + b.ToTable("transactionadditionalrequestdata"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.TransactionAdditionalResponseData", b => + { + b.Property("TransactionId") + .HasColumnType("char(36)"); + + b.Property("TransactionReportingId") + .HasColumnType("int"); + + b.HasKey("TransactionId"); + + b.ToTable("transactionadditionalresponsedata"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.TransactionTimings", b => + { + b.Property("TransactionId") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("OperatorCommunicationsCompletedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("OperatorCommunicationsDurationInMilliseconds") + .HasColumnType("double"); + + b.Property("OperatorCommunicationsStartedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("TotalTransactionInMilliseconds") + .HasColumnType("double"); + + b.Property("TransactionCompletedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("TransactionProcessingDurationInMilliseconds") + .HasColumnType("double"); + + b.Property("TransactionStartedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("TransactionId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("transactiontimings"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.VoucherProjectionState", b => + { + b.Property("VoucherId") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Barcode") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("ExpiryDate") + .HasColumnType("date"); + + b.Property("ExpiryDateTime") + .HasColumnType("datetime(6)"); + + b.Property("GenerateDate") + .HasColumnType("date"); + + b.Property("GenerateDateTime") + .HasColumnType("datetime(6)"); + + b.Property("IsGenerated") + .HasColumnType("tinyint(1)"); + + b.Property("IsIssued") + .HasColumnType("tinyint(1)"); + + b.Property("IsRedeemed") + .HasColumnType("tinyint(1)"); + + b.Property("IssuedDate") + .HasColumnType("date"); + + b.Property("IssuedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("OperatorIdentifier") + .HasColumnType("longtext"); + + b.Property("RecipientEmail") + .HasColumnType("longtext"); + + b.Property("RecipientMobile") + .HasColumnType("longtext"); + + b.Property("RedeemedDate") + .HasColumnType("datetime(6)"); + + b.Property("RedeemedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp(6)"); + + b.Property("TransactionId") + .HasColumnType("char(36)"); + + b.Property("Value") + .HasColumnType("decimal(65,30)"); + + b.Property("VoucherCode") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("VoucherId"); + + b.HasIndex("TransactionId"); + + b.HasIndex("VoucherCode"); + + b.ToTable("voucherprojectionstate"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.ViewEntities.SettlementView", b => + { + b.Property("Amount") + .HasColumnType("decimal(65,30)"); + + b.Property("CalculatedValue") + .HasColumnType("decimal(65,30)"); + + b.Property("DayOfWeek") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("FeeDescription") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsCompleted") + .HasColumnType("tinyint(1)"); + + b.Property("IsSettled") + .HasColumnType("tinyint(1)"); + + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("MerchantName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Month") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("MonthNumber") + .HasColumnType("int"); + + b.Property("OperatorIdentifier") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SettlementDate") + .HasColumnType("datetime(6)"); + + b.Property("SettlementId") + .HasColumnType("char(36)"); + + b.Property("TransactionId") + .HasColumnType("char(36)"); + + b.Property("WeekNumber") + .HasColumnType("int"); + + b.Property("YearNumber") + .HasColumnType("int"); + + b.ToTable((string)null); + + b.ToView("uvwSettlements", (string)null); + }); + + modelBuilder.Entity("TransactionProcessor.ProjectionEngine.Database.Database.Entities.Event", b => + { + b.Property("EventId") + .HasColumnType("char(36)"); + + b.Property("Type") + .HasColumnType("varchar(255)"); + + b.Property("Date") + .HasColumnType("date"); + + b.HasKey("EventId", "Type") + .HasAnnotation("SqlServer:Clustered", true); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("TransactionProcessor.ProjectionEngine.Database.Database.Entities.MerchantBalanceChangedEntry", b => + { + b.Property("AggregateId") + .HasColumnType("char(36)"); + + b.Property("OriginalEventId") + .HasColumnType("char(36)"); + + b.Property("CauseOfChangeId") + .HasColumnType("char(36)"); + + b.Property("ChangeAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("DateTime") + .HasColumnType("datetime(6)"); + + b.Property("DebitOrCredit") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("Reference") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("AggregateId", "OriginalEventId"); + + b.ToTable("MerchantBalanceChangedEntry"); + }); + + modelBuilder.Entity("TransactionProcessor.ProjectionEngine.Database.Database.Entities.MerchantBalanceProjectionState", b => + { + b.Property("EstateId") + .HasColumnType("char(36)"); + + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("AuthorisedSales") + .HasColumnType("decimal(65,30)"); + + b.Property("AvailableBalance") + .HasColumnType("decimal(65,30)"); + + b.Property("Balance") + .HasColumnType("decimal(65,30)"); + + b.Property("CompletedTransactionCount") + .HasColumnType("int"); + + b.Property("DeclinedSales") + .HasColumnType("decimal(65,30)"); + + b.Property("DepositCount") + .HasColumnType("int"); + + b.Property("FeeCount") + .HasColumnType("int"); + + b.Property("LastDeposit") + .HasColumnType("datetime(6)"); + + b.Property("LastFee") + .HasColumnType("datetime(6)"); + + b.Property("LastSale") + .HasColumnType("datetime(6)"); + + b.Property("LastWithdrawal") + .HasColumnType("datetime(6)"); + + b.Property("MerchantName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SaleCount") + .HasColumnType("int"); + + b.Property("StartedTransactionCount") + .HasColumnType("int"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp(6)"); + + b.Property("TotalDeposited") + .HasColumnType("decimal(65,30)"); + + b.Property("TotalWithdrawn") + .HasColumnType("decimal(65,30)"); + + b.Property("ValueOfFees") + .HasColumnType("decimal(65,30)"); + + b.Property("WithdrawalCount") + .HasColumnType("int"); + + b.HasKey("EstateId", "MerchantId"); + + b.ToTable("MerchantBalanceProjectionState"); + }); + + modelBuilder.Entity("TransactionProcessor.ProjectionEngine.Database.Database.ViewEntities.MerchantBalanceHistoryViewEntry", b => + { + b.Property("Balance") + .HasColumnType("decimal(65,30)"); + + b.Property("ChangeAmount") + .HasColumnType("decimal(65,30)"); + + b.Property("DebitOrCredit") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("EntryDateTime") + .HasColumnType("datetime(6)"); + + b.Property("MerchantId") + .HasColumnType("char(36)"); + + b.Property("OriginalEventId") + .HasColumnType("char(36)"); + + b.Property("Reference") + .IsRequired() + .HasColumnType("longtext"); + + b.ToTable((string)null); + + b.ToView("uvwMerchantBalanceHistory", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TransactionProcessor.Database/Migrations/MySql/20250505085317_LoadTransactionTimings.cs b/TransactionProcessor.Database/Migrations/MySql/20250505085317_LoadTransactionTimings.cs new file mode 100644 index 00000000..4dfc839c --- /dev/null +++ b/TransactionProcessor.Database/Migrations/MySql/20250505085317_LoadTransactionTimings.cs @@ -0,0 +1,41 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TransactionProcessor.Database.Migrations.MySql +{ + /// + public partial class LoadTransactionTimings : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "transactiontimings", + columns: table => new + { + TransactionId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + TransactionStartedDateTime = table.Column(type: "datetime(6)", nullable: false), + OperatorCommunicationsStartedDateTime = table.Column(type: "datetime(6)", nullable: true), + OperatorCommunicationsCompletedDateTime = table.Column(type: "datetime(6)", nullable: true), + TransactionCompletedDateTime = table.Column(type: "datetime(6)", nullable: false), + TotalTransactionInMilliseconds = table.Column(type: "double", nullable: false), + OperatorCommunicationsDurationInMilliseconds = table.Column(type: "double", nullable: false), + TransactionProcessingDurationInMilliseconds = table.Column(type: "double", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_transactiontimings", x => x.TransactionId); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "transactiontimings"); + } + } +} diff --git a/TransactionProcessor.Database/Migrations/MySql/EstateManagementMySqlContextModelSnapshot.cs b/TransactionProcessor.Database/Migrations/MySql/EstateManagementMySqlContextModelSnapshot.cs index 05cc0087..f97e671c 100644 --- a/TransactionProcessor.Database/Migrations/MySql/EstateManagementMySqlContextModelSnapshot.cs +++ b/TransactionProcessor.Database/Migrations/MySql/EstateManagementMySqlContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("ProductVersion", "8.0.14") .HasAnnotation("Relational:MaxIdentifierLength", 64); MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); @@ -1165,6 +1165,39 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("transactionadditionalresponsedata"); }); + modelBuilder.Entity("TransactionProcessor.Database.Entities.TransactionTimings", b => + { + b.Property("TransactionId") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("OperatorCommunicationsCompletedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("OperatorCommunicationsDurationInMilliseconds") + .HasColumnType("double"); + + b.Property("OperatorCommunicationsStartedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("TotalTransactionInMilliseconds") + .HasColumnType("double"); + + b.Property("TransactionCompletedDateTime") + .HasColumnType("datetime(6)"); + + b.Property("TransactionProcessingDurationInMilliseconds") + .HasColumnType("double"); + + b.Property("TransactionStartedDateTime") + .HasColumnType("datetime(6)"); + + b.HasKey("TransactionId") + .HasAnnotation("SqlServer:Clustered", false); + + b.ToTable("transactiontimings"); + }); + modelBuilder.Entity("TransactionProcessor.Database.Entities.VoucherProjectionState", b => { b.Property("VoucherId") diff --git a/TransactionProcessor.Database/Migrations/SqlServer/20250505085213_LoadTransactionTimings.Designer.cs b/TransactionProcessor.Database/Migrations/SqlServer/20250505085213_LoadTransactionTimings.Designer.cs new file mode 100644 index 00000000..c6e9d995 --- /dev/null +++ b/TransactionProcessor.Database/Migrations/SqlServer/20250505085213_LoadTransactionTimings.Designer.cs @@ -0,0 +1,1527 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TransactionProcessor.Database.Contexts; + +#nullable disable + +namespace TransactionProcessor.Database.Migrations.SqlServer +{ + [DbContext(typeof(EstateManagementSqlServerContext))] + [Migration("20250505085213_LoadTransactionTimings")] + partial class LoadTransactionTimings + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Calendar", b => + { + b.Property("Date") + .HasColumnType("datetime2"); + + b.Property("DayOfWeek") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DayOfWeekNumber") + .HasColumnType("int"); + + b.Property("DayOfWeekShort") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MonthNameLong") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MonthNameShort") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MonthNumber") + .HasColumnType("int"); + + b.Property("WeekNumber") + .HasColumnType("int"); + + b.Property("WeekNumberString") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Year") + .HasColumnType("int"); + + b.Property("YearWeekNumber") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Date"); + + b.ToTable("calendar"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Contract", b => + { + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("OperatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("ContractId") + .HasColumnType("uniqueidentifier"); + + b.Property("ContractReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ContractReportingId")); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("EstateId", "OperatorId", "ContractId"); + + b.ToTable("contract"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.ContractProduct", b => + { + b.Property("ContractProductReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ContractProductReportingId")); + + b.Property("ContractId") + .HasColumnType("uniqueidentifier"); + + b.Property("ContractProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("DisplayText") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProductName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProductType") + .HasColumnType("int"); + + b.Property("Value") + .HasColumnType("decimal(18,2)"); + + b.HasKey("ContractProductReportingId"); + + b.HasIndex("ContractProductId", "ContractId") + .IsUnique(); + + b.ToTable("contractproduct"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.ContractProductTransactionFee", b => + { + b.Property("ContractProductTransactionFeeReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ContractProductTransactionFeeReportingId")); + + b.Property("CalculationType") + .HasColumnType("int"); + + b.Property("ContractProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("ContractProductTransactionFeeId") + .HasColumnType("uniqueidentifier"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FeeType") + .HasColumnType("int"); + + b.Property("IsEnabled") + .HasColumnType("bit"); + + b.Property("Value") + .HasColumnType("decimal(18,4)"); + + b.HasKey("ContractProductTransactionFeeReportingId"); + + b.HasIndex("ContractProductTransactionFeeId", "ContractProductId") + .IsUnique(); + + b.ToTable("contractproducttransactionfee"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Estate", b => + { + b.Property("EstateReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("EstateReportingId")); + + b.Property("CreatedDateTime") + .HasColumnType("datetime2"); + + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Reference") + .HasColumnType("nvarchar(max)"); + + b.HasKey("EstateReportingId"); + + b.HasIndex("EstateId") + .IsUnique(); + + b.ToTable("estate"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.EstateSecurityUser", b => + { + b.Property("SecurityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime2"); + + b.Property("EmailAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("SecurityUserId", "EstateId"); + + b.ToTable("estatesecurityuser"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.File", b => + { + b.Property("FileReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("FileReportingId")); + + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileImportLogId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileLocation") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileProfileId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileReceivedDate") + .HasColumnType("date"); + + b.Property("FileReceivedDateTime") + .HasColumnType("datetime2"); + + b.Property("IsCompleted") + .HasColumnType("bit"); + + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("FileReportingId"); + + b.HasIndex("FileId") + .IsUnique(); + + b.ToTable("file"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.FileImportLog", b => + { + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileImportLogReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("FileImportLogReportingId")); + + b.Property("FileImportLogId") + .HasColumnType("uniqueidentifier"); + + b.Property("ImportLogDate") + .HasColumnType("date"); + + b.Property("ImportLogDateTime") + .HasColumnType("datetime2"); + + b.HasKey("EstateId", "FileImportLogReportingId"); + + b.HasIndex("EstateId", "FileImportLogId") + .IsUnique(); + + b.ToTable("fileimportlog"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.FileImportLogFile", b => + { + b.Property("FileImportLogId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileId") + .HasColumnType("uniqueidentifier"); + + b.Property("FilePath") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileProfileId") + .HasColumnType("uniqueidentifier"); + + b.Property("FileUploadedDate") + .HasColumnType("date"); + + b.Property("FileUploadedDateTime") + .HasColumnType("datetime2"); + + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("OriginalFileName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("FileImportLogId", "FileId"); + + b.ToTable("fileimportlogfile"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.FileLine", b => + { + b.Property("FileId") + .HasColumnType("uniqueidentifier"); + + b.Property("LineNumber") + .HasColumnType("int"); + + b.Property("FileLineData") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("FileId", "LineNumber"); + + SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("FileId", "LineNumber")); + + b.ToTable("fileline"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Float", b => + { + b.Property("FloatId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ContractId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDate") + .HasColumnType("date"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime2"); + + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("FloatId"); + + SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("FloatId"), false); + + b.HasIndex("CreatedDate"); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("CreatedDate")); + + b.ToTable("Floats"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.FloatActivity", b => + { + b.Property("EventId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ActivityDate") + .HasColumnType("date"); + + b.Property("ActivityDateTime") + .HasColumnType("datetime2"); + + b.Property("Amount") + .HasColumnType("decimal(18,2)"); + + b.Property("CostPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("CreditOrDebit") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FloatId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("EventId"); + + SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("EventId"), false); + + b.HasIndex("ActivityDate"); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("ActivityDate")); + + b.ToTable("FloatActivity"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Merchant", b => + { + b.Property("MerchantReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("MerchantReportingId")); + + b.Property("CreatedDateTime") + .HasColumnType("datetime2"); + + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("LastSaleDate") + .HasColumnType("date"); + + b.Property("LastSaleDateTime") + .HasColumnType("datetime2"); + + b.Property("LastStatementGenerated") + .HasColumnType("datetime2"); + + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Reference") + .HasColumnType("nvarchar(max)"); + + b.Property("SettlementSchedule") + .HasColumnType("int"); + + b.HasKey("MerchantReportingId"); + + b.HasIndex("EstateId", "MerchantId") + .IsUnique(); + + b.ToTable("merchant"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantAddress", b => + { + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("AddressId") + .HasColumnType("uniqueidentifier"); + + b.Property("AddressLine1") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AddressLine2") + .HasColumnType("nvarchar(max)"); + + b.Property("AddressLine3") + .HasColumnType("nvarchar(max)"); + + b.Property("AddressLine4") + .HasColumnType("nvarchar(max)"); + + b.Property("Country") + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime2"); + + b.Property("PostalCode") + .HasColumnType("nvarchar(max)"); + + b.Property("Region") + .HasColumnType("nvarchar(max)"); + + b.Property("Town") + .HasColumnType("nvarchar(max)"); + + b.HasKey("MerchantId", "AddressId"); + + b.ToTable("merchantaddress"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantContact", b => + { + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("ContactId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime2"); + + b.Property("EmailAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.HasKey("MerchantId", "ContactId"); + + b.ToTable("merchantcontact"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantContract", b => + { + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("ContractId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.HasKey("MerchantId", "ContractId"); + + b.ToTable("MerchantContracts"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantDevice", b => + { + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeviceId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime2"); + + b.Property("DeviceIdentifier") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("MerchantId", "DeviceId"); + + b.ToTable("merchantdevice"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantOperator", b => + { + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("OperatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("MerchantNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TerminalNumber") + .HasColumnType("nvarchar(max)"); + + b.HasKey("MerchantId", "OperatorId"); + + b.ToTable("merchantoperator"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantSecurityUser", b => + { + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("SecurityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedDateTime") + .HasColumnType("datetime2"); + + b.Property("EmailAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("MerchantId", "SecurityUserId"); + + b.ToTable("merchantsecurityuser"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.MerchantSettlementFee", b => + { + b.Property("SettlementId") + .HasColumnType("uniqueidentifier"); + + b.Property("TransactionId") + .HasColumnType("uniqueidentifier"); + + b.Property("ContractProductTransactionFeeId") + .HasColumnType("uniqueidentifier"); + + b.Property("CalculatedValue") + .HasColumnType("decimal(18,2)"); + + b.Property("FeeCalculatedDateTime") + .HasColumnType("datetime2"); + + b.Property("FeeValue") + .HasColumnType("decimal(18,2)"); + + b.Property("IsSettled") + .HasColumnType("bit"); + + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("SettlementId", "TransactionId", "ContractProductTransactionFeeId"); + + b.ToTable("merchantsettlementfee"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Operator", b => + { + b.Property("OperatorReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("OperatorReportingId")); + + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OperatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("RequireCustomMerchantNumber") + .HasColumnType("bit"); + + b.Property("RequireCustomTerminalNumber") + .HasColumnType("bit"); + + b.HasKey("OperatorReportingId"); + + b.HasIndex("OperatorId") + .IsUnique(); + + b.ToTable("operator"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Reconciliation", b => + { + b.Property("TransactionReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("TransactionReportingId")); + + b.Property("DeviceIdentifier") + .HasColumnType("nvarchar(max)"); + + b.Property("IsAuthorised") + .HasColumnType("bit"); + + b.Property("IsCompleted") + .HasColumnType("bit"); + + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("ResponseCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ResponseMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionCount") + .HasColumnType("int"); + + b.Property("TransactionDate") + .HasColumnType("date"); + + b.Property("TransactionDateTime") + .HasColumnType("datetime2"); + + b.Property("TransactionId") + .HasColumnType("uniqueidentifier"); + + b.Property("TransactionTime") + .HasColumnType("time"); + + b.Property("TransactionValue") + .HasColumnType("decimal(18,2)"); + + b.HasKey("TransactionReportingId"); + + SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("TransactionReportingId"), false); + + b.HasIndex("TransactionDate"); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("TransactionDate")); + + b.HasIndex("TransactionId", "MerchantId") + .IsUnique(); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("TransactionId", "MerchantId"), false); + + b.ToTable("reconciliation"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.ResponseCodes", b => + { + b.Property("ResponseCode") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ResponseCode")); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ResponseCode"); + + b.ToTable("responsecodes"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Settlement", b => + { + b.Property("SettlementReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("SettlementReportingId")); + + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsCompleted") + .HasColumnType("bit"); + + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProcessingStarted") + .HasColumnType("bit"); + + b.Property("ProcessingStartedDateTIme") + .HasColumnType("datetime2"); + + b.Property("SettlementDate") + .HasColumnType("date"); + + b.Property("SettlementId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("SettlementReportingId"); + + SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("SettlementReportingId"), false); + + b.HasIndex("SettlementDate"); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("SettlementDate")); + + b.HasIndex("EstateId", "SettlementId") + .IsUnique(); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("EstateId", "SettlementId"), false); + + b.ToTable("settlement"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.StatementHeader", b => + { + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("StatementId") + .HasColumnType("uniqueidentifier"); + + b.Property("StatementCreatedDate") + .HasColumnType("date"); + + b.Property("StatementCreatedDateTime") + .HasColumnType("datetime2"); + + b.Property("StatementGeneratedDate") + .HasColumnType("date"); + + b.Property("StatementGeneratedDateTime") + .HasColumnType("datetime2"); + + b.Property("StatementReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("StatementReportingId")); + + b.HasKey("MerchantId", "StatementId"); + + SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("MerchantId", "StatementId"), false); + + b.HasIndex("StatementGeneratedDate"); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("StatementGeneratedDate")); + + b.ToTable("statementheader"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.StatementLine", b => + { + b.Property("StatementId") + .HasColumnType("uniqueidentifier"); + + b.Property("TransactionId") + .HasColumnType("uniqueidentifier"); + + b.Property("ActivityDateTime") + .HasColumnType("datetime2"); + + b.Property("ActivityType") + .HasColumnType("int"); + + b.Property("ActivityDate") + .HasColumnType("date"); + + b.Property("ActivityDescription") + .HasColumnType("nvarchar(max)"); + + b.Property("InAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("OutAmount") + .HasColumnType("decimal(18,2)"); + + b.HasKey("StatementId", "TransactionId", "ActivityDateTime", "ActivityType"); + + b.ToTable("statementline"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Summary.SettlementSummary", b => + { + b.Property("ContractProductReportingId") + .HasColumnType("int"); + + b.Property("FeeCount") + .HasColumnType("int"); + + b.Property("FeeValue") + .HasColumnType("decimal(18,2)"); + + b.Property("IsCompleted") + .HasColumnType("bit"); + + b.Property("IsSettled") + .HasColumnType("bit"); + + b.Property("MerchantReportingId") + .HasColumnType("int"); + + b.Property("OperatorReportingId") + .HasColumnType("int"); + + b.Property("SalesCount") + .HasColumnType("int"); + + b.Property("SalesValue") + .HasColumnType("decimal(18,2)"); + + b.Property("SettlementDate") + .HasColumnType("date"); + + b.HasIndex("SettlementDate"); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("SettlementDate")); + + b.HasIndex("SettlementDate", "MerchantReportingId", "OperatorReportingId", "ContractProductReportingId", "IsCompleted", "IsSettled") + .IsUnique(); + + b.ToTable("SettlementSummary"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Summary.TodayTransaction", b => + { + b.Property("AuthorisationCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ContractProductReportingId") + .HasColumnType("int"); + + b.Property("ContractReportingId") + .HasColumnType("int"); + + b.Property("DeviceIdentifier") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Hour") + .HasColumnType("int"); + + b.Property("IsAuthorised") + .HasColumnType("bit"); + + b.Property("IsCompleted") + .HasColumnType("bit"); + + b.Property("MerchantReportingId") + .HasColumnType("int"); + + b.Property("OperatorReportingId") + .HasColumnType("int"); + + b.Property("ResponseCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ResponseMessage") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("TransactionDate") + .HasColumnType("date"); + + b.Property("TransactionDateTime") + .HasColumnType("datetime2"); + + b.Property("TransactionId") + .HasColumnType("uniqueidentifier"); + + b.Property("TransactionNumber") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionReference") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionReportingId") + .HasColumnType("int"); + + b.Property("TransactionSource") + .HasColumnType("int"); + + b.Property("TransactionTime") + .HasColumnType("time"); + + b.Property("TransactionType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasIndex("TransactionDate"); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("TransactionDate")); + + b.HasIndex("TransactionId") + .IsUnique(); + + b.ToTable("TodayTransactions"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Summary.TransactionHistory", b => + { + b.Property("AuthorisationCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ContractProductReportingId") + .HasColumnType("int"); + + b.Property("ContractReportingId") + .HasColumnType("int"); + + b.Property("DeviceIdentifier") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Hour") + .HasColumnType("int"); + + b.Property("IsAuthorised") + .HasColumnType("bit"); + + b.Property("IsCompleted") + .HasColumnType("bit"); + + b.Property("MerchantReportingId") + .HasColumnType("int"); + + b.Property("OperatorReportingId") + .HasColumnType("int"); + + b.Property("ResponseCode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ResponseMessage") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("TransactionDate") + .HasColumnType("date"); + + b.Property("TransactionDateTime") + .HasColumnType("datetime2"); + + b.Property("TransactionId") + .HasColumnType("uniqueidentifier"); + + b.Property("TransactionNumber") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionReference") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionReportingId") + .HasColumnType("int"); + + b.Property("TransactionSource") + .HasColumnType("int"); + + b.Property("TransactionTime") + .HasColumnType("time"); + + b.Property("TransactionType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasIndex("TransactionDate"); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("TransactionDate")); + + b.HasIndex("TransactionId") + .IsUnique(); + + b.ToTable("TransactionHistory"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.Transaction", b => + { + b.Property("TransactionReportingId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("TransactionReportingId")); + + b.Property("AuthorisationCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ContractId") + .HasColumnType("uniqueidentifier"); + + b.Property("ContractProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("DeviceIdentifier") + .HasColumnType("nvarchar(max)"); + + b.Property("IsAuthorised") + .HasColumnType("bit"); + + b.Property("IsCompleted") + .HasColumnType("bit"); + + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("OperatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("ResponseCode") + .HasColumnType("nvarchar(max)"); + + b.Property("ResponseMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("TransactionDate") + .HasColumnType("date"); + + b.Property("TransactionDateTime") + .HasColumnType("datetime2"); + + b.Property("TransactionId") + .HasColumnType("uniqueidentifier"); + + b.Property("TransactionNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionReference") + .HasColumnType("nvarchar(max)"); + + b.Property("TransactionSource") + .HasColumnType("int"); + + b.Property("TransactionTime") + .HasColumnType("time"); + + b.Property("TransactionType") + .HasColumnType("nvarchar(max)"); + + b.HasKey("TransactionReportingId"); + + SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("TransactionReportingId"), false); + + b.HasIndex("TransactionDate"); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("TransactionDate")); + + b.HasIndex("TransactionId") + .IsUnique(); + + SqlServerIndexBuilderExtensions.IsClustered(b.HasIndex("TransactionId"), false); + + b.ToTable("transaction"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.TransactionAdditionalRequestData", b => + { + b.Property("TransactionId") + .HasColumnType("uniqueidentifier"); + + b.Property("Amount") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerAccountNumber") + .HasColumnType("nvarchar(max)"); + + b.HasKey("TransactionId"); + + b.ToTable("transactionadditionalrequestdata"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.TransactionAdditionalResponseData", b => + { + b.Property("TransactionId") + .HasColumnType("uniqueidentifier"); + + b.Property("TransactionReportingId") + .HasColumnType("int"); + + b.HasKey("TransactionId"); + + b.ToTable("transactionadditionalresponsedata"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.TransactionTimings", b => + { + b.Property("TransactionId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("OperatorCommunicationsCompletedDateTime") + .HasColumnType("datetime2"); + + b.Property("OperatorCommunicationsDurationInMilliseconds") + .HasColumnType("float"); + + b.Property("OperatorCommunicationsStartedDateTime") + .HasColumnType("datetime2"); + + b.Property("TotalTransactionInMilliseconds") + .HasColumnType("float"); + + b.Property("TransactionCompletedDateTime") + .HasColumnType("datetime2"); + + b.Property("TransactionProcessingDurationInMilliseconds") + .HasColumnType("float"); + + b.Property("TransactionStartedDateTime") + .HasColumnType("datetime2"); + + b.HasKey("TransactionId"); + + SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("TransactionId"), false); + + b.ToTable("transactiontimings"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.Entities.VoucherProjectionState", b => + { + b.Property("VoucherId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Barcode") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("ExpiryDate") + .HasColumnType("date"); + + b.Property("ExpiryDateTime") + .HasColumnType("datetime2"); + + b.Property("GenerateDate") + .HasColumnType("date"); + + b.Property("GenerateDateTime") + .HasColumnType("datetime2"); + + b.Property("IsGenerated") + .HasColumnType("bit"); + + b.Property("IsIssued") + .HasColumnType("bit"); + + b.Property("IsRedeemed") + .HasColumnType("bit"); + + b.Property("IssuedDate") + .HasColumnType("date"); + + b.Property("IssuedDateTime") + .HasColumnType("datetime2"); + + b.Property("OperatorIdentifier") + .HasColumnType("nvarchar(max)"); + + b.Property("RecipientEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("RecipientMobile") + .HasColumnType("nvarchar(max)"); + + b.Property("RedeemedDate") + .HasColumnType("datetime2"); + + b.Property("RedeemedDateTime") + .HasColumnType("datetime2"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.Property("TransactionId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .HasColumnType("decimal(18,2)"); + + b.Property("VoucherCode") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("VoucherId"); + + b.HasIndex("TransactionId"); + + b.HasIndex("VoucherCode"); + + b.ToTable("voucherprojectionstate"); + }); + + modelBuilder.Entity("TransactionProcessor.Database.ViewEntities.SettlementView", b => + { + b.Property("Amount") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedValue") + .HasColumnType("decimal(18,2)"); + + b.Property("DayOfWeek") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("FeeDescription") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsCompleted") + .HasColumnType("bit"); + + b.Property("IsSettled") + .HasColumnType("bit"); + + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("MerchantName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Month") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MonthNumber") + .HasColumnType("int"); + + b.Property("OperatorIdentifier") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SettlementDate") + .HasColumnType("datetime2"); + + b.Property("SettlementId") + .HasColumnType("uniqueidentifier"); + + b.Property("TransactionId") + .HasColumnType("uniqueidentifier"); + + b.Property("WeekNumber") + .HasColumnType("int"); + + b.Property("YearNumber") + .HasColumnType("int"); + + b.ToTable((string)null); + + b.ToView("uvwSettlements", (string)null); + }); + + modelBuilder.Entity("TransactionProcessor.ProjectionEngine.Database.Database.Entities.Event", b => + { + b.Property("EventId") + .HasColumnType("uniqueidentifier"); + + b.Property("Type") + .HasColumnType("nvarchar(450)"); + + b.Property("Date") + .HasColumnType("date"); + + b.HasKey("EventId", "Type"); + + SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("EventId", "Type")); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("TransactionProcessor.ProjectionEngine.Database.Database.Entities.MerchantBalanceChangedEntry", b => + { + b.Property("AggregateId") + .HasColumnType("uniqueidentifier"); + + b.Property("OriginalEventId") + .HasColumnType("uniqueidentifier"); + + b.Property("CauseOfChangeId") + .HasColumnType("uniqueidentifier"); + + b.Property("ChangeAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("DateTime") + .HasColumnType("datetime2"); + + b.Property("DebitOrCredit") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("Reference") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("AggregateId", "OriginalEventId"); + + b.ToTable("MerchantBalanceChangedEntry"); + }); + + modelBuilder.Entity("TransactionProcessor.ProjectionEngine.Database.Database.Entities.MerchantBalanceProjectionState", b => + { + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("AuthorisedSales") + .HasColumnType("decimal(18,2)"); + + b.Property("AvailableBalance") + .HasColumnType("decimal(18,2)"); + + b.Property("Balance") + .HasColumnType("decimal(18,2)"); + + b.Property("CompletedTransactionCount") + .HasColumnType("int"); + + b.Property("DeclinedSales") + .HasColumnType("decimal(18,2)"); + + b.Property("DepositCount") + .HasColumnType("int"); + + b.Property("FeeCount") + .HasColumnType("int"); + + b.Property("LastDeposit") + .HasColumnType("datetime2"); + + b.Property("LastFee") + .HasColumnType("datetime2"); + + b.Property("LastSale") + .HasColumnType("datetime2"); + + b.Property("LastWithdrawal") + .HasColumnType("datetime2"); + + b.Property("MerchantName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SaleCount") + .HasColumnType("int"); + + b.Property("StartedTransactionCount") + .HasColumnType("int"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.Property("TotalDeposited") + .HasColumnType("decimal(18,2)"); + + b.Property("TotalWithdrawn") + .HasColumnType("decimal(18,2)"); + + b.Property("ValueOfFees") + .HasColumnType("decimal(18,2)"); + + b.Property("WithdrawalCount") + .HasColumnType("int"); + + b.HasKey("EstateId", "MerchantId"); + + b.ToTable("MerchantBalanceProjectionState"); + }); + + modelBuilder.Entity("TransactionProcessor.ProjectionEngine.Database.Database.ViewEntities.MerchantBalanceHistoryViewEntry", b => + { + b.Property("Balance") + .HasColumnType("decimal(18,2)"); + + b.Property("ChangeAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("DebitOrCredit") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EntryDateTime") + .HasColumnType("datetime2"); + + b.Property("MerchantId") + .HasColumnType("uniqueidentifier"); + + b.Property("OriginalEventId") + .HasColumnType("uniqueidentifier"); + + b.Property("Reference") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.ToTable((string)null); + + b.ToView("uvwMerchantBalanceHistory", (string)null); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TransactionProcessor.Database/Migrations/SqlServer/20250505085213_LoadTransactionTimings.cs b/TransactionProcessor.Database/Migrations/SqlServer/20250505085213_LoadTransactionTimings.cs new file mode 100644 index 00000000..a4a5868e --- /dev/null +++ b/TransactionProcessor.Database/Migrations/SqlServer/20250505085213_LoadTransactionTimings.cs @@ -0,0 +1,41 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TransactionProcessor.Database.Migrations.SqlServer +{ + /// + public partial class LoadTransactionTimings : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "transactiontimings", + columns: table => new + { + TransactionId = table.Column(type: "uniqueidentifier", nullable: false), + TransactionStartedDateTime = table.Column(type: "datetime2", nullable: false), + OperatorCommunicationsStartedDateTime = table.Column(type: "datetime2", nullable: true), + OperatorCommunicationsCompletedDateTime = table.Column(type: "datetime2", nullable: true), + TransactionCompletedDateTime = table.Column(type: "datetime2", nullable: false), + TotalTransactionInMilliseconds = table.Column(type: "float", nullable: false), + OperatorCommunicationsDurationInMilliseconds = table.Column(type: "float", nullable: false), + TransactionProcessingDurationInMilliseconds = table.Column(type: "float", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_transactiontimings", x => x.TransactionId) + .Annotation("SqlServer:Clustered", false); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "transactiontimings"); + } + } +} diff --git a/TransactionProcessor.Database/Migrations/SqlServer/EstateManagementSqlServerContextModelSnapshot.cs b/TransactionProcessor.Database/Migrations/SqlServer/EstateManagementSqlServerContextModelSnapshot.cs index d53b41a8..53c6ae78 100644 --- a/TransactionProcessor.Database/Migrations/SqlServer/EstateManagementSqlServerContextModelSnapshot.cs +++ b/TransactionProcessor.Database/Migrations/SqlServer/EstateManagementSqlServerContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("ProductVersion", "8.0.14") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -1184,6 +1184,40 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("transactionadditionalresponsedata"); }); + modelBuilder.Entity("TransactionProcessor.Database.Entities.TransactionTimings", b => + { + b.Property("TransactionId") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("OperatorCommunicationsCompletedDateTime") + .HasColumnType("datetime2"); + + b.Property("OperatorCommunicationsDurationInMilliseconds") + .HasColumnType("float"); + + b.Property("OperatorCommunicationsStartedDateTime") + .HasColumnType("datetime2"); + + b.Property("TotalTransactionInMilliseconds") + .HasColumnType("float"); + + b.Property("TransactionCompletedDateTime") + .HasColumnType("datetime2"); + + b.Property("TransactionProcessingDurationInMilliseconds") + .HasColumnType("float"); + + b.Property("TransactionStartedDateTime") + .HasColumnType("datetime2"); + + b.HasKey("TransactionId"); + + SqlServerKeyBuilderExtensions.IsClustered(b.HasKey("TransactionId"), false); + + b.ToTable("transactiontimings"); + }); + modelBuilder.Entity("TransactionProcessor.Database.Entities.VoucherProjectionState", b => { b.Property("VoucherId") diff --git a/TransactionProcessor.DomainEvents/TransactionDomainEvents.cs b/TransactionProcessor.DomainEvents/TransactionDomainEvents.cs index 420aadc2..837edc47 100644 --- a/TransactionProcessor.DomainEvents/TransactionDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/TransactionDomainEvents.cs @@ -21,4 +21,7 @@ public record TransactionHasBeenLocallyAuthorisedEvent(Guid TransactionId, Guid public record TransactionHasBeenLocallyDeclinedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, String ResponseCode, String ResponseMessage, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); public record TransactionHasStartedEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, DateTime TransactionDateTime, String TransactionNumber, String TransactionType, String TransactionReference, String DeviceIdentifier, Decimal? TransactionAmount) : DomainEvent(TransactionId, Guid.NewGuid()); public record TransactionSourceAddedToTransactionEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, Int32 TransactionSource, DateTime TransactionDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); + + public record TransactionTimingsAddedToTransactionEvent(Guid TransactionId, Guid EstateId, Guid MerchantId, DateTime TransactionStartedDateTime, DateTime? OperatorCommunicationsStartedEvent, + DateTime? OperatorCommunicationsCompletedEvent, DateTime TransactionCompletedDateTime) : DomainEvent(TransactionId, Guid.NewGuid()); } \ No newline at end of file diff --git a/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature b/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature index bbffc3aa..b923a6e4 100644 --- a/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature +++ b/TransactionProcessor.IntegrationTests/Features/SaleTransactionFeature.feature @@ -263,6 +263,4 @@ Scenario: Sale Transactions Then transaction response should contain the following information | EstateName | MerchantName | TransactionNumber | ResponseCode | ResponseMessage | - | Test Estate 1 | Test Merchant 4 | 19 | 1009 | Merchant [Test Merchant 4] does not have enough credit available [100.00] to perform transaction amount [300.00] | - - \ No newline at end of file + | Test Estate 1 | Test Merchant 4 | 19 | 1009 | Merchant [Test Merchant 4] does not have enough credit available [100.00] to perform transaction amount [300.00] | \ No newline at end of file diff --git a/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs b/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs index e9563f07..7971355a 100644 --- a/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs +++ b/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs @@ -12,9 +12,12 @@ using TransactionProcessor.Database.ViewEntities; using TransactionProcessor.DomainEvents; using TransactionProcessor.Models.Contract; +using TransactionProcessor.Models.Estate; using TransactionProcessor.Models.Settlement; using static TransactionProcessor.DomainEvents.MerchantDomainEvents; using static TransactionProcessor.DomainEvents.MerchantStatementDomainEvents; +using Estate = TransactionProcessor.Database.Entities.Estate; +using Operator = TransactionProcessor.Database.Entities.Operator; using Contract = TransactionProcessor.Database.Entities.Contract; using ContractModel = TransactionProcessor.Models.Contract.Contract; using ContractProductTransactionFee = TransactionProcessor.Database.Entities.ContractProductTransactionFee; @@ -292,6 +295,9 @@ Task>> GetSettlements(Guid estateId, String startDate, String endDate, CancellationToken cancellationToken); + + Task RecordTransactionTimings(TransactionDomainEvents.TransactionTimingsAddedToTransactionEvent de, + CancellationToken cancellationToken); } [ExcludeFromCodeCoverage] @@ -2054,5 +2060,31 @@ public async Task>> GetSettlements(Guid estateId, return Result.Success(result); } + + public async Task RecordTransactionTimings(TransactionDomainEvents.TransactionTimingsAddedToTransactionEvent domainEvent, + CancellationToken cancellationToken) { + // Calculate the timings for the transaction + TimeSpan totalTime = domainEvent.TransactionCompletedDateTime.Subtract(domainEvent.TransactionStartedDateTime); + // Calculte the timings for the operator communications + TimeSpan operatorCommunicationsTime = domainEvent.OperatorCommunicationsCompletedEvent?.Subtract(domainEvent.OperatorCommunicationsStartedEvent ?? DateTime.MinValue) ?? TimeSpan.Zero; + // Calculate the timings for the transaction without operator timings + TimeSpan transactionTime = totalTime.Subtract(operatorCommunicationsTime); + + // Load this information to the database + EstateManagementGenericContext context = await this.DbContextFactory.GetContext(domainEvent.EstateId, ConnectionStringIdentifier, cancellationToken); + + TransactionTimings timings = new() { + TransactionStartedDateTime = domainEvent.TransactionStartedDateTime, + TransactionCompletedDateTime = domainEvent.TransactionCompletedDateTime, + OperatorCommunicationsCompletedDateTime = domainEvent.OperatorCommunicationsCompletedEvent, + OperatorCommunicationsStartedDateTime = domainEvent.OperatorCommunicationsStartedEvent, + TransactionId = domainEvent.TransactionId, + OperatorCommunicationsDurationInMilliseconds = operatorCommunicationsTime.TotalMilliseconds, + TotalTransactionInMilliseconds = totalTime.TotalMilliseconds, + TransactionProcessingDurationInMilliseconds = transactionTime.TotalMilliseconds + }; + await context.TransactionTimings.AddAsync(timings, cancellationToken); + return await context.SaveChangesWithDuplicateHandling(cancellationToken); + } } } diff --git a/TransactionProcessor.Testing/TestData.cs b/TransactionProcessor.Testing/TestData.cs index 3bd10a83..73d0ec0f 100644 --- a/TransactionProcessor.Testing/TestData.cs +++ b/TransactionProcessor.Testing/TestData.cs @@ -408,6 +408,8 @@ public class TestData public static String TransactionNumber = "0001"; + public static DateTime TransactionReceivedDateTime; + public static TransactionType TransactionTypeLogon = TransactionType.Logon; public static String TransactionReference = "ABCDEFGHI"; @@ -2116,7 +2118,8 @@ public static class Commands { TestData.AdditionalTransactionMetaDataForMobileTopup(), TestData.ContractId, TestData.ProductId, - TestData.TransactionSource); + TestData.TransactionSource, + TestData.TransactionReceivedDateTime); public static TransactionCommands.ProcessReconciliationCommand ProcessReconciliationCommand => new(TestData.TransactionId, TestData.EstateId, TestData.MerchantId, TestData.DeviceIdentifier, TestData.TransactionDateTime, TestData.ReconciliationTransactionCount, TestData.ReconciliationTransactionValue); public static TransactionCommands.ProcessLogonTransactionCommand ProcessLogonTransactionCommand => @@ -2126,7 +2129,8 @@ public static class Commands { TestData.DeviceIdentifier, TestData.TransactionTypeLogon.ToString(), TestData.TransactionDateTime, - TestData.TransactionNumber); + TestData.TransactionNumber, + TestData.TransactionReceivedDateTime); public static SettlementCommands.ProcessSettlementCommand ProcessSettlementCommand => new(TestData.SettlementDate, diff --git a/TransactionProcessor/Controllers/TransactionController.cs b/TransactionProcessor/Controllers/TransactionController.cs index 9fbf3d56..4aa1adff 100644 --- a/TransactionProcessor/Controllers/TransactionController.cs +++ b/TransactionProcessor/Controllers/TransactionController.cs @@ -69,6 +69,8 @@ public TransactionController(IMediator mediator) public async Task PerformTransaction([FromBody] SerialisedMessage transactionRequest, CancellationToken cancellationToken) { + DateTime transactionReceivedDateTime = DateTime.Now; + // Reject password tokens if (ClaimsHelper.IsPasswordToken(this.User)) { return this.Forbid(); @@ -91,8 +93,8 @@ public async Task PerformTransaction([FromBody] SerialisedMessage } Result transactionResult = dto switch { - LogonTransactionRequest ltr => await this.ProcessSpecificMessage(ltr, cancellationToken), - SaleTransactionRequest str => await this.ProcessSpecificMessage(str, cancellationToken), + LogonTransactionRequest ltr => await this.ProcessSpecificMessage(ltr, transactionReceivedDateTime, cancellationToken), + SaleTransactionRequest str => await this.ProcessSpecificMessage(str, transactionReceivedDateTime,cancellationToken), ReconciliationRequest rr => await this.ProcessSpecificMessage(rr, cancellationToken), _ => Result.Invalid($"DTO Type {dto.GetType().Name} not supported)") }; @@ -127,6 +129,7 @@ public async Task ResendTransactionReceipt([FromRoute] Guid estat /// The cancellation token. /// private async Task> ProcessSpecificMessage(LogonTransactionRequest logonTransactionRequest, + DateTime transactionReceivedDateTime, CancellationToken cancellationToken) { Guid transactionId = Guid.NewGuid(); @@ -137,7 +140,8 @@ private async Task> ProcessSpecificMessage(LogonTransa logonTransactionRequest.DeviceIdentifier, logonTransactionRequest.TransactionType, logonTransactionRequest.TransactionDateTime, - logonTransactionRequest.TransactionNumber); + logonTransactionRequest.TransactionNumber, + transactionReceivedDateTime); var result = await this.Mediator.Send(command, cancellationToken); if (result.IsFailed) @@ -153,6 +157,7 @@ private async Task> ProcessSpecificMessage(LogonTransa /// The cancellation token. /// private async Task> ProcessSpecificMessage(SaleTransactionRequest saleTransactionRequest, + DateTime transactionReceivedDateTime, CancellationToken cancellationToken) { Guid transactionId = Guid.NewGuid(); @@ -170,7 +175,8 @@ private async Task> ProcessSpecificMessage(SaleTransac saleTransactionRequest.ContractId, saleTransactionRequest.ProductId, // Default to an online sale - saleTransactionRequest.TransactionSource.GetValueOrDefault(1)); + saleTransactionRequest.TransactionSource.GetValueOrDefault(1), + transactionReceivedDateTime); var result= await this.Mediator.Send(command, cancellationToken); if (result.IsFailed) diff --git a/TransactionProcessor/appsettings.json b/TransactionProcessor/appsettings.json index cc72a6a5..cb381966 100644 --- a/TransactionProcessor/appsettings.json +++ b/TransactionProcessor/appsettings.json @@ -41,6 +41,7 @@ "TransactionFeeForProductAddedToContractEvent", "TransactionFeeForProductDisabledEvent", "TransactionHasBeenCompletedEvent", + "TransactionTimingsAddedToTransactionEvent", "SettledMerchantFeeAddedToTransactionEvent", "MerchantFeeSettledEvent", "TransactionCostInformationRecordedEvent",