diff --git a/src/Meezi.API/Services/DemoSeedService.cs b/src/Meezi.API/Services/DemoSeedService.cs
index c3021b5..60e875d 100644
--- a/src/Meezi.API/Services/DemoSeedService.cs
+++ b/src/Meezi.API/Services/DemoSeedService.cs
@@ -56,10 +56,12 @@ public class DemoSeedService : IDemoSeedService
.FirstAsync(ct);
}
- // 2. Seed menu (categories + items) using café-agnostic seeder
+ // 2. Seed menu (categories + items) using café-agnostic seeder.
+ // useScopedIds=true prefixes all IDs with cafeId so multiple cafés
+ // can each have their own demo menu without primary-key collisions.
var beforeCats = await _db.MenuCategories.CountAsync(c => c.CafeId == cafeId, ct);
var beforeItems = await _db.MenuItems.CountAsync(i => i.CafeId == cafeId, ct);
- await DemoMenuSeeder.EnsureMenuAsync(_db, cafeId, taxId, _logger);
+ await DemoMenuSeeder.EnsureMenuAsync(_db, cafeId, taxId, _logger, useScopedIds: true);
var afterCats = await _db.MenuCategories.CountAsync(c => c.CafeId == cafeId, ct);
var afterItems = await _db.MenuItems.CountAsync(i => i.CafeId == cafeId, ct);
diff --git a/src/Meezi.Infrastructure/Data/DemoMenuSeeder.cs b/src/Meezi.Infrastructure/Data/DemoMenuSeeder.cs
index 9e2aaf7..d105eed 100644
--- a/src/Meezi.Infrastructure/Data/DemoMenuSeeder.cs
+++ b/src/Meezi.Infrastructure/Data/DemoMenuSeeder.cs
@@ -6,8 +6,23 @@ namespace Meezi.Infrastructure.Data;
public static class DemoMenuSeeder
{
- public static async Task EnsureMenuAsync(AppDbContext db, string cafeId, string taxId, ILogger logger)
+ ///
+ /// When true, category and item IDs are prefixed with so
+ /// multiple cafés can each have their own copy of the demo menu without a primary-key
+ /// collision. Pass false only for the legacy demo café (cafe_demo_001) whose IDs are
+ /// already in the database without a café prefix.
+ ///
+ public static async Task EnsureMenuAsync(
+ AppDbContext db, string cafeId, string taxId, ILogger logger,
+ bool useScopedIds = false)
{
+ // When useScopedIds=true every row gets a deterministic ID that is unique per café:
+ // category → "{cafeId}_{catalogId}"
+ // item → "{cafeId}_{catalogId}"
+ // The catalog item's CategoryId is remapped through the same function.
+ string Scoped(string catalogId) =>
+ useScopedIds ? $"{cafeId}_{catalogId}" : catalogId;
+
if (!await db.Taxes.AnyAsync(t => t.Id == taxId && t.CafeId == cafeId))
{
db.Taxes.Add(new Tax
@@ -29,7 +44,8 @@ public static class DemoMenuSeeder
var categoriesAdded = 0;
foreach (var cat in DemoMenuCatalog.Categories)
{
- if (existingCategoryIds.TryGetValue(cat.Id, out var row))
+ var catId = Scoped(cat.Id);
+ if (existingCategoryIds.TryGetValue(catId, out var row))
{
if (string.IsNullOrWhiteSpace(row.Icon) && !string.IsNullOrWhiteSpace(cat.Icon))
row.Icon = cat.Icon;
@@ -46,7 +62,7 @@ public static class DemoMenuSeeder
db.MenuCategories.Add(new MenuCategory
{
- Id = cat.Id,
+ Id = catId,
CafeId = cafeId,
Name = cat.Name,
NameEn = cat.NameEn,
@@ -69,14 +85,15 @@ public static class DemoMenuSeeder
var itemsAdded = 0;
foreach (var item in DemoMenuCatalog.Items)
{
- if (existingItemIds.Contains(item.Id))
+ var itemId = Scoped(item.Id);
+ if (existingItemIds.Contains(itemId))
continue;
db.MenuItems.Add(new MenuItem
{
- Id = item.Id,
+ Id = itemId,
CafeId = cafeId,
- CategoryId = item.CategoryId,
+ CategoryId = Scoped(item.CategoryId), // FK must point at scoped category ID
Name = item.Name,
NameEn = item.NameEn,
NameAr = item.NameAr,