173 lines
6.5 KiB
C#
173 lines
6.5 KiB
C#
|
|
using Meezi.Core.Entities;
|
||
|
|
using Meezi.Infrastructure.Data;
|
||
|
|
using Microsoft.EntityFrameworkCore;
|
||
|
|
using Microsoft.Extensions.Logging;
|
||
|
|
|
||
|
|
namespace Meezi.API.Services;
|
||
|
|
|
||
|
|
public record DemoSeedResult(
|
||
|
|
int CategoriesAdded,
|
||
|
|
int ItemsAdded,
|
||
|
|
int TablesAdded,
|
||
|
|
int IngredientsAdded,
|
||
|
|
bool TaxCreated);
|
||
|
|
|
||
|
|
public interface IDemoSeedService
|
||
|
|
{
|
||
|
|
Task<DemoSeedResult> SeedAsync(string cafeId, CancellationToken ct = default);
|
||
|
|
}
|
||
|
|
|
||
|
|
public class DemoSeedService : IDemoSeedService
|
||
|
|
{
|
||
|
|
private readonly AppDbContext _db;
|
||
|
|
private readonly ILogger<DemoSeedService> _logger;
|
||
|
|
|
||
|
|
public DemoSeedService(AppDbContext db, ILogger<DemoSeedService> logger)
|
||
|
|
{
|
||
|
|
_db = db;
|
||
|
|
_logger = logger;
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task<DemoSeedResult> SeedAsync(string cafeId, CancellationToken ct = default)
|
||
|
|
{
|
||
|
|
// 1. Ensure 9% default tax
|
||
|
|
var taxId = $"{cafeId}_demo_tax";
|
||
|
|
var taxCreated = false;
|
||
|
|
if (!await _db.Taxes.AnyAsync(t => t.CafeId == cafeId && t.IsDefault, ct))
|
||
|
|
{
|
||
|
|
_db.Taxes.Add(new Tax
|
||
|
|
{
|
||
|
|
Id = taxId,
|
||
|
|
CafeId = cafeId,
|
||
|
|
Name = "مالیات ارزش افزوده",
|
||
|
|
Rate = 9,
|
||
|
|
IsDefault = true,
|
||
|
|
IsRequired = true,
|
||
|
|
IsCompound = false
|
||
|
|
});
|
||
|
|
await _db.SaveChangesAsync(ct);
|
||
|
|
taxCreated = true;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
taxId = await _db.Taxes
|
||
|
|
.Where(t => t.CafeId == cafeId && t.IsDefault)
|
||
|
|
.Select(t => t.Id)
|
||
|
|
.FirstAsync(ct);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. Seed menu (categories + items) using café-agnostic seeder
|
||
|
|
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);
|
||
|
|
var afterCats = await _db.MenuCategories.CountAsync(c => c.CafeId == cafeId, ct);
|
||
|
|
var afterItems = await _db.MenuItems.CountAsync(i => i.CafeId == cafeId, ct);
|
||
|
|
|
||
|
|
// 3. Seed ingredients if warehouse is empty
|
||
|
|
var ingredientsAdded = 0;
|
||
|
|
if (!await _db.Ingredients.AnyAsync(i => i.CafeId == cafeId, ct))
|
||
|
|
{
|
||
|
|
var demoIngredients = BuildDemoIngredients(cafeId);
|
||
|
|
_db.Ingredients.AddRange(demoIngredients);
|
||
|
|
await _db.SaveChangesAsync(ct);
|
||
|
|
ingredientsAdded = demoIngredients.Count;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 4. Seed 10 tables if no tables exist for this café's first active branch
|
||
|
|
var tablesAdded = 0;
|
||
|
|
if (!await _db.Tables.AnyAsync(t => t.CafeId == cafeId, ct))
|
||
|
|
{
|
||
|
|
var branchId = await _db.Branches
|
||
|
|
.Where(b => b.CafeId == cafeId && b.IsActive && b.DeletedAt == null)
|
||
|
|
.OrderBy(b => b.Id)
|
||
|
|
.Select(b => b.Id)
|
||
|
|
.FirstOrDefaultAsync(ct);
|
||
|
|
|
||
|
|
if (branchId is not null)
|
||
|
|
{
|
||
|
|
var tables = BuildDemoTables(cafeId, branchId);
|
||
|
|
_db.Tables.AddRange(tables);
|
||
|
|
await _db.SaveChangesAsync(ct);
|
||
|
|
tablesAdded = tables.Count;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
_logger.LogInformation(
|
||
|
|
"Demo seed complete for cafe {CafeId}: +{Cats} cats, +{Items} items, +{Tables} tables, +{Ing} ingredients, tax={TaxCreated}",
|
||
|
|
cafeId, afterCats - beforeCats, afterItems - beforeItems, tablesAdded, ingredientsAdded, taxCreated);
|
||
|
|
|
||
|
|
return new DemoSeedResult(
|
||
|
|
CategoriesAdded: afterCats - beforeCats,
|
||
|
|
ItemsAdded: afterItems - beforeItems,
|
||
|
|
TablesAdded: tablesAdded,
|
||
|
|
IngredientsAdded: ingredientsAdded,
|
||
|
|
TaxCreated: taxCreated);
|
||
|
|
}
|
||
|
|
|
||
|
|
private static List<Ingredient> BuildDemoIngredients(string cafeId) =>
|
||
|
|
[
|
||
|
|
Ingredient(cafeId, "قهوه اسپرسو", "گرم", 2000, 500, 80, 2000),
|
||
|
|
Ingredient(cafeId, "شیر", "میلیلیتر", 10000, 2000, 15, 10000),
|
||
|
|
Ingredient(cafeId, "شکر", "گرم", 5000, 1000, 5, 5000),
|
||
|
|
Ingredient(cafeId, "وانیل", "میلیلیتر", 500, 100, 50, 500),
|
||
|
|
Ingredient(cafeId, "شکلات تلخ", "گرم", 1000, 200, 120, 1000),
|
||
|
|
Ingredient(cafeId, "خامه", "میلیلیتر", 2000, 500, 30, 2000),
|
||
|
|
Ingredient(cafeId, "دارچین", "گرم", 300, 50, 40, 300),
|
||
|
|
Ingredient(cafeId, "چای سیاه", "گرم", 1000, 200, 60, 1000),
|
||
|
|
Ingredient(cafeId, "آب معدنی", "میلیلیتر", 20000, 5000, 3, 20000),
|
||
|
|
Ingredient(cafeId, "نان تست", "عدد", 100, 20, 8000, 100),
|
||
|
|
Ingredient(cafeId, "تخممرغ", "عدد", 60, 12, 6000, 60),
|
||
|
|
Ingredient(cafeId, "کره", "گرم", 500, 100, 80, 500),
|
||
|
|
Ingredient(cafeId, "پنیر", "گرم", 1000, 200, 90, 1000),
|
||
|
|
Ingredient(cafeId, "اسپاتولا یخ", "عدد", 200, 50, 2000, 200),
|
||
|
|
Ingredient(cafeId, "سس کارامل", "میلیلیتر", 1000, 200, 60, 1000),
|
||
|
|
];
|
||
|
|
|
||
|
|
private static Ingredient Ingredient(
|
||
|
|
string cafeId, string name, string unit,
|
||
|
|
decimal qty, decimal reorder, decimal cost, decimal par) =>
|
||
|
|
new()
|
||
|
|
{
|
||
|
|
Id = $"{cafeId}_ing_{Guid.NewGuid():N}"[..36],
|
||
|
|
CafeId = cafeId,
|
||
|
|
Name = name,
|
||
|
|
Unit = unit,
|
||
|
|
QuantityOnHand = qty,
|
||
|
|
ReorderLevel = reorder,
|
||
|
|
UnitCost = cost,
|
||
|
|
ParLevel = par,
|
||
|
|
LowStockWarningPercent = 20m
|
||
|
|
};
|
||
|
|
|
||
|
|
private static List<Table> BuildDemoTables(string cafeId, string branchId)
|
||
|
|
{
|
||
|
|
var tables = new List<Table>();
|
||
|
|
// Floor 1: tables 1-4
|
||
|
|
for (var i = 1; i <= 4; i++)
|
||
|
|
tables.Add(Table(cafeId, branchId, i.ToString(), 4, "طبقه اول", i));
|
||
|
|
// Floor 2: tables 5-8
|
||
|
|
for (var i = 5; i <= 8; i++)
|
||
|
|
tables.Add(Table(cafeId, branchId, i.ToString(), 4, "طبقه دوم", i));
|
||
|
|
// VIP: tables 9-10
|
||
|
|
for (var i = 9; i <= 10; i++)
|
||
|
|
tables.Add(Table(cafeId, branchId, i.ToString(), 6, "VIP", i));
|
||
|
|
return tables;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static Table Table(
|
||
|
|
string cafeId, string branchId, string number, int capacity, string floor, int sortOrder) =>
|
||
|
|
new()
|
||
|
|
{
|
||
|
|
Id = $"{cafeId}_tbl_{Guid.NewGuid():N}"[..36],
|
||
|
|
CafeId = cafeId,
|
||
|
|
BranchId = branchId,
|
||
|
|
Number = number,
|
||
|
|
Capacity = capacity,
|
||
|
|
Floor = floor,
|
||
|
|
SortOrder = sortOrder,
|
||
|
|
QrCode = Guid.NewGuid().ToString("N"),
|
||
|
|
IsActive = true,
|
||
|
|
IsCleaning = false
|
||
|
|
};
|
||
|
|
}
|