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 к