diff --git a/src/food-market.api/Seed/DevDataSeeder.cs b/src/food-market.api/Seed/DevDataSeeder.cs index a2055af..20ce0fe 100644 --- a/src/food-market.api/Seed/DevDataSeeder.cs +++ b/src/food-market.api/Seed/DevDataSeeder.cs @@ -135,6 +135,43 @@ public async Task StartAsync(CancellationToken ct) db.SystemSettings.Add(new SystemSettings { ArchiveRetentionDays = 30 }); await db.SaveChangesAsync(ct); } + + // Sprint 33: бэкофилл дефолтов модуля «Деньги» для УЖЕ существующих + // организаций (их роли/статьи были засижены до появления модуля — + // SeedEmployeeRolesAsync их пропускает по guard'у `anyRole`). Без этого + // на stage/prod кассир демо-орги получил бы 403 на /api/money/shifts. + await EnsureMoneyDefaultsAsync(db, ct); + } + + /// Идемпотентный back-fill модуля «Деньги» по всем организациям: + /// системной роли «Кассир» проставляет MoneyView + CashShiftOperate (если + /// ещё не стоят), и гарантирует 5 системных статей расходов в каждой орге. + /// Нужно только для орг, созданных ДО Sprint 33 — у новых всё ставит + /// . + private static async Task EnsureMoneyDefaultsAsync(AppDbContext db, CancellationToken ct) + { + var cashierRoles = await db.EmployeeRoles.IgnoreQueryFilters() + .Where(r => r.IsSystem && r.Name == "Кассир") + .ToListAsync(ct); + var changed = false; + foreach (var role in cashierRoles) + { + if (!role.Permissions.MoneyView || !role.Permissions.CashShiftOperate) + { + // Permissions — owned-тип; мутация скалярных флагов на + // отслеживаемой сущности ловится DetectChanges при SaveChanges. + // Остальные права кассира (ProductsView/RetailSalesOperate/…) + // сохраняются — мы их не трогаем. + role.Permissions.MoneyView = true; + role.Permissions.CashShiftOperate = true; + changed = true; + } + } + if (changed) await db.SaveChangesAsync(ct); + + var orgIds = await db.Organizations.IgnoreQueryFilters().Select(o => o.Id).ToListAsync(ct); + foreach (var orgId in orgIds) + await SeedExpenseItemsAsync(db, orgId, ct); } /// Привязывает существующего admin@food-market.local к