feat(menu): per-item print station (cold bar / kitchen / barista)
CI/CD / CI · API (dotnet build + test) (push) Successful in 41s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 29s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 3m28s

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 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-25 10:08:07 +03:30
parent aede5bfd97
commit 27b3ac60c7
14 changed files with 3693 additions and 70 deletions
@@ -707,6 +707,46 @@ namespace Meezi.Infrastructure.Data.Migrations
b.ToTable("Coupons");
});
modelBuilder.Entity("Meezi.Core.Entities.CustomRole", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("CafeId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Color")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("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<string>("Id")
@@ -928,46 +968,6 @@ namespace Meezi.Infrastructure.Data.Migrations
b.ToTable("DemoRequests");
});
modelBuilder.Entity("Meezi.Core.Entities.CustomRole", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("CafeId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Color")
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("DeletedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Description")
.HasMaxLength(500)
.HasColumnType("character varying(500)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.Property<string>("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<string>("Id")
@@ -1516,6 +1516,9 @@ namespace Meezi.Infrastructure.Data.Migrations
b.Property<bool>("IsAvailable")
.HasColumnType("boolean");
b.Property<string>("KitchenStationId")
.HasColumnType("text");
b.Property<string>("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 =>