From 27b3ac60c796ade51dc8deaa64f13dc8dc9df5d2 Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Thu, 25 Jun 2026 10:08:07 +0330 Subject: [PATCH] feat(menu): per-item print station (cold bar / kitchen / barista) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each menu item can now pick its own print station, overriding the category's — so a category can fan out to different printers (e.g. a drink → cold bar, a food → kitchen). Adds MenuItem.KitchenStationId (+ migration, FK SetNull), wires create/update/DTO, and updates kitchen-ticket routing to group by the item's station ?? the category's station ?? the branch kitchen printer. Deleting a station now also clears item assignments. Menu item editor gains a "Print station" dropdown (default = "same as category"). fa/en/ar added. Backend built clean via the Nexus mirror; migration applies on deploy (MigrateAsync). Co-Authored-By: Claude Opus 4.8 --- src/Meezi.API/Models/Menu/MenuDtos.cs | 9 +- .../Services/KitchenStationService.cs | 6 + src/Meezi.API/Services/MenuService.cs | 8 +- .../Printing/NetworkPrinterService.cs | 13 +- src/Meezi.Core/Entities/KitchenStation.cs | 1 + src/Meezi.Core/Entities/MenuItem.cs | 4 + src/Meezi.Infrastructure/Data/AppDbContext.cs | 1 + ...2731_AddMenuItemKitchenStation.Designer.cs | 3511 +++++++++++++++++ ...0260625062731_AddMenuItemKitchenStation.cs | 49 + .../Migrations/AppDbContextModelSnapshot.cs | 126 +- web/dashboard/messages/ar.json | 3 +- web/dashboard/messages/en.json | 3 +- web/dashboard/messages/fa.json | 3 +- .../src/components/menu/menu-admin-screen.tsx | 26 + 14 files changed, 3693 insertions(+), 70 deletions(-) create mode 100644 src/Meezi.Infrastructure/Data/Migrations/20260625062731_AddMenuItemKitchenStation.Designer.cs create mode 100644 src/Meezi.Infrastructure/Data/Migrations/20260625062731_AddMenuItemKitchenStation.cs diff --git a/src/Meezi.API/Models/Menu/MenuDtos.cs b/src/Meezi.API/Models/Menu/MenuDtos.cs index 2c222f6..98473b1 100644 --- a/src/Meezi.API/Models/Menu/MenuDtos.cs +++ b/src/Meezi.API/Models/Menu/MenuDtos.cs @@ -55,7 +55,8 @@ public record MenuItemDto( string? ImageUrl, string? VideoUrl, string? Model3dUrl, - bool IsAvailable); + bool IsAvailable, + string? KitchenStationId); public record CreateMenuItemRequest( string CategoryId, @@ -68,7 +69,8 @@ public record CreateMenuItemRequest( string? ImageUrl = null, string? VideoUrl = null, string? Model3dUrl = null, - bool IsAvailable = true); + bool IsAvailable = true, + string? KitchenStationId = null); public record UpdateMenuItemRequest( string? CategoryId, @@ -81,6 +83,7 @@ public record UpdateMenuItemRequest( string? ImageUrl, string? VideoUrl, string? Model3dUrl, - bool? IsAvailable); + bool? IsAvailable, + string? KitchenStationId); public record UpdateMenuItemAvailabilityRequest(bool IsAvailable); diff --git a/src/Meezi.API/Services/KitchenStationService.cs b/src/Meezi.API/Services/KitchenStationService.cs index 4e248f8..7f62f8a 100644 --- a/src/Meezi.API/Services/KitchenStationService.cs +++ b/src/Meezi.API/Services/KitchenStationService.cs @@ -114,6 +114,12 @@ public class KitchenStationService : IKitchenStationService foreach (var cat in categories) cat.KitchenStationId = null; + var items = await _db.MenuItems + .Where(i => i.KitchenStationId == id && i.CafeId == cafeId) + .ToListAsync(ct); + foreach (var item in items) + item.KitchenStationId = null; + entity.DeletedAt = DateTime.UtcNow; await _db.SaveChangesAsync(ct); return true; diff --git a/src/Meezi.API/Services/MenuService.cs b/src/Meezi.API/Services/MenuService.cs index abfd048..aec878d 100644 --- a/src/Meezi.API/Services/MenuService.cs +++ b/src/Meezi.API/Services/MenuService.cs @@ -139,7 +139,8 @@ public class MenuService : IMenuService ImageUrl = imageUrl, VideoUrl = request.VideoUrl, Model3dUrl = NormalizeOptionalText(request.Model3dUrl), - IsAvailable = request.IsAvailable + IsAvailable = request.IsAvailable, + KitchenStationId = string.IsNullOrWhiteSpace(request.KitchenStationId) ? null : request.KitchenStationId, }; _db.MenuItems.Add(entity); @@ -178,6 +179,8 @@ public class MenuService : IMenuService if (request.Model3dUrl is not null) entity.Model3dUrl = string.IsNullOrWhiteSpace(request.Model3dUrl) ? null : request.Model3dUrl.Trim(); if (request.IsAvailable.HasValue) entity.IsAvailable = request.IsAvailable.Value; + if (request.KitchenStationId is not null) + entity.KitchenStationId = string.IsNullOrWhiteSpace(request.KitchenStationId) ? null : request.KitchenStationId; await _db.SaveChangesAsync(cancellationToken); return ToItemDto(entity); @@ -236,5 +239,6 @@ public class MenuService : IMenuService MenuItemImageDefaults.ResolveDisplayImageUrl(i), i.VideoUrl, i.Model3dUrl, - i.IsAvailable); + i.IsAvailable, + i.KitchenStationId); } diff --git a/src/Meezi.API/Services/Printing/NetworkPrinterService.cs b/src/Meezi.API/Services/Printing/NetworkPrinterService.cs index 6c19803..138a022 100644 --- a/src/Meezi.API/Services/Printing/NetworkPrinterService.cs +++ b/src/Meezi.API/Services/Printing/NetworkPrinterService.cs @@ -76,15 +76,16 @@ public class NetworkPrinterService : IPrinterService return PrintResult.Ok(); var menuItemIds = activeItems.Select(i => i.MenuItemId).Distinct().ToList(); - var categoryStations = await ( + // Per-item station overrides the category's station; fall back to category. + var itemStations = await ( from m in _db.MenuItems.AsNoTracking() join c in _db.MenuCategories.AsNoTracking() on m.CategoryId equals c.Id where menuItemIds.Contains(m.Id) && m.CafeId == cafeId - select new { m.Id, c.KitchenStationId } + select new { m.Id, StationId = m.KitchenStationId ?? c.KitchenStationId } ).ToListAsync(ct); - var stationIds = categoryStations - .Select(x => x.KitchenStationId) + var stationIds = itemStations + .Select(x => x.StationId) .Where(id => !string.IsNullOrEmpty(id)) .Distinct() .ToList(); @@ -99,8 +100,8 @@ public class NetworkPrinterService : IPrinterService var groups = activeItems .GroupBy(item => { - var cat = categoryStations.FirstOrDefault(c => c.Id == item.MenuItemId); - return cat?.KitchenStationId; + var map = itemStations.FirstOrDefault(c => c.Id == item.MenuItemId); + return map?.StationId; }) .ToList(); diff --git a/src/Meezi.Core/Entities/KitchenStation.cs b/src/Meezi.Core/Entities/KitchenStation.cs index 1b78221..6362936 100644 --- a/src/Meezi.Core/Entities/KitchenStation.cs +++ b/src/Meezi.Core/Entities/KitchenStation.cs @@ -12,4 +12,5 @@ public class KitchenStation : TenantEntity public Cafe Cafe { get; set; } = null!; public Branch? Branch { get; set; } public ICollection Categories { get; set; } = []; + public ICollection MenuItems { get; set; } = []; } diff --git a/src/Meezi.Core/Entities/MenuItem.cs b/src/Meezi.Core/Entities/MenuItem.cs index 973312c..34c1000 100644 --- a/src/Meezi.Core/Entities/MenuItem.cs +++ b/src/Meezi.Core/Entities/MenuItem.cs @@ -15,9 +15,13 @@ public class MenuItem : TenantEntity /// GLB/GLTF 3D model for QR menu interactive view (poster = ImageUrl). public string? Model3dUrl { get; set; } public bool IsAvailable { get; set; } = true; + /// Optional per-item print station (cold bar, kitchen, barista …). + /// Overrides the item's category station when set. + public string? KitchenStationId { get; set; } public Cafe Cafe { get; set; } = null!; public MenuCategory Category { get; set; } = null!; + public KitchenStation? KitchenStation { get; set; } public ICollection OrderItems { get; set; } = []; public ICollection BranchOverrides { get; set; } = []; public ICollection RecipeIngredients { get; set; } = []; diff --git a/src/Meezi.Infrastructure/Data/AppDbContext.cs b/src/Meezi.Infrastructure/Data/AppDbContext.cs index 25fd36e..8578b72 100644 --- a/src/Meezi.Infrastructure/Data/AppDbContext.cs +++ b/src/Meezi.Infrastructure/Data/AppDbContext.cs @@ -271,6 +271,7 @@ public class AppDbContext : DbContext e.Property(x => x.Price).HasPrecision(18, 2); e.HasOne(x => x.Cafe).WithMany(c => c.MenuItems).HasForeignKey(x => x.CafeId).OnDelete(DeleteBehavior.Cascade); e.HasOne(x => x.Category).WithMany(c => c.MenuItems).HasForeignKey(x => x.CategoryId).OnDelete(DeleteBehavior.Cascade); + e.HasOne(x => x.KitchenStation).WithMany(s => s.MenuItems).HasForeignKey(x => x.KitchenStationId).OnDelete(DeleteBehavior.SetNull); e.HasQueryFilter(x => x.DeletedAt == null); }); diff --git a/src/Meezi.Infrastructure/Data/Migrations/20260625062731_AddMenuItemKitchenStation.Designer.cs b/src/Meezi.Infrastructure/Data/Migrations/20260625062731_AddMenuItemKitchenStation.Designer.cs new file mode 100644 index 0000000..5501850 --- /dev/null +++ b/src/Meezi.Infrastructure/Data/Migrations/20260625062731_AddMenuItemKitchenStation.Designer.cs @@ -0,0 +1,3511 @@ +// +using System; +using Meezi.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Meezi.Infrastructure.Data.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260625062731_AddMenuItemKitchenStation")] + partial class AddMenuItemKitchenStation + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Meezi.Core.Entities.Attendance", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ClockIn") + .HasColumnType("timestamp with time zone"); + + b.Property("ClockOut") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Notes") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId", "Date") + .IsUnique(); + + b.ToTable("Attendances"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.AuditLog", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Action") + .IsRequired() + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("ActorId") + .HasColumnType("text"); + + b.Property("ActorName") + .HasMaxLength(160) + .HasColumnType("character varying(160)"); + + b.Property("ActorRole") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("BranchId") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DetailsJson") + .HasColumnType("text"); + + b.Property("EntityId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("EntityType") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Summary") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.HasKey("Id"); + + b.HasIndex("CafeId", "BranchId"); + + b.HasIndex("CafeId", "Category"); + + b.HasIndex("CafeId", "CreatedAt"); + + b.ToTable("AuditLogs"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Branch", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccentColor") + .HasColumnType("text"); + + b.Property("Address") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("AutoCutEnabled") + .HasColumnType("boolean"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("KitchenPrinterIp") + .HasMaxLength(45) + .HasColumnType("character varying(45)"); + + b.Property("KitchenPrinterPort") + .HasColumnType("integer"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PaperWidthMm") + .HasColumnType("integer"); + + b.Property("Phone") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("PosDeviceIp") + .HasMaxLength(45) + .HasColumnType("character varying(45)"); + + b.Property("PosDevicePort") + .HasColumnType("integer"); + + b.Property("ReceiptFooter") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ReceiptHeader") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ReceiptPrinterIp") + .HasMaxLength(45) + .HasColumnType("character varying(45)"); + + b.Property("ReceiptPrinterPort") + .HasColumnType("integer"); + + b.Property("ScheduledPermanentDeleteAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TaxRate") + .HasColumnType("numeric"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("WelcomeText") + .HasColumnType("text"); + + b.Property("WifiPassword") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("Id"); + + b.HasIndex("CafeId", "IsActive"); + + b.ToTable("Branches"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.BranchMenuItemOverride", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BranchId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsAvailable") + .HasColumnType("boolean"); + + b.Property("MenuItemId") + .IsRequired() + .HasColumnType("text"); + + b.Property("PriceOverride") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("SortOrderOverride") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedByUserId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CafeId"); + + b.HasIndex("MenuItemId"); + + b.HasIndex("BranchId", "MenuItemId") + .IsUnique(); + + b.ToTable("BranchMenuItemOverrides"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Cafe", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("AllowBranchTaxOverride") + .HasColumnType("boolean"); + + b.Property("City") + .HasColumnType("text"); + + b.Property("CoverImageUrl") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DefaultTaxRate") + .HasPrecision(5, 2) + .HasColumnType("numeric(5,2)"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DigikalaVendorId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("DiscoverBadgesJson") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("DiscoverProfileJson") + .HasMaxLength(8000) + .HasColumnType("character varying(8000)"); + + b.Property("GalleryJson") + .HasColumnType("text"); + + b.Property("InstagramHandle") + .HasColumnType("text"); + + b.Property("IsSuspended") + .HasColumnType("boolean"); + + b.Property("IsVerified") + .HasColumnType("boolean"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("LogoUrl") + .HasColumnType("text"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("NameAr") + .HasColumnType("text"); + + b.Property("NameEn") + .HasColumnType("text"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("PlanExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("PlanTier") + .HasColumnType("integer"); + + b.Property("PreferredLanguage") + .IsRequired() + .HasColumnType("text"); + + b.Property("RecoveryKeyCreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("RecoveryKeyHash") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ShowOnKoja") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SmsApiKey") + .HasColumnType("text"); + + b.Property("SmsSenderNumber") + .HasColumnType("text"); + + b.Property("SnappfoodVendorId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Tap30VendorId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ThemeJson") + .HasMaxLength(8000) + .HasColumnType("character varying(8000)"); + + b.Property("WebsiteUrl") + .HasColumnType("text"); + + b.Property("WorkingHoursJson") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RecoveryKeyHash") + .IsUnique(); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Cafes"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CafeFeatureOverride", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("FeatureKey") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("CafeId", "FeatureKey") + .IsUnique(); + + b.ToTable("CafeFeatureOverrides"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CafeNotification", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Body") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsRead") + .HasColumnType("boolean"); + + b.Property("ReadAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("TableNumber") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("character varying(60)"); + + b.HasKey("Id"); + + b.HasIndex("CafeId", "IsRead", "CreatedAt"); + + b.ToTable("CafeNotifications"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CafeReview", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AuthorName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("AuthorPhone") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Comment") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsHidden") + .HasColumnType("boolean"); + + b.Property("OwnerRepliedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("OwnerReply") + .HasColumnType("text"); + + b.Property("Rating") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CafeId", "CreatedAt"); + + b.ToTable("CafeReviews"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CafeReviewPhoto", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ReviewId") + .IsRequired() + .HasColumnType("text"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.HasKey("Id"); + + b.HasIndex("ReviewId"); + + b.ToTable("CafeReviewPhotos"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CashTransaction", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("BranchId") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedByUserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Method") + .HasColumnType("integer"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("ReferenceId") + .HasColumnType("text"); + + b.Property("ShiftId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("BranchId"); + + b.HasIndex("ShiftId"); + + b.HasIndex("CafeId", "BranchId"); + + b.ToTable("CashTransactions"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.ConsumerAccount", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Phone") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.HasKey("Id"); + + b.HasIndex("Phone") + .IsUnique(); + + b.ToTable("ConsumerAccounts"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Coupon", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("MaxDiscount") + .HasColumnType("numeric"); + + b.Property("MinOrderAmount") + .HasColumnType("numeric"); + + b.Property("StartsAt") + .HasColumnType("timestamp with time zone"); + + b.Property("TargetGroup") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("integer"); + + b.Property("UsageLimit") + .HasColumnType("integer"); + + b.Property("UsedCount") + .HasColumnType("integer"); + + b.Property("Value") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("CafeId", "Code") + .IsUnique(); + + b.ToTable("Coupons"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CustomRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Color") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PermissionsJson") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("CafeId"); + + b.ToTable("CustomRoles"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Customer", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BirthDateJalali") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Group") + .HasColumnType("integer"); + + b.Property("LoyaltyPoints") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NationalId") + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReferredBy") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CafeId", "Phone"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.DailyReport", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AvgOrderValue") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("BranchId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CardRevenue") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CashRevenue") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreditRevenue") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("GeneratedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("NetIncome") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("TopProducts") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("TotalExpenses") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("TotalOrders") + .HasColumnType("integer"); + + b.Property("TotalRevenue") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("TotalVoids") + .HasColumnType("integer"); + + b.Property("VoidAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("BranchId"); + + b.HasIndex("CafeId", "BranchId", "Date") + .IsUnique(); + + b.ToTable("DailyReports"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.DeliveryCommissionRate", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Platform") + .HasColumnType("integer"); + + b.Property("RatePercent") + .HasPrecision(5, 2) + .HasColumnType("numeric(5,2)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CafeId", "Platform") + .IsUnique(); + + b.ToTable("DeliveryCommissionRates"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.DemoRequest", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AdminNotes") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("BranchCount") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("BusinessName") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("ContactName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ContactedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Notes") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("Phone") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Source") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("character varying(50)") + .HasDefaultValue("website"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Status", "CreatedAt"); + + b.ToTable("DemoRequests"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Employee", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BaseSalary") + .HasColumnType("numeric"); + + b.Property("BranchId") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CustomRoleId") + .HasColumnType("text"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NationalId") + .HasColumnType("text"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text"); + + b.Property("PinCode") + .HasColumnType("text"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("Username") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("BranchId"); + + b.HasIndex("CustomRoleId"); + + b.HasIndex("CafeId", "Phone") + .IsUnique() + .HasFilter("\"DeletedAt\" IS NULL"); + + b.ToTable("Employees"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.EmployeeBranchRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BranchId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Role") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("BranchId"); + + b.HasIndex("CafeId", "BranchId"); + + b.HasIndex("EmployeeId", "BranchId") + .IsUnique() + .HasFilter("\"DeletedAt\" IS NULL"); + + b.ToTable("EmployeeBranchRoles"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.EmployeeSalary", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BaseSalary") + .HasColumnType("numeric"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Deductions") + .HasColumnType("numeric"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsPaid") + .HasColumnType("boolean"); + + b.Property("MonthYear") + .IsRequired() + .HasColumnType("text"); + + b.Property("NetSalary") + .HasColumnType("numeric"); + + b.Property("OvertimePay") + .HasColumnType("numeric"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId", "MonthYear") + .IsUnique(); + + b.ToTable("EmployeeSalaries"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.EmployeeSchedule", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DayOfWeek") + .HasColumnType("integer"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ShiftType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId", "DayOfWeek") + .IsUnique(); + + b.ToTable("EmployeeSchedules", (string)null); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Expense", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("BranchId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Category") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedByUserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Note") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ReceiptImageUrl") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ShiftId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("BranchId"); + + b.HasIndex("ShiftId"); + + b.HasIndex("CafeId", "BranchId", "CreatedAt"); + + b.ToTable("Expenses"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.IdempotencyRecord", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CompletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Method") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("ResponseBody") + .HasColumnType("text"); + + b.Property("ResponseStatusCode") + .HasColumnType("integer"); + + b.Property("Scope") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Scope", "Key") + .IsUnique(); + + b.ToTable("IdempotencyRecords"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Ingredient", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LowStockWarningPercent") + .HasPrecision(5, 2) + .HasColumnType("numeric(5,2)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ParLevel") + .HasPrecision(18, 3) + .HasColumnType("numeric(18,3)"); + + b.Property("QuantityOnHand") + .HasPrecision(18, 3) + .HasColumnType("numeric(18,3)"); + + b.Property("ReorderLevel") + .HasPrecision(18, 3) + .HasColumnType("numeric(18,3)"); + + b.Property("Unit") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UnitCost") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("CafeId"); + + b.ToTable("Ingredients"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.KitchenStation", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BranchId") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PrinterIp") + .HasMaxLength(45) + .HasColumnType("character varying(45)"); + + b.Property("PrinterPort") + .HasColumnType("integer"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("BranchId"); + + b.HasIndex("CafeId", "SortOrder"); + + b.ToTable("KitchenStations"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.LeaveRequest", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EmployeeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("EndDate") + .HasColumnType("date"); + + b.Property("Reason") + .HasColumnType("text"); + + b.Property("ReviewedBy") + .HasColumnType("text"); + + b.Property("StartDate") + .HasColumnType("date"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.ToTable("LeaveRequests"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.MediaAsset", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CafeId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ContentHash") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ContentType") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Kind") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("OriginalFileName") + .HasMaxLength(260) + .HasColumnType("character varying(260)"); + + b.Property("SizeBytes") + .HasColumnType("bigint"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.HasKey("Id"); + + b.HasIndex("CafeId", "ContentHash"); + + b.ToTable("MediaAssets"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.MenuCategory", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DiscountPercent") + .HasColumnType("numeric"); + + b.Property("Icon") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("IconPresetId") + .HasMaxLength(48) + .HasColumnType("character varying(48)"); + + b.Property("IconStyle") + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("ImageUrl") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("KitchenStationId") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NameAr") + .HasColumnType("text"); + + b.Property("NameEn") + .HasColumnType("text"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("TaxId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CafeId"); + + b.HasIndex("KitchenStationId"); + + b.HasIndex("TaxId"); + + b.ToTable("MenuCategories"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.MenuItem", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CategoryId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DiscountPercent") + .HasColumnType("numeric"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("IsAvailable") + .HasColumnType("boolean"); + + b.Property("KitchenStationId") + .HasColumnType("text"); + + b.Property("Model3dUrl") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NameAr") + .HasColumnType("text"); + + b.Property("NameEn") + .HasColumnType("text"); + + b.Property("Price") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("VideoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CafeId"); + + b.HasIndex("CategoryId"); + + b.HasIndex("KitchenStationId"); + + b.ToTable("MenuItems"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.MenuItemIngredient", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IngredientId") + .IsRequired() + .HasColumnType("text"); + + b.Property("MenuItemId") + .IsRequired() + .HasColumnType("text"); + + b.Property("QuantityPerUnit") + .HasPrecision(18, 3) + .HasColumnType("numeric(18,3)"); + + b.HasKey("Id"); + + b.HasIndex("IngredientId"); + + b.HasIndex("MenuItemId"); + + b.HasIndex("CafeId", "MenuItemId", "IngredientId") + .IsUnique(); + + b.ToTable("MenuItemIngredients"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Order", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BranchId") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CancelReason") + .HasColumnType("text"); + + b.Property("CancelledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CancelledByEmployeeId") + .HasColumnType("text"); + + b.Property("CouponId") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerId") + .HasColumnType("text"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeliveryMetaJson") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("DeliveryPlatform") + .HasColumnType("integer"); + + b.Property("DiscountAmount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("DisplayNumber") + .HasColumnType("integer"); + + b.Property("EmployeeId") + .HasColumnType("text"); + + b.Property("ExternalOrderId") + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("GuestName") + .HasColumnType("text"); + + b.Property("GuestPhone") + .HasColumnType("text"); + + b.Property("GuestTrackingToken") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("OrderType") + .HasColumnType("integer"); + + b.Property("PlatformCommission") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ReservationId") + .HasColumnType("text"); + + b.Property("SnappfoodOrderId") + .HasColumnType("text"); + + b.Property("Source") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("StatusUpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Subtotal") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("TableId") + .HasColumnType("text"); + + b.Property("TaxTotal") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("Total") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("BranchId"); + + b.HasIndex("CouponId"); + + b.HasIndex("CustomerId"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("GuestTrackingToken"); + + b.HasIndex("ReservationId"); + + b.HasIndex("TableId"); + + b.HasIndex("CafeId", "DisplayNumber") + .IsUnique(); + + b.HasIndex("CafeId", "DeliveryPlatform", "ExternalOrderId"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.OrderItem", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsVoided") + .HasColumnType("boolean"); + + b.Property("MenuItemId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Notes") + .HasColumnType("text"); + + b.Property("OrderId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Quantity") + .HasColumnType("integer"); + + b.Property("UnitPrice") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("VoidedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("VoidedByUserId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("MenuItemId"); + + b.HasIndex("OrderId"); + + b.ToTable("OrderItems"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Payment", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Method") + .HasColumnType("integer"); + + b.Property("OrderId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Reference") + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.ToTable("Payments"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.PlatformFeature", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DisplayNameEn") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DisplayNameFa") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("IsEnabledGlobally") + .HasColumnType("boolean"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("character varying(80)"); + + b.Property("ModuleGroup") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("character varying(60)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("PlatformFeatures"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.PlatformPlanDefinition", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DisplayNameEn") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DisplayNameFa") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("FeaturesJson") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsBillableOnline") + .HasColumnType("boolean"); + + b.Property("LimitsJson") + .IsRequired() + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("MonthlyPriceToman") + .HasPrecision(18) + .HasColumnType("numeric(18,0)"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.Property("Tier") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Tier") + .IsUnique(); + + b.ToTable("PlatformPlanDefinitions"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.PlatformSetting", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Category") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("character varying(60)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DescriptionFa") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(8000) + .HasColumnType("character varying(8000)"); + + b.HasKey("Id"); + + b.HasIndex("Key") + .IsUnique(); + + b.ToTable("PlatformSettings"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.PushDevice", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("City") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ConsumerAccountId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LastSeenAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Platform") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Token") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("City"); + + b.HasIndex("Token") + .IsUnique(); + + b.ToTable("PushDevices"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.QueueTicket", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BranchId") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerLabel") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IssuedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IssuedByUserId") + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("integer"); + + b.Property("OrderId") + .HasColumnType("text"); + + b.Property("ServiceDate") + .HasColumnType("date"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("BranchId"); + + b.HasIndex("OrderId"); + + b.HasIndex("CafeId", "BranchId", "ServiceDate", "Number") + .IsUnique(); + + b.ToTable("QueueTickets"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Shift", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BranchId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ClosedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ClosedByUserId") + .HasColumnType("text"); + + b.Property("ClosingCash") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Discrepancy") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ExpectedCash") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("OpenedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("OpenedByUserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("OpeningCash") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CafeId"); + + b.HasIndex("ClosedByUserId"); + + b.HasIndex("OpenedByUserId"); + + b.HasIndex("BranchId", "Status"); + + b.ToTable("RegisterShifts", (string)null); + }); + + modelBuilder.Entity("Meezi.Core.Entities.StockMovement", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BranchId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Delta") + .HasPrecision(18, 3) + .HasColumnType("numeric(18,3)"); + + b.Property("ExpenseId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("IngredientId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Kind") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("character varying(30)"); + + b.Property("Note") + .HasColumnType("text"); + + b.Property("OrderId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("TotalCostToman") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.HasKey("Id"); + + b.HasIndex("IngredientId"); + + b.HasIndex("CafeId", "OrderId"); + + b.ToTable("StockMovements"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.SubscriptionPayment", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AmountRials") + .HasColumnType("bigint"); + + b.Property("AmountToman") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("Authority") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("EffectiveFrom") + .HasColumnType("timestamp with time zone"); + + b.Property("EffectiveTo") + .HasColumnType("timestamp with time zone"); + + b.Property("Months") + .HasColumnType("integer"); + + b.Property("PlanTier") + .HasColumnType("integer"); + + b.Property("Provider") + .HasColumnType("integer"); + + b.Property("RefId") + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Authority"); + + b.HasIndex("CafeId"); + + b.ToTable("SubscriptionPayments"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.SupportTicket", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AssignedAdminId") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ClosedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedByEmployeeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Priority") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("Subject") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CreatedByEmployeeId"); + + b.HasIndex("CafeId", "Status", "UpdatedAt"); + + b.ToTable("SupportTickets"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.SupportTicketMessage", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Body") + .IsRequired() + .HasMaxLength(8000) + .HasColumnType("character varying(8000)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SenderId") + .IsRequired() + .HasColumnType("text"); + + b.Property("SenderKind") + .HasColumnType("integer"); + + b.Property("TicketId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("TicketId", "CreatedAt"); + + b.ToTable("SupportTicketMessages"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.SystemAdmin", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("Phone") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Username") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Phone") + .IsUnique(); + + b.ToTable("SystemAdmins"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Table", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BranchId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Floor") + .HasColumnType("text"); + + b.Property("ImageUrl") + .HasColumnType("text"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsCleaning") + .HasColumnType("boolean"); + + b.Property("Number") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("QrCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("SectionId") + .HasColumnType("text"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("VideoUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CafeId"); + + b.HasIndex("QrCode") + .IsUnique(); + + b.HasIndex("SectionId"); + + b.HasIndex("BranchId", "SectionId", "SortOrder"); + + b.ToTable("Tables"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.TableReservation", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerId") + .HasColumnType("text"); + + b.Property("Date") + .HasColumnType("date"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("GuestName") + .IsRequired() + .HasColumnType("text"); + + b.Property("GuestPhone") + .IsRequired() + .HasColumnType("text"); + + b.Property("Notes") + .HasColumnType("text"); + + b.Property("PartySize") + .HasColumnType("integer"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TableId") + .HasColumnType("text"); + + b.Property("Time") + .HasColumnType("time without time zone"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("TableId"); + + b.HasIndex("CafeId", "Date", "Time"); + + b.ToTable("TableReservations"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.TableSection", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("BranchId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SortOrder") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CafeId"); + + b.HasIndex("BranchId", "Name"); + + b.ToTable("TableSections"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Tax", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsCompound") + .HasColumnType("boolean"); + + b.Property("IsDefault") + .HasColumnType("boolean"); + + b.Property("IsRequired") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Rate") + .HasPrecision(5, 2) + .HasColumnType("numeric(5,2)"); + + b.HasKey("Id"); + + b.HasIndex("CafeId"); + + b.ToTable("Taxes"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.WebhookLog", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AttemptCount") + .HasColumnType("integer"); + + b.Property("CafeId") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ExternalOrderId") + .HasMaxLength(120) + .HasColumnType("character varying(120)"); + + b.Property("MeeziOrderId") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Platform") + .HasColumnType("integer"); + + b.Property("Processed") + .HasColumnType("boolean"); + + b.Property("ProcessedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("RawBody") + .IsRequired() + .HasColumnType("text"); + + b.Property("SignatureHeader") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("SignatureValid") + .HasColumnType("boolean"); + + b.Property("Success") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("CafeId"); + + b.HasIndex("Platform", "CreatedAt"); + + b.ToTable("WebhookLogs"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.WebsiteBlogPost", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Author") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("CategoryEn") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("CategoryFa") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ContentEn") + .IsRequired() + .HasColumnType("text"); + + b.Property("ContentFa") + .IsRequired() + .HasColumnType("text"); + + b.Property("CoverImage") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExcerptEn") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("ExcerptFa") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("IsPublished") + .HasColumnType("boolean"); + + b.Property("PublishedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Slug") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("TagsJson") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasDefaultValue("[]"); + + b.Property("TitleEn") + .IsRequired() + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("TitleFa") + .IsRequired() + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("ViewCount") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Slug") + .IsUnique(); + + b.HasIndex("IsPublished", "PublishedAt"); + + b.ToTable("WebsiteBlogPosts"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.WebsiteComment", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AuthorEmail") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("AuthorName") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(3000) + .HasColumnType("character varying(3000)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("IsApproved") + .HasColumnType("boolean"); + + b.Property("PostSlug") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("PostSlug", "IsApproved", "CreatedAt"); + + b.ToTable("WebsiteComments"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Attendance", b => + { + b.HasOne("Meezi.Core.Entities.Employee", "Employee") + .WithMany("Attendances") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.AuditLog", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", null) + .WithMany() + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Branch", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany("Branches") + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cafe"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.BranchMenuItemOverride", b => + { + b.HasOne("Meezi.Core.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.MenuItem", "MenuItem") + .WithMany("BranchOverrides") + .HasForeignKey("MenuItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("MenuItem"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CafeFeatureOverride", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", null) + .WithMany() + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CafeReview", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany("Reviews") + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cafe"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CafeReviewPhoto", b => + { + b.HasOne("Meezi.Core.Entities.CafeReview", "Review") + .WithMany("Photos") + .HasForeignKey("ReviewId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Review"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CashTransaction", b => + { + b.HasOne("Meezi.Core.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Meezi.Core.Entities.Shift", "Shift") + .WithMany("Transactions") + .HasForeignKey("ShiftId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Shift"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Coupon", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany("Coupons") + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cafe"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CustomRole", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany() + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cafe"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Customer", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany("Customers") + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cafe"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.DailyReport", b => + { + b.HasOne("Meezi.Core.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.DeliveryCommissionRate", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany() + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cafe"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Employee", b => + { + b.HasOne("Meezi.Core.Entities.Branch", "Branch") + .WithMany("Staff") + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany("Employees") + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.CustomRole", "CustomRole") + .WithMany("Employees") + .HasForeignKey("CustomRoleId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("Cafe"); + + b.Navigation("CustomRole"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.EmployeeBranchRole", b => + { + b.HasOne("Meezi.Core.Entities.Branch", "Branch") + .WithMany("StaffRoles") + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.Employee", "Employee") + .WithMany("BranchRoles") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.EmployeeSalary", b => + { + b.HasOne("Meezi.Core.Entities.Employee", "Employee") + .WithMany("Salaries") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.EmployeeSchedule", b => + { + b.HasOne("Meezi.Core.Entities.Employee", "Employee") + .WithMany("Schedules") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Expense", b => + { + b.HasOne("Meezi.Core.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.Shift", "Shift") + .WithMany() + .HasForeignKey("ShiftId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("Shift"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Ingredient", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany("Ingredients") + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cafe"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.KitchenStation", b => + { + b.HasOne("Meezi.Core.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany() + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Cafe"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.LeaveRequest", b => + { + b.HasOne("Meezi.Core.Entities.Employee", "Employee") + .WithMany("LeaveRequests") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.MenuCategory", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany("MenuCategories") + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.KitchenStation", "KitchenStation") + .WithMany("Categories") + .HasForeignKey("KitchenStationId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Meezi.Core.Entities.Tax", "Tax") + .WithMany("MenuCategories") + .HasForeignKey("TaxId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Cafe"); + + b.Navigation("KitchenStation"); + + b.Navigation("Tax"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.MenuItem", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany("MenuItems") + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.MenuCategory", "Category") + .WithMany("MenuItems") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.KitchenStation", "KitchenStation") + .WithMany("MenuItems") + .HasForeignKey("KitchenStationId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Cafe"); + + b.Navigation("Category"); + + b.Navigation("KitchenStation"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.MenuItemIngredient", b => + { + b.HasOne("Meezi.Core.Entities.Ingredient", "Ingredient") + .WithMany("MenuItemRecipes") + .HasForeignKey("IngredientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.MenuItem", "MenuItem") + .WithMany("RecipeIngredients") + .HasForeignKey("MenuItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ingredient"); + + b.Navigation("MenuItem"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Order", b => + { + b.HasOne("Meezi.Core.Entities.Branch", "Branch") + .WithMany("Orders") + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany("Orders") + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.Coupon", "Coupon") + .WithMany("Orders") + .HasForeignKey("CouponId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Meezi.Core.Entities.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Meezi.Core.Entities.Employee", "Employee") + .WithMany("Orders") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Meezi.Core.Entities.TableReservation", "Reservation") + .WithMany() + .HasForeignKey("ReservationId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Meezi.Core.Entities.Table", "Table") + .WithMany("Orders") + .HasForeignKey("TableId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("Cafe"); + + b.Navigation("Coupon"); + + b.Navigation("Customer"); + + b.Navigation("Employee"); + + b.Navigation("Reservation"); + + b.Navigation("Table"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.OrderItem", b => + { + b.HasOne("Meezi.Core.Entities.MenuItem", "MenuItem") + .WithMany("OrderItems") + .HasForeignKey("MenuItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.Order", "Order") + .WithMany("Items") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MenuItem"); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Payment", b => + { + b.HasOne("Meezi.Core.Entities.Order", "Order") + .WithMany("Payments") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.QueueTicket", b => + { + b.HasOne("Meezi.Core.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany() + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.Order", "Order") + .WithMany() + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("Cafe"); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Shift", b => + { + b.HasOne("Meezi.Core.Entities.Branch", "Branch") + .WithMany() + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany() + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.Employee", "ClosedBy") + .WithMany() + .HasForeignKey("ClosedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Meezi.Core.Entities.Employee", "OpenedBy") + .WithMany() + .HasForeignKey("OpenedByUserId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Branch"); + + b.Navigation("Cafe"); + + b.Navigation("ClosedBy"); + + b.Navigation("OpenedBy"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.StockMovement", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany() + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.Ingredient", "Ingredient") + .WithMany("Movements") + .HasForeignKey("IngredientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cafe"); + + b.Navigation("Ingredient"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.SubscriptionPayment", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany("SubscriptionPayments") + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cafe"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.SupportTicket", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany() + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.Employee", "CreatedByEmployee") + .WithMany() + .HasForeignKey("CreatedByEmployeeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Cafe"); + + b.Navigation("CreatedByEmployee"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.SupportTicketMessage", b => + { + b.HasOne("Meezi.Core.Entities.SupportTicket", "Ticket") + .WithMany("Messages") + .HasForeignKey("TicketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Ticket"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Table", b => + { + b.HasOne("Meezi.Core.Entities.Branch", "Branch") + .WithMany("Tables") + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany("Tables") + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.TableSection", "Section") + .WithMany("Tables") + .HasForeignKey("SectionId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Branch"); + + b.Navigation("Cafe"); + + b.Navigation("Section"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.TableReservation", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany() + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Meezi.Core.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Meezi.Core.Entities.Table", "Table") + .WithMany() + .HasForeignKey("TableId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Cafe"); + + b.Navigation("Customer"); + + b.Navigation("Table"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.TableSection", b => + { + b.HasOne("Meezi.Core.Entities.Branch", "Branch") + .WithMany("Sections") + .HasForeignKey("BranchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Branch"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Tax", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany("Taxes") + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cafe"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.WebhookLog", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany() + .HasForeignKey("CafeId"); + + b.Navigation("Cafe"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.WebsiteComment", b => + { + b.HasOne("Meezi.Core.Entities.WebsiteBlogPost", "Post") + .WithMany("Comments") + .HasForeignKey("PostSlug") + .HasPrincipalKey("Slug") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Post"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Branch", b => + { + b.Navigation("Orders"); + + b.Navigation("Sections"); + + b.Navigation("Staff"); + + b.Navigation("StaffRoles"); + + b.Navigation("Tables"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Cafe", b => + { + b.Navigation("Branches"); + + b.Navigation("Coupons"); + + b.Navigation("Customers"); + + b.Navigation("Employees"); + + b.Navigation("Ingredients"); + + b.Navigation("MenuCategories"); + + b.Navigation("MenuItems"); + + b.Navigation("Orders"); + + b.Navigation("Reviews"); + + b.Navigation("SubscriptionPayments"); + + b.Navigation("Tables"); + + b.Navigation("Taxes"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CafeReview", b => + { + b.Navigation("Photos"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Coupon", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.CustomRole", b => + { + b.Navigation("Employees"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Customer", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Employee", b => + { + b.Navigation("Attendances"); + + b.Navigation("BranchRoles"); + + b.Navigation("LeaveRequests"); + + b.Navigation("Orders"); + + b.Navigation("Salaries"); + + b.Navigation("Schedules"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Ingredient", b => + { + b.Navigation("MenuItemRecipes"); + + b.Navigation("Movements"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.KitchenStation", b => + { + b.Navigation("Categories"); + + b.Navigation("MenuItems"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.MenuCategory", b => + { + b.Navigation("MenuItems"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.MenuItem", b => + { + b.Navigation("BranchOverrides"); + + b.Navigation("OrderItems"); + + b.Navigation("RecipeIngredients"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Order", b => + { + b.Navigation("Items"); + + b.Navigation("Payments"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Shift", b => + { + b.Navigation("Transactions"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.SupportTicket", b => + { + b.Navigation("Messages"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Table", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.TableSection", b => + { + b.Navigation("Tables"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.Tax", b => + { + b.Navigation("MenuCategories"); + }); + + modelBuilder.Entity("Meezi.Core.Entities.WebsiteBlogPost", b => + { + b.Navigation("Comments"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Meezi.Infrastructure/Data/Migrations/20260625062731_AddMenuItemKitchenStation.cs b/src/Meezi.Infrastructure/Data/Migrations/20260625062731_AddMenuItemKitchenStation.cs new file mode 100644 index 0000000..b647c19 --- /dev/null +++ b/src/Meezi.Infrastructure/Data/Migrations/20260625062731_AddMenuItemKitchenStation.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Meezi.Infrastructure.Data.Migrations +{ + /// + public partial class AddMenuItemKitchenStation : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "KitchenStationId", + table: "MenuItems", + type: "text", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_MenuItems_KitchenStationId", + table: "MenuItems", + column: "KitchenStationId"); + + migrationBuilder.AddForeignKey( + name: "FK_MenuItems_KitchenStations_KitchenStationId", + table: "MenuItems", + column: "KitchenStationId", + principalTable: "KitchenStations", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_MenuItems_KitchenStations_KitchenStationId", + table: "MenuItems"); + + migrationBuilder.DropIndex( + name: "IX_MenuItems_KitchenStationId", + table: "MenuItems"); + + migrationBuilder.DropColumn( + name: "KitchenStationId", + table: "MenuItems"); + } + } +} diff --git a/src/Meezi.Infrastructure/Data/Migrations/AppDbContextModelSnapshot.cs b/src/Meezi.Infrastructure/Data/Migrations/AppDbContextModelSnapshot.cs index 90ccda3..ea57197 100644 --- a/src/Meezi.Infrastructure/Data/Migrations/AppDbContextModelSnapshot.cs +++ b/src/Meezi.Infrastructure/Data/Migrations/AppDbContextModelSnapshot.cs @@ -707,6 +707,46 @@ namespace Meezi.Infrastructure.Data.Migrations b.ToTable("Coupons"); }); + modelBuilder.Entity("Meezi.Core.Entities.CustomRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("CafeId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Color") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("PermissionsJson") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("Id"); + + b.HasIndex("CafeId"); + + b.ToTable("CustomRoles"); + }); + modelBuilder.Entity("Meezi.Core.Entities.Customer", b => { b.Property("Id") @@ -928,46 +968,6 @@ namespace Meezi.Infrastructure.Data.Migrations b.ToTable("DemoRequests"); }); - modelBuilder.Entity("Meezi.Core.Entities.CustomRole", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("CafeId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Color") - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("DeletedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasMaxLength(500) - .HasColumnType("character varying(500)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("PermissionsJson") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.HasKey("Id"); - - b.HasIndex("CafeId"); - - b.ToTable("CustomRoles"); - }); - modelBuilder.Entity("Meezi.Core.Entities.Employee", b => { b.Property("Id") @@ -1516,6 +1516,9 @@ namespace Meezi.Infrastructure.Data.Migrations b.Property("IsAvailable") .HasColumnType("boolean"); + b.Property("KitchenStationId") + .HasColumnType("text"); + b.Property("Model3dUrl") .HasMaxLength(500) .HasColumnType("character varying(500)"); @@ -1543,6 +1546,8 @@ namespace Meezi.Infrastructure.Data.Migrations b.HasIndex("CategoryId"); + b.HasIndex("KitchenStationId"); + b.ToTable("MenuItems"); }); @@ -2824,6 +2829,17 @@ namespace Meezi.Infrastructure.Data.Migrations b.Navigation("Cafe"); }); + modelBuilder.Entity("Meezi.Core.Entities.CustomRole", b => + { + b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") + .WithMany() + .HasForeignKey("CafeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Cafe"); + }); + modelBuilder.Entity("Meezi.Core.Entities.Customer", b => { b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") @@ -2857,17 +2873,6 @@ namespace Meezi.Infrastructure.Data.Migrations b.Navigation("Cafe"); }); - modelBuilder.Entity("Meezi.Core.Entities.CustomRole", b => - { - b.HasOne("Meezi.Core.Entities.Cafe", "Cafe") - .WithMany() - .HasForeignKey("CafeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Cafe"); - }); - modelBuilder.Entity("Meezi.Core.Entities.Employee", b => { b.HasOne("Meezi.Core.Entities.Branch", "Branch") @@ -3031,9 +3036,16 @@ namespace Meezi.Infrastructure.Data.Migrations .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("Meezi.Core.Entities.KitchenStation", "KitchenStation") + .WithMany("MenuItems") + .HasForeignKey("KitchenStationId") + .OnDelete(DeleteBehavior.SetNull); + b.Navigation("Cafe"); b.Navigation("Category"); + + b.Navigation("KitchenStation"); }); modelBuilder.Entity("Meezi.Core.Entities.MenuItemIngredient", b => @@ -3401,16 +3413,16 @@ namespace Meezi.Infrastructure.Data.Migrations b.Navigation("Orders"); }); - modelBuilder.Entity("Meezi.Core.Entities.Customer", b => - { - b.Navigation("Orders"); - }); - modelBuilder.Entity("Meezi.Core.Entities.CustomRole", b => { b.Navigation("Employees"); }); + modelBuilder.Entity("Meezi.Core.Entities.Customer", b => + { + b.Navigation("Orders"); + }); + modelBuilder.Entity("Meezi.Core.Entities.Employee", b => { b.Navigation("Attendances"); @@ -3436,6 +3448,8 @@ namespace Meezi.Infrastructure.Data.Migrations modelBuilder.Entity("Meezi.Core.Entities.KitchenStation", b => { b.Navigation("Categories"); + + b.Navigation("MenuItems"); }); modelBuilder.Entity("Meezi.Core.Entities.MenuCategory", b => diff --git a/web/dashboard/messages/ar.json b/web/dashboard/messages/ar.json index 6a3fee9..7d25c7f 100644 --- a/web/dashboard/messages/ar.json +++ b/web/dashboard/messages/ar.json @@ -917,7 +917,8 @@ "deleteItemSuccess": "تم حذف الصنف", "deleteCategoryConfirmTitle": "حذف الفئة", "deleteCategoryConfirmDesc": "هل أنت متأكد من حذف الفئة «{name}»؟", - "deleteCategorySuccess": "تم حذف الفئة" + "deleteCategorySuccess": "تم حذف الفئة", + "printStationInherit": "نفس الفئة" }, "branchMenu": { "title": "قائمة الفرع", diff --git a/web/dashboard/messages/en.json b/web/dashboard/messages/en.json index fe95953..8db3b92 100644 --- a/web/dashboard/messages/en.json +++ b/web/dashboard/messages/en.json @@ -951,7 +951,8 @@ "deleteItemSuccess": "Item deleted", "deleteCategoryConfirmTitle": "Delete category", "deleteCategoryConfirmDesc": "Are you sure you want to delete the “{name}” category?", - "deleteCategorySuccess": "Category deleted" + "deleteCategorySuccess": "Category deleted", + "printStationInherit": "Same as category" }, "branchMenu": { "title": "Branch Menu", diff --git a/web/dashboard/messages/fa.json b/web/dashboard/messages/fa.json index 83d79b4..e3477e3 100644 --- a/web/dashboard/messages/fa.json +++ b/web/dashboard/messages/fa.json @@ -951,7 +951,8 @@ "deleteItemSuccess": "آیتم حذف شد", "deleteCategoryConfirmTitle": "حذف دسته‌بندی", "deleteCategoryConfirmDesc": "آیا از حذف دسته «{name}» مطمئن هستید؟", - "deleteCategorySuccess": "دسته حذف شد" + "deleteCategorySuccess": "دسته حذف شد", + "printStationInherit": "مثل دستهٔ منو" }, "branchMenu": { "title": "منوی شعبه", diff --git a/web/dashboard/src/components/menu/menu-admin-screen.tsx b/web/dashboard/src/components/menu/menu-admin-screen.tsx index a5cd4fa..9401c86 100644 --- a/web/dashboard/src/components/menu/menu-admin-screen.tsx +++ b/web/dashboard/src/components/menu/menu-admin-screen.tsx @@ -73,6 +73,7 @@ interface MenuItem { videoUrl?: string; model3dUrl?: string; isAvailable: boolean; + kitchenStationId?: string | null; } interface ItemForm { @@ -84,6 +85,7 @@ interface ItemForm { imageUrl: string; videoUrl: string; model3dUrl: string; + kitchenStationId: string; } interface CatForm { @@ -114,6 +116,7 @@ const defaultItemForm: ItemForm = { imageUrl: "", videoUrl: "", model3dUrl: "", + kitchenStationId: "", }; const defaultCatForm: CatForm = { @@ -296,6 +299,7 @@ export function MenuAdminScreen() { imageUrl: itemForm.imageUrl || null, videoUrl: itemForm.videoUrl || null, model3dUrl: itemForm.model3dUrl || null, + kitchenStationId: itemForm.kitchenStationId || null, }), onSuccess: () => { setItemModalOpen(false); @@ -314,6 +318,7 @@ export function MenuAdminScreen() { imageUrl: mediaField(itemForm.imageUrl), videoUrl: mediaField(itemForm.videoUrl), model3dUrl: mediaField(itemForm.model3dUrl), + kitchenStationId: itemForm.kitchenStationId || null, }), onSuccess: () => { setItemModalOpen(false); @@ -411,6 +416,7 @@ export function MenuAdminScreen() { imageUrl: item.imageUrl ?? "", videoUrl: item.videoUrl ?? "", model3dUrl: item.model3dUrl ?? "", + kitchenStationId: item.kitchenStationId ?? "", }); setItemModalOpen(true); }; @@ -956,6 +962,26 @@ export function MenuAdminScreen() { ) : null} + {stations.length > 0 ? ( + + + + ) : null} + {/* Actions */}
{editingItem ? (