mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 18:48:11 +00:00
Merge branch 'dev' of github.com:Tria-plc/edr-platform into freight/feature/first_mile_invoice
This commit is contained in:
@@ -214,7 +214,6 @@ export class ApprovedFirstLastMileDemoBookingsSeeder {
|
||||
includesFirstMile: true,
|
||||
includesLastMile: true,
|
||||
includesCustoms: false,
|
||||
priorityBonusPoints: 0,
|
||||
isActive: true,
|
||||
displayOrder: 10,
|
||||
},
|
||||
|
||||
@@ -295,7 +295,6 @@ export class DemoBookingsSeeder {
|
||||
includesFirstMile: false,
|
||||
includesLastMile: false,
|
||||
includesCustoms: false,
|
||||
priorityBonusPoints: 0,
|
||||
isActive: true,
|
||||
displayOrder: 1,
|
||||
},
|
||||
|
||||
171
apps/edr-freight-api/src/seed/freight-positions.seeder.ts
Normal file
171
apps/edr-freight-api/src/seed/freight-positions.seeder.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import {
|
||||
Organization,
|
||||
Permission,
|
||||
Position,
|
||||
PositionPermission,
|
||||
Unit,
|
||||
} from '@tria-plc/iamapi-common';
|
||||
import { DataSource, EntityManager, In } from 'typeorm';
|
||||
|
||||
import { EDR_FREIGHT_POSITIONS } from './edr-freight.seed';
|
||||
|
||||
const SEED_FLAG = 'SEED_EDR_ORG';
|
||||
const EDR_ORG_KEY = 'edr_freight';
|
||||
const EDR_UNIT_KEY = 'edr_freight_app';
|
||||
|
||||
/**
|
||||
* Seeds the operational freight positions (CEO, Chief, Director, Marketer,
|
||||
* Operation, Ethiopian GL, Djibouti GL) as Position + PositionPermission rows
|
||||
* on the `edr_freight_app` unit. Positions-as-roles: users get their freight
|
||||
* access by being assigned to a Position (via EmployeePosition), and the
|
||||
* position's PositionPermission grants come from EDR_FREIGHT_POSITIONS.
|
||||
*
|
||||
* Gated behind the same SEED_EDR_ORG flag as EdrOrgSeeder and depends on the
|
||||
* org/unit/permission catalog it seeds, so it must run AFTER EdrOrgSeeder.
|
||||
* Idempotent: positions upsert by (key, unitId); grants insert only the
|
||||
* permission ids a position is still missing.
|
||||
*/
|
||||
@Injectable()
|
||||
export class FreightPositionsSeeder {
|
||||
private readonly logger = new Logger(FreightPositionsSeeder.name);
|
||||
|
||||
constructor(private readonly dataSource: DataSource) {}
|
||||
|
||||
async run() {
|
||||
if (process.env[SEED_FLAG]?.trim().toLowerCase() !== 'true') {
|
||||
this.logger.log(
|
||||
`Skipping freight positions seed because ${SEED_FLAG} is not enabled`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.dataSource.transaction(async (manager) => {
|
||||
const organization = await manager.getRepository(Organization).findOne({
|
||||
where: { key: EDR_ORG_KEY },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!organization) {
|
||||
throw new Error(`missing_organization:${EDR_ORG_KEY}`);
|
||||
}
|
||||
|
||||
const unit = await manager.getRepository(Unit).findOne({
|
||||
where: { key: EDR_UNIT_KEY, organizationId: organization.id },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (!unit) {
|
||||
throw new Error(`missing_unit:${EDR_UNIT_KEY}`);
|
||||
}
|
||||
|
||||
const permissionKeyToId = await this.loadPermissionIds(manager);
|
||||
|
||||
for (const seed of EDR_FREIGHT_POSITIONS) {
|
||||
const positionId = await this.ensurePosition(
|
||||
manager,
|
||||
seed,
|
||||
unit.id as string,
|
||||
organization.id as string,
|
||||
);
|
||||
|
||||
await this.ensurePositionPermissions(
|
||||
manager,
|
||||
positionId,
|
||||
seed,
|
||||
permissionKeyToId,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
this.logger.log(
|
||||
`Ensured ${EDR_FREIGHT_POSITIONS.length} freight positions on unit '${EDR_UNIT_KEY}'`,
|
||||
);
|
||||
}
|
||||
|
||||
/** Resolve every permission key referenced by any position to its id. */
|
||||
private async loadPermissionIds(
|
||||
manager: EntityManager,
|
||||
): Promise<Map<string, string>> {
|
||||
const keys = [
|
||||
...new Set(EDR_FREIGHT_POSITIONS.flatMap((p) => p.permissionKeys)),
|
||||
];
|
||||
|
||||
const permissions = await manager.getRepository(Permission).find({
|
||||
where: { key: In(keys) },
|
||||
select: { id: true, key: true },
|
||||
});
|
||||
|
||||
const map = new Map(permissions.map((p) => [p.key, p.id as string]));
|
||||
|
||||
const missing = keys.filter((key) => !map.has(key));
|
||||
if (missing.length > 0) {
|
||||
throw new Error(`missing_permissions:${missing.join(',')}`);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private async ensurePosition(
|
||||
manager: EntityManager,
|
||||
seed: (typeof EDR_FREIGHT_POSITIONS)[number],
|
||||
unitId: string,
|
||||
organizationId: string,
|
||||
): Promise<string> {
|
||||
const positionRepository = manager.getRepository(Position);
|
||||
|
||||
const existing = await positionRepository.findOne({
|
||||
where: { key: seed.key, unitId },
|
||||
select: { id: true },
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
return existing.id as string;
|
||||
}
|
||||
|
||||
const inserted = await positionRepository.insert({
|
||||
key: seed.key,
|
||||
name: { ...seed.name },
|
||||
rank: seed.rank,
|
||||
unitId,
|
||||
organizationId,
|
||||
});
|
||||
|
||||
this.logger.log(`Seeded freight position '${seed.key}'`);
|
||||
|
||||
return inserted.identifiers[0]?.id as string;
|
||||
}
|
||||
|
||||
private async ensurePositionPermissions(
|
||||
manager: EntityManager,
|
||||
positionId: string,
|
||||
seed: (typeof EDR_FREIGHT_POSITIONS)[number],
|
||||
permissionKeyToId: Map<string, string>,
|
||||
) {
|
||||
const positionPermissionRepository =
|
||||
manager.getRepository(PositionPermission);
|
||||
|
||||
const existing = await positionPermissionRepository.find({
|
||||
where: { positionId },
|
||||
select: { permissionId: true },
|
||||
});
|
||||
const existingPermissionIds = new Set(
|
||||
existing.map((row) => row.permissionId),
|
||||
);
|
||||
|
||||
const rowsToInsert = seed.permissionKeys
|
||||
.map((key) => permissionKeyToId.get(key) as string)
|
||||
.filter((permissionId) => !existingPermissionIds.has(permissionId))
|
||||
.map((permissionId) => ({ positionId, permissionId }));
|
||||
|
||||
if (rowsToInsert.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await positionPermissionRepository.insert(rowsToInsert);
|
||||
|
||||
this.logger.log(
|
||||
`Granted ${rowsToInsert.length} permissions to position '${seed.key}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import { DataSource } from 'typeorm';
|
||||
|
||||
const SEED_FLAG = 'SEED_FREIGHT_STAFF';
|
||||
const EDR_ORG_KEY = 'edr_freight';
|
||||
const EDR_UNIT_KEY = 'edr_freight_hq';
|
||||
const EDR_UNIT_KEY = 'edr_freight_app';
|
||||
|
||||
// roleKey is kept only for backwards compatibility with existing UserRole rows;
|
||||
// access is granted via the assigned position (positionKey) + PositionPermission.
|
||||
|
||||
@@ -136,7 +136,6 @@ export class PaidImportExportMileDemoSeeder {
|
||||
includesFirstMile: true,
|
||||
includesLastMile: true,
|
||||
includesCustoms: false,
|
||||
priorityBonusPoints: 0,
|
||||
isActive: true,
|
||||
displayOrder: 11,
|
||||
},
|
||||
|
||||
@@ -12,6 +12,7 @@ import { WeightLimitRule } from "../modules/rule-engine/entities/weight-limit-ru
|
||||
import { Route } from "../modules/routes/entities/route.entity";
|
||||
import { RouteMilestone } from "../modules/routes/entities/route-milestone.entity";
|
||||
import { Yard } from "../modules/rule-engine/entities/yard.entity";
|
||||
import { deriveTradeDirection } from "../common/derive-trade-direction.util";
|
||||
|
||||
const STAFF_USER_ID = "00000000-0000-0000-0000-000000000001";
|
||||
const CEO_USER_ID = "00000000-0000-0000-0000-000000000002";
|
||||
@@ -295,6 +296,7 @@ export class PricingDataSeeder {
|
||||
originYardId: addis.id,
|
||||
destinationYardId: direDawa.id,
|
||||
status: 'AVAILABLE',
|
||||
direction: deriveTradeDirection(addis, direDawa),
|
||||
}),
|
||||
);
|
||||
await milestoneRepo.save([
|
||||
|
||||
Reference in New Issue
Block a user