diff --git a/LittleShop/Areas/Admin/Views/Products/ProductVariants.cshtml b/LittleShop/Areas/Admin/Views/Products/ProductVariants.cshtml
index f780ea7..0539122 100644
--- a/LittleShop/Areas/Admin/Views/Products/ProductVariants.cshtml
+++ b/LittleShop/Areas/Admin/Views/Products/ProductVariants.cshtml
@@ -33,6 +33,7 @@
| Name |
Type |
+ Price |
Stock Level |
Sort Order |
Status |
@@ -49,6 +50,16 @@
@variant.VariantType
|
+
+ @if (variant.Price.HasValue)
+ {
+ £@variant.Price.Value.ToString("F2")
+ }
+ else
+ {
+ £@product?.Price.ToString("F2") (base)
+ }
+ |
@if (variant.StockLevel > 0)
{
diff --git a/LittleShop/DTOs/OrderDto.cs b/LittleShop/DTOs/OrderDto.cs
index d4ee4ef..7ee203c 100644
--- a/LittleShop/DTOs/OrderDto.cs
+++ b/LittleShop/DTOs/OrderDto.cs
@@ -51,8 +51,10 @@ public class OrderItemDto
public Guid Id { get; set; }
public Guid ProductId { get; set; }
public Guid? ProductMultiBuyId { get; set; }
+ public Guid? ProductVariantId { get; set; }
public string ProductName { get; set; } = string.Empty;
public string? ProductMultiBuyName { get; set; }
+ public string? ProductVariantName { get; set; }
public string? SelectedVariant { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
@@ -97,7 +99,9 @@ public class CreateOrderItemDto
public Guid? ProductMultiBuyId { get; set; } // Optional: if specified, use multi-buy pricing
- public string? SelectedVariant { get; set; } // Optional: variant choice (color/flavor)
+ public Guid? ProductVariantId { get; set; } // Optional: specific variant (used for variant price override)
+
+ public string? SelectedVariant { get; set; } // Optional: variant choice (color/flavor) - deprecated, use ProductVariantId
[Range(1, int.MaxValue)]
public int Quantity { get; set; }
diff --git a/LittleShop/DTOs/ProductDto.cs b/LittleShop/DTOs/ProductDto.cs
index 5007cbb..e14ba30 100644
--- a/LittleShop/DTOs/ProductDto.cs
+++ b/LittleShop/DTOs/ProductDto.cs
@@ -125,6 +125,7 @@ public class ProductVariantDto
public string VariantType { get; set; } = "Standard";
public int SortOrder { get; set; }
public int StockLevel { get; set; }
+ public decimal? Price { get; set; } // If null, uses product.Price
public bool IsActive { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
@@ -170,6 +171,9 @@ public class CreateProductVariantDto
[Range(0, int.MaxValue)]
public int StockLevel { get; set; } = 0;
+
+ [Range(0.01, double.MaxValue, ErrorMessage = "Price must be greater than 0")]
+ public decimal? Price { get; set; } // Optional: If null, uses product.Price
}
public class UpdateProductMultiBuyDto
@@ -205,5 +209,8 @@ public class UpdateProductVariantDto
[Range(0, int.MaxValue)]
public int? StockLevel { get; set; }
+ [Range(0.01, double.MaxValue, ErrorMessage = "Price must be greater than 0")]
+ public decimal? Price { get; set; } // Optional: If null, uses product.Price
+
public bool? IsActive { get; set; }
}
\ No newline at end of file
diff --git a/LittleShop/Migrations/20251003173458_AddVariantPricing.Designer.cs b/LittleShop/Migrations/20251003173458_AddVariantPricing.Designer.cs
new file mode 100644
index 0000000..e6cc7fa
--- /dev/null
+++ b/LittleShop/Migrations/20251003173458_AddVariantPricing.Designer.cs
@@ -0,0 +1,1739 @@
+//
+using System;
+using LittleShop.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace LittleShop.Migrations
+{
+ [DbContext(typeof(LittleShopContext))]
+ [Migration("20251003173458_AddVariantPricing")]
+ partial class AddVariantPricing
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "9.0.9");
+
+ modelBuilder.Entity("LittleShop.Models.Bot", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("BotKey")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("IpAddress")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("IsActive")
+ .HasColumnType("INTEGER");
+
+ b.Property("LastConfigSyncAt")
+ .HasColumnType("TEXT");
+
+ b.Property("LastSeenAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("PersonalityName")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("PlatformDisplayName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("PlatformId")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("PlatformUsername")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Settings")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Status")
+ .HasColumnType("INTEGER");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.Property("Version")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("BotKey")
+ .IsUnique();
+
+ b.HasIndex("Name");
+
+ b.HasIndex("Status");
+
+ b.ToTable("Bots");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.BotActivity", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("ActivityDescription")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("ActivityType")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("BotId")
+ .HasColumnType("TEXT");
+
+ b.Property("CategoryName")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("DeviceType")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("Location")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Metadata")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("OrderId")
+ .HasColumnType("TEXT");
+
+ b.Property("Platform")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("ProductId")
+ .HasColumnType("TEXT");
+
+ b.Property("ProductName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("Quantity")
+ .HasColumnType("INTEGER");
+
+ b.Property("SessionIdentifier")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("Timestamp")
+ .HasColumnType("TEXT");
+
+ b.Property("UserDisplayName")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Value")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ActivityType");
+
+ b.HasIndex("OrderId");
+
+ b.HasIndex("ProductId");
+
+ b.HasIndex("SessionIdentifier");
+
+ b.HasIndex("Timestamp");
+
+ b.HasIndex("BotId", "Timestamp");
+
+ b.ToTable("BotActivities");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.BotContact", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("BotId")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("CustomerId")
+ .HasColumnType("TEXT");
+
+ b.Property("DisplayName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("EncryptedContactData")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("FirstContactDate")
+ .HasColumnType("TEXT");
+
+ b.Property("FirstName")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("IsActive")
+ .HasColumnType("INTEGER");
+
+ b.Property("IsRecovered")
+ .HasColumnType("INTEGER");
+
+ b.Property("LastContactDate")
+ .HasColumnType("TEXT");
+
+ b.Property("LastKnownLanguage")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("LastName")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("Notes")
+ .HasMaxLength(1000)
+ .HasColumnType("TEXT");
+
+ b.Property("Preferences")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("RecoveredAt")
+ .HasColumnType("TEXT");
+
+ b.Property("RecoveredFromBotId")
+ .HasColumnType("TEXT");
+
+ b.Property("Status")
+ .HasColumnType("INTEGER");
+
+ b.Property("StatusReason")
+ .HasColumnType("TEXT");
+
+ b.Property("TelegramUserId")
+ .HasColumnType("INTEGER");
+
+ b.Property("TelegramUsername")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("TotalInteractions")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("BotId");
+
+ b.HasIndex("CustomerId");
+
+ b.ToTable("BotContacts");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.BotMetric", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("BotId")
+ .HasColumnType("TEXT");
+
+ b.Property("Category")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("Metadata")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("MetricType")
+ .HasColumnType("INTEGER");
+
+ b.Property("RecordedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Value")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("MetricType");
+
+ b.HasIndex("BotId", "RecordedAt");
+
+ b.ToTable("BotMetrics");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.BotSession", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("BotId")
+ .HasColumnType("TEXT");
+
+ b.Property("Country")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("EndedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("IsAnonymous")
+ .HasColumnType("INTEGER");
+
+ b.Property("Language")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("LastActivityAt")
+ .HasColumnType("TEXT");
+
+ b.Property("MessageCount")
+ .HasColumnType("INTEGER");
+
+ b.Property("Metadata")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("OrderCount")
+ .HasColumnType("INTEGER");
+
+ b.Property("Platform")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("SessionIdentifier")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("StartedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("TotalSpent")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("LastActivityAt");
+
+ b.HasIndex("StartedAt");
+
+ b.HasIndex("BotId", "SessionIdentifier");
+
+ b.ToTable("BotSessions");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.Category", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Description")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("IsActive")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("Categories");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.CryptoPayment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("BTCPayInvoiceId")
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Currency")
+ .HasColumnType("INTEGER");
+
+ b.Property("ExpiresAt")
+ .HasColumnType("TEXT");
+
+ b.Property("OrderId")
+ .HasColumnType("TEXT");
+
+ b.Property("PaidAmount")
+ .HasColumnType("decimal(18,8)");
+
+ b.Property("PaidAt")
+ .HasColumnType("TEXT");
+
+ b.Property("RequiredAmount")
+ .HasColumnType("decimal(18,8)");
+
+ b.Property("SilverPayOrderId")
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("Status")
+ .HasColumnType("INTEGER");
+
+ b.Property("TransactionHash")
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("WalletAddress")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("BTCPayInvoiceId");
+
+ b.HasIndex("OrderId");
+
+ b.HasIndex("WalletAddress");
+
+ b.ToTable("CryptoPayments");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.Customer", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("AllowMarketing")
+ .HasColumnType("INTEGER");
+
+ b.Property("AllowOrderUpdates")
+ .HasColumnType("INTEGER");
+
+ b.Property("AverageOrderValue")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("BlockReason")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("CancelledOrders")
+ .HasColumnType("INTEGER");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("CustomerNotes")
+ .HasMaxLength(2000)
+ .HasColumnType("TEXT");
+
+ b.Property("DataRetentionDate")
+ .HasColumnType("TEXT");
+
+ b.Property("DisputedOrders")
+ .HasColumnType("INTEGER");
+
+ b.Property("Email")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("FirstOrderDate")
+ .HasColumnType("TEXT");
+
+ b.Property("IsActive")
+ .HasColumnType("INTEGER");
+
+ b.Property("IsBlocked")
+ .HasColumnType("INTEGER");
+
+ b.Property("Language")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("TEXT");
+
+ b.Property("LastActiveAt")
+ .HasColumnType("TEXT");
+
+ b.Property("LastOrderDate")
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumber")
+ .HasMaxLength(20)
+ .HasColumnType("TEXT");
+
+ b.Property("RiskScore")
+ .HasColumnType("INTEGER");
+
+ b.Property("SuccessfulOrders")
+ .HasColumnType("INTEGER");
+
+ b.Property("TelegramDisplayName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("TelegramFirstName")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("TelegramLastName")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("TelegramUserId")
+ .HasColumnType("INTEGER");
+
+ b.Property("TelegramUsername")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Timezone")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("TEXT");
+
+ b.Property("TotalOrders")
+ .HasColumnType("INTEGER");
+
+ b.Property("TotalSpent")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedAt");
+
+ b.HasIndex("DataRetentionDate");
+
+ b.HasIndex("Email");
+
+ b.HasIndex("LastActiveAt");
+
+ b.HasIndex("TelegramUserId")
+ .IsUnique();
+
+ b.HasIndex("TelegramUsername");
+
+ b.ToTable("Customers");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.CustomerMessage", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("AdminUserId")
+ .HasColumnType("TEXT");
+
+ b.Property("ArchivedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("AutoGenerationTrigger")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Content")
+ .IsRequired()
+ .HasMaxLength(4000)
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("CustomerId")
+ .HasColumnType("TEXT");
+
+ b.Property("DeliveredAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Direction")
+ .HasColumnType("INTEGER");
+
+ b.Property("ExpiresAt")
+ .HasColumnType("TEXT");
+
+ b.Property("FailedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("FailureReason")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("IsArchived")
+ .HasColumnType("INTEGER");
+
+ b.Property("IsAutoGenerated")
+ .HasColumnType("INTEGER");
+
+ b.Property("IsMarketing")
+ .HasColumnType("INTEGER");
+
+ b.Property("IsUrgent")
+ .HasColumnType("INTEGER");
+
+ b.Property("NextRetryAt")
+ .HasColumnType("TEXT");
+
+ b.Property("OrderId")
+ .HasColumnType("TEXT");
+
+ b.Property("ParentMessageId")
+ .HasColumnType("TEXT");
+
+ b.Property("Platform")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("PlatformMessageId")
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("Priority")
+ .HasColumnType("INTEGER");
+
+ b.Property("ReadAt")
+ .HasColumnType("TEXT");
+
+ b.Property("RequiresResponse")
+ .HasColumnType("INTEGER");
+
+ b.Property("RetryCount")
+ .HasColumnType("INTEGER");
+
+ b.Property("ScheduledFor")
+ .HasColumnType("TEXT");
+
+ b.Property("SentAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Status")
+ .HasColumnType("INTEGER");
+
+ b.Property("Subject")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("ThreadId")
+ .HasColumnType("TEXT");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AdminUserId");
+
+ b.HasIndex("Direction");
+
+ b.HasIndex("OrderId");
+
+ b.HasIndex("ParentMessageId");
+
+ b.HasIndex("ScheduledFor");
+
+ b.HasIndex("Status");
+
+ b.HasIndex("ThreadId");
+
+ b.HasIndex("Type");
+
+ b.HasIndex("CustomerId", "CreatedAt");
+
+ b.ToTable("CustomerMessages");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.Order", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("AcceptedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("AcceptedByUser")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("ActualDeliveryDate")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Currency")
+ .IsRequired()
+ .HasMaxLength(10)
+ .HasColumnType("TEXT");
+
+ b.Property("CustomerId")
+ .HasColumnType("TEXT");
+
+ b.Property("DispatchedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("DispatchedByUser")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("ExpectedDeliveryDate")
+ .HasColumnType("TEXT");
+
+ b.Property("IdentityReference")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Notes")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("OnHoldAt")
+ .HasColumnType("TEXT");
+
+ b.Property("OnHoldReason")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("PackedByUser")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("PackingStartedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("PaidAt")
+ .HasColumnType("TEXT");
+
+ b.Property("ShippedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("ShippingAddress")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("ShippingCity")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("ShippingCountry")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("ShippingName")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("ShippingPostCode")
+ .IsRequired()
+ .HasMaxLength(20)
+ .HasColumnType("TEXT");
+
+ b.Property("Status")
+ .HasColumnType("INTEGER");
+
+ b.Property("TotalAmount")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("TrackingNumber")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatedAt");
+
+ b.HasIndex("CustomerId");
+
+ b.HasIndex("IdentityReference");
+
+ b.ToTable("Orders");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.OrderItem", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("OrderId")
+ .HasColumnType("TEXT");
+
+ b.Property("ProductId")
+ .HasColumnType("TEXT");
+
+ b.Property("ProductMultiBuyId")
+ .HasColumnType("TEXT");
+
+ b.Property("ProductVariantId")
+ .HasColumnType("TEXT");
+
+ b.Property("Quantity")
+ .HasColumnType("INTEGER");
+
+ b.Property("SelectedVariants")
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("TotalPrice")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("UnitPrice")
+ .HasColumnType("decimal(18,2)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrderId");
+
+ b.HasIndex("ProductId");
+
+ b.HasIndex("ProductMultiBuyId");
+
+ b.HasIndex("ProductVariantId");
+
+ b.ToTable("OrderItems");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.Product", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CategoryId")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("IsActive")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(200)
+ .HasColumnType("TEXT");
+
+ b.Property("Price")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("StockQuantity")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("VariantCollectionId")
+ .HasColumnType("TEXT");
+
+ b.Property("VariantsJson")
+ .HasColumnType("TEXT");
+
+ b.Property("Weight")
+ .HasColumnType("decimal(18,4)");
+
+ b.Property("WeightUnit")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CategoryId");
+
+ b.HasIndex("VariantCollectionId");
+
+ b.ToTable("Products");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.ProductMultiBuy", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("IsActive")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Price")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("PricePerUnit")
+ .HasColumnType("decimal(18,2)");
+
+ b.Property("ProductId")
+ .HasColumnType("TEXT");
+
+ b.Property("Quantity")
+ .HasColumnType("INTEGER");
+
+ b.Property("SortOrder")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IsActive");
+
+ b.HasIndex("ProductId", "Quantity")
+ .IsUnique();
+
+ b.HasIndex("ProductId", "SortOrder");
+
+ b.ToTable("ProductMultiBuys");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.ProductPhoto", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("AltText")
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("FileName")
+ .IsRequired()
+ .HasMaxLength(500)
+ .HasColumnType("TEXT");
+
+ b.Property("FilePath")
+ .IsRequired()
+ .HasMaxLength(1000)
+ .HasColumnType("TEXT");
+
+ b.Property("ProductId")
+ .HasColumnType("TEXT");
+
+ b.Property("SortOrder")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ProductId");
+
+ b.ToTable("ProductPhotos");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.ProductVariant", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("IsActive")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(100)
+ .HasColumnType("TEXT");
+
+ b.Property("Price")
+ .HasColumnType("TEXT");
+
+ b.Property("ProductId")
+ .HasColumnType("TEXT");
+
+ b.Property("SortOrder")
+ .HasColumnType("INTEGER");
+
+ b.Property("StockLevel")
+ .HasColumnType("INTEGER");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("VariantType")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("TEXT");
+
+ b.Property("Weight")
+ .HasColumnType("TEXT");
+
+ b.Property("WeightUnit")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("IsActive");
+
+ b.HasIndex("ProductId", "Name")
+ .IsUnique();
+
+ b.HasIndex("ProductId", "SortOrder");
+
+ b.ToTable("ProductVariants");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.PushSubscription", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Auth")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("CustomerId")
+ .HasColumnType("TEXT");
+
+ b.Property("Endpoint")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("IpAddress")
+ .HasColumnType("TEXT");
+
+ b.Property("IsActive")
+ .HasColumnType("INTEGER");
+
+ b.Property("LastUsedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("P256DH")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("SubscribedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("UserAgent")
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CustomerId");
+
+ b.HasIndex("Endpoint")
+ .IsUnique();
+
+ b.HasIndex("IsActive");
+
+ b.HasIndex("SubscribedAt");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("PushSubscriptions");
+ });
+
+ modelBuilder.Entity("LittleShop.Models.Review", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("ApprovedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("ApprovedByUserId")
+ .HasColumnType("TEXT");
+
+ b.Property("Comment")
+ .HasMaxLength(2000)
+ .HasColumnType("TEXT");
+
+ b.Property |