From 28c4f55c4ddb0284a5636f987c3d377565b3f4f7 Mon Sep 17 00:00:00 2001 From: Jason Sylvestre Date: Mon, 15 Sep 2025 10:39:32 -0700 Subject: [PATCH 1/9] Adds invoice type to invoices and teams Introduces a type property to invoices, allowing differentiation between credit card and recharge invoices. Adds an allowed invoice type setting to teams, controlling which types of invoices are permitted. Sets default invoice type to CreditCard. --- src/Payments.Core/Domain/Invoice.cs | 15 +++++++++++++++ src/Payments.Core/Domain/Team.cs | 9 +++++++++ 2 files changed, 24 insertions(+) diff --git a/src/Payments.Core/Domain/Invoice.cs b/src/Payments.Core/Domain/Invoice.cs index 55039aeb..ecde72af 100644 --- a/src/Payments.Core/Domain/Invoice.cs +++ b/src/Payments.Core/Domain/Invoice.cs @@ -115,6 +115,8 @@ public string GetFormattedId() [JsonIgnore] public IList PaymentEvents { get; set; } + public string Type { get; set; } = Team.InvoiceTypes.CreditCard; // CC or Recharge + // ---------------------- // Calculated Values // ---------------------- @@ -234,6 +236,13 @@ protected internal static void OnModelCreating(ModelBuilder builder) builder.Entity() .Property(i => i.CalculatedTotal) .HasColumnType("decimal(18,2)"); + + // Set default value for Type column + builder.Entity() + .Property(i => i.Type) + .HasDefaultValue("CC"); + + builder.Entity().HasIndex(a => a.Type); } public Dictionary GetPaymentDictionary() @@ -320,5 +329,11 @@ public static string GetBadgeClass(string status) } } } + + public static class InvoiceTypes + { + public const string CreditCard = "CC"; + public const string Recharge = "Recharge"; + } } } diff --git a/src/Payments.Core/Domain/Team.cs b/src/Payments.Core/Domain/Team.cs index 6a66e4db..57747228 100644 --- a/src/Payments.Core/Domain/Team.cs +++ b/src/Payments.Core/Domain/Team.cs @@ -71,6 +71,8 @@ public Team() [StringLength(128)] public string WebHookApiKey { get; set; } + public string AllowedInvoiceType { get; set; } = InvoiceTypes.CreditCard; + [NotMapped] public FinancialAccount DefaultAccount { get { @@ -91,5 +93,12 @@ public TeamPermission AddPermission(User user, TeamRole role) return permission; } + + public static class AllowedInvoiceTypes + { + public const string CreditCard = "CreditCard"; + public const string Recharge = "Recharge"; + public const string Both = "Both"; + } } } From 156e9a61a6ea4ec5f93703c713c56efbfa2be949 Mon Sep 17 00:00:00 2001 From: Jason Sylvestre Date: Mon, 15 Sep 2025 10:57:20 -0700 Subject: [PATCH 2/9] Renames InvoiceTypes to AllowedInvoiceTypes Renames InvoiceTypes class in the Team domain object to AllowedInvoiceTypes to more accurately reflect its usage. Updates the Invoice class to reference the InvoiceTypes constants via the AllowedInvoiceTypes class. --- src/Payments.Core/Domain/Invoice.cs | 2 +- src/Payments.Core/Domain/Team.cs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Payments.Core/Domain/Invoice.cs b/src/Payments.Core/Domain/Invoice.cs index ecde72af..31d4c7d7 100644 --- a/src/Payments.Core/Domain/Invoice.cs +++ b/src/Payments.Core/Domain/Invoice.cs @@ -115,7 +115,7 @@ public string GetFormattedId() [JsonIgnore] public IList PaymentEvents { get; set; } - public string Type { get; set; } = Team.InvoiceTypes.CreditCard; // CC or Recharge + public string Type { get; set; } = InvoiceTypes.CreditCard; // CC or Recharge // ---------------------- // Calculated Values diff --git a/src/Payments.Core/Domain/Team.cs b/src/Payments.Core/Domain/Team.cs index 57747228..b533485f 100644 --- a/src/Payments.Core/Domain/Team.cs +++ b/src/Payments.Core/Domain/Team.cs @@ -71,7 +71,7 @@ public Team() [StringLength(128)] public string WebHookApiKey { get; set; } - public string AllowedInvoiceType { get; set; } = InvoiceTypes.CreditCard; + public string AllowedInvoiceType { get; set; } = AllowedInvoiceTypes.CreditCard; [NotMapped] public FinancialAccount DefaultAccount { @@ -94,6 +94,10 @@ public TeamPermission AddPermission(User user, TeamRole role) return permission; } + /// + /// These are a superset of the Invoice.Types + /// (Adds Both value) + /// public static class AllowedInvoiceTypes { public const string CreditCard = "CreditCard"; From 2a65b2ef5177306a831f8135b444983fbb65da52 Mon Sep 17 00:00:00 2001 From: Jason Sylvestre Date: Mon, 15 Sep 2025 11:19:42 -0700 Subject: [PATCH 3/9] Adds invoice type to invoices and teams Adds a "Type" column to the Invoices table and an "AllowedInvoiceType" column to the Teams table. This allows for better tracking and filtering of invoices based on their type. The default invoice type is set to 'CreditCard'. Also adds an index on the Invoices table for the Type column. --- src/Payments.Sql/dbo/Tables/Invoices.sql | 5 +++++ src/Payments.Sql/dbo/Tables/Teams.sql | 1 + 2 files changed, 6 insertions(+) diff --git a/src/Payments.Sql/dbo/Tables/Invoices.sql b/src/Payments.Sql/dbo/Tables/Invoices.sql index 6d56d759..7a238ac3 100644 --- a/src/Payments.Sql/dbo/Tables/Invoices.sql +++ b/src/Payments.Sql/dbo/Tables/Invoices.sql @@ -31,6 +31,7 @@ CREATE TABLE [dbo].[Invoices] ( [KfsTrackingNumber] NVARCHAR(20) NULL, [Refunded] BIT NOT NULL DEFAULT 0, [RefundedAt] DATETIME2 NULL, + [Type] NVARCHAR(10) NOT NULL DEFAULT 'CreditCard', CONSTRAINT [PK_Invoices] PRIMARY KEY CLUSTERED ([Id] ASC), CONSTRAINT [FK_Invoices_Coupons_CouponId] FOREIGN KEY ([CouponId]) REFERENCES [dbo].[Coupons] ([Id]), CONSTRAINT [FK_Invoices_FinancialAccounts_AccountId] FOREIGN KEY ([AccountId]) REFERENCES [dbo].[FinancialAccounts] ([Id]), @@ -47,3 +48,7 @@ GO CREATE NONCLUSTERED INDEX [IX_Invoices_AccountId] ON [dbo].[Invoices]([AccountId] ASC); +Go +CREATE NONCLUSTERED INDEX [IX_Invoices_Type] + ON [dbo].[Invoices]([Type] ASC); + diff --git a/src/Payments.Sql/dbo/Tables/Teams.sql b/src/Payments.Sql/dbo/Tables/Teams.sql index 70d863fe..ea1895f0 100644 --- a/src/Payments.Sql/dbo/Tables/Teams.sql +++ b/src/Payments.Sql/dbo/Tables/Teams.sql @@ -8,6 +8,7 @@ CREATE TABLE [dbo].[Teams] ( [ContactPhoneNumber] NVARCHAR (40) NULL, [ApiKey] NVARCHAR (50) CONSTRAINT [DF_Teams_ApiKey] DEFAULT (replace(newid(),'-','')) NOT NULL, [WebHookApiKey] NVARCHAR(128) NULL, + [AllowedInvoiceType] NVARCHAR(10) NOT NULL DEFAULT 'CreditCard', CONSTRAINT [PK_Teams] PRIMARY KEY CLUSTERED ([Id] ASC) ); From 257f1dac4ff2656bf0c642e94f3a6916b4a7d79c Mon Sep 17 00:00:00 2001 From: Scott Kirkland Date: Wed, 17 Sep 2025 15:59:07 -0700 Subject: [PATCH 4/9] Created baseline migrations from existing db state --- .../20250917225545_Baseline.Designer.cs | 1096 +++++++++++++++++ .../Migrations/20250917225545_Baseline.cs | 20 + .../ApplicationDbContextModelSnapshot.cs | 1094 ++++++++++++++++ src/Payments.Mvc/Payments.Mvc.csproj | 4 + 4 files changed, 2214 insertions(+) create mode 100644 src/Payments.Core/Migrations/20250917225545_Baseline.Designer.cs create mode 100644 src/Payments.Core/Migrations/20250917225545_Baseline.cs create mode 100644 src/Payments.Core/Migrations/ApplicationDbContextModelSnapshot.cs diff --git a/src/Payments.Core/Migrations/20250917225545_Baseline.Designer.cs b/src/Payments.Core/Migrations/20250917225545_Baseline.Designer.cs new file mode 100644 index 00000000..5d0f5aaf --- /dev/null +++ b/src/Payments.Core/Migrations/20250917225545_Baseline.Designer.cs @@ -0,0 +1,1096 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Payments.Core.Data; + +#nullable disable + +namespace Payments.Core.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250917225545_Baseline")] + partial class Baseline + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Payments.Core.Domain.Coupon", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Code") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("DiscountPercent") + .HasColumnType("decimal(18,5)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Coupons"); + }); + + modelBuilder.Entity("Payments.Core.Domain.FinancialAccount", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Account") + .HasMaxLength(7) + .HasColumnType("nvarchar(7)"); + + b.Property("Chart") + .HasMaxLength(1) + .HasColumnType("nvarchar(1)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("FinancialSegmentString") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDefault") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Project") + .HasMaxLength(9) + .HasColumnType("nvarchar(9)"); + + b.Property("SubAccount") + .HasMaxLength(5) + .HasColumnType("nvarchar(5)"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("FinancialAccounts"); + }); + + modelBuilder.Entity("Payments.Core.Domain.History", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ActionDateTime") + .HasColumnType("datetime2"); + + b.Property("Actor") + .HasColumnType("nvarchar(max)"); + + b.Property("Data") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("History"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("AccountId") + .HasColumnType("int"); + + b.Property("CalculatedDiscount") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedSubtotal") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedTaxAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedTaxableAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedTotal") + .HasColumnType("decimal(18,2)"); + + b.Property("CouponId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CustomerAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerCompany") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerName") + .HasColumnType("nvarchar(max)"); + + b.Property("Deleted") + .HasColumnType("bit"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("DraftCount") + .HasColumnType("int"); + + b.Property("DueDate") + .HasColumnType("datetime2"); + + b.Property("KfsTrackingNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("LinkId") + .HasColumnType("nvarchar(max)"); + + b.Property("ManualDiscount") + .HasColumnType("decimal(18,2)"); + + b.Property("Memo") + .HasColumnType("nvarchar(max)"); + + b.Property("Paid") + .HasColumnType("bit"); + + b.Property("PaidAt") + .HasColumnType("datetime2"); + + b.Property("PaymentProcessorId") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentType") + .HasColumnType("nvarchar(max)"); + + b.Property("Refunded") + .HasColumnType("bit"); + + b.Property("RefundedAt") + .HasColumnType("datetime2"); + + b.Property("Sent") + .HasColumnType("bit"); + + b.Property("SentAt") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.Property("TaxPercent") + .HasColumnType("decimal(18,5)"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("CouponId"); + + b.HasIndex("TeamId"); + + b.ToTable("Invoices"); + }); + + modelBuilder.Entity("Payments.Core.Domain.InvoiceAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ContentType") + .HasColumnType("nvarchar(max)"); + + b.Property("FileName") + .HasColumnType("nvarchar(max)"); + + b.Property("Identifier") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("InvoiceAttachments"); + }); + + modelBuilder.Entity("Payments.Core.Domain.InvoiceLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Expired") + .HasColumnType("bit"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("LinkId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("InvoiceLinks"); + }); + + modelBuilder.Entity("Payments.Core.Domain.LineItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Amount") + .HasColumnType("decimal(18,2)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("decimal(18,2)"); + + b.Property("TaxExempt") + .HasColumnType("bit"); + + b.Property("Total") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("LineItems"); + }); + + modelBuilder.Entity("Payments.Core.Domain.LogMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CorrelationId") + .HasColumnType("nvarchar(max)"); + + b.Property("Exception") + .HasColumnType("nvarchar(max)"); + + b.Property("JobId") + .HasColumnType("nvarchar(450)"); + + b.Property("JobName") + .HasColumnType("nvarchar(max)"); + + b.Property("Level") + .HasColumnType("nvarchar(max)"); + + b.Property("LogEvent") + .HasColumnType("nvarchar(max)"); + + b.Property("Message") + .HasColumnType("nvarchar(max)"); + + b.Property("MessageTemplate") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("xml"); + + b.Property("Source") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeStamp") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("JobId"); + + b.ToTable("Logs", (string)null); + }); + + modelBuilder.Entity("Payments.Core.Domain.MoneyMovementJobRecord", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("RanOn") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("MoneyMovementJobRecords"); + }); + + modelBuilder.Entity("Payments.Core.Domain.PaymentEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Amount") + .HasColumnType("decimal(18,2)"); + + b.Property("BillingCity") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("BillingCompany") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.Property("BillingCountry") + .HasMaxLength(2) + .HasColumnType("nvarchar(2)"); + + b.Property("BillingEmail") + .HasMaxLength(1500) + .HasColumnType("nvarchar(1500)"); + + b.Property("BillingFirstName") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.Property("BillingLastName") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.Property("BillingPhone") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("BillingPostalCode") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("BillingState") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("BillingStreet1") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("BillingStreet2") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("CardExpiry") + .HasColumnType("datetime2"); + + b.Property("CardNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("CardType") + .HasMaxLength(3) + .HasColumnType("nvarchar(3)"); + + b.Property("Decision") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("OccuredAt") + .HasColumnType("datetime2"); + + b.Property("Processor") + .HasColumnType("nvarchar(max)"); + + b.Property("ProcessorId") + .HasColumnType("nvarchar(max)"); + + b.Property("ReturnedResults") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("PaymentEvents"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TaxReportJobRecord", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("RanOn") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("TaxReportJobRecords"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ApiKey") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactEmail") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ContactName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ContactPhoneNumber") + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("WebHookApiKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("Teams"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TeamPermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("TeamId"); + + b.HasIndex("UserId"); + + b.ToTable("TeamPermissions"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TeamRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("TeamRoles"); + }); + + modelBuilder.Entity("Payments.Core.Domain.User", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("CampusKerberos") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LastName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Payments.Core.Domain.WebHook", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ContentType") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.Property("TriggerOnPaid") + .HasColumnType("bit"); + + b.Property("TriggerOnReconcile") + .HasColumnType("bit"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("WebHooks"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Payments.Core.Domain.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Payments.Core.Domain.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Payments.Core.Domain.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Payments.Core.Domain.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Payments.Core.Domain.Coupon", b => + { + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany("Coupons") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Payments.Core.Domain.FinancialAccount", b => + { + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany("Accounts") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Payments.Core.Domain.History", b => + { + b.HasOne("Payments.Core.Domain.Invoice", null) + .WithMany("History") + .HasForeignKey("InvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Payments.Core.Domain.Invoice", b => + { + b.HasOne("Payments.Core.Domain.FinancialAccount", "Account") + .WithMany() + .HasForeignKey("AccountId"); + + b.HasOne("Payments.Core.Domain.Coupon", "Coupon") + .WithMany("Invoices") + .HasForeignKey("CouponId"); + + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("Coupon"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Payments.Core.Domain.InvoiceAttachment", b => + { + b.HasOne("Payments.Core.Domain.Invoice", "Invoice") + .WithMany("Attachments") + .HasForeignKey("InvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("Payments.Core.Domain.InvoiceLink", b => + { + b.HasOne("Payments.Core.Domain.Invoice", "Invoice") + .WithMany() + .HasForeignKey("InvoiceId"); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("Payments.Core.Domain.LineItem", b => + { + b.HasOne("Payments.Core.Domain.Invoice", null) + .WithMany("Items") + .HasForeignKey("InvoiceId"); + }); + + modelBuilder.Entity("Payments.Core.Domain.LogMessage", b => + { + b.HasOne("Payments.Core.Domain.MoneyMovementJobRecord", null) + .WithMany("Logs") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Payments.Core.Domain.TaxReportJobRecord", null) + .WithMany("Logs") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("Payments.Core.Domain.PaymentEvent", b => + { + b.HasOne("Payments.Core.Domain.Invoice", "Invoice") + .WithMany("PaymentEvents") + .HasForeignKey("InvoiceId"); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TeamPermission", b => + { + b.HasOne("Payments.Core.Domain.TeamRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany("Permissions") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Payments.Core.Domain.User", "User") + .WithMany("TeamPermissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("Team"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Payments.Core.Domain.WebHook", b => + { + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany("WebHooks") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Coupon", b => + { + b.Navigation("Invoices"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Invoice", b => + { + b.Navigation("Attachments"); + + b.Navigation("History"); + + b.Navigation("Items"); + + b.Navigation("PaymentEvents"); + }); + + modelBuilder.Entity("Payments.Core.Domain.MoneyMovementJobRecord", b => + { + b.Navigation("Logs"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TaxReportJobRecord", b => + { + b.Navigation("Logs"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Team", b => + { + b.Navigation("Accounts"); + + b.Navigation("Coupons"); + + b.Navigation("Permissions"); + + b.Navigation("WebHooks"); + }); + + modelBuilder.Entity("Payments.Core.Domain.User", b => + { + b.Navigation("TeamPermissions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Payments.Core/Migrations/20250917225545_Baseline.cs b/src/Payments.Core/Migrations/20250917225545_Baseline.cs new file mode 100644 index 00000000..7971636d --- /dev/null +++ b/src/Payments.Core/Migrations/20250917225545_Baseline.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Payments.Core.Migrations +{ + public partial class Baseline : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/src/Payments.Core/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Payments.Core/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 00000000..86a8ced2 --- /dev/null +++ b/src/Payments.Core/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,1094 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Payments.Core.Data; + +#nullable disable + +namespace Payments.Core.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Payments.Core.Domain.Coupon", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Code") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("DiscountPercent") + .HasColumnType("decimal(18,5)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Coupons"); + }); + + modelBuilder.Entity("Payments.Core.Domain.FinancialAccount", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Account") + .HasMaxLength(7) + .HasColumnType("nvarchar(7)"); + + b.Property("Chart") + .HasMaxLength(1) + .HasColumnType("nvarchar(1)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("FinancialSegmentString") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDefault") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Project") + .HasMaxLength(9) + .HasColumnType("nvarchar(9)"); + + b.Property("SubAccount") + .HasMaxLength(5) + .HasColumnType("nvarchar(5)"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("FinancialAccounts"); + }); + + modelBuilder.Entity("Payments.Core.Domain.History", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ActionDateTime") + .HasColumnType("datetime2"); + + b.Property("Actor") + .HasColumnType("nvarchar(max)"); + + b.Property("Data") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("History"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("AccountId") + .HasColumnType("int"); + + b.Property("CalculatedDiscount") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedSubtotal") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedTaxAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedTaxableAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedTotal") + .HasColumnType("decimal(18,2)"); + + b.Property("CouponId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CustomerAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerCompany") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerName") + .HasColumnType("nvarchar(max)"); + + b.Property("Deleted") + .HasColumnType("bit"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("DraftCount") + .HasColumnType("int"); + + b.Property("DueDate") + .HasColumnType("datetime2"); + + b.Property("KfsTrackingNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("LinkId") + .HasColumnType("nvarchar(max)"); + + b.Property("ManualDiscount") + .HasColumnType("decimal(18,2)"); + + b.Property("Memo") + .HasColumnType("nvarchar(max)"); + + b.Property("Paid") + .HasColumnType("bit"); + + b.Property("PaidAt") + .HasColumnType("datetime2"); + + b.Property("PaymentProcessorId") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentType") + .HasColumnType("nvarchar(max)"); + + b.Property("Refunded") + .HasColumnType("bit"); + + b.Property("RefundedAt") + .HasColumnType("datetime2"); + + b.Property("Sent") + .HasColumnType("bit"); + + b.Property("SentAt") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.Property("TaxPercent") + .HasColumnType("decimal(18,5)"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("CouponId"); + + b.HasIndex("TeamId"); + + b.ToTable("Invoices"); + }); + + modelBuilder.Entity("Payments.Core.Domain.InvoiceAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ContentType") + .HasColumnType("nvarchar(max)"); + + b.Property("FileName") + .HasColumnType("nvarchar(max)"); + + b.Property("Identifier") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("InvoiceAttachments"); + }); + + modelBuilder.Entity("Payments.Core.Domain.InvoiceLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Expired") + .HasColumnType("bit"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("LinkId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("InvoiceLinks"); + }); + + modelBuilder.Entity("Payments.Core.Domain.LineItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Amount") + .HasColumnType("decimal(18,2)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("decimal(18,2)"); + + b.Property("TaxExempt") + .HasColumnType("bit"); + + b.Property("Total") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("LineItems"); + }); + + modelBuilder.Entity("Payments.Core.Domain.LogMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CorrelationId") + .HasColumnType("nvarchar(max)"); + + b.Property("Exception") + .HasColumnType("nvarchar(max)"); + + b.Property("JobId") + .HasColumnType("nvarchar(450)"); + + b.Property("JobName") + .HasColumnType("nvarchar(max)"); + + b.Property("Level") + .HasColumnType("nvarchar(max)"); + + b.Property("LogEvent") + .HasColumnType("nvarchar(max)"); + + b.Property("Message") + .HasColumnType("nvarchar(max)"); + + b.Property("MessageTemplate") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("xml"); + + b.Property("Source") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeStamp") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("JobId"); + + b.ToTable("Logs", (string)null); + }); + + modelBuilder.Entity("Payments.Core.Domain.MoneyMovementJobRecord", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("RanOn") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("MoneyMovementJobRecords"); + }); + + modelBuilder.Entity("Payments.Core.Domain.PaymentEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Amount") + .HasColumnType("decimal(18,2)"); + + b.Property("BillingCity") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("BillingCompany") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.Property("BillingCountry") + .HasMaxLength(2) + .HasColumnType("nvarchar(2)"); + + b.Property("BillingEmail") + .HasMaxLength(1500) + .HasColumnType("nvarchar(1500)"); + + b.Property("BillingFirstName") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.Property("BillingLastName") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.Property("BillingPhone") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("BillingPostalCode") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("BillingState") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("BillingStreet1") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("BillingStreet2") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("CardExpiry") + .HasColumnType("datetime2"); + + b.Property("CardNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("CardType") + .HasMaxLength(3) + .HasColumnType("nvarchar(3)"); + + b.Property("Decision") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("OccuredAt") + .HasColumnType("datetime2"); + + b.Property("Processor") + .HasColumnType("nvarchar(max)"); + + b.Property("ProcessorId") + .HasColumnType("nvarchar(max)"); + + b.Property("ReturnedResults") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("PaymentEvents"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TaxReportJobRecord", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("RanOn") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("TaxReportJobRecords"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ApiKey") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactEmail") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ContactName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ContactPhoneNumber") + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("WebHookApiKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("Teams"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TeamPermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("TeamId"); + + b.HasIndex("UserId"); + + b.ToTable("TeamPermissions"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TeamRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("TeamRoles"); + }); + + modelBuilder.Entity("Payments.Core.Domain.User", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("CampusKerberos") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LastName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Payments.Core.Domain.WebHook", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ContentType") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.Property("TriggerOnPaid") + .HasColumnType("bit"); + + b.Property("TriggerOnReconcile") + .HasColumnType("bit"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("WebHooks"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Payments.Core.Domain.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Payments.Core.Domain.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Payments.Core.Domain.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Payments.Core.Domain.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Payments.Core.Domain.Coupon", b => + { + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany("Coupons") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Payments.Core.Domain.FinancialAccount", b => + { + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany("Accounts") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Payments.Core.Domain.History", b => + { + b.HasOne("Payments.Core.Domain.Invoice", null) + .WithMany("History") + .HasForeignKey("InvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Payments.Core.Domain.Invoice", b => + { + b.HasOne("Payments.Core.Domain.FinancialAccount", "Account") + .WithMany() + .HasForeignKey("AccountId"); + + b.HasOne("Payments.Core.Domain.Coupon", "Coupon") + .WithMany("Invoices") + .HasForeignKey("CouponId"); + + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("Coupon"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Payments.Core.Domain.InvoiceAttachment", b => + { + b.HasOne("Payments.Core.Domain.Invoice", "Invoice") + .WithMany("Attachments") + .HasForeignKey("InvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("Payments.Core.Domain.InvoiceLink", b => + { + b.HasOne("Payments.Core.Domain.Invoice", "Invoice") + .WithMany() + .HasForeignKey("InvoiceId"); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("Payments.Core.Domain.LineItem", b => + { + b.HasOne("Payments.Core.Domain.Invoice", null) + .WithMany("Items") + .HasForeignKey("InvoiceId"); + }); + + modelBuilder.Entity("Payments.Core.Domain.LogMessage", b => + { + b.HasOne("Payments.Core.Domain.MoneyMovementJobRecord", null) + .WithMany("Logs") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Payments.Core.Domain.TaxReportJobRecord", null) + .WithMany("Logs") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("Payments.Core.Domain.PaymentEvent", b => + { + b.HasOne("Payments.Core.Domain.Invoice", "Invoice") + .WithMany("PaymentEvents") + .HasForeignKey("InvoiceId"); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TeamPermission", b => + { + b.HasOne("Payments.Core.Domain.TeamRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany("Permissions") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Payments.Core.Domain.User", "User") + .WithMany("TeamPermissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("Team"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Payments.Core.Domain.WebHook", b => + { + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany("WebHooks") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Coupon", b => + { + b.Navigation("Invoices"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Invoice", b => + { + b.Navigation("Attachments"); + + b.Navigation("History"); + + b.Navigation("Items"); + + b.Navigation("PaymentEvents"); + }); + + modelBuilder.Entity("Payments.Core.Domain.MoneyMovementJobRecord", b => + { + b.Navigation("Logs"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TaxReportJobRecord", b => + { + b.Navigation("Logs"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Team", b => + { + b.Navigation("Accounts"); + + b.Navigation("Coupons"); + + b.Navigation("Permissions"); + + b.Navigation("WebHooks"); + }); + + modelBuilder.Entity("Payments.Core.Domain.User", b => + { + b.Navigation("TeamPermissions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Payments.Mvc/Payments.Mvc.csproj b/src/Payments.Mvc/Payments.Mvc.csproj index c90cc6db..80032913 100644 --- a/src/Payments.Mvc/Payments.Mvc.csproj +++ b/src/Payments.Mvc/Payments.Mvc.csproj @@ -21,6 +21,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 21b0f6824be059954f7659c2ab2e16af5ae15d09 Mon Sep 17 00:00:00 2001 From: Scott Kirkland Date: Wed, 17 Sep 2025 16:15:32 -0700 Subject: [PATCH 5/9] two helper scripts like in finjector --- src/Payments.Core/CreateMigration.sh | 6 ++++++ src/Payments.Core/ExecuteMigration.sh | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 src/Payments.Core/CreateMigration.sh create mode 100644 src/Payments.Core/ExecuteMigration.sh diff --git a/src/Payments.Core/CreateMigration.sh b/src/Payments.Core/CreateMigration.sh new file mode 100644 index 00000000..f1d82bea --- /dev/null +++ b/src/Payments.Core/CreateMigration.sh @@ -0,0 +1,6 @@ +[ "$#" -eq 1 ] || { echo "1 argument required, $# provided. Useage: sh CreateMigration "; exit 1; } + +dotnet ef migrations add $1 --context ApplicationDbContext --output-dir Migrations --startup-project ../Payments.Mvc --project ../Payments.Core +# usage from PM console in the Payments.Core directory: ./CreateMigration.sh + +echo 'All done'; \ No newline at end of file diff --git a/src/Payments.Core/ExecuteMigration.sh b/src/Payments.Core/ExecuteMigration.sh new file mode 100644 index 00000000..71b8ea4e --- /dev/null +++ b/src/Payments.Core/ExecuteMigration.sh @@ -0,0 +1,4 @@ +dotnet ef database update --startup-project ../Payments.Mvc/Payments.Mvc.csproj --context ApplicationDbContext +# usage from PM console in the Payments.Core directory: ./ExecuteMigration.sh + +echo 'All done'; \ No newline at end of file From 175348f0366d8aa1cb5ec58b94d7d40dcaa711dc Mon Sep 17 00:00:00 2001 From: Jason Sylvestre Date: Thu, 18 Sep 2025 09:04:08 -0700 Subject: [PATCH 6/9] Adds EF Core migrations history table Adds the '__EFMigrationsHistory' table to the database project. This table is used by Entity Framework Core to track applied migrations. Including it in the project allows EF Core to manage database schema updates. --- src/Payments.Sql/Payments.Sql.sqlproj | 1 + src/Payments.Sql/dbo/Tables/__EFMigrationsHistory.sql | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 src/Payments.Sql/dbo/Tables/__EFMigrationsHistory.sql diff --git a/src/Payments.Sql/Payments.Sql.sqlproj b/src/Payments.Sql/Payments.Sql.sqlproj index fd3f7bc4..06415d0a 100644 --- a/src/Payments.Sql/Payments.Sql.sqlproj +++ b/src/Payments.Sql/Payments.Sql.sqlproj @@ -91,6 +91,7 @@ + diff --git a/src/Payments.Sql/dbo/Tables/__EFMigrationsHistory.sql b/src/Payments.Sql/dbo/Tables/__EFMigrationsHistory.sql new file mode 100644 index 00000000..b9722234 --- /dev/null +++ b/src/Payments.Sql/dbo/Tables/__EFMigrationsHistory.sql @@ -0,0 +1,6 @@ +CREATE TABLE [dbo].[__EFMigrationsHistory] ( + [MigrationId] NVARCHAR (150) NOT NULL, + [ProductVersion] NVARCHAR (32) NOT NULL, + CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY CLUSTERED ([MigrationId] ASC) +); + From acb5feccbca9cd2d9c7f2a49f278ebb9b266db0d Mon Sep 17 00:00:00 2001 From: Jason Sylvestre Date: Thu, 18 Sep 2025 09:49:38 -0700 Subject: [PATCH 7/9] Validates and standardizes invoice types Adds validation to invoice types to ensure data integrity. Shortens the "CreditCard" invoice type to "CC" for consistency and to adhere to the length constraint. --- src/Payments.Core/Domain/Invoice.cs | 2 ++ src/Payments.Core/Domain/Team.cs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Payments.Core/Domain/Invoice.cs b/src/Payments.Core/Domain/Invoice.cs index 31d4c7d7..cbeae751 100644 --- a/src/Payments.Core/Domain/Invoice.cs +++ b/src/Payments.Core/Domain/Invoice.cs @@ -115,6 +115,8 @@ public string GetFormattedId() [JsonIgnore] public IList PaymentEvents { get; set; } + [Required] + [StringLength(10)] public string Type { get; set; } = InvoiceTypes.CreditCard; // CC or Recharge // ---------------------- diff --git a/src/Payments.Core/Domain/Team.cs b/src/Payments.Core/Domain/Team.cs index b533485f..a801b167 100644 --- a/src/Payments.Core/Domain/Team.cs +++ b/src/Payments.Core/Domain/Team.cs @@ -71,6 +71,8 @@ public Team() [StringLength(128)] public string WebHookApiKey { get; set; } + [Required] + [StringLength(10)] public string AllowedInvoiceType { get; set; } = AllowedInvoiceTypes.CreditCard; [NotMapped] @@ -100,7 +102,7 @@ public TeamPermission AddPermission(User user, TeamRole role) /// public static class AllowedInvoiceTypes { - public const string CreditCard = "CreditCard"; + public const string CreditCard = "CC"; public const string Recharge = "Recharge"; public const string Both = "Both"; } From 195438d966931ce45ada7877cebda91c8b49ed1d Mon Sep 17 00:00:00 2001 From: Jason Sylvestre Date: Thu, 18 Sep 2025 10:28:41 -0700 Subject: [PATCH 8/9] Configures default invoice types for new teams Ensures that newly created teams default to only allowing credit card invoice types. This improves consistency and simplifies onboarding. Sets the default value for invoice types to CreditCard and configures max length. --- .../Data/ApplicationDbContext.cs | 1 + src/Payments.Core/Domain/Invoice.cs | 2 +- src/Payments.Core/Domain/Team.cs | 11 +- .../20250918170102_Recharges.Designer.cs | 1112 +++++++++++++++++ .../Migrations/20250918170102_Recharges.cs | 48 + .../ApplicationDbContextModelSnapshot.cs | 16 + 6 files changed, 1188 insertions(+), 2 deletions(-) create mode 100644 src/Payments.Core/Migrations/20250918170102_Recharges.Designer.cs create mode 100644 src/Payments.Core/Migrations/20250918170102_Recharges.cs diff --git a/src/Payments.Core/Data/ApplicationDbContext.cs b/src/Payments.Core/Data/ApplicationDbContext.cs index e0d80a86..7bf55748 100644 --- a/src/Payments.Core/Data/ApplicationDbContext.cs +++ b/src/Payments.Core/Data/ApplicationDbContext.cs @@ -58,6 +58,7 @@ protected override void OnModelCreating(ModelBuilder builder) LogMessage.OnModelCreating(builder); MoneyMovementJobRecord.OnModelCreating(builder); TaxReportJobRecord.OnModelCreating(builder); + Team.OnModelCreating(builder); } } } diff --git a/src/Payments.Core/Domain/Invoice.cs b/src/Payments.Core/Domain/Invoice.cs index cbeae751..d967e1fc 100644 --- a/src/Payments.Core/Domain/Invoice.cs +++ b/src/Payments.Core/Domain/Invoice.cs @@ -242,7 +242,7 @@ protected internal static void OnModelCreating(ModelBuilder builder) // Set default value for Type column builder.Entity() .Property(i => i.Type) - .HasDefaultValue("CC"); + .HasDefaultValue(InvoiceTypes.CreditCard); builder.Entity().HasIndex(a => a.Type); } diff --git a/src/Payments.Core/Domain/Team.cs b/src/Payments.Core/Domain/Team.cs index a801b167..f5236267 100644 --- a/src/Payments.Core/Domain/Team.cs +++ b/src/Payments.Core/Domain/Team.cs @@ -1,9 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using Newtonsoft.Json; namespace Payments.Core.Domain { @@ -96,6 +97,14 @@ public TeamPermission AddPermission(User user, TeamRole role) return permission; } + protected internal static void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .Property(t => t.AllowedInvoiceType) + .HasMaxLength(10) + .HasDefaultValue(AllowedInvoiceTypes.CreditCard); + } + /// /// These are a superset of the Invoice.Types /// (Adds Both value) diff --git a/src/Payments.Core/Migrations/20250918170102_Recharges.Designer.cs b/src/Payments.Core/Migrations/20250918170102_Recharges.Designer.cs new file mode 100644 index 00000000..2fcf71e6 --- /dev/null +++ b/src/Payments.Core/Migrations/20250918170102_Recharges.Designer.cs @@ -0,0 +1,1112 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Payments.Core.Data; + +#nullable disable + +namespace Payments.Core.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250918170102_Recharges")] + partial class Recharges + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Payments.Core.Domain.Coupon", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Code") + .HasColumnType("nvarchar(max)"); + + b.Property("DiscountAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("DiscountPercent") + .HasColumnType("decimal(18,5)"); + + b.Property("ExpiresAt") + .HasColumnType("datetime2"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("Coupons"); + }); + + modelBuilder.Entity("Payments.Core.Domain.FinancialAccount", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Account") + .HasMaxLength(7) + .HasColumnType("nvarchar(7)"); + + b.Property("Chart") + .HasMaxLength(1) + .HasColumnType("nvarchar(1)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("FinancialSegmentString") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("IsDefault") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Project") + .HasMaxLength(9) + .HasColumnType("nvarchar(9)"); + + b.Property("SubAccount") + .HasMaxLength(5) + .HasColumnType("nvarchar(5)"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("FinancialAccounts"); + }); + + modelBuilder.Entity("Payments.Core.Domain.History", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ActionDateTime") + .HasColumnType("datetime2"); + + b.Property("Actor") + .HasColumnType("nvarchar(max)"); + + b.Property("Data") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("History"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Invoice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("AccountId") + .HasColumnType("int"); + + b.Property("CalculatedDiscount") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedSubtotal") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedTaxAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedTaxableAmount") + .HasColumnType("decimal(18,2)"); + + b.Property("CalculatedTotal") + .HasColumnType("decimal(18,2)"); + + b.Property("CouponId") + .HasColumnType("int"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CustomerAddress") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerCompany") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("CustomerName") + .HasColumnType("nvarchar(max)"); + + b.Property("Deleted") + .HasColumnType("bit"); + + b.Property("DeletedAt") + .HasColumnType("datetime2"); + + b.Property("DraftCount") + .HasColumnType("int"); + + b.Property("DueDate") + .HasColumnType("datetime2"); + + b.Property("KfsTrackingNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("LinkId") + .HasColumnType("nvarchar(max)"); + + b.Property("ManualDiscount") + .HasColumnType("decimal(18,2)"); + + b.Property("Memo") + .HasColumnType("nvarchar(max)"); + + b.Property("Paid") + .HasColumnType("bit"); + + b.Property("PaidAt") + .HasColumnType("datetime2"); + + b.Property("PaymentProcessorId") + .HasColumnType("nvarchar(max)"); + + b.Property("PaymentType") + .HasColumnType("nvarchar(max)"); + + b.Property("Refunded") + .HasColumnType("bit"); + + b.Property("RefundedAt") + .HasColumnType("datetime2"); + + b.Property("Sent") + .HasColumnType("bit"); + + b.Property("SentAt") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.Property("TaxPercent") + .HasColumnType("decimal(18,5)"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.Property("Type") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)") + .HasDefaultValue("CC"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("CouponId"); + + b.HasIndex("TeamId"); + + b.HasIndex("Type"); + + b.ToTable("Invoices"); + }); + + modelBuilder.Entity("Payments.Core.Domain.InvoiceAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ContentType") + .HasColumnType("nvarchar(max)"); + + b.Property("FileName") + .HasColumnType("nvarchar(max)"); + + b.Property("Identifier") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("Size") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("InvoiceAttachments"); + }); + + modelBuilder.Entity("Payments.Core.Domain.InvoiceLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Expired") + .HasColumnType("bit"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("LinkId") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("InvoiceLinks"); + }); + + modelBuilder.Entity("Payments.Core.Domain.LineItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Amount") + .HasColumnType("decimal(18,2)"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("decimal(18,2)"); + + b.Property("TaxExempt") + .HasColumnType("bit"); + + b.Property("Total") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("LineItems"); + }); + + modelBuilder.Entity("Payments.Core.Domain.LogMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("CorrelationId") + .HasColumnType("nvarchar(max)"); + + b.Property("Exception") + .HasColumnType("nvarchar(max)"); + + b.Property("JobId") + .HasColumnType("nvarchar(450)"); + + b.Property("JobName") + .HasColumnType("nvarchar(max)"); + + b.Property("Level") + .HasColumnType("nvarchar(max)"); + + b.Property("LogEvent") + .HasColumnType("nvarchar(max)"); + + b.Property("Message") + .HasColumnType("nvarchar(max)"); + + b.Property("MessageTemplate") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("xml"); + + b.Property("Source") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeStamp") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("JobId"); + + b.ToTable("Logs", (string)null); + }); + + modelBuilder.Entity("Payments.Core.Domain.MoneyMovementJobRecord", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("RanOn") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("MoneyMovementJobRecords"); + }); + + modelBuilder.Entity("Payments.Core.Domain.PaymentEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Amount") + .HasColumnType("decimal(18,2)"); + + b.Property("BillingCity") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("BillingCompany") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.Property("BillingCountry") + .HasMaxLength(2) + .HasColumnType("nvarchar(2)"); + + b.Property("BillingEmail") + .HasMaxLength(1500) + .HasColumnType("nvarchar(1500)"); + + b.Property("BillingFirstName") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.Property("BillingLastName") + .HasMaxLength(60) + .HasColumnType("nvarchar(60)"); + + b.Property("BillingPhone") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("BillingPostalCode") + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("BillingState") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("BillingStreet1") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("BillingStreet2") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("CardExpiry") + .HasColumnType("datetime2"); + + b.Property("CardNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("CardType") + .HasMaxLength(3) + .HasColumnType("nvarchar(3)"); + + b.Property("Decision") + .HasColumnType("nvarchar(max)"); + + b.Property("InvoiceId") + .HasColumnType("int"); + + b.Property("OccuredAt") + .HasColumnType("datetime2"); + + b.Property("Processor") + .HasColumnType("nvarchar(max)"); + + b.Property("ProcessorId") + .HasColumnType("nvarchar(max)"); + + b.Property("ReturnedResults") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("InvoiceId"); + + b.ToTable("PaymentEvents"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TaxReportJobRecord", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("RanOn") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("TaxReportJobRecords"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("AllowedInvoiceType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)") + .HasDefaultValue("CC"); + + b.Property("ApiKey") + .HasColumnType("nvarchar(max)"); + + b.Property("ContactEmail") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ContactName") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ContactPhoneNumber") + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("nvarchar(40)"); + + b.Property("WebHookApiKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("Teams"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TeamPermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("RoleId") + .HasColumnType("int"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("TeamId"); + + b.HasIndex("UserId"); + + b.ToTable("TeamPermissions"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TeamRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.ToTable("TeamRoles"); + }); + + modelBuilder.Entity("Payments.Core.Domain.User", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("CampusKerberos") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LastName") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Payments.Core.Domain.WebHook", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("ContentType") + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("TeamId") + .HasColumnType("int"); + + b.Property("TriggerOnPaid") + .HasColumnType("bit"); + + b.Property("TriggerOnReconcile") + .HasColumnType("bit"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("TeamId"); + + b.ToTable("WebHooks"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Payments.Core.Domain.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Payments.Core.Domain.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Payments.Core.Domain.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Payments.Core.Domain.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Payments.Core.Domain.Coupon", b => + { + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany("Coupons") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Payments.Core.Domain.FinancialAccount", b => + { + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany("Accounts") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Payments.Core.Domain.History", b => + { + b.HasOne("Payments.Core.Domain.Invoice", null) + .WithMany("History") + .HasForeignKey("InvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Payments.Core.Domain.Invoice", b => + { + b.HasOne("Payments.Core.Domain.FinancialAccount", "Account") + .WithMany() + .HasForeignKey("AccountId"); + + b.HasOne("Payments.Core.Domain.Coupon", "Coupon") + .WithMany("Invoices") + .HasForeignKey("CouponId"); + + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("Coupon"); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Payments.Core.Domain.InvoiceAttachment", b => + { + b.HasOne("Payments.Core.Domain.Invoice", "Invoice") + .WithMany("Attachments") + .HasForeignKey("InvoiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("Payments.Core.Domain.InvoiceLink", b => + { + b.HasOne("Payments.Core.Domain.Invoice", "Invoice") + .WithMany() + .HasForeignKey("InvoiceId"); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("Payments.Core.Domain.LineItem", b => + { + b.HasOne("Payments.Core.Domain.Invoice", null) + .WithMany("Items") + .HasForeignKey("InvoiceId"); + }); + + modelBuilder.Entity("Payments.Core.Domain.LogMessage", b => + { + b.HasOne("Payments.Core.Domain.MoneyMovementJobRecord", null) + .WithMany("Logs") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Payments.Core.Domain.TaxReportJobRecord", null) + .WithMany("Logs") + .HasForeignKey("JobId") + .OnDelete(DeleteBehavior.Restrict); + }); + + modelBuilder.Entity("Payments.Core.Domain.PaymentEvent", b => + { + b.HasOne("Payments.Core.Domain.Invoice", "Invoice") + .WithMany("PaymentEvents") + .HasForeignKey("InvoiceId"); + + b.Navigation("Invoice"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TeamPermission", b => + { + b.HasOne("Payments.Core.Domain.TeamRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany("Permissions") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Payments.Core.Domain.User", "User") + .WithMany("TeamPermissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Role"); + + b.Navigation("Team"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Payments.Core.Domain.WebHook", b => + { + b.HasOne("Payments.Core.Domain.Team", "Team") + .WithMany("WebHooks") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Team"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Coupon", b => + { + b.Navigation("Invoices"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Invoice", b => + { + b.Navigation("Attachments"); + + b.Navigation("History"); + + b.Navigation("Items"); + + b.Navigation("PaymentEvents"); + }); + + modelBuilder.Entity("Payments.Core.Domain.MoneyMovementJobRecord", b => + { + b.Navigation("Logs"); + }); + + modelBuilder.Entity("Payments.Core.Domain.TaxReportJobRecord", b => + { + b.Navigation("Logs"); + }); + + modelBuilder.Entity("Payments.Core.Domain.Team", b => + { + b.Navigation("Accounts"); + + b.Navigation("Coupons"); + + b.Navigation("Permissions"); + + b.Navigation("WebHooks"); + }); + + modelBuilder.Entity("Payments.Core.Domain.User", b => + { + b.Navigation("TeamPermissions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Payments.Core/Migrations/20250918170102_Recharges.cs b/src/Payments.Core/Migrations/20250918170102_Recharges.cs new file mode 100644 index 00000000..5782266b --- /dev/null +++ b/src/Payments.Core/Migrations/20250918170102_Recharges.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Payments.Core.Migrations +{ + public partial class Recharges : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "AllowedInvoiceType", + table: "Teams", + type: "nvarchar(10)", + maxLength: 10, + nullable: false, + defaultValue: "CC"); + + migrationBuilder.AddColumn( + name: "Type", + table: "Invoices", + type: "nvarchar(10)", + maxLength: 10, + nullable: false, + defaultValue: "CC"); + + migrationBuilder.CreateIndex( + name: "IX_Invoices_Type", + table: "Invoices", + column: "Type"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Invoices_Type", + table: "Invoices"); + + migrationBuilder.DropColumn( + name: "AllowedInvoiceType", + table: "Teams"); + + migrationBuilder.DropColumn( + name: "Type", + table: "Invoices"); + } + } +} diff --git a/src/Payments.Core/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Payments.Core/Migrations/ApplicationDbContextModelSnapshot.cs index 86a8ced2..ba6f4902 100644 --- a/src/Payments.Core/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Payments.Core/Migrations/ApplicationDbContextModelSnapshot.cs @@ -372,6 +372,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("TeamId") .HasColumnType("int"); + b.Property("Type") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)") + .HasDefaultValue("CC"); + b.HasKey("Id"); b.HasIndex("AccountId"); @@ -380,6 +387,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("TeamId"); + b.HasIndex("Type"); + b.ToTable("Invoices"); }); @@ -655,6 +664,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + b.Property("AllowedInvoiceType") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)") + .HasDefaultValue("CC"); + b.Property("ApiKey") .HasColumnType("nvarchar(max)"); From 968a9dd2bcb48aba2b7844c0762ec3152285d581 Mon Sep 17 00:00:00 2001 From: Jason Sylvestre Date: Fri, 26 Sep 2025 07:12:19 -0700 Subject: [PATCH 9/9] Adds type and allowed invoice type to tests Adds new fields to the Invoice and Team database tests to include validation attributes. This ensures that the tests accurately reflect the database schema and its constraints. --- tests/Payments.Tests/DatabaseTests/InvoiceTests.cs | 5 +++++ tests/Payments.Tests/DatabaseTests/TeamTests.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/tests/Payments.Tests/DatabaseTests/InvoiceTests.cs b/tests/Payments.Tests/DatabaseTests/InvoiceTests.cs index c3507071..515d500a 100644 --- a/tests/Payments.Tests/DatabaseTests/InvoiceTests.cs +++ b/tests/Payments.Tests/DatabaseTests/InvoiceTests.cs @@ -132,6 +132,11 @@ public void TestAllFieldsInTheDatabaseHaveBeenTested() { "[System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute()]", })); + expectedFields.Add(new NameAndType("Type", "System.String", new List + { + "[System.ComponentModel.DataAnnotations.RequiredAttribute()]", + "[System.ComponentModel.DataAnnotations.StringLengthAttribute((Int32)10)]", + })); diff --git a/tests/Payments.Tests/DatabaseTests/TeamTests.cs b/tests/Payments.Tests/DatabaseTests/TeamTests.cs index f1f73741..1b4d1008 100644 --- a/tests/Payments.Tests/DatabaseTests/TeamTests.cs +++ b/tests/Payments.Tests/DatabaseTests/TeamTests.cs @@ -18,6 +18,11 @@ public void TestAllFieldsInTheDatabaseHaveBeenTested() { "[Newtonsoft.Json.JsonIgnoreAttribute()]", })); + expectedFields.Add(new NameAndType("AllowedInvoiceType", "System.String", new List + { + "[System.ComponentModel.DataAnnotations.RequiredAttribute()]", + "[System.ComponentModel.DataAnnotations.StringLengthAttribute((Int32)10)]", + })); expectedFields.Add(new NameAndType("ApiKey", "System.String", new List { "[Newtonsoft.Json.JsonIgnoreAttribute()]",