diff --git a/apps/edr-passenger-api/src/seed/edr-passenger-org.seeder.ts b/apps/edr-passenger-api/src/seed/edr-passenger-org.seeder.ts index 1bade05ae..5cc4e8878 100644 --- a/apps/edr-passenger-api/src/seed/edr-passenger-org.seeder.ts +++ b/apps/edr-passenger-api/src/seed/edr-passenger-org.seeder.ts @@ -42,6 +42,7 @@ export class EdrPassengerOrgSeeder { await this.ensureRoles(manager, EDR_PASSENGER_ROLES); await this.ensureRolePermissions(manager, EDR_PASSENGER_ROLES); await this.ensureSuperAdminPermissions(manager); + await this.ensurePositions(manager, organization.id); }); this.logger.log(`Ensured EDR passenger organization seed for '${EDR_ORG_KEY}'`); @@ -122,21 +123,51 @@ export class EdrPassengerOrgSeeder { const roleByKey = new Map(roles.map((r) => [r.key, r])); const permByKey = new Map(permissions.map((p) => [p.key, p])); - const links = seedRoles.flatMap((seedRole) => { + let totalUpserted = 0; + let totalPruned = 0; + + for (const seedRole of seedRoles) { const role = roleByKey.get(seedRole.key); if (!role) throw new Error(`missing_role:${seedRole.key}`); - return seedRole.permissionKeys.map((key) => { - const perm = permByKey.get(key); - if (!perm) throw new Error(`missing_permission:${key}`); - return { roleId: role.id, permissionId: perm.id }; - }); - }); + const desiredPermissionIds = new Set( + seedRole.permissionKeys.map((key) => { + const perm = permByKey.get(key); + if (!perm) throw new Error(`missing_permission:${key}`); + return perm.id as string; + }), + ); - await manager.getRepository(RolePermission).upsert(links, { - conflictPaths: { roleId: true, permissionId: true }, - }); - this.logger.log(`Ensured ${links.length} passenger role-permission links`); + // Remove links that are no longer in this role's preset + const existing = await manager.getRepository(RolePermission).find({ + where: { roleId: role.id as string }, + select: { permissionId: true }, + }); + const toRemove = existing + .map((rp) => rp.permissionId as string) + .filter((permId) => !desiredPermissionIds.has(permId)); + + if (toRemove.length > 0) { + await manager.getRepository(RolePermission).delete( + toRemove.map((permissionId) => ({ roleId: role.id as string, permissionId })), + ); + totalPruned += toRemove.length; + } + + // Upsert the full desired set + const links = [...desiredPermissionIds].map((permissionId) => ({ + roleId: role.id as string, + permissionId, + })); + await manager.getRepository(RolePermission).upsert(links, { + conflictPaths: { roleId: true, permissionId: true }, + }); + totalUpserted += links.length; + } + + this.logger.log( + `Synced passenger role-permission links: ${totalUpserted} upserted, ${totalPruned} pruned`, + ); } private async ensureSuperAdminPermissions(manager: EntityManager) { @@ -163,4 +194,34 @@ export class EdrPassengerOrgSeeder { ); this.logger.log(`Ensured ${permissions.length} passenger permissions on super_admin`); } + + private async ensurePositions(manager: EntityManager, organizationId: string) { + const positions = [ + { key: 'edr_passenger_director', name: { en: 'Director', am: 'ዳይሬክተር' }, rank: 1 }, + { key: 'edr_passenger_finance_manager', name: { en: 'Finance Manager', am: 'የፋይናንስ ሥራ አስኪያጅ' }, rank: 2 }, + { key: 'edr_passenger_finance_officer', name: { en: 'Finance Officer', am: 'የፋይናንስ ኦፊሰር' }, rank: 3 }, + { key: 'edr_passenger_team_leader', name: { en: 'Team Leader', am: 'ቡድን መሪ' }, rank: 4 }, + { key: 'edr_passenger_station_master', name: { en: 'Station Master', am: 'ጣቢያ ሃላፊ' }, rank: 5 }, + { key: 'edr_passenger_station_supervisor', name: { en: 'Station Supervisor', am: 'ጣቢያ ተቆጣጣሪ' }, rank: 6 }, + { key: 'edr_passenger_ticket_officer', name: { en: 'Passenger Ticket Officer', am: 'የተሳፋሪ ቲኬት ኦፊሰር' }, rank: 7 }, + { key: 'edr_passenger_operational_staff', name: { en: 'Operational Staff', am: 'ስራ ሰራተኛ' }, rank: 8 }, + ]; + + let created = 0; + for (const pos of positions) { + const existing = await manager.query>( + `SELECT id FROM iam.positions WHERE key = $1 AND organization_id = $2 LIMIT 1`, + [pos.key, organizationId], + ); + if (existing.length === 0) { + await manager.query( + `INSERT INTO iam.positions (id, name, key, rank, organization_id, unit_id, position_type_id, created_at, updated_at) + VALUES (gen_random_uuid(), $1::jsonb, $2, $3, $4::uuid, NULL, NULL, NOW(), NOW())`, + [JSON.stringify(pos.name), pos.key, pos.rank, organizationId], + ); + created++; + } + } + this.logger.log(`Ensured ${positions.length} passenger positions (${created} newly created)`); + } } diff --git a/apps/edr-passenger-api/src/seed/edr-passenger.seed.ts b/apps/edr-passenger-api/src/seed/edr-passenger.seed.ts index cd178139a..578cf66ff 100644 --- a/apps/edr-passenger-api/src/seed/edr-passenger.seed.ts +++ b/apps/edr-passenger-api/src/seed/edr-passenger.seed.ts @@ -30,15 +30,25 @@ export const EDR_PASSENGER_ROLES: PassengerSeedRole[] = [ permissionKeys: [...ROLE_PERMISSION_PRESETS.backofficeAdmin], }, { - key: 'edr_passenger_backoffice_staff', - name: { en: 'EDR Passenger Backoffice Staff' }, - permissionKeys: [...ROLE_PERMISSION_PRESETS.backofficeStaff], + key: 'edr_passenger_station_master', + name: { en: 'EDR Passenger Station Master' }, + permissionKeys: [...ROLE_PERMISSION_PRESETS.stationMaster], + }, + { + key: 'edr_passenger_ticket_officer', + name: { en: 'EDR Passenger Ticket Officer' }, + permissionKeys: [...ROLE_PERMISSION_PRESETS.ticketOfficer], }, { key: 'edr_passenger_agent', name: { en: 'EDR Passenger Agent' }, permissionKeys: [...ROLE_PERMISSION_PRESETS.agent], }, + { + key: 'edr_passenger_backoffice_staff', + name: { en: 'EDR Passenger Backoffice Staff' }, + permissionKeys: [...ROLE_PERMISSION_PRESETS.backofficeStaff], + }, { key: 'edr_passenger_finance', name: { en: 'EDR Passenger Finance' }, diff --git a/apps/edr-passenger-api/src/seed/passenger-permissions.registry.ts b/apps/edr-passenger-api/src/seed/passenger-permissions.registry.ts index d6a804b3a..bab7787b9 100644 --- a/apps/edr-passenger-api/src/seed/passenger-permissions.registry.ts +++ b/apps/edr-passenger-api/src/seed/passenger-permissions.registry.ts @@ -87,29 +87,42 @@ export const PASSENGER_PERMS = { export const ROLE_PERMISSION_PRESETS = { backofficeAdmin: [...PASSENGER_PERMISSION_KEYS], - backofficeStaff: [ + stationMaster: [ PASSENGER_PERMS.bookings.view, PASSENGER_PERMS.bookings.manage, - PASSENGER_PERMS.bookings.cancel, - PASSENGER_PERMS.passengers.view, - PASSENGER_PERMS.passengers.manage, PASSENGER_PERMS.tickets.view, PASSENGER_PERMS.tickets.manage, - PASSENGER_PERMS.payments.viewAll, - PASSENGER_PERMS.reports.view, - PASSENGER_PERMS.dashboard.view, - PASSENGER_PERMS.notifications.send, + PASSENGER_PERMS.passengers.view, PASSENGER_PERMS.agents.view, - PASSENGER_PERMS.fraud.view, PASSENGER_PERMS.audit.view, + PASSENGER_PERMS.notifications.send, + PASSENGER_PERMS.dashboard.view, + ], + + ticketOfficer: [ + PASSENGER_PERMS.tickets.view, + PASSENGER_PERMS.tickets.manage, + PASSENGER_PERMS.bookings.view, + PASSENGER_PERMS.passengers.view, + PASSENGER_PERMS.dashboard.view, ], agent: [ PASSENGER_PERMS.bookings.view, PASSENGER_PERMS.bookings.manage, + PASSENGER_PERMS.bookings.cancel, PASSENGER_PERMS.passengers.view, PASSENGER_PERMS.tickets.view, + PASSENGER_PERMS.tickets.manage, PASSENGER_PERMS.payments.refund, + PASSENGER_PERMS.dashboard.view, + ], + + backofficeStaff: [ + PASSENGER_PERMS.bookings.view, + PASSENGER_PERMS.passengers.view, + PASSENGER_PERMS.tickets.view, + PASSENGER_PERMS.dashboard.view, ], finance: [