using foodmarket.Domain.Catalog; using foodmarket.Domain.Organizations; using foodmarket.Infrastructure.Identity; using foodmarket.Infrastructure.Persistence; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; namespace foodmarket.Api.Seed; public class DevDataSeeder : IHostedService { private readonly IServiceProvider _services; private readonly IHostEnvironment _env; public DevDataSeeder(IServiceProvider services, IHostEnvironment env) { _services = services; _env = env; } public async Task StartAsync(CancellationToken ct) { // Idempotent — runs in all envs to bootstrap a usable admin + demo org. // Once first real user/org is set up via UI, rename/disable demo. // (Wired regardless of env so stage/prod first-deploy lands a working // admin, otherwise nobody can log in.) using var scope = _services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); var userMgr = scope.ServiceProvider.GetRequiredService>(); var roleMgr = scope.ServiceProvider.GetRequiredService>(); foreach (var role in new[] { SystemRoles.SuperAdmin, SystemRoles.Admin, SystemRoles.Manager, SystemRoles.Cashier, SystemRoles.Storekeeper }) { if (!await roleMgr.RoleExistsAsync(role)) { await roleMgr.CreateAsync(new Role { Name = role }); } } var demoOrg = await db.Organizations.FirstOrDefaultAsync(o => o.Name == "Demo Market", ct); if (demoOrg is null) { demoOrg = new Organization { Name = "Demo Market", CountryCode = "KZ", Bin = "000000000000", Address = "Алматы, ул. Пример 1", Phone = "+7 (777) 000-00-00", Email = "demo@food-market.local" }; db.Organizations.Add(demoOrg); await db.SaveChangesAsync(ct); } await SeedTenantReferencesAsync(db, demoOrg.Id, ct); const string adminEmail = "admin@food-market.local"; var admin = await userMgr.FindByEmailAsync(adminEmail); if (admin is null) { admin = new User { UserName = adminEmail, Email = adminEmail, EmailConfirmed = true, FullName = "System Admin", OrganizationId = demoOrg.Id, }; var result = await userMgr.CreateAsync(admin, "Admin12345!"); if (result.Succeeded) { await userMgr.AddToRoleAsync(admin, SystemRoles.Admin); } } } private static async Task SeedTenantReferencesAsync(AppDbContext db, Guid orgId, CancellationToken ct) { var anyVat = await db.VatRates.IgnoreQueryFilters().AnyAsync(v => v.OrganizationId == orgId, ct); if (!anyVat) { db.VatRates.AddRange( new VatRate { OrganizationId = orgId, Name = "Без НДС", Percent = 0m, IsDefault = false }, new VatRate { OrganizationId = orgId, Name = "НДС 12%", Percent = 12m, IsIncludedInPrice = true, IsDefault = true } ); } var anyUnit = await db.UnitsOfMeasure.IgnoreQueryFilters().AnyAsync(u => u.OrganizationId == orgId, ct); if (!anyUnit) { db.UnitsOfMeasure.AddRange( new UnitOfMeasure { OrganizationId = orgId, Code = "796", Symbol = "шт", Name = "штука", DecimalPlaces = 0, IsBase = true }, new UnitOfMeasure { OrganizationId = orgId, Code = "166", Symbol = "кг", Name = "килограмм", DecimalPlaces = 3 }, new UnitOfMeasure { OrganizationId = orgId, Code = "112", Symbol = "л", Name = "литр", DecimalPlaces = 3 }, new UnitOfMeasure { OrganizationId = orgId, Code = "006", Symbol = "м", Name = "метр", DecimalPlaces = 3 }, new UnitOfMeasure { OrganizationId = orgId, Code = "625", Symbol = "уп", Name = "упаковка", DecimalPlaces = 0 } ); } var anyPriceType = await db.PriceTypes.IgnoreQueryFilters().AnyAsync(p => p.OrganizationId == orgId, ct); if (!anyPriceType) { db.PriceTypes.AddRange( new PriceType { OrganizationId = orgId, Name = "Розничная", IsDefault = true, IsRetail = true, SortOrder = 1 }, new PriceType { OrganizationId = orgId, Name = "Оптовая", SortOrder = 2 } ); } var mainStore = await db.Stores.IgnoreQueryFilters().FirstOrDefaultAsync(s => s.OrganizationId == orgId && s.IsMain, ct); if (mainStore is null) { mainStore = new Store { OrganizationId = orgId, Name = "Основной склад", Code = "MAIN", Kind = StoreKind.Warehouse, IsMain = true, Address = "Алматы, ул. Пример 1", }; db.Stores.Add(mainStore); await db.SaveChangesAsync(ct); } var anyRetail = await db.RetailPoints.IgnoreQueryFilters().AnyAsync(r => r.OrganizationId == orgId, ct); if (!anyRetail) { db.RetailPoints.Add(new RetailPoint { OrganizationId = orgId, Name = "Касса 1", Code = "POS-1", StoreId = mainStore.Id, }); } await db.SaveChangesAsync(ct); } public Task StopAsync(CancellationToken ct) => Task.CompletedTask; }