diff --git a/TransactionProcessor.Aggregates/MerchantAggregate.cs b/TransactionProcessor.Aggregates/MerchantAggregate.cs index 8369a08e..c7000e82 100644 --- a/TransactionProcessor.Aggregates/MerchantAggregate.cs +++ b/TransactionProcessor.Aggregates/MerchantAggregate.cs @@ -3,6 +3,7 @@ using Shared.General; using SimpleResults; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using TransactionProcessor.DomainEvents; using TransactionProcessor.Models.Contract; using TransactionProcessor.Models.Merchant; @@ -294,10 +295,13 @@ public static Result SwapDevice(this MerchantAggregate aggregate, if (result.IsFailed) return result; + KeyValuePair originalDevice = aggregate.Devices.Single(d => d.Value.DeviceIdentifier == originalDeviceIdentifier); + Guid deviceId = Guid.NewGuid(); - MerchantDomainEvents.DeviceSwappedForMerchantEvent deviceSwappedForMerchantEvent = new MerchantDomainEvents.DeviceSwappedForMerchantEvent(aggregate.AggregateId, aggregate.EstateId, - deviceId, originalDeviceIdentifier, newDeviceIdentifier); + MerchantDomainEvents.DeviceSwappedForMerchantEvent deviceSwappedForMerchantEvent = new(aggregate.AggregateId, aggregate.EstateId, + deviceId, + originalDevice.Value.DeviceId, newDeviceIdentifier); aggregate.ApplyAndAppend(deviceSwappedForMerchantEvent); @@ -852,7 +856,7 @@ public static void PlayEvent(this MerchantAggregate aggregate, MerchantDomainEve public static void PlayEvent(this MerchantAggregate aggregate, MerchantDomainEvents.DeviceSwappedForMerchantEvent domainEvent) { - KeyValuePair device = aggregate.Devices.Single(d => d.Value.DeviceIdentifier == domainEvent.OriginalDeviceIdentifier); + KeyValuePair device = aggregate.Devices.Single(d => d.Key == domainEvent.OriginalDeviceId); aggregate.Devices[device.Key] = device.Value with{ IsEnabled = false }; diff --git a/TransactionProcessor.Database/Contexts/EstateManagementGenericContext.cs b/TransactionProcessor.Database/Contexts/EstateManagementGenericContext.cs index ed43dea3..d68aec07 100644 --- a/TransactionProcessor.Database/Contexts/EstateManagementGenericContext.cs +++ b/TransactionProcessor.Database/Contexts/EstateManagementGenericContext.cs @@ -429,12 +429,26 @@ public static async Task> LoadOperator(this EstateManagementCon return loadOperatorResult; } + public static async Task> LoadOriginalMerchantDevice(this EstateManagementContext context, IDomainEvent domainEvent, CancellationToken cancellationToken) + { + Guid merchantId = DomainEventHelper.GetMerchantId(domainEvent); + Guid deviceId = DomainEventHelper.GetOriginalDeviceId(domainEvent); + MerchantDevice device = await context.MerchantDevices.SingleOrDefaultAsync(d => d.DeviceId == deviceId && + d.MerchantId == merchantId, cancellationToken); + + return device switch + { + null => Result.NotFound($"Original Device Id {deviceId} not found for Merchant {merchantId}"), + _ => Result.Success(device) + }; + } + public static async Task> LoadMerchantDevice(this EstateManagementContext context, IDomainEvent domainEvent, CancellationToken cancellationToken) { Guid merchantId = DomainEventHelper.GetMerchantId(domainEvent); Guid deviceId = DomainEventHelper.GetDeviceId(domainEvent); MerchantDevice device = await context.MerchantDevices.SingleOrDefaultAsync(d => d.DeviceId == deviceId && - d.MerchantId == merchantId, cancellationToken); + d.MerchantId == merchantId, cancellationToken); return device switch { @@ -596,6 +610,7 @@ public static class DomainEventHelper public static Guid GetFileId(IDomainEvent domainEvent) => DomainEventHelper.GetProperty(domainEvent, "FileId"); public static Guid GetFileImportLogId(IDomainEvent domainEvent) => DomainEventHelper.GetProperty(domainEvent, "FileImportLogId"); + public static Guid GetOriginalDeviceId(IDomainEvent domainEvent) => DomainEventHelper.GetProperty(domainEvent, "OriginalDeviceId"); public static Guid GetDeviceId(IDomainEvent domainEvent) => DomainEventHelper.GetProperty(domainEvent, "DeviceId"); public static Guid GetMerchantId(IDomainEvent domainEvent) => DomainEventHelper.GetProperty(domainEvent, "MerchantId"); public static Guid GetAddressId(IDomainEvent domainEvent) => DomainEventHelper.GetProperty(domainEvent, "AddressId"); diff --git a/TransactionProcessor.Database/Entities/MerchantDevice.cs b/TransactionProcessor.Database/Entities/MerchantDevice.cs index c8f46bca..05514ccd 100644 --- a/TransactionProcessor.Database/Entities/MerchantDevice.cs +++ b/TransactionProcessor.Database/Entities/MerchantDevice.cs @@ -17,6 +17,7 @@ public class MerchantDevice public String DeviceIdentifier { get; set; } public Guid MerchantId { get; set; } + public Boolean IsEnabled { get; set; } #endregion } diff --git a/TransactionProcessor.Database/Migrations/SqlServer/20260201193603_add_isenabled_to_merchantdevice.Designer.cs b/TransactionProcessor.Database/Migrations/SqlServer/20260201193603_add_isenabled_to_merchantdevice.Designer.cs new file mode 100644 index 00000000..5e86f15e --- /dev/null +++ b/TransactionProcessor.Database/Migrations/SqlServer/20260201193603_add_isenabled_to_merchantdevice.Designer.cs @@ -0,0 +1,1546 @@ +// +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(EstateManagementContext))] + [Migration("20260201193603_add_isenabled_to_merchantdevice")] + partial class add_isenabled_to_merchantdevice + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.1") + .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.EstateOperator", b => + { + b.Property("EstateId") + .HasColumnType("uniqueidentifier"); + + b.Property("OperatorId") + .HasColumnType("uniqueidentifier"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.HasKey("EstateId", "OperatorId"); + + b.ToTable("estateoperator"); + }); + + 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.Property("IsEnabled") + .HasColumnType("bit"); + + 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/20260201193603_add_isenabled_to_merchantdevice.cs b/TransactionProcessor.Database/Migrations/SqlServer/20260201193603_add_isenabled_to_merchantdevice.cs new file mode 100644 index 00000000..d1f76b57 --- /dev/null +++ b/TransactionProcessor.Database/Migrations/SqlServer/20260201193603_add_isenabled_to_merchantdevice.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace TransactionProcessor.Database.Migrations.SqlServer +{ + /// + public partial class add_isenabled_to_merchantdevice : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsEnabled", + table: "merchantdevice", + type: "bit", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsEnabled", + table: "merchantdevice"); + } + } +} diff --git a/TransactionProcessor.Database/Migrations/SqlServer/EstateManagementSqlServerContextModelSnapshot.cs b/TransactionProcessor.Database/Migrations/SqlServer/EstateManagementSqlServerContextModelSnapshot.cs index 9ec47464..55a4759d 100644 --- a/TransactionProcessor.Database/Migrations/SqlServer/EstateManagementSqlServerContextModelSnapshot.cs +++ b/TransactionProcessor.Database/Migrations/SqlServer/EstateManagementSqlServerContextModelSnapshot.cs @@ -580,6 +580,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("nvarchar(max)"); + b.Property("IsEnabled") + .HasColumnType("bit"); + b.HasKey("MerchantId", "DeviceId"); b.ToTable("merchantdevice"); diff --git a/TransactionProcessor.DatabaseTests/BaseTest.cs b/TransactionProcessor.DatabaseTests/BaseTest.cs index 56b9cb45..7c6135e9 100644 --- a/TransactionProcessor.DatabaseTests/BaseTest.cs +++ b/TransactionProcessor.DatabaseTests/BaseTest.cs @@ -1,28 +1,18 @@ -using Ductus.FluentDocker.Services; -using Ductus.FluentDocker.Services.Extensions; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.Extensions.Configuration; +using System.Diagnostics; +using DotNet.Testcontainers.Builders; +using DotNet.Testcontainers.Networks; using Microsoft.Extensions.DependencyInjection; using Moq; using NLog; using Shared.EntityFramework; using Shared.IntegrationTesting; +using Shared.IntegrationTesting.TestContainers; using Shared.Logger; using Shouldly; -using SimpleResults; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http.Headers; -using System.Text; -using System.Threading.Tasks; -using Shared.IntegrationTesting.Ductus; using TransactionProcessor.Database.Contexts; using TransactionProcessor.Repository; using Xunit.Abstractions; using Logger = Shared.Logger.Logger; -using NullLogger = Microsoft.Extensions.Logging.Abstractions.NullLogger; namespace TransactionProcessor.DatabaseTests { @@ -76,43 +66,43 @@ public virtual async Task DisposeAsync() protected Guid TestId; - public static IContainerService DatabaseServerContainer; - public static INetworkService DatabaseServerNetwork; + public static INetwork DatabaseServerNetwork; public static (String usename, String password) SqlCredentials = ("sa", "thisisalongpassword123!"); - public static String GetLocalConnectionString(String databaseName) + public String GetLocalConnectionString(String databaseName) { - Int32 databaseHostPort = DatabaseServerContainer.ToHostExposedEndpoint("1433/tcp").Port; - + Int32? databaseHostPort = DockerHelper.GetHostPort(ContainerType.SqlServer); + return $"server=localhost,{databaseHostPort};database={databaseName};user id={SqlCredentials.usename};password={SqlCredentials.password};Encrypt=false"; } + private DockerHelper DockerHelper; + internal async Task StartSqlContainer() { - DockerHelper dockerHelper = new TestDockerHelper(); + DockerHelper = new TestDockerHelper(); NlogLogger logger = new NlogLogger(); logger.Initialise(LogManager.GetLogger("Specflow"), "Specflow"); LogManager.AddHiddenAssembly(typeof(NlogLogger).Assembly); - dockerHelper.Logger = logger; - dockerHelper.SqlCredentials = SqlCredentials; - dockerHelper.SqlServerContainerName = "sharedsqlserver_repotests"; - dockerHelper.RequiredDockerServices = DockerServices.SqlServer; - - DatabaseServerNetwork = dockerHelper.SetupTestNetwork("sharednetwork", true); - await Retry.For(async () => { - DatabaseServerContainer = await dockerHelper.SetupSqlServerContainer(DatabaseServerNetwork); - }); + DockerHelper.Logger = logger; + DockerHelper.SqlCredentials = SqlCredentials; + DockerHelper.SqlServerContainerName = $"sqlserver_{this.TestId}"; + DockerHelper.RequiredDockerServices = DockerServices.SqlServer; + + //DatabaseServerNetwork = await DockerHelper.SetupTestNetwork($"nw{this.TestId}", true); + await DockerHelper.StartContainersForScenarioRun(this.TestId.ToString(), DockerServices.SqlServer); } public void Dispose() { - EstateManagementContext context = new EstateManagementContext(BaseTest.GetLocalConnectionString($"EstateReportingReadModel{this.TestId.ToString()}")); + EstateManagementContext context = new EstateManagementContext(this.GetLocalConnectionString($"EstateReportingReadModel{this.TestId.ToString()}")); Console.WriteLine($"About to delete database EstateReportingReadModel{this.TestId.ToString()}"); Boolean result = context.Database.EnsureDeleted(); Console.WriteLine($"Delete result is {result}"); result.ShouldBeTrue(); + } } diff --git a/TransactionProcessor.DatabaseTests/ContractEventTests.cs b/TransactionProcessor.DatabaseTests/ContractEventTests.cs index 3192684c..7cf896fa 100644 --- a/TransactionProcessor.DatabaseTests/ContractEventTests.cs +++ b/TransactionProcessor.DatabaseTests/ContractEventTests.cs @@ -13,6 +13,93 @@ namespace TransactionProcessor.DatabaseTests { + public class MerchantEventTests : BaseTest { + [Fact] + public async Task AddMerchant_MerchantIsAdded() { + Result result = await this.Repository.AddMerchant(TestData.DomainEvents.MerchantCreatedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + EstateManagementContext context = this.GetContext(); + Merchant? merchant = await context.Merchants.SingleOrDefaultAsync(c => c.MerchantId == TestData.DomainEvents.MerchantCreatedEvent.MerchantId); + merchant.ShouldNotBeNull(); + } + + [Fact] + public async Task AddMerchant_EventReplayHandled() { + Result result = await this.Repository.AddMerchant(TestData.DomainEvents.MerchantCreatedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result = await this.Repository.AddMerchant(TestData.DomainEvents.MerchantCreatedEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + } + + [Fact] + public async Task AddMerchantDevice_MerchantContractIsAdded() { + Result result = await this.Repository.AddMerchantDevice(TestData.DomainEvents.DeviceAddedToMerchantEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + EstateManagementContext context = this.GetContext(); + var merchantDevice = await context.MerchantDevices.SingleOrDefaultAsync(c => c.DeviceId == TestData.DomainEvents.DeviceAddedToMerchantEvent.DeviceId); + merchantDevice.ShouldNotBeNull(); + } + + [Fact] + public async Task AddMerchantDevice_EventReplayHandled() { + Result result = await this.Repository.AddMerchantDevice(TestData.DomainEvents.DeviceAddedToMerchantEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result = await this.Repository.AddMerchantDevice(TestData.DomainEvents.DeviceAddedToMerchantEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + } + + [Fact] + public async Task SwapMerchantDevice_MerchantContractIsAdded() + { + Result result = await this.Repository.AddMerchantDevice(TestData.DomainEvents.DeviceAddedToMerchantEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result = await this.Repository.SwapMerchantDevice(TestData.DomainEvents.DeviceSwappedForMerchantEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + EstateManagementContext context = this.GetContext(); + var merchantDevice = await context.MerchantDevices.SingleOrDefaultAsync(c => c.DeviceId == TestData.DomainEvents.DeviceAddedToMerchantEvent.DeviceId); + merchantDevice.ShouldNotBeNull(); + merchantDevice.DeviceIdentifier.ShouldBe(TestData.DomainEvents.DeviceAddedToMerchantEvent.DeviceIdentifier); + merchantDevice.IsEnabled.ShouldBeFalse(); + + merchantDevice = await context.MerchantDevices.SingleOrDefaultAsync(c => c.DeviceId == TestData.DomainEvents.DeviceSwappedForMerchantEvent.DeviceId); + merchantDevice.ShouldNotBeNull(); + merchantDevice.DeviceIdentifier.ShouldBe(TestData.DomainEvents.DeviceSwappedForMerchantEvent.NewDeviceIdentifier); + merchantDevice.IsEnabled.ShouldBeTrue(); + } + + [Fact] + public async Task SwapMerchantDevice_EventReplayHandled() + { + Result result = await this.Repository.AddMerchantDevice(TestData.DomainEvents.DeviceAddedToMerchantEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + result = await this.Repository.SwapMerchantDevice(TestData.DomainEvents.DeviceSwappedForMerchantEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + EstateManagementContext context = this.GetContext(); + var merchantDevice = await context.MerchantDevices.SingleOrDefaultAsync(c => c.DeviceId == TestData.DomainEvents.DeviceAddedToMerchantEvent.DeviceId); + merchantDevice.ShouldNotBeNull(); + merchantDevice.DeviceIdentifier.ShouldBe(TestData.DomainEvents.DeviceAddedToMerchantEvent.DeviceIdentifier); + merchantDevice.IsEnabled.ShouldBeFalse(); + + merchantDevice = await context.MerchantDevices.SingleOrDefaultAsync(c => c.DeviceId == TestData.DomainEvents.DeviceSwappedForMerchantEvent.DeviceId); + merchantDevice.ShouldNotBeNull(); + merchantDevice.DeviceIdentifier.ShouldBe(TestData.DomainEvents.DeviceSwappedForMerchantEvent.NewDeviceIdentifier); + merchantDevice.IsEnabled.ShouldBeTrue(); + + result = await this.Repository.SwapMerchantDevice(TestData.DomainEvents.DeviceSwappedForMerchantEvent, CancellationToken.None); + result.IsSuccess.ShouldBeTrue(); + + merchantDevice = await context.MerchantDevices.SingleOrDefaultAsync(c => c.DeviceId == TestData.DomainEvents.DeviceAddedToMerchantEvent.DeviceId); + merchantDevice.ShouldNotBeNull(); + merchantDevice.DeviceIdentifier.ShouldBe(TestData.DomainEvents.DeviceAddedToMerchantEvent.DeviceIdentifier); + merchantDevice.IsEnabled.ShouldBeFalse(); + + merchantDevice = await context.MerchantDevices.SingleOrDefaultAsync(c => c.DeviceId == TestData.DomainEvents.DeviceSwappedForMerchantEvent.DeviceId); + merchantDevice.ShouldNotBeNull(); + merchantDevice.DeviceIdentifier.ShouldBe(TestData.DomainEvents.DeviceSwappedForMerchantEvent.NewDeviceIdentifier); + merchantDevice.IsEnabled.ShouldBeTrue(); + } + } + public class ContractEventTests : BaseTest { diff --git a/TransactionProcessor.DatabaseTests/TransactionProcessor.DatabaseTests.csproj b/TransactionProcessor.DatabaseTests/TransactionProcessor.DatabaseTests.csproj index 53bcd56f..5d50a9d6 100644 --- a/TransactionProcessor.DatabaseTests/TransactionProcessor.DatabaseTests.csproj +++ b/TransactionProcessor.DatabaseTests/TransactionProcessor.DatabaseTests.csproj @@ -5,7 +5,7 @@ enable enable false - None + Full @@ -22,7 +22,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/TransactionProcessor.DomainEvents/MerchantDomainEvents.cs b/TransactionProcessor.DomainEvents/MerchantDomainEvents.cs index 95943591..812619da 100644 --- a/TransactionProcessor.DomainEvents/MerchantDomainEvents.cs +++ b/TransactionProcessor.DomainEvents/MerchantDomainEvents.cs @@ -9,7 +9,7 @@ public record AddressAddedEvent(Guid MerchantId, Guid EstateId, Guid AddressId, public record AutomaticDepositMadeEvent(Guid MerchantId, Guid EstateId, Guid DepositId, String Reference, DateTime DepositDateTime, Decimal Amount) : DomainEvent(MerchantId, Guid.NewGuid()); public record ContactAddedEvent(Guid MerchantId, Guid EstateId, Guid ContactId, String ContactName, String ContactPhoneNumber, String ContactEmailAddress) : DomainEvent(MerchantId, Guid.NewGuid()); public record DeviceAddedToMerchantEvent(Guid MerchantId, Guid EstateId, Guid DeviceId, String DeviceIdentifier) : DomainEvent(MerchantId, Guid.NewGuid()); - public record DeviceSwappedForMerchantEvent(Guid MerchantId, Guid EstateId, Guid DeviceId, String OriginalDeviceIdentifier, String NewDeviceIdentifier) : DomainEvent(MerchantId, Guid.NewGuid()); + public record DeviceSwappedForMerchantEvent(Guid MerchantId, Guid EstateId, Guid DeviceId, Guid OriginalDeviceId, String NewDeviceIdentifier) : DomainEvent(MerchantId, Guid.NewGuid()); public record ManualDepositMadeEvent(Guid MerchantId, Guid EstateId, Guid DepositId, String Reference, DateTime DepositDateTime, Decimal Amount) : DomainEvent(MerchantId, Guid.NewGuid()); public record MerchantCreatedEvent(Guid MerchantId, Guid EstateId, String MerchantName, DateTime DateCreated) : DomainEvent(MerchantId, Guid.NewGuid()); public record MerchantDepositListCreatedEvent(Guid MerchantId, Guid EstateId, DateTime DateCreated) : DomainEvent(MerchantId, Guid.NewGuid()); diff --git a/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs b/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs index ce160fc7..495caf05 100644 --- a/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs +++ b/TransactionProcessor.Repository/ITransactionProcessorReadModelRepository.cs @@ -359,7 +359,7 @@ public async Task AddMerchant(MerchantDomainEvents.MerchantCreatedEvent await context.Merchants.AddAsync(merchant, cancellationToken); - return await context.SaveChangesAsync(cancellationToken); + return await context.SaveChangesWithDuplicateHandling(cancellationToken); } public async Task UpdateMerchant(MerchantNameUpdatedEvent domainEvent, CancellationToken cancellationToken) @@ -423,8 +423,7 @@ public async Task AddMerchantDevice(DeviceAddedToMerchantEvent domainEve { EstateManagementContext context = await this.GetContext(domainEvent.EstateId); - MerchantDevice merchantDevice = new MerchantDevice - { + MerchantDevice merchantDevice = new() { MerchantId = domainEvent.MerchantId, DeviceId = domainEvent.DeviceId, DeviceIdentifier = domainEvent.DeviceIdentifier @@ -432,21 +431,24 @@ public async Task AddMerchantDevice(DeviceAddedToMerchantEvent domainEve await context.MerchantDevices.AddAsync(merchantDevice, cancellationToken); - return await context.SaveChangesAsync(cancellationToken); + return await context.SaveChangesWithDuplicateHandling(cancellationToken); } public async Task SwapMerchantDevice(DeviceSwappedForMerchantEvent domainEvent, CancellationToken cancellationToken) { EstateManagementContext context = await this.GetContext(domainEvent.EstateId); - var getDeviceResult = await context.LoadMerchantDevice(domainEvent, cancellationToken); - if (getDeviceResult.IsFailed) - return ResultHelpers.CreateFailure(getDeviceResult); - var device = getDeviceResult.Data; + Result getOriginalDeviceResult = await context.LoadOriginalMerchantDevice(domainEvent, cancellationToken); + if (getOriginalDeviceResult.IsFailed) + return ResultHelpers.CreateFailure(getOriginalDeviceResult); + MerchantDevice? originalDevice = getOriginalDeviceResult.Data; - device.DeviceIdentifier = domainEvent.NewDeviceIdentifier; + originalDevice.IsEnabled = false; - return await context.SaveChangesAsync(cancellationToken); + MerchantDevice newDevice = new() { DeviceId = domainEvent.DeviceId, IsEnabled = true, DeviceIdentifier = domainEvent.NewDeviceIdentifier, MerchantId = domainEvent.MerchantId }; + await context.MerchantDevices.AddAsync(newDevice, cancellationToken); + + return await context.SaveChangesWithDuplicateHandling(cancellationToken); } public async Task AddMerchantOperator(OperatorAssignedToMerchantEvent domainEvent, diff --git a/TransactionProcessor.Testing/TestData.cs b/TransactionProcessor.Testing/TestData.cs index a7fcfb42..eaaf52a6 100644 --- a/TransactionProcessor.Testing/TestData.cs +++ b/TransactionProcessor.Testing/TestData.cs @@ -353,7 +353,8 @@ public class TestData public static String DeclinedResponseMessage = "DeclinedResponseMessage"; public static Guid DeviceId = Guid.Parse("840F32FF-8B74-467C-8078-F5D9297FED56"); - + public static Guid OriginalDeviceId = Guid.Parse("840F32FF-8B74-467C-8078-F5D9297FED56"); + public static Guid NewDeviceId = Guid.Parse("55C261AC-7E80-47E1-9169-4041329D3F12"); public static String DeviceIdentifier = "1234567890"; public static String DeviceIdentifier1 = "1234567891"; @@ -2690,8 +2691,8 @@ public static class DomainEvents { public static MerchantDomainEvents.DeviceSwappedForMerchantEvent DeviceSwappedForMerchantEvent => new MerchantDomainEvents.DeviceSwappedForMerchantEvent(TestData.MerchantId, TestData.EstateId, - TestData.DeviceId, - TestData.DeviceIdentifier, + TestData.NewDeviceId, + TestData.OriginalDeviceId, TestData.NewDeviceIdentifier); public static MerchantDomainEvents.OperatorRemovedFromMerchantEvent OperatorRemovedFromMerchantEvent => new MerchantDomainEvents.OperatorRemovedFromMerchantEvent(TestData.MerchantId, TestData.EstateId, TestData.OperatorId);