From e8c5fbb5cc1f0141f3818f2a09ca86c8d1f556f7 Mon Sep 17 00:00:00 2001 From: lumijiez <59575049+lumijiez@users.noreply.github.com> Date: Mon, 26 May 2025 13:10:38 +0300 Subject: [PATCH] Redo entities and configs, plus dummy aspnet identity config --- src/Printbase.Domain/Entities/EntityBase.cs | 10 + src/Printbase.Domain/Entities/Orders/Order.cs | 17 + .../Entities/Orders/OrderAddress.cs | 13 + .../Entities/Orders/OrderItem.cs | 17 + .../Entities/Orders/OrderStatus.cs | 9 + .../Entities/Orders/ShippingStatus.cs | 9 + .../Entities/Product/Category.cs | 15 + .../Entities/Product/Product.cs | 16 + .../Entities/Product/ProductVariant.cs | 16 + .../Entities/Users/Address.cs | 14 + .../Entities/Users/ApplicationRole.cs | 16 + .../Entities/Users/ApplicationUser.cs | 17 + src/Printbase.Domain/Printbase.Domain.csproj | 4 + .../Configuration/AddressConfiguration.cs | 63 + .../ApplicationRoleConfiguration.cs | 65 + .../ApplicationUserConfiguration.cs | 30 + .../Configuration/CategoryConfiguration.cs | 88 ++ .../Configuration/EntityBaseConfiguration.cs | 40 + .../OrderAddressConfiguration.cs | 39 + .../Configuration/OrderConfiguration.cs | 77 ++ .../Configuration/OrderItemConfiguration.cs | 64 + .../Configuration/OrderStatusConfiguration.cs | 31 + .../Configuration/ProductConfiguration.cs | 61 + .../ProductVariantConfiguration.cs | 60 + .../ShippingStatusConfiguration.cs | 31 + .../Database/ApplicationDbContext.cs | 33 +- .../20250526100748_InitialSetup.Designer.cs | 1150 +++++++++++++++++ .../Migrations/20250526100748_InitialSetup.cs | 777 +++++++++++ .../ApplicationDbContextModelSnapshot.cs | 1147 ++++++++++++++++ .../Printbase.Infrastructure.csproj | 7 +- src/Printbase.WebApi/Startup.cs | 51 + .../appsettings.Development.json | 2 +- src/Printbase.WebApi/appsettings.json | 2 +- 33 files changed, 3986 insertions(+), 5 deletions(-) create mode 100644 src/Printbase.Domain/Entities/EntityBase.cs create mode 100644 src/Printbase.Domain/Entities/Orders/Order.cs create mode 100644 src/Printbase.Domain/Entities/Orders/OrderAddress.cs create mode 100644 src/Printbase.Domain/Entities/Orders/OrderItem.cs create mode 100644 src/Printbase.Domain/Entities/Orders/OrderStatus.cs create mode 100644 src/Printbase.Domain/Entities/Orders/ShippingStatus.cs create mode 100644 src/Printbase.Domain/Entities/Product/Category.cs create mode 100644 src/Printbase.Domain/Entities/Product/Product.cs create mode 100644 src/Printbase.Domain/Entities/Product/ProductVariant.cs create mode 100644 src/Printbase.Domain/Entities/Users/Address.cs create mode 100644 src/Printbase.Domain/Entities/Users/ApplicationRole.cs create mode 100644 src/Printbase.Domain/Entities/Users/ApplicationUser.cs create mode 100644 src/Printbase.Infrastructure/Configuration/AddressConfiguration.cs create mode 100644 src/Printbase.Infrastructure/Configuration/ApplicationRoleConfiguration.cs create mode 100644 src/Printbase.Infrastructure/Configuration/ApplicationUserConfiguration.cs create mode 100644 src/Printbase.Infrastructure/Configuration/CategoryConfiguration.cs create mode 100644 src/Printbase.Infrastructure/Configuration/EntityBaseConfiguration.cs create mode 100644 src/Printbase.Infrastructure/Configuration/OrderAddressConfiguration.cs create mode 100644 src/Printbase.Infrastructure/Configuration/OrderConfiguration.cs create mode 100644 src/Printbase.Infrastructure/Configuration/OrderItemConfiguration.cs create mode 100644 src/Printbase.Infrastructure/Configuration/OrderStatusConfiguration.cs create mode 100644 src/Printbase.Infrastructure/Configuration/ProductConfiguration.cs create mode 100644 src/Printbase.Infrastructure/Configuration/ProductVariantConfiguration.cs create mode 100644 src/Printbase.Infrastructure/Configuration/ShippingStatusConfiguration.cs create mode 100644 src/Printbase.Infrastructure/Migrations/20250526100748_InitialSetup.Designer.cs create mode 100644 src/Printbase.Infrastructure/Migrations/20250526100748_InitialSetup.cs create mode 100644 src/Printbase.Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs diff --git a/src/Printbase.Domain/Entities/EntityBase.cs b/src/Printbase.Domain/Entities/EntityBase.cs new file mode 100644 index 0000000..6da79c0 --- /dev/null +++ b/src/Printbase.Domain/Entities/EntityBase.cs @@ -0,0 +1,10 @@ +namespace Printbase.Domain.Entities; + +public abstract class EntityBase +{ + public Guid Id { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime ModifiedAt { get; set; } + public string CreatedBy { get; set; } + public string ModifiedBy { get; set; } +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Orders/Order.cs b/src/Printbase.Domain/Entities/Orders/Order.cs new file mode 100644 index 0000000..f93d7a1 --- /dev/null +++ b/src/Printbase.Domain/Entities/Orders/Order.cs @@ -0,0 +1,17 @@ +namespace Printbase.Domain.Entities; + +public class Order : EntityBase +{ + public string UserId { get; set; } + public DateTime OrderDate { get; set; } + public decimal TotalPrice { get; set; } + public int OrderStatusId { get; set; } + public int ShippingStatusId { get; set; } + public string OrderNumber { get; set; } + public string Notes { get; set; } + + public virtual OrderStatus OrderStatus { get; set; } + public virtual ShippingStatus ShippingStatus { get; set; } + public virtual OrderAddress OrderAddress { get; set; } + public virtual ICollection OrderItems { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Orders/OrderAddress.cs b/src/Printbase.Domain/Entities/Orders/OrderAddress.cs new file mode 100644 index 0000000..30573a5 --- /dev/null +++ b/src/Printbase.Domain/Entities/Orders/OrderAddress.cs @@ -0,0 +1,13 @@ +namespace Printbase.Domain.Entities; + +public class OrderAddress : EntityBase +{ + public Guid OrderId { get; set; } + public string Street { get; set; } + public string City { get; set; } + public string State { get; set; } + public string PostalCode { get; set; } + public string Country { get; set; } + + public virtual Order Order { get; set; } +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Orders/OrderItem.cs b/src/Printbase.Domain/Entities/Orders/OrderItem.cs new file mode 100644 index 0000000..c818182 --- /dev/null +++ b/src/Printbase.Domain/Entities/Orders/OrderItem.cs @@ -0,0 +1,17 @@ +namespace Printbase.Domain.Entities; + +public class OrderItem : EntityBase +{ + public Guid OrderId { get; set; } + public Guid ProductId { get; set; } + public Guid? ProductVariantId { get; set; } + public int Quantity { get; set; } + public decimal UnitPrice { get; set; } + public decimal TotalPrice { get; set; } + public string CustomizationImageUrl { get; set; } + public string CustomizationDescription { get; set; } + + public virtual Order Order { get; set; } + public virtual Product Product { get; set; } + public virtual ProductVariant ProductVariant { get; set; } +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Orders/OrderStatus.cs b/src/Printbase.Domain/Entities/Orders/OrderStatus.cs new file mode 100644 index 0000000..94211c5 --- /dev/null +++ b/src/Printbase.Domain/Entities/Orders/OrderStatus.cs @@ -0,0 +1,9 @@ +namespace Printbase.Domain.Entities; + +public class OrderStatus +{ + public int Id { get; set; } + public string Name { get; set; } + + public virtual ICollection Orders { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Orders/ShippingStatus.cs b/src/Printbase.Domain/Entities/Orders/ShippingStatus.cs new file mode 100644 index 0000000..7f4a923 --- /dev/null +++ b/src/Printbase.Domain/Entities/Orders/ShippingStatus.cs @@ -0,0 +1,9 @@ +namespace Printbase.Domain.Entities; + +public class ShippingStatus +{ + public int Id { get; set; } + public string Name { get; set; } + + public virtual ICollection Orders { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Product/Category.cs b/src/Printbase.Domain/Entities/Product/Category.cs new file mode 100644 index 0000000..9eab20d --- /dev/null +++ b/src/Printbase.Domain/Entities/Product/Category.cs @@ -0,0 +1,15 @@ +namespace Printbase.Domain.Entities; + +public class Category : EntityBase +{ + public string Name { get; set; } + public string Description { get; set; } + public string ImageUrl { get; set; } + public int SortOrder { get; set; } + public bool IsActive { get; set; } + public Guid? ParentCategoryId { get; set; } + + public virtual Category ParentCategory { get; set; } + public virtual ICollection SubCategories { get; set; } = new List(); + public virtual ICollection Products { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Product/Product.cs b/src/Printbase.Domain/Entities/Product/Product.cs new file mode 100644 index 0000000..7ac0b54 --- /dev/null +++ b/src/Printbase.Domain/Entities/Product/Product.cs @@ -0,0 +1,16 @@ +namespace Printbase.Domain.Entities; + +public class Product : EntityBase +{ + public string Name { get; set; } + public string Description { get; set; } + public decimal BasePrice { get; set; } + public bool IsCustomizable { get; set; } + public bool IsActive { get; set; } + public string ImageUrl { get; set; } + public Guid? CategoryId { get; set; } + + public virtual Category Category { get; set; } + public virtual ICollection ProductVariants { get; set; } = new List(); + public virtual ICollection OrderItems { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Product/ProductVariant.cs b/src/Printbase.Domain/Entities/Product/ProductVariant.cs new file mode 100644 index 0000000..8d19a67 --- /dev/null +++ b/src/Printbase.Domain/Entities/Product/ProductVariant.cs @@ -0,0 +1,16 @@ +namespace Printbase.Domain.Entities; + +public class ProductVariant : EntityBase +{ + public Guid ProductId { get; set; } + public string Size { get; set; } + public string Color { get; set; } + public decimal Price { get; set; } + public string ImageUrl { get; set; } + public string SKU { get; set; } + public int StockQuantity { get; set; } + public bool IsActive { get; set; } + + public virtual Product Product { get; set; } + public virtual ICollection OrderItems { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Users/Address.cs b/src/Printbase.Domain/Entities/Users/Address.cs new file mode 100644 index 0000000..3d2b016 --- /dev/null +++ b/src/Printbase.Domain/Entities/Users/Address.cs @@ -0,0 +1,14 @@ +namespace Printbase.Domain.Entities; + +public class Address : EntityBase +{ + public string UserId { get; set; } + public string AddressType { get; set; } + public string Street { get; set; } + public string City { get; set; } + public string State { get; set; } + public string PostalCode { get; set; } + public string Country { get; set; } + public bool IsDefault { get; set; } + public bool IsActive { get; set; } +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Users/ApplicationRole.cs b/src/Printbase.Domain/Entities/Users/ApplicationRole.cs new file mode 100644 index 0000000..9930478 --- /dev/null +++ b/src/Printbase.Domain/Entities/Users/ApplicationRole.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Identity; + +public class ApplicationRole : IdentityRole +{ + public string Description { get; set; } + public DateTime CreatedAt { get; set; } + public bool IsActive { get; set; } + + public ApplicationRole() : base() + { + } + + public ApplicationRole(string roleName) : base(roleName) + { + } +} \ No newline at end of file diff --git a/src/Printbase.Domain/Entities/Users/ApplicationUser.cs b/src/Printbase.Domain/Entities/Users/ApplicationUser.cs new file mode 100644 index 0000000..fee1451 --- /dev/null +++ b/src/Printbase.Domain/Entities/Users/ApplicationUser.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore.Identity; + +namespace Printbase.Domain.Entities.Users; + +public class ApplicationUser : IdentityUser +{ + public string FirstName { get; set; } + public string LastName { get; set; } + public DateTime? DateOfBirth { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime LastLoginAt { get; set; } + public bool IsActive { get; set; } + public string ProfileImageUrl { get; set; } + + public virtual ICollection
Addresses { get; set; } = new List
(); + public virtual ICollection Orders { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/Printbase.Domain/Printbase.Domain.csproj b/src/Printbase.Domain/Printbase.Domain.csproj index 93e8640..17f66ee 100644 --- a/src/Printbase.Domain/Printbase.Domain.csproj +++ b/src/Printbase.Domain/Printbase.Domain.csproj @@ -10,4 +10,8 @@ + + + + diff --git a/src/Printbase.Infrastructure/Configuration/AddressConfiguration.cs b/src/Printbase.Infrastructure/Configuration/AddressConfiguration.cs new file mode 100644 index 0000000..a0e16f8 --- /dev/null +++ b/src/Printbase.Infrastructure/Configuration/AddressConfiguration.cs @@ -0,0 +1,63 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Printbase.Domain.Entities; +using Printbase.Domain.Entities.Users; + +namespace Printbase.Infrastructure.Configuration; + +public class AddressConfiguration : EntityBaseConfiguration
+{ + public override void Configure(EntityTypeBuilder
builder) + { + base.Configure(builder); + + builder.Property(a => a.UserId) + .IsRequired() + .HasMaxLength(450); + + builder.Property(a => a.AddressType) + .IsRequired() + .HasMaxLength(50); + + builder.Property(a => a.Street) + .IsRequired() + .HasMaxLength(200); + + builder.Property(a => a.City) + .IsRequired() + .HasMaxLength(100); + + builder.Property(a => a.State) + .HasMaxLength(100); + + builder.Property(a => a.PostalCode) + .IsRequired() + .HasMaxLength(20); + + builder.Property(a => a.Country) + .IsRequired() + .HasMaxLength(100); + + builder.Property(a => a.IsDefault) + .IsRequired() + .HasDefaultValue(false); + + builder.Property(a => a.IsActive) + .IsRequired() + .HasDefaultValue(true); + + builder.HasOne() + .WithMany(u => u.Addresses) + .HasForeignKey(a => a.UserId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasIndex(a => a.UserId) + .HasDatabaseName("IX_Address_UserId"); + + builder.HasIndex(a => new { a.UserId, a.AddressType }) + .HasDatabaseName("IX_Address_User_Type"); + + builder.HasIndex(a => new { a.UserId, a.IsDefault }) + .HasDatabaseName("IX_Address_User_Default"); + } +} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Configuration/ApplicationRoleConfiguration.cs b/src/Printbase.Infrastructure/Configuration/ApplicationRoleConfiguration.cs new file mode 100644 index 0000000..d1b098f --- /dev/null +++ b/src/Printbase.Infrastructure/Configuration/ApplicationRoleConfiguration.cs @@ -0,0 +1,65 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Printbase.Infrastructure.Configuration; + +public class ApplicationRoleConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + builder.Property(r => r.Description) + .HasMaxLength(500); + + builder.Property(r => r.CreatedAt) + .IsRequired() + .HasDefaultValueSql("GETUTCDATE()"); + + builder.Property(r => r.IsActive) + .IsRequired() + .HasDefaultValue(true); + + builder.HasIndex(r => r.IsActive) + .HasDatabaseName("IX_ApplicationRole_IsActive"); + + var seedDate = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + builder.HasData( + new ApplicationRole + { + Id = "1", + Name = "Administrator", + NormalizedName = "ADMINISTRATOR", + Description = "Full system access", + CreatedAt = seedDate, + IsActive = true + }, + new ApplicationRole + { + Id = "2", + Name = "Customer", + NormalizedName = "CUSTOMER", + Description = "Standard customer access", + CreatedAt = seedDate, + IsActive = true + }, + new ApplicationRole + { + Id = "3", + Name = "OrderManager", + NormalizedName = "ORDERMANAGER", + Description = "Manage orders and fulfillment", + CreatedAt = seedDate, + IsActive = true + }, + new ApplicationRole + { + Id = "4", + Name = "ProductManager", + NormalizedName = "PRODUCTMANAGER", + Description = "Manage products and inventory", + CreatedAt = seedDate, + IsActive = true + } + ); + } + } \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Configuration/ApplicationUserConfiguration.cs b/src/Printbase.Infrastructure/Configuration/ApplicationUserConfiguration.cs new file mode 100644 index 0000000..cdfdd80 --- /dev/null +++ b/src/Printbase.Infrastructure/Configuration/ApplicationUserConfiguration.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Printbase.Domain.Entities.Users; + +namespace Printbase.Infrastructure.Configuration; + +public class ApplicationUserConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.Property(u => u.FirstName) + .HasMaxLength(100); + + builder.Property(u => u.LastName) + .HasMaxLength(100); + + builder.Property(u => u.ProfileImageUrl) + .HasMaxLength(500); + + builder.Property(u => u.CreatedAt) + .IsRequired() + .HasDefaultValueSql("GETUTCDATE()"); + + builder.Property(u => u.LastLoginAt) + .HasDefaultValueSql("GETUTCDATE()"); + + builder.Property(u => u.IsActive) + .HasDefaultValue(true); + } +} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Configuration/CategoryConfiguration.cs b/src/Printbase.Infrastructure/Configuration/CategoryConfiguration.cs new file mode 100644 index 0000000..6bc442d --- /dev/null +++ b/src/Printbase.Infrastructure/Configuration/CategoryConfiguration.cs @@ -0,0 +1,88 @@ +using Printbase.Domain.Entities; + +namespace Printbase.Infrastructure.Configuration; + +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +public class CategoryConfiguration : EntityBaseConfiguration +{ + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(c => c.Name) + .IsRequired() + .HasMaxLength(200); + + builder.Property(c => c.Description) + .HasMaxLength(2000); + + builder.Property(c => c.ImageUrl) + .HasMaxLength(500); + + builder.Property(c => c.SortOrder) + .IsRequired() + .HasDefaultValue(0); + + builder.Property(c => c.IsActive) + .IsRequired() + .HasDefaultValue(true); + + builder.Property(c => c.ParentCategoryId) + .IsRequired(false); + + builder.HasOne(c => c.ParentCategory) + .WithMany(c => c.SubCategories) + .HasForeignKey(c => c.ParentCategoryId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasIndex(c => c.Name) + .HasDatabaseName("IX_Category_Name"); + + builder.HasIndex(c => c.IsActive) + .HasDatabaseName("IX_Category_IsActive"); + + builder.HasIndex(c => c.ParentCategoryId) + .HasDatabaseName("IX_Category_ParentCategoryId"); + + builder.HasIndex(c => new { c.ParentCategoryId, c.SortOrder }) + .HasDatabaseName("IX_Category_Parent_SortOrder"); + + builder.HasIndex(c => new { c.IsActive, c.SortOrder }) + .HasDatabaseName("IX_Category_Active_SortOrder"); + + builder.HasData( + new Category + { + Id = Guid.Parse("11111111-1111-1111-1111-111111111111"), + Name = "Textile", + Description = "Textile and fabric-based products", + IsActive = true, + SortOrder = 1, + CreatedAt = DateTime.UtcNow, + ModifiedAt = DateTime.UtcNow + }, + new Category + { + Id = Guid.Parse("22222222-2222-2222-2222-222222222222"), + Name = "Hard Surfaces", + Description = "Products for hard surface printing", + IsActive = true, + SortOrder = 2, + CreatedAt = DateTime.UtcNow, + ModifiedAt = DateTime.UtcNow + }, + new Category + { + Id = Guid.Parse("33333333-3333-3333-3333-333333333333"), + Name = "Paper", + Description = "Paper-based printing products", + IsActive = true, + SortOrder = 3, + CreatedAt = DateTime.UtcNow, + ModifiedAt = DateTime.UtcNow + } + ); + } +} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Configuration/EntityBaseConfiguration.cs b/src/Printbase.Infrastructure/Configuration/EntityBaseConfiguration.cs new file mode 100644 index 0000000..e4ae93b --- /dev/null +++ b/src/Printbase.Infrastructure/Configuration/EntityBaseConfiguration.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Printbase.Domain.Entities; + +namespace Printbase.Infrastructure.Configuration; + +public class EntityBaseConfiguration : IEntityTypeConfiguration where T : EntityBase +{ + public virtual void Configure(EntityTypeBuilder builder) + { + builder.HasKey(e => e.Id); + + builder.Property(e => e.Id) + .HasDefaultValueSql("NEWID()"); + + builder.Property(e => e.CreatedAt) + .IsRequired(); + + builder.Property(e => e.ModifiedAt) + .IsRequired() + .HasDefaultValueSql("GETUTCDATE()"); + + builder.Property(e => e.CreatedBy) + .IsRequired() + .HasMaxLength(450); + + builder.Property(e => e.ModifiedBy) + .IsRequired() + .HasMaxLength(450); + + builder.HasIndex(e => e.CreatedAt) + .HasDatabaseName($"IX_{typeof(T).Name}_CreatedAt"); + + builder.HasIndex(e => e.ModifiedAt) + .HasDatabaseName($"IX_{typeof(T).Name}_ModifiedAt"); + + builder.HasIndex(e => e.CreatedBy) + .HasDatabaseName($"IX_{typeof(T).Name}_CreatedBy"); + } +} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Configuration/OrderAddressConfiguration.cs b/src/Printbase.Infrastructure/Configuration/OrderAddressConfiguration.cs new file mode 100644 index 0000000..6a5da97 --- /dev/null +++ b/src/Printbase.Infrastructure/Configuration/OrderAddressConfiguration.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Printbase.Domain.Entities; + +namespace Printbase.Infrastructure.Configuration; + +public class OrderAddressConfiguration : EntityBaseConfiguration +{ + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(oa => oa.OrderId) + .IsRequired(); + + builder.Property(oa => oa.Street) + .IsRequired() + .HasMaxLength(200); + + builder.Property(oa => oa.City) + .IsRequired() + .HasMaxLength(100); + + builder.Property(oa => oa.State) + .HasMaxLength(100); + + builder.Property(oa => oa.PostalCode) + .IsRequired() + .HasMaxLength(20); + + builder.Property(oa => oa.Country) + .IsRequired() + .HasMaxLength(100); + + builder.HasIndex(oa => oa.OrderId) + .IsUnique() + .HasDatabaseName("IX_OrderAddress_OrderId"); + } +} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Configuration/OrderConfiguration.cs b/src/Printbase.Infrastructure/Configuration/OrderConfiguration.cs new file mode 100644 index 0000000..1df0d21 --- /dev/null +++ b/src/Printbase.Infrastructure/Configuration/OrderConfiguration.cs @@ -0,0 +1,77 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Printbase.Domain.Entities; +using Printbase.Domain.Entities.Users; + +namespace Printbase.Infrastructure.Configuration; + +public class OrderConfiguration : EntityBaseConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(o => o.UserId) + .IsRequired() + .HasMaxLength(450); + + builder.Property(o => o.OrderDate) + .IsRequired(); + + builder.Property(o => o.TotalPrice) + .IsRequired() + .HasColumnType("decimal(18,2)"); + + builder.Property(o => o.OrderStatusId) + .IsRequired(); + + builder.Property(o => o.ShippingStatusId) + .IsRequired(); + + builder.Property(o => o.OrderNumber) + .IsRequired() + .HasMaxLength(50); + + builder.Property(o => o.Notes) + .HasMaxLength(1000); + + builder.HasOne() + .WithMany(u => u.Orders) + .HasForeignKey(o => o.UserId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(o => o.OrderStatus) + .WithMany(os => os.Orders) + .HasForeignKey(o => o.OrderStatusId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(o => o.ShippingStatus) + .WithMany(ss => ss.Orders) + .HasForeignKey(o => o.ShippingStatusId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(o => o.OrderAddress) + .WithOne(oa => oa.Order) + .HasForeignKey(oa => oa.OrderId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasIndex(o => o.UserId) + .HasDatabaseName("IX_Order_UserId"); + + builder.HasIndex(o => o.OrderNumber) + .IsUnique() + .HasDatabaseName("IX_Order_OrderNumber"); + + builder.HasIndex(o => o.OrderDate) + .HasDatabaseName("IX_Order_OrderDate"); + + builder.HasIndex(o => o.OrderStatusId) + .HasDatabaseName("IX_Order_OrderStatusId"); + + builder.HasIndex(o => o.ShippingStatusId) + .HasDatabaseName("IX_Order_ShippingStatusId"); + + builder.HasIndex(o => new { o.UserId, o.OrderDate }) + .HasDatabaseName("IX_Order_User_Date"); + } + } \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Configuration/OrderItemConfiguration.cs b/src/Printbase.Infrastructure/Configuration/OrderItemConfiguration.cs new file mode 100644 index 0000000..db6ab19 --- /dev/null +++ b/src/Printbase.Infrastructure/Configuration/OrderItemConfiguration.cs @@ -0,0 +1,64 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Printbase.Domain.Entities; + +namespace Printbase.Infrastructure.Configuration; + +public class OrderItemConfiguration : EntityBaseConfiguration + { + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(oi => oi.OrderId) + .IsRequired(); + + builder.Property(oi => oi.ProductId) + .IsRequired(); + + builder.Property(oi => oi.Quantity) + .IsRequired() + .HasDefaultValue(1); + + builder.Property(oi => oi.UnitPrice) + .IsRequired() + .HasColumnType("decimal(18,2)"); + + builder.Property(oi => oi.TotalPrice) + .IsRequired() + .HasColumnType("decimal(18,2)"); + + builder.Property(oi => oi.CustomizationImageUrl) + .HasMaxLength(500); + + builder.Property(oi => oi.CustomizationDescription) + .HasMaxLength(2000); + + builder.HasOne(oi => oi.Order) + .WithMany(o => o.OrderItems) + .HasForeignKey(oi => oi.OrderId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(oi => oi.Product) + .WithMany(p => p.OrderItems) + .HasForeignKey(oi => oi.ProductId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasOne(oi => oi.ProductVariant) + .WithMany(pv => pv.OrderItems) + .HasForeignKey(oi => oi.ProductVariantId) + .OnDelete(DeleteBehavior.Restrict); + + builder.HasIndex(oi => oi.OrderId) + .HasDatabaseName("IX_OrderItem_OrderId"); + + builder.HasIndex(oi => oi.ProductId) + .HasDatabaseName("IX_OrderItem_ProductId"); + + builder.HasIndex(oi => oi.ProductVariantId) + .HasDatabaseName("IX_OrderItem_ProductVariantId"); + + builder.HasIndex(oi => new { oi.OrderId, oi.ProductId }) + .HasDatabaseName("IX_OrderItem_Order_Product"); + } + } \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Configuration/OrderStatusConfiguration.cs b/src/Printbase.Infrastructure/Configuration/OrderStatusConfiguration.cs new file mode 100644 index 0000000..d7ca898 --- /dev/null +++ b/src/Printbase.Infrastructure/Configuration/OrderStatusConfiguration.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Printbase.Domain.Entities; + +namespace Printbase.Infrastructure.Configuration; + +public class OrderStatusConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(os => os.Id); + + builder.Property(os => os.Id) + .ValueGeneratedNever(); + + builder.Property(os => os.Name) + .IsRequired() + .HasMaxLength(50); + + builder.HasIndex(os => os.Name) + .IsUnique() + .HasDatabaseName("IX_OrderStatus_Name"); + + builder.HasData( + new OrderStatus { Id = 0, Name = "Pending" }, + new OrderStatus { Id = 1, Name = "Processing" }, + new OrderStatus { Id = 2, Name = "Completed" }, + new OrderStatus { Id = 3, Name = "Cancelled" } + ); + } +} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Configuration/ProductConfiguration.cs b/src/Printbase.Infrastructure/Configuration/ProductConfiguration.cs new file mode 100644 index 0000000..c00499c --- /dev/null +++ b/src/Printbase.Infrastructure/Configuration/ProductConfiguration.cs @@ -0,0 +1,61 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Printbase.Domain.Entities; + +namespace Printbase.Infrastructure.Configuration; + +public class ProductConfiguration : EntityBaseConfiguration +{ + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(p => p.Name) + .IsRequired() + .HasMaxLength(200); + + builder.Property(p => p.Description) + .HasMaxLength(2000); + + builder.Property(p => p.BasePrice) + .IsRequired() + .HasColumnType("decimal(18,2)"); + + builder.Property(p => p.IsCustomizable) + .IsRequired() + .HasDefaultValue(false); + + builder.Property(p => p.IsActive) + .IsRequired() + .HasDefaultValue(true); + + builder.Property(p => p.ImageUrl) + .HasMaxLength(500); + + builder.Property(p => p.CategoryId) + .IsRequired(false); + + builder.HasOne(p => p.Category) + .WithMany(c => c.Products) + .HasForeignKey(p => p.CategoryId) + .OnDelete(DeleteBehavior.SetNull); + + builder.HasIndex(p => p.Name) + .HasDatabaseName("IX_Product_Name"); + + builder.HasIndex(p => p.IsActive) + .HasDatabaseName("IX_Product_IsActive"); + + builder.HasIndex(p => p.IsCustomizable) + .HasDatabaseName("IX_Product_IsCustomizable"); + + builder.HasIndex(p => p.CategoryId) + .HasDatabaseName("IX_Product_CategoryId"); + + builder.HasIndex(p => new { p.IsActive, p.IsCustomizable }) + .HasDatabaseName("IX_Product_Active_Customizable"); + + builder.HasIndex(p => new { p.CategoryId, p.IsActive }) + .HasDatabaseName("IX_Product_Category_Active"); + } +} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Configuration/ProductVariantConfiguration.cs b/src/Printbase.Infrastructure/Configuration/ProductVariantConfiguration.cs new file mode 100644 index 0000000..3fbb87a --- /dev/null +++ b/src/Printbase.Infrastructure/Configuration/ProductVariantConfiguration.cs @@ -0,0 +1,60 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Printbase.Domain.Entities; + +namespace Printbase.Infrastructure.Configuration; + +public class ProductVariantConfiguration : EntityBaseConfiguration +{ + public override void Configure(EntityTypeBuilder builder) + { + base.Configure(builder); + + builder.Property(pv => pv.ProductId) + .IsRequired(); + + builder.Property(pv => pv.Size) + .HasMaxLength(50); + + builder.Property(pv => pv.Color) + .HasMaxLength(50); + + builder.Property(pv => pv.Price) + .IsRequired() + .HasColumnType("decimal(18,2)"); + + builder.Property(pv => pv.ImageUrl) + .HasMaxLength(500); + + builder.Property(pv => pv.SKU) + .IsRequired() + .HasMaxLength(100); + + builder.Property(pv => pv.StockQuantity) + .IsRequired() + .HasDefaultValue(0); + + builder.Property(pv => pv.IsActive) + .IsRequired() + .HasDefaultValue(true); + + builder.HasOne(pv => pv.Product) + .WithMany(p => p.ProductVariants) + .HasForeignKey(pv => pv.ProductId) + .OnDelete(DeleteBehavior.Cascade); + + builder.HasIndex(pv => pv.ProductId) + .HasDatabaseName("IX_ProductVariant_ProductId"); + + builder.HasIndex(pv => pv.SKU) + .IsUnique() + .HasDatabaseName("IX_ProductVariant_SKU"); + + builder.HasIndex(pv => pv.IsActive) + .HasDatabaseName("IX_ProductVariant_IsActive"); + + builder.HasIndex(pv => new { pv.ProductId, pv.Size, pv.Color }) + .IsUnique() + .HasDatabaseName("IX_ProductVariant_Product_Size_Color"); + } +} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Configuration/ShippingStatusConfiguration.cs b/src/Printbase.Infrastructure/Configuration/ShippingStatusConfiguration.cs new file mode 100644 index 0000000..9461bc0 --- /dev/null +++ b/src/Printbase.Infrastructure/Configuration/ShippingStatusConfiguration.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Printbase.Domain.Entities; + +namespace Printbase.Infrastructure.Configuration; + +public class ShippingStatusConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(ss => ss.Id); + + builder.Property(ss => ss.Id) + .ValueGeneratedNever(); + + builder.Property(ss => ss.Name) + .IsRequired() + .HasMaxLength(50); + + builder.HasIndex(ss => ss.Name) + .IsUnique() + .HasDatabaseName("IX_ShippingStatus_Name"); + + builder.HasData( + new ShippingStatus { Id = 0, Name = "Prepping" }, + new ShippingStatus { Id = 1, Name = "Packaging" }, + new ShippingStatus { Id = 2, Name = "Shipped" }, + new ShippingStatus { Id = 3, Name = "Delivered" } + ); + } +} \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Database/ApplicationDbContext.cs b/src/Printbase.Infrastructure/Database/ApplicationDbContext.cs index a8aaeb4..23a9ec9 100644 --- a/src/Printbase.Infrastructure/Database/ApplicationDbContext.cs +++ b/src/Printbase.Infrastructure/Database/ApplicationDbContext.cs @@ -1,8 +1,37 @@ +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; -using Printbase.Infrastructure.DbEntities.Products; +using Printbase.Domain.Entities; +using Printbase.Domain.Entities.Users; +using Printbase.Infrastructure.Configuration; namespace Printbase.Infrastructure.Database; -public class ApplicationDbContext(DbContextOptions? options) : DbContext(options) +public class ApplicationDbContext(DbContextOptions options) + : IdentityDbContext(options) { + public DbSet Products { get; set; } + public DbSet ProductVariants { get; set; } + public DbSet Orders { get; set; } + public DbSet OrderItems { get; set; } + public DbSet OrderAddresses { get; set; } + public DbSet
Addresses { get; set; } + public DbSet OrderStatuses { get; set; } + public DbSet ShippingStatuses { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.ApplyConfiguration(new ApplicationUserConfiguration()); + modelBuilder.ApplyConfiguration(new ApplicationRoleConfiguration()); + + modelBuilder.ApplyConfiguration(new ProductConfiguration()); + modelBuilder.ApplyConfiguration(new ProductVariantConfiguration()); + modelBuilder.ApplyConfiguration(new OrderConfiguration()); + modelBuilder.ApplyConfiguration(new OrderItemConfiguration()); + modelBuilder.ApplyConfiguration(new OrderAddressConfiguration()); + modelBuilder.ApplyConfiguration(new AddressConfiguration()); + modelBuilder.ApplyConfiguration(new OrderStatusConfiguration()); + modelBuilder.ApplyConfiguration(new ShippingStatusConfiguration()); + } } \ No newline at end of file diff --git a/src/Printbase.Infrastructure/Migrations/20250526100748_InitialSetup.Designer.cs b/src/Printbase.Infrastructure/Migrations/20250526100748_InitialSetup.Designer.cs new file mode 100644 index 0000000..632d4c0 --- /dev/null +++ b/src/Printbase.Infrastructure/Migrations/20250526100748_InitialSetup.Designer.cs @@ -0,0 +1,1150 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Printbase.Infrastructure.Database; + +#nullable disable + +namespace Printbase.Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250526100748_InitialSetup")] + partial class InitialSetup + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("ApplicationRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_ApplicationRole_IsActive"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = "1", + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Full system access", + IsActive = true, + Name = "Administrator", + NormalizedName = "ADMINISTRATOR" + }, + new + { + Id = "2", + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Standard customer access", + IsActive = true, + Name = "Customer", + NormalizedName = "CUSTOMER" + }, + new + { + Id = "3", + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Manage orders and fulfillment", + IsActive = true, + Name = "OrderManager", + NormalizedName = "ORDERMANAGER" + }, + new + { + Id = "4", + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Manage products and inventory", + IsActive = true, + Name = "ProductManager", + NormalizedName = "PRODUCTMANAGER" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + 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")); + + 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("Printbase.Domain.Entities.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("AddressType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("City") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDefault") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("ModifiedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("ModifiedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("PostalCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Street") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_Address_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_Address_CreatedBy"); + + b.HasIndex("ModifiedAt") + .HasDatabaseName("IX_Address_ModifiedAt"); + + b.HasIndex("UserId") + .HasDatabaseName("IX_Address_UserId"); + + b.HasIndex("UserId", "AddressType") + .HasDatabaseName("IX_Address_User_Type"); + + b.HasIndex("UserId", "IsDefault") + .HasDatabaseName("IX_Address_User_Default"); + + b.ToTable("Addresses"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ImageUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("ModifiedAt") + .HasColumnType("datetime2"); + + b.Property("ModifiedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ParentCategoryId") + .HasColumnType("uniqueidentifier"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ParentCategoryId"); + + b.ToTable("Category"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ModifiedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("ModifiedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Notes") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("OrderDate") + .HasColumnType("datetime2"); + + b.Property("OrderNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("OrderStatusId") + .HasColumnType("int"); + + b.Property("ShippingStatusId") + .HasColumnType("int"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_Order_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_Order_CreatedBy"); + + b.HasIndex("ModifiedAt") + .HasDatabaseName("IX_Order_ModifiedAt"); + + b.HasIndex("OrderDate") + .HasDatabaseName("IX_Order_OrderDate"); + + b.HasIndex("OrderNumber") + .IsUnique() + .HasDatabaseName("IX_Order_OrderNumber"); + + b.HasIndex("OrderStatusId") + .HasDatabaseName("IX_Order_OrderStatusId"); + + b.HasIndex("ShippingStatusId") + .HasDatabaseName("IX_Order_ShippingStatusId"); + + b.HasIndex("UserId") + .HasDatabaseName("IX_Order_UserId"); + + b.HasIndex("UserId", "OrderDate") + .HasDatabaseName("IX_Order_User_Date"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.OrderAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("City") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ModifiedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("ModifiedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("PostalCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Street") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_OrderAddress_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_OrderAddress_CreatedBy"); + + b.HasIndex("ModifiedAt") + .HasDatabaseName("IX_OrderAddress_ModifiedAt"); + + b.HasIndex("OrderId") + .IsUnique() + .HasDatabaseName("IX_OrderAddress_OrderId"); + + b.ToTable("OrderAddresses"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.OrderItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("CustomizationDescription") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("CustomizationImageUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ModifiedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("ModifiedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductVariantId") + .HasColumnType("uniqueidentifier"); + + b.Property("Quantity") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_OrderItem_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_OrderItem_CreatedBy"); + + b.HasIndex("ModifiedAt") + .HasDatabaseName("IX_OrderItem_ModifiedAt"); + + b.HasIndex("OrderId") + .HasDatabaseName("IX_OrderItem_OrderId"); + + b.HasIndex("ProductId") + .HasDatabaseName("IX_OrderItem_ProductId"); + + b.HasIndex("ProductVariantId") + .HasDatabaseName("IX_OrderItem_ProductVariantId"); + + b.HasIndex("OrderId", "ProductId") + .HasDatabaseName("IX_OrderItem_Order_Product"); + + b.ToTable("OrderItems"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.OrderStatus", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("IX_OrderStatus_Name"); + + b.ToTable("OrderStatuses"); + + b.HasData( + new + { + Id = 0, + Name = "Pending" + }, + new + { + Id = 1, + Name = "Processing" + }, + new + { + Id = 2, + Name = "Completed" + }, + new + { + Id = 3, + Name = "Cancelled" + }); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("BasePrice") + .HasColumnType("decimal(18,2)"); + + b.Property("CategoryId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("ImageUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsCustomizable") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("ModifiedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("ModifiedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId") + .HasDatabaseName("IX_Product_CategoryId"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_Product_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_Product_CreatedBy"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_Product_IsActive"); + + b.HasIndex("IsCustomizable") + .HasDatabaseName("IX_Product_IsCustomizable"); + + b.HasIndex("ModifiedAt") + .HasDatabaseName("IX_Product_ModifiedAt"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Product_Name"); + + b.HasIndex("CategoryId", "IsActive") + .HasDatabaseName("IX_Product_Category_Active"); + + b.HasIndex("IsActive", "IsCustomizable") + .HasDatabaseName("IX_Product_Active_Customizable"); + + b.ToTable("Products"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.ProductVariant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("Color") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ImageUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("ModifiedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("ModifiedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("SKU") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Size") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("StockQuantity") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_ProductVariant_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_ProductVariant_CreatedBy"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_ProductVariant_IsActive"); + + b.HasIndex("ModifiedAt") + .HasDatabaseName("IX_ProductVariant_ModifiedAt"); + + b.HasIndex("ProductId") + .HasDatabaseName("IX_ProductVariant_ProductId"); + + b.HasIndex("SKU") + .IsUnique() + .HasDatabaseName("IX_ProductVariant_SKU"); + + b.HasIndex("ProductId", "Size", "Color") + .IsUnique() + .HasDatabaseName("IX_ProductVariant_Product_Size_Color"); + + b.ToTable("ProductVariants"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.ShippingStatus", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("IX_ShippingStatus_Name"); + + b.ToTable("ShippingStatuses"); + + b.HasData( + new + { + Id = 0, + Name = "Prepping" + }, + new + { + Id = 1, + Name = "Packaging" + }, + new + { + Id = 2, + Name = "Shipped" + }, + new + { + Id = 3, + Name = "Delivered" + }); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Users.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("DateOfBirth") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LastLoginAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + 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("ProfileImageUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + 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("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Printbase.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Printbase.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Printbase.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Printbase.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Address", b => + { + b.HasOne("Printbase.Domain.Entities.Users.ApplicationUser", null) + .WithMany("Addresses") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Category", b => + { + b.HasOne("Printbase.Domain.Entities.Category", "ParentCategory") + .WithMany("SubCategories") + .HasForeignKey("ParentCategoryId"); + + b.Navigation("ParentCategory"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Order", b => + { + b.HasOne("Printbase.Domain.Entities.OrderStatus", "OrderStatus") + .WithMany("Orders") + .HasForeignKey("OrderStatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Printbase.Domain.Entities.ShippingStatus", "ShippingStatus") + .WithMany("Orders") + .HasForeignKey("ShippingStatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Printbase.Domain.Entities.Users.ApplicationUser", null) + .WithMany("Orders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("OrderStatus"); + + b.Navigation("ShippingStatus"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.OrderAddress", b => + { + b.HasOne("Printbase.Domain.Entities.Order", "Order") + .WithOne("OrderAddress") + .HasForeignKey("Printbase.Domain.Entities.OrderAddress", "OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.OrderItem", b => + { + b.HasOne("Printbase.Domain.Entities.Order", "Order") + .WithMany("OrderItems") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Printbase.Domain.Entities.Product", "Product") + .WithMany("OrderItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Printbase.Domain.Entities.ProductVariant", "ProductVariant") + .WithMany("OrderItems") + .HasForeignKey("ProductVariantId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Order"); + + b.Navigation("Product"); + + b.Navigation("ProductVariant"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Product", b => + { + b.HasOne("Printbase.Domain.Entities.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.ProductVariant", b => + { + b.HasOne("Printbase.Domain.Entities.Product", "Product") + .WithMany("ProductVariants") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Category", b => + { + b.Navigation("Products"); + + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Order", b => + { + b.Navigation("OrderAddress") + .IsRequired(); + + b.Navigation("OrderItems"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.OrderStatus", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Product", b => + { + b.Navigation("OrderItems"); + + b.Navigation("ProductVariants"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.ProductVariant", b => + { + b.Navigation("OrderItems"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.ShippingStatus", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Users.ApplicationUser", b => + { + b.Navigation("Addresses"); + + b.Navigation("Orders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Printbase.Infrastructure/Migrations/20250526100748_InitialSetup.cs b/src/Printbase.Infrastructure/Migrations/20250526100748_InitialSetup.cs new file mode 100644 index 0000000..ab38160 --- /dev/null +++ b/src/Printbase.Infrastructure/Migrations/20250526100748_InitialSetup.cs @@ -0,0 +1,777 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace Printbase.Infrastructure.Migrations +{ + /// + public partial class InitialSetup : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + Description = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"), + IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + FirstName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + LastName = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + DateOfBirth = table.Column(type: "datetime2", nullable: true), + CreatedAt = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"), + LastLoginAt = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"), + IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), + ProfileImageUrl = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: false), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Category", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(max)", nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: false), + ImageUrl = table.Column(type: "nvarchar(max)", nullable: false), + SortOrder = table.Column(type: "int", nullable: false), + IsActive = table.Column(type: "bit", nullable: false), + ParentCategoryId = table.Column(type: "uniqueidentifier", nullable: true), + CreatedAt = table.Column(type: "datetime2", nullable: false), + ModifiedAt = table.Column(type: "datetime2", nullable: false), + CreatedBy = table.Column(type: "nvarchar(max)", nullable: false), + ModifiedBy = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Category", x => x.Id); + table.ForeignKey( + name: "FK_Category_Category_ParentCategoryId", + column: x => x.ParentCategoryId, + principalTable: "Category", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "OrderStatuses", + columns: table => new + { + Id = table.Column(type: "int", nullable: false), + Name = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderStatuses", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ShippingStatuses", + columns: table => new + { + Id = table.Column(type: "int", nullable: false), + Name = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ShippingStatuses", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RoleId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Addresses", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"), + UserId = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false), + AddressType = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + Street = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + City = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + State = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + PostalCode = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: false), + Country = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + IsDefault = table.Column(type: "bit", nullable: false, defaultValue: false), + IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), + CreatedAt = table.Column(type: "datetime2", nullable: false), + ModifiedAt = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"), + CreatedBy = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false), + ModifiedBy = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Addresses", x => x.Id); + table.ForeignKey( + name: "FK_Addresses_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "nvarchar(450)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "nvarchar(450)", nullable: false), + RoleId = table.Column(type: "nvarchar(450)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "nvarchar(450)", nullable: false), + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Products", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"), + Name = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + Description = table.Column(type: "nvarchar(2000)", maxLength: 2000, nullable: false), + BasePrice = table.Column(type: "decimal(18,2)", nullable: false), + IsCustomizable = table.Column(type: "bit", nullable: false, defaultValue: false), + IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), + ImageUrl = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: false), + CategoryId = table.Column(type: "uniqueidentifier", nullable: true), + CreatedAt = table.Column(type: "datetime2", nullable: false), + ModifiedAt = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"), + CreatedBy = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false), + ModifiedBy = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Products", x => x.Id); + table.ForeignKey( + name: "FK_Products_Category_CategoryId", + column: x => x.CategoryId, + principalTable: "Category", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "Orders", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"), + UserId = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false), + OrderDate = table.Column(type: "datetime2", nullable: false), + TotalPrice = table.Column(type: "decimal(18,2)", nullable: false), + OrderStatusId = table.Column(type: "int", nullable: false), + ShippingStatusId = table.Column(type: "int", nullable: false), + OrderNumber = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + Notes = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + ModifiedAt = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"), + CreatedBy = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false), + ModifiedBy = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Orders", x => x.Id); + table.ForeignKey( + name: "FK_Orders_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Orders_OrderStatuses_OrderStatusId", + column: x => x.OrderStatusId, + principalTable: "OrderStatuses", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Orders_ShippingStatuses_ShippingStatusId", + column: x => x.ShippingStatusId, + principalTable: "ShippingStatuses", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "ProductVariants", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"), + ProductId = table.Column(type: "uniqueidentifier", nullable: false), + Size = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + Color = table.Column(type: "nvarchar(50)", maxLength: 50, nullable: false), + Price = table.Column(type: "decimal(18,2)", nullable: false), + ImageUrl = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: false), + SKU = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + StockQuantity = table.Column(type: "int", nullable: false, defaultValue: 0), + IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), + CreatedAt = table.Column(type: "datetime2", nullable: false), + ModifiedAt = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"), + CreatedBy = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false), + ModifiedBy = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ProductVariants", x => x.Id); + table.ForeignKey( + name: "FK_ProductVariants_Products_ProductId", + column: x => x.ProductId, + principalTable: "Products", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "OrderAddresses", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"), + OrderId = table.Column(type: "uniqueidentifier", nullable: false), + Street = table.Column(type: "nvarchar(200)", maxLength: 200, nullable: false), + City = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + State = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + PostalCode = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: false), + Country = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + ModifiedAt = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"), + CreatedBy = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false), + ModifiedBy = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderAddresses", x => x.Id); + table.ForeignKey( + name: "FK_OrderAddresses_Orders_OrderId", + column: x => x.OrderId, + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "OrderItems", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false, defaultValueSql: "NEWID()"), + OrderId = table.Column(type: "uniqueidentifier", nullable: false), + ProductId = table.Column(type: "uniqueidentifier", nullable: false), + ProductVariantId = table.Column(type: "uniqueidentifier", nullable: true), + Quantity = table.Column(type: "int", nullable: false, defaultValue: 1), + UnitPrice = table.Column(type: "decimal(18,2)", nullable: false), + TotalPrice = table.Column(type: "decimal(18,2)", nullable: false), + CustomizationImageUrl = table.Column(type: "nvarchar(500)", maxLength: 500, nullable: false), + CustomizationDescription = table.Column(type: "nvarchar(2000)", maxLength: 2000, nullable: false), + CreatedAt = table.Column(type: "datetime2", nullable: false), + ModifiedAt = table.Column(type: "datetime2", nullable: false, defaultValueSql: "GETUTCDATE()"), + CreatedBy = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false), + ModifiedBy = table.Column(type: "nvarchar(450)", maxLength: 450, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderItems", x => x.Id); + table.ForeignKey( + name: "FK_OrderItems_Orders_OrderId", + column: x => x.OrderId, + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_OrderItems_ProductVariants_ProductVariantId", + column: x => x.ProductVariantId, + principalTable: "ProductVariants", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_OrderItems_Products_ProductId", + column: x => x.ProductId, + principalTable: "Products", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "CreatedAt", "Description", "IsActive", "Name", "NormalizedName" }, + values: new object[,] + { + { "1", null, new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), "Full system access", true, "Administrator", "ADMINISTRATOR" }, + { "2", null, new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), "Standard customer access", true, "Customer", "CUSTOMER" }, + { "3", null, new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), "Manage orders and fulfillment", true, "OrderManager", "ORDERMANAGER" }, + { "4", null, new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), "Manage products and inventory", true, "ProductManager", "PRODUCTMANAGER" } + }); + + migrationBuilder.InsertData( + table: "OrderStatuses", + columns: new[] { "Id", "Name" }, + values: new object[,] + { + { 0, "Pending" }, + { 1, "Processing" }, + { 2, "Completed" }, + { 3, "Cancelled" } + }); + + migrationBuilder.InsertData( + table: "ShippingStatuses", + columns: new[] { "Id", "Name" }, + values: new object[,] + { + { 0, "Prepping" }, + { 1, "Packaging" }, + { 2, "Shipped" }, + { 3, "Delivered" } + }); + + migrationBuilder.CreateIndex( + name: "IX_Address_CreatedAt", + table: "Addresses", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Address_CreatedBy", + table: "Addresses", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_Address_ModifiedAt", + table: "Addresses", + column: "ModifiedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Address_User_Default", + table: "Addresses", + columns: new[] { "UserId", "IsDefault" }); + + migrationBuilder.CreateIndex( + name: "IX_Address_User_Type", + table: "Addresses", + columns: new[] { "UserId", "AddressType" }); + + migrationBuilder.CreateIndex( + name: "IX_Address_UserId", + table: "Addresses", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_ApplicationRole_IsActive", + table: "AspNetRoles", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true, + filter: "[NormalizedName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true, + filter: "[NormalizedUserName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_Category_ParentCategoryId", + table: "Category", + column: "ParentCategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_OrderAddress_CreatedAt", + table: "OrderAddresses", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_OrderAddress_CreatedBy", + table: "OrderAddresses", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_OrderAddress_ModifiedAt", + table: "OrderAddresses", + column: "ModifiedAt"); + + migrationBuilder.CreateIndex( + name: "IX_OrderAddress_OrderId", + table: "OrderAddresses", + column: "OrderId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_OrderItem_CreatedAt", + table: "OrderItems", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_OrderItem_CreatedBy", + table: "OrderItems", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_OrderItem_ModifiedAt", + table: "OrderItems", + column: "ModifiedAt"); + + migrationBuilder.CreateIndex( + name: "IX_OrderItem_Order_Product", + table: "OrderItems", + columns: new[] { "OrderId", "ProductId" }); + + migrationBuilder.CreateIndex( + name: "IX_OrderItem_OrderId", + table: "OrderItems", + column: "OrderId"); + + migrationBuilder.CreateIndex( + name: "IX_OrderItem_ProductId", + table: "OrderItems", + column: "ProductId"); + + migrationBuilder.CreateIndex( + name: "IX_OrderItem_ProductVariantId", + table: "OrderItems", + column: "ProductVariantId"); + + migrationBuilder.CreateIndex( + name: "IX_Order_CreatedAt", + table: "Orders", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Order_CreatedBy", + table: "Orders", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_Order_ModifiedAt", + table: "Orders", + column: "ModifiedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Order_OrderDate", + table: "Orders", + column: "OrderDate"); + + migrationBuilder.CreateIndex( + name: "IX_Order_OrderNumber", + table: "Orders", + column: "OrderNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Order_OrderStatusId", + table: "Orders", + column: "OrderStatusId"); + + migrationBuilder.CreateIndex( + name: "IX_Order_ShippingStatusId", + table: "Orders", + column: "ShippingStatusId"); + + migrationBuilder.CreateIndex( + name: "IX_Order_User_Date", + table: "Orders", + columns: new[] { "UserId", "OrderDate" }); + + migrationBuilder.CreateIndex( + name: "IX_Order_UserId", + table: "Orders", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_OrderStatus_Name", + table: "OrderStatuses", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Product_Active_Customizable", + table: "Products", + columns: new[] { "IsActive", "IsCustomizable" }); + + migrationBuilder.CreateIndex( + name: "IX_Product_Category_Active", + table: "Products", + columns: new[] { "CategoryId", "IsActive" }); + + migrationBuilder.CreateIndex( + name: "IX_Product_CategoryId", + table: "Products", + column: "CategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_Product_CreatedAt", + table: "Products", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Product_CreatedBy", + table: "Products", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_Product_IsActive", + table: "Products", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX_Product_IsCustomizable", + table: "Products", + column: "IsCustomizable"); + + migrationBuilder.CreateIndex( + name: "IX_Product_ModifiedAt", + table: "Products", + column: "ModifiedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Product_Name", + table: "Products", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ProductVariant_CreatedAt", + table: "ProductVariants", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ProductVariant_CreatedBy", + table: "ProductVariants", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX_ProductVariant_IsActive", + table: "ProductVariants", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX_ProductVariant_ModifiedAt", + table: "ProductVariants", + column: "ModifiedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ProductVariant_Product_Size_Color", + table: "ProductVariants", + columns: new[] { "ProductId", "Size", "Color" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_ProductVariant_ProductId", + table: "ProductVariants", + column: "ProductId"); + + migrationBuilder.CreateIndex( + name: "IX_ProductVariant_SKU", + table: "ProductVariants", + column: "SKU", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_ShippingStatus_Name", + table: "ShippingStatuses", + column: "Name", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Addresses"); + + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "OrderAddresses"); + + migrationBuilder.DropTable( + name: "OrderItems"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "Orders"); + + migrationBuilder.DropTable( + name: "ProductVariants"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + + migrationBuilder.DropTable( + name: "OrderStatuses"); + + migrationBuilder.DropTable( + name: "ShippingStatuses"); + + migrationBuilder.DropTable( + name: "Products"); + + migrationBuilder.DropTable( + name: "Category"); + } + } +} diff --git a/src/Printbase.Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Printbase.Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000..95a5181 --- /dev/null +++ b/src/Printbase.Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,1147 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Printbase.Infrastructure.Database; + +#nullable disable + +namespace Printbase.Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("ApplicationRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_ApplicationRole_IsActive"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = "1", + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Full system access", + IsActive = true, + Name = "Administrator", + NormalizedName = "ADMINISTRATOR" + }, + new + { + Id = "2", + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Standard customer access", + IsActive = true, + Name = "Customer", + NormalizedName = "CUSTOMER" + }, + new + { + Id = "3", + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Manage orders and fulfillment", + IsActive = true, + Name = "OrderManager", + NormalizedName = "ORDERMANAGER" + }, + new + { + Id = "4", + CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc), + Description = "Manage products and inventory", + IsActive = true, + Name = "ProductManager", + NormalizedName = "PRODUCTMANAGER" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + 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")); + + 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("Printbase.Domain.Entities.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("AddressType") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("City") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsDefault") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("ModifiedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("ModifiedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("PostalCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Street") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_Address_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_Address_CreatedBy"); + + b.HasIndex("ModifiedAt") + .HasDatabaseName("IX_Address_ModifiedAt"); + + b.HasIndex("UserId") + .HasDatabaseName("IX_Address_UserId"); + + b.HasIndex("UserId", "AddressType") + .HasDatabaseName("IX_Address_User_Type"); + + b.HasIndex("UserId", "IsDefault") + .HasDatabaseName("IX_Address_User_Default"); + + b.ToTable("Addresses"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ImageUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsActive") + .HasColumnType("bit"); + + b.Property("ModifiedAt") + .HasColumnType("datetime2"); + + b.Property("ModifiedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ParentCategoryId") + .HasColumnType("uniqueidentifier"); + + b.Property("SortOrder") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ParentCategoryId"); + + b.ToTable("Category"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ModifiedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("ModifiedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Notes") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("OrderDate") + .HasColumnType("datetime2"); + + b.Property("OrderNumber") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("OrderStatusId") + .HasColumnType("int"); + + b.Property("ShippingStatusId") + .HasColumnType("int"); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_Order_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_Order_CreatedBy"); + + b.HasIndex("ModifiedAt") + .HasDatabaseName("IX_Order_ModifiedAt"); + + b.HasIndex("OrderDate") + .HasDatabaseName("IX_Order_OrderDate"); + + b.HasIndex("OrderNumber") + .IsUnique() + .HasDatabaseName("IX_Order_OrderNumber"); + + b.HasIndex("OrderStatusId") + .HasDatabaseName("IX_Order_OrderStatusId"); + + b.HasIndex("ShippingStatusId") + .HasDatabaseName("IX_Order_ShippingStatusId"); + + b.HasIndex("UserId") + .HasDatabaseName("IX_Order_UserId"); + + b.HasIndex("UserId", "OrderDate") + .HasDatabaseName("IX_Order_User_Date"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.OrderAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("City") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Country") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ModifiedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("ModifiedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("PostalCode") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Street") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_OrderAddress_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_OrderAddress_CreatedBy"); + + b.HasIndex("ModifiedAt") + .HasDatabaseName("IX_OrderAddress_ModifiedAt"); + + b.HasIndex("OrderId") + .IsUnique() + .HasDatabaseName("IX_OrderAddress_OrderId"); + + b.ToTable("OrderAddresses"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.OrderItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("CustomizationDescription") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("CustomizationImageUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("ModifiedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("ModifiedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("ProductVariantId") + .HasColumnType("uniqueidentifier"); + + b.Property("Quantity") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("UnitPrice") + .HasColumnType("decimal(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_OrderItem_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_OrderItem_CreatedBy"); + + b.HasIndex("ModifiedAt") + .HasDatabaseName("IX_OrderItem_ModifiedAt"); + + b.HasIndex("OrderId") + .HasDatabaseName("IX_OrderItem_OrderId"); + + b.HasIndex("ProductId") + .HasDatabaseName("IX_OrderItem_ProductId"); + + b.HasIndex("ProductVariantId") + .HasDatabaseName("IX_OrderItem_ProductVariantId"); + + b.HasIndex("OrderId", "ProductId") + .HasDatabaseName("IX_OrderItem_Order_Product"); + + b.ToTable("OrderItems"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.OrderStatus", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("IX_OrderStatus_Name"); + + b.ToTable("OrderStatuses"); + + b.HasData( + new + { + Id = 0, + Name = "Pending" + }, + new + { + Id = 1, + Name = "Processing" + }, + new + { + Id = 2, + Name = "Completed" + }, + new + { + Id = 3, + Name = "Cancelled" + }); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("BasePrice") + .HasColumnType("decimal(18,2)"); + + b.Property("CategoryId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("ImageUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("IsCustomizable") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false); + + b.Property("ModifiedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("ModifiedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId") + .HasDatabaseName("IX_Product_CategoryId"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_Product_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_Product_CreatedBy"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_Product_IsActive"); + + b.HasIndex("IsCustomizable") + .HasDatabaseName("IX_Product_IsCustomizable"); + + b.HasIndex("ModifiedAt") + .HasDatabaseName("IX_Product_ModifiedAt"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Product_Name"); + + b.HasIndex("CategoryId", "IsActive") + .HasDatabaseName("IX_Product_Category_Active"); + + b.HasIndex("IsActive", "IsCustomizable") + .HasDatabaseName("IX_Product_Active_Customizable"); + + b.ToTable("Products"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.ProductVariant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier") + .HasDefaultValueSql("NEWID()"); + + b.Property("Color") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ImageUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("ModifiedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("ModifiedBy") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("ProductId") + .HasColumnType("uniqueidentifier"); + + b.Property("SKU") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Size") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("StockQuantity") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt") + .HasDatabaseName("IX_ProductVariant_CreatedAt"); + + b.HasIndex("CreatedBy") + .HasDatabaseName("IX_ProductVariant_CreatedBy"); + + b.HasIndex("IsActive") + .HasDatabaseName("IX_ProductVariant_IsActive"); + + b.HasIndex("ModifiedAt") + .HasDatabaseName("IX_ProductVariant_ModifiedAt"); + + b.HasIndex("ProductId") + .HasDatabaseName("IX_ProductVariant_ProductId"); + + b.HasIndex("SKU") + .IsUnique() + .HasDatabaseName("IX_ProductVariant_SKU"); + + b.HasIndex("ProductId", "Size", "Color") + .IsUnique() + .HasDatabaseName("IX_ProductVariant_Product_Size_Color"); + + b.ToTable("ProductVariants"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.ShippingStatus", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("IX_ShippingStatus_Name"); + + b.ToTable("ShippingStatuses"); + + b.HasData( + new + { + Id = 0, + Name = "Prepping" + }, + new + { + Id = 1, + Name = "Packaging" + }, + new + { + Id = 2, + Name = "Shipped" + }, + new + { + Id = 3, + Name = "Delivered" + }); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Users.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("DateOfBirth") + .HasColumnType("datetime2"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LastLoginAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime2") + .HasDefaultValueSql("GETUTCDATE()"); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + 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("ProfileImageUrl") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("nvarchar(500)"); + + 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("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Printbase.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Printbase.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Printbase.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Printbase.Domain.Entities.Users.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Address", b => + { + b.HasOne("Printbase.Domain.Entities.Users.ApplicationUser", null) + .WithMany("Addresses") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Category", b => + { + b.HasOne("Printbase.Domain.Entities.Category", "ParentCategory") + .WithMany("SubCategories") + .HasForeignKey("ParentCategoryId"); + + b.Navigation("ParentCategory"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Order", b => + { + b.HasOne("Printbase.Domain.Entities.OrderStatus", "OrderStatus") + .WithMany("Orders") + .HasForeignKey("OrderStatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Printbase.Domain.Entities.ShippingStatus", "ShippingStatus") + .WithMany("Orders") + .HasForeignKey("ShippingStatusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Printbase.Domain.Entities.Users.ApplicationUser", null) + .WithMany("Orders") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("OrderStatus"); + + b.Navigation("ShippingStatus"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.OrderAddress", b => + { + b.HasOne("Printbase.Domain.Entities.Order", "Order") + .WithOne("OrderAddress") + .HasForeignKey("Printbase.Domain.Entities.OrderAddress", "OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.OrderItem", b => + { + b.HasOne("Printbase.Domain.Entities.Order", "Order") + .WithMany("OrderItems") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Printbase.Domain.Entities.Product", "Product") + .WithMany("OrderItems") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Printbase.Domain.Entities.ProductVariant", "ProductVariant") + .WithMany("OrderItems") + .HasForeignKey("ProductVariantId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Order"); + + b.Navigation("Product"); + + b.Navigation("ProductVariant"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Product", b => + { + b.HasOne("Printbase.Domain.Entities.Category", "Category") + .WithMany("Products") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Category"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.ProductVariant", b => + { + b.HasOne("Printbase.Domain.Entities.Product", "Product") + .WithMany("ProductVariants") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Category", b => + { + b.Navigation("Products"); + + b.Navigation("SubCategories"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Order", b => + { + b.Navigation("OrderAddress") + .IsRequired(); + + b.Navigation("OrderItems"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.OrderStatus", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Product", b => + { + b.Navigation("OrderItems"); + + b.Navigation("ProductVariants"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.ProductVariant", b => + { + b.Navigation("OrderItems"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.ShippingStatus", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Printbase.Domain.Entities.Users.ApplicationUser", b => + { + b.Navigation("Addresses"); + + b.Navigation("Orders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Printbase.Infrastructure/Printbase.Infrastructure.csproj b/src/Printbase.Infrastructure/Printbase.Infrastructure.csproj index c6bbce1..7e22d89 100644 --- a/src/Printbase.Infrastructure/Printbase.Infrastructure.csproj +++ b/src/Printbase.Infrastructure/Printbase.Infrastructure.csproj @@ -8,7 +8,12 @@ - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/src/Printbase.WebApi/Startup.cs b/src/Printbase.WebApi/Startup.cs index 9ec1ea7..a0921e4 100644 --- a/src/Printbase.WebApi/Startup.cs +++ b/src/Printbase.WebApi/Startup.cs @@ -1,4 +1,6 @@ +using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; +using Printbase.Domain.Entities.Users; using Printbase.Infrastructure.Database; namespace Printbase.WebApi; @@ -13,6 +15,52 @@ public static class Startup options.UseSqlServer( builder.Configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName))); + + services.AddIdentity(options => + { + options.Password.RequireDigit = true; + options.Password.RequireLowercase = true; + options.Password.RequireUppercase = true; + options.Password.RequireNonAlphanumeric = true; + options.Password.RequiredLength = 8; + options.Password.RequiredUniqueChars = 1; + + options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30); + options.Lockout.MaxFailedAccessAttempts = 5; + options.Lockout.AllowedForNewUsers = true; + + options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+"; + options.User.RequireUniqueEmail = true; + + options.SignIn.RequireConfirmedEmail = true; + options.SignIn.RequireConfirmedPhoneNumber = false; + }) + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); + + services.ConfigureApplicationCookie(options => + { + options.Cookie.HttpOnly = true; + options.ExpireTimeSpan = TimeSpan.FromMinutes(60); + options.LoginPath = "/Account/Login"; + options.AccessDeniedPath = "/Account/AccessDenied"; + options.SlidingExpiration = true; + }); + + services.Configure(options => + { + options.TokenLifespan = TimeSpan.FromHours(24); + }); + + services.AddAuthorizationBuilder() + .AddPolicy("AdminPolicy", policy => + policy.RequireRole("Administrator")) + .AddPolicy("OrderManagementPolicy", policy => + policy.RequireRole("Administrator", "OrderManager")) + .AddPolicy("ProductManagementPolicy", policy => + policy.RequireRole("Administrator", "ProductManager")) + .AddPolicy("CustomerPolicy", policy => + policy.RequireRole("Customer", "Administrator", "OrderManager", "ProductManager")); } public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) @@ -46,6 +94,9 @@ public static class Startup } app.UseRouting(); + + app.UseAuthentication(); + app.UseAuthorization(); app.UseEndpoints(endpoints => { diff --git a/src/Printbase.WebApi/appsettings.Development.json b/src/Printbase.WebApi/appsettings.Development.json index e5f79c5..e0a33a4 100644 --- a/src/Printbase.WebApi/appsettings.Development.json +++ b/src/Printbase.WebApi/appsettings.Development.json @@ -7,7 +7,7 @@ } }, "ConnectionStrings": { - "DefaultConnection": "Server=db;Database=PrintbaseDb;User Id=sa;Password=Str0ng!Passw0rd383838@@@$23;TrustServerCertificate=True;" + "DefaultConnection": "Server=localhost;Database=Printbase;Encrypt=false;Trusted_Connection=True;TrustServerCertificate=true;MultipleActiveResultSets=true;" }, "DatabaseOptions": { "ApplyMigrationsAtStartup": true diff --git a/src/Printbase.WebApi/appsettings.json b/src/Printbase.WebApi/appsettings.json index e5f79c5..f215cd6 100644 --- a/src/Printbase.WebApi/appsettings.json +++ b/src/Printbase.WebApi/appsettings.json @@ -7,7 +7,7 @@ } }, "ConnectionStrings": { - "DefaultConnection": "Server=db;Database=PrintbaseDb;User Id=sa;Password=Str0ng!Passw0rd383838@@@$23;TrustServerCertificate=True;" + "DefaultConnection": "Server=localhost;Database=Printbase;Encrypt=false;TrustServerCertificate=true;Trusted_Connection=True;MultipleActiveResultSets=true;" }, "DatabaseOptions": { "ApplyMigrationsAtStartup": true