feat(seed): system roles per organization + map admin → Employee

DevDataSeeder.SeedEmployeeRolesAsync — 6 системных ролей с готовыми
наборами Permissions:
- Администратор    — RolePermissions.All() (все 21 флаг)
- Менеджер         — каталог + закупки + контрагенты + отчёты + остатки
- Кладовщик        — приёмки + остатки + view товаров
- Кассир           — продажи + view товаров (привязка к кассе на UI-этапе)
- Закупщик         — закупки + контрагенты + view товаров
- Бухгалтер        — все *View, никаких edit

IsSystem=true, SortOrder сохраняет порядок отображения в селектах.
Сидируется один раз per организацию (anyRole? skip) — чтобы кастомные
правки галок админа не сбрасывались на каждый старт.

SeedAdminEmployeeAsync — после создания admin@food-market.local
(SuperAdmin Identity user) заводит Employee-запись с ролью
«Администратор» в Demo Market организации, чтобы UI «Сотрудники»
сразу показывал учётку, а не пустой список.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
nns 2026-04-26 12:01:44 +05:00
parent f38d34f42d
commit 0ff31d1450

View file

@ -81,6 +81,34 @@ public async Task StartAsync(CancellationToken ct)
await userMgr.AddToRoleAsync(admin, SystemRoles.Admin); await userMgr.AddToRoleAsync(admin, SystemRoles.Admin);
} }
} }
await SeedAdminEmployeeAsync(db, demoOrg.Id, admin?.Id, ct);
}
/// <summary>Привязывает существующего admin@food-market.local к
/// Employee-записи с системной ролью «Администратор» — чтобы UI «Сотрудники»
/// сразу показывал учётку с правильной ролью, а не пустой список.</summary>
private static async Task SeedAdminEmployeeAsync(AppDbContext db, Guid orgId, Guid? adminUserId, CancellationToken ct)
{
if (adminUserId is null) return;
var existing = await db.Employees.IgnoreQueryFilters()
.FirstOrDefaultAsync(e => e.OrganizationId == orgId && e.UserId == adminUserId, ct);
if (existing is not null) return;
var adminRole = await db.EmployeeRoles.IgnoreQueryFilters()
.FirstOrDefaultAsync(r => r.OrganizationId == orgId && r.IsSystem && r.Name == "Администратор", ct);
if (adminRole is null) return;
db.Employees.Add(new Employee
{
OrganizationId = orgId,
UserId = adminUserId,
LastName = "Admin",
FirstName = "System",
Position = "Администратор",
Email = "admin@food-market.local",
RoleId = adminRole.Id,
IsActive = true,
});
await db.SaveChangesAsync(ct);
} }
private static async Task SeedTenantReferencesAsync(AppDbContext db, Guid orgId, CancellationToken ct) private static async Task SeedTenantReferencesAsync(AppDbContext db, Guid orgId, CancellationToken ct)
@ -138,6 +166,96 @@ private static async Task SeedTenantReferencesAsync(AppDbContext db, Guid orgId,
} }
await db.SaveChangesAsync(ct); await db.SaveChangesAsync(ct);
await SeedEmployeeRolesAsync(db, orgId, ct);
}
/// <summary>Системные роли (IsSystem=true): Администратор / Менеджер /
/// Кладовщик / Кассир / Закупщик / Бухгалтер. Сидируется один раз
/// per организацию; обновлять не пытаемся, чтобы не сбросить кастомные
/// правки галок которые админ мог сделать.</summary>
private static async Task SeedEmployeeRolesAsync(AppDbContext db, Guid orgId, CancellationToken ct)
{
var anyRole = await db.EmployeeRoles.IgnoreQueryFilters().AnyAsync(r => r.OrganizationId == orgId, ct);
if (anyRole) return;
var admin = new EmployeeRole
{
OrganizationId = orgId,
Name = "Администратор",
Description = "Полный доступ ко всем разделам организации",
IsSystem = true, SortOrder = 0,
Permissions = RolePermissions.All(),
};
var manager = new EmployeeRole
{
OrganizationId = orgId,
Name = "Менеджер",
Description = "Управление каталогом, документами и контрагентами",
IsSystem = true, SortOrder = 10,
Permissions = new RolePermissions
{
ProductsView = true, ProductsEdit = true, ProductGroupsManage = true, PriceTypesManage = true,
SuppliesView = true, SuppliesEdit = true, SuppliesPost = true,
CounterpartiesView = true, CounterpartiesEdit = true,
ReportsView = true, StocksView = true,
},
};
var keeper = new EmployeeRole
{
OrganizationId = orgId,
Name = "Кладовщик",
Description = "Приёмки, инвентаризация, остатки",
IsSystem = true, SortOrder = 20,
Permissions = new RolePermissions
{
ProductsView = true,
SuppliesView = true, SuppliesEdit = true, SuppliesPost = true,
StocksView = true,
},
};
var cashier = new EmployeeRole
{
OrganizationId = orgId,
Name = "Кассир",
Description = "Продажи на кассе. Привязка к конкретной кассе обязательна.",
IsSystem = true, SortOrder = 30,
Permissions = new RolePermissions
{
ProductsView = true,
RetailSalesOperate = true, RetailSalesRefund = true,
},
};
var buyer = new EmployeeRole
{
OrganizationId = orgId,
Name = "Закупщик",
Description = "Заказы поставщикам и приёмка товара",
IsSystem = true, SortOrder = 40,
Permissions = new RolePermissions
{
ProductsView = true,
SuppliesView = true, SuppliesEdit = true,
CounterpartiesView = true, CounterpartiesEdit = true,
},
};
var accountant = new EmployeeRole
{
OrganizationId = orgId,
Name = "Бухгалтер",
Description = "Просмотр всех данных и отчётов, без редактирования",
IsSystem = true, SortOrder = 50,
Permissions = new RolePermissions
{
ProductsView = true,
SuppliesView = true,
CounterpartiesView = true,
ReportsView = true, StocksView = true,
},
};
db.EmployeeRoles.AddRange(admin, manager, keeper, cashier, buyer, accountant);
await db.SaveChangesAsync(ct);
} }
public Task StopAsync(CancellationToken ct) => Task.CompletedTask; public Task StopAsync(CancellationToken ct) => Task.CompletedTask;