mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
- Added ShippingLineBookingCompletionController and associated service to handle the completion of shipping line bookings. - Introduced a new module for booking completion to maintain module separation and avoid cyclic dependencies. - Updated the train scheduling global rules to set default desk hours to 24 hours. - Modified existing services and entities to accommodate the new booking completion logic. - Enhanced the front-end components to support the new booking completion flow, including updates to the booking detail and bookings pages. - Implemented validation and error handling for booking completion, ensuring that only approved bookings can be completed. - Added migration to set default desk hours in the database.
398 lines
19 KiB
TypeScript
398 lines
19 KiB
TypeScript
import {
|
|
MiddlewareConsumer,
|
|
Module,
|
|
OnApplicationBootstrap,
|
|
RequestMethod,
|
|
} from "@nestjs/common";
|
|
import { ConfigModule, ConfigService } from "@nestjs/config";
|
|
import { TypeOrmModule, TypeOrmModuleOptions } from "@nestjs/typeorm";
|
|
import { ScheduleModule } from "@nestjs/schedule";
|
|
import { EventEmitterModule } from "@nestjs/event-emitter";
|
|
import { DataSource, DataSourceOptions } from "typeorm";
|
|
import {
|
|
ensurePostgresSchemas,
|
|
APPLICATION_SEARCH_PATH,
|
|
} from "./config/ensure-postgres-schemas";
|
|
import { IamBaselineSeeder, IamSeedModule } from "@edr/iam-seed";
|
|
import { IamModule } from "@tria-plc/iamapi-common";
|
|
import { SharedAuthModule } from "@tria-plc/api-common/modules/auth/shared-auth.module";
|
|
|
|
import appConfig from "./config/app.config";
|
|
import databaseConfig from "./config/database.config";
|
|
import telebirrConfig from "./config/telebirr.config";
|
|
import rabbitmqConfig from "./config/rabbitmq.config";
|
|
import faydaConfig from "./config/fayda.config";
|
|
import eimsConfig from "./config/eims.config";
|
|
|
|
import { BookingsModule } from "./modules/bookings/bookings.module";
|
|
import { ContractsModule } from "./modules/contracts/contracts.module";
|
|
import { SignaturesModule } from "./modules/signatures/signatures.module";
|
|
import { FilesModule } from "./modules/files/files.module";
|
|
import { ConsignmentsModule } from "./modules/consignments/consignments.module";
|
|
|
|
// import { TrainsModule } from "./modules/trains/trains.module";
|
|
import { LocomotivesModule } from "./modules/locomotives/locomotives.module";
|
|
import { TruckTypesModule } from "./modules/truck-types/truck-types.module";
|
|
import { TransitAgentsModule } from "./modules/transit-agents/transit-agents.module";
|
|
import { WagonTypesModule } from "./modules/wagon-types/wagon-types.module";
|
|
import { TrainSetsModule } from "./modules/train-sets/train-sets.module";
|
|
import { TrainSchedulesModule } from "./modules/train-schedules/train-schedules.module";
|
|
import { TrainSchedulingModule } from "./modules/train-scheduling/train-scheduling.module";
|
|
import { SchedulingRescheduleModule } from "./modules/scheduling-reschedule/scheduling-reschedule.module";
|
|
import { CompaniesModule } from "./modules/companies/companies.module";
|
|
import { ShippingLineBookingCompletionModule } from "./modules/shipping-lines/shipping-line-booking-completion.module";
|
|
import { ShippingLineCompaniesModule } from "./modules/shipping-lines/shipping-line-companies.module";
|
|
import { TrackingModule } from "./modules/tracking/tracking.module";
|
|
import { BillingModule } from "./modules/billing/billing.module";
|
|
import { NotificationsModule } from "./modules/notifications/notifications.module";
|
|
import { NotificationInboxModule } from "./modules/notification-inbox/notification-inbox.module";
|
|
import { SupportChatModule } from "./modules/support-chat/support-chat.module";
|
|
import { FileUploadSettingsModule } from "./modules/file-upload-settings/file-upload-settings.module";
|
|
import { DropdownSettingsModule } from "./modules/dropdown-settings/dropdown-settings.module";
|
|
import { ExchangeSettingsModule } from "./modules/exchange-settings/exchange-settings.module";
|
|
import { StampSettingsModule } from "./modules/stamp-settings/stamp-settings.module";
|
|
import { LogoSettingsModule } from "./modules/logo-settings/logo-settings.module";
|
|
import { ContractTemplatesModule } from "./modules/contract-templates/contract-templates.module";
|
|
import { SupportContentModule } from "./modules/support-content/support-content.module";
|
|
import { OtpModule } from "./modules/otp/otp.module";
|
|
import { HealthModule } from "./modules/health/health.module";
|
|
import { RuleEngineModule } from "./modules/rule-engine/rule-engine.module";
|
|
import { BackofficeModule } from "./modules/backoffice/backoffice.module";
|
|
import { FreightAuthModule } from "./modules/auth/freight-auth.module";
|
|
import {
|
|
EDR_FREIGHT_APPLICATION,
|
|
EDR_FREIGHT_PERMISSIONS,
|
|
} from "./seed/edr-freight.seed";
|
|
import { EdrOrgSeeder } from "./seed/edr-org.seeder";
|
|
import { FreightPositionsSeeder } from "./seed/freight-positions.seeder";
|
|
// Disabled seeds — imports commented out with their provider/injection/run below.
|
|
// import { DemoUsersSeeder } from "./seed/demo-users.seeder";
|
|
// import { FreightStaffUsersSeeder } from "./seed/freight-staff-users.seeder";
|
|
import { PaymentModule } from "./modules/payment/payment.module";
|
|
// import { PricingDataSeeder } from "./seed/pricing-data.seeder";
|
|
import { FileUploadSettingsSeeder } from "./seed/file-upload-settings.seeder";
|
|
import { SupportContentSeeder } from "./seed/support-content.seeder";
|
|
// import { YardFacilitiesSeeder } from "./seed/yard-facilities.seeder";
|
|
// import { IndodeFacilitySeeder } from "./seed/indode-facility.seeder";
|
|
// import { Batch14TestDataSeeder } from "./seed/batch1-4-test-data.seeder";
|
|
// import { Batch5TestDataSeeder } from "./seed/batch5-test-data.seeder";
|
|
// import { Batch7TestDataSeeder } from "./seed/batch7-test-data.seeder";
|
|
// import { Batch8TestDataSeeder } from "./seed/batch8-test-data.seeder";
|
|
// import { WarehouseDemoSeeder } from "./seed/warehouse-demo.seeder";
|
|
// import { ExportDjiboutiInterchangeDemoSeeder } from "./seed/export-djibouti-interchange-demo.seeder";
|
|
// import { MarshallingDemoTrainsSeeder } from "./seed/marshalling-demo-trains.seeder";
|
|
import { FreightPermissionKeyMigrationSeeder } from "./seed/freight-permission-key-migration.seeder";
|
|
import { FreightNotificationPermissionsSeeder } from "./seed/freight-notification-permissions.seeder";
|
|
// import { DemoFreightDataSeeder } from "./seed/demo-freight-data.seeder";
|
|
// import { GovCompaniesSeeder } from "./seed/gov-companies.seeder";
|
|
// import { ApprovedFirstLastMileDemoBookingsSeeder } from "./seed/approved-first-lastmile-demo-bookings.seeder";
|
|
// import { PaidImportExportMileDemoSeeder } from "./seed/paid-import-export-mile-demo.seeder";
|
|
//New Trains, Wagons, Container and Cargo management modules
|
|
import { TrainsModule } from "./modules/trains/trains.module";
|
|
import { VerifaydaModule } from "./modules/verifayda/verifayda.module";
|
|
import { EimsModule } from "./modules/eims/eims.module";
|
|
import { FleetHistoryModule } from "./modules/fleet-history/fleet-history.module";
|
|
import { WagonsModule } from "./modules/wagons/wagons.module";
|
|
import { ContainersModule } from "./modules/container-management/containers.module";
|
|
import { CargoesModule } from "./modules/cargoes/cargoes.module";
|
|
import { RoutesModule } from "./modules/routes/routes.module";
|
|
import { WarehousesModule } from "./modules/warehouses/warehouses.module";
|
|
import { OverviewModule } from "./modules/overview/overview.module";
|
|
import { ReportsModule } from "./modules/reports/reports.module";
|
|
import { UserTradeAccessModule } from "./modules/user-trade-access/user-trade-access.module";
|
|
import { VehiclesModule } from "./modules/vehicles/vehicles.module";
|
|
import { DriversModule } from "./modules/drivers/drivers.module";
|
|
import { FuelModule } from "./modules/fuel/fuel.module";
|
|
import { MaintenanceModule } from "./modules/maintenance/maintenance.module";
|
|
import { ComplianceModule } from "./modules/compliance/compliance.module";
|
|
import { IncidentsModule } from "./modules/incidents/incidents.module";
|
|
import { ProcurementModule } from "./modules/procurement/procurement.module";
|
|
import { FacilitiesModule } from "./modules/facilities/facilities.module";
|
|
import { GpsTrackingModule } from "./modules/gps-tracking/gps-tracking.module";
|
|
import { FirstMileModule } from "./modules/first-mile/first-mile.module";
|
|
import { LastMileModule } from "./modules/last-mile/last-mile.module";
|
|
import { LastMileRequestsModule } from "./modules/last-mile-requests/last-mile-requests.module";
|
|
import { InterchangeDocumentsModule } from "./modules/interchange-documents/interchange-documents.module";
|
|
import { ImportOperationsModule } from "./modules/import-operations/import-operations.module";
|
|
import { AiModule } from "./modules/ai/ai.module";
|
|
import { AuditModule } from "./modules/audit/audit.module";
|
|
import { RequestLogMiddleware } from "@edr/api-common";
|
|
import { LoginAudienceMiddleware } from "./modules/auth/login-audience.middleware";
|
|
import { PositionTypePermissionsCache } from "./common/position-type-permissions.cache";
|
|
|
|
if (!process.env.APPLICATION_NAME) {
|
|
process.env.APPLICATION_NAME = "freight";
|
|
}
|
|
|
|
@Module({
|
|
imports: [
|
|
ConfigModule.forRoot({
|
|
isGlobal: true,
|
|
load: [
|
|
appConfig,
|
|
databaseConfig,
|
|
telebirrConfig,
|
|
rabbitmqConfig,
|
|
faydaConfig,
|
|
eimsConfig,
|
|
],
|
|
}),
|
|
ScheduleModule.forRoot(),
|
|
EventEmitterModule.forRoot(),
|
|
TypeOrmModule.forRootAsync({
|
|
inject: [ConfigService],
|
|
useFactory: (config: ConfigService): TypeOrmModuleOptions =>
|
|
config.get<TypeOrmModuleOptions>("database")!,
|
|
dataSourceFactory: async (options) => {
|
|
if (!options) {
|
|
throw new Error("Missing TypeORM DataSource options");
|
|
}
|
|
await ensurePostgresSchemas(options as DataSourceOptions);
|
|
const dataSource = new DataSource(options as DataSourceOptions);
|
|
await dataSource.initialize();
|
|
|
|
// The remote edr_dev DB sits behind a connection pooler/proxy that rejects
|
|
// the Postgres `options` startup parameter (08P01). Instead of setting
|
|
// search_path at connect time, apply it per physical connection: the pg
|
|
// Pool emits `connect` for every new client (initial fill, pool growth,
|
|
// reconnect), so every backend session gets the schema search order.
|
|
const pool = (dataSource.driver as { master?: unknown }).master as
|
|
| { on?: (event: string, cb: (client: unknown) => void) => void }
|
|
| undefined;
|
|
if (pool?.on) {
|
|
pool.on("connect", (client) => {
|
|
(client as { query: (sql: string) => Promise<unknown> })
|
|
.query(`SET search_path TO ${APPLICATION_SEARCH_PATH}`)
|
|
.catch(() => {
|
|
/* connection will be validated on first real query */
|
|
});
|
|
});
|
|
}
|
|
|
|
return dataSource;
|
|
},
|
|
}),
|
|
SharedAuthModule,
|
|
IamModule.forRoot({
|
|
applications: [EDR_FREIGHT_APPLICATION],
|
|
permissions: EDR_FREIGHT_PERMISSIONS,
|
|
}),
|
|
// Replaces the package's DataSeeder. Shared with edr-passenger-api, which
|
|
// seeds the same `iam` schema — see packages/iam-seed.
|
|
IamSeedModule.forRoot({
|
|
superAdmin: {
|
|
username: "superadmin",
|
|
name: { am: "ሱፐር አድሚን", en: "Super Admin" },
|
|
roleKey: "super_admin",
|
|
organizationKey: "edr_freight",
|
|
unitKey: "edr_freight_app",
|
|
fallbackEmail: "superadmin@tria.com",
|
|
},
|
|
}),
|
|
BookingsModule,
|
|
ContractsModule,
|
|
SignaturesModule,
|
|
FilesModule,
|
|
ConsignmentsModule,
|
|
LocomotivesModule,
|
|
TruckTypesModule,
|
|
TransitAgentsModule,
|
|
WagonTypesModule,
|
|
TrainSetsModule,
|
|
TrainSchedulesModule,
|
|
TrainSchedulingModule,
|
|
SchedulingRescheduleModule,
|
|
CompaniesModule,
|
|
ShippingLineCompaniesModule,
|
|
ShippingLineBookingCompletionModule,
|
|
TrackingModule,
|
|
BillingModule,
|
|
NotificationsModule,
|
|
NotificationInboxModule,
|
|
SupportChatModule,
|
|
FileUploadSettingsModule,
|
|
DropdownSettingsModule,
|
|
ExchangeSettingsModule,
|
|
StampSettingsModule,
|
|
LogoSettingsModule,
|
|
ContractTemplatesModule,
|
|
SupportContentModule,
|
|
OtpModule,
|
|
HealthModule,
|
|
RuleEngineModule,
|
|
BackofficeModule,
|
|
FreightAuthModule,
|
|
PaymentModule,
|
|
//New Modules
|
|
TrainsModule,
|
|
WagonsModule,
|
|
ContainersModule,
|
|
CargoesModule,
|
|
RoutesModule,
|
|
WarehousesModule,
|
|
OverviewModule,
|
|
ReportsModule,
|
|
UserTradeAccessModule,
|
|
VehiclesModule,
|
|
DriversModule,
|
|
FuelModule,
|
|
MaintenanceModule,
|
|
ComplianceModule,
|
|
IncidentsModule,
|
|
ProcurementModule,
|
|
FacilitiesModule,
|
|
GpsTrackingModule,
|
|
FirstMileModule,
|
|
LastMileModule,
|
|
LastMileRequestsModule,
|
|
InterchangeDocumentsModule,
|
|
ImportOperationsModule,
|
|
VerifaydaModule,
|
|
EimsModule,
|
|
FleetHistoryModule,
|
|
AiModule,
|
|
AuditModule,
|
|
],
|
|
providers: [
|
|
EdrOrgSeeder,
|
|
FreightPositionsSeeder,
|
|
FileUploadSettingsSeeder,
|
|
SupportContentSeeder,
|
|
// YardFacilitiesSeeder,
|
|
FreightPermissionKeyMigrationSeeder,
|
|
FreightNotificationPermissionsSeeder,
|
|
// Disabled seeds — providers commented out (imports/injection/run too):
|
|
// DemoUsersSeeder,
|
|
// FreightStaffUsersSeeder,
|
|
// PricingDataSeeder,
|
|
// DemoFreightDataSeeder,
|
|
// GovCompaniesSeeder,
|
|
// IndodeFacilitySeeder,
|
|
// Batch14TestDataSeeder,
|
|
// Batch5TestDataSeeder,
|
|
// Batch7TestDataSeeder,
|
|
// Batch8TestDataSeeder,
|
|
// WarehouseDemoSeeder,
|
|
// ExportDjiboutiInterchangeDemoSeeder,
|
|
// MarshallingDemoTrainsSeeder,
|
|
// ApprovedFirstLastMileDemoBookingsSeeder,
|
|
// PaidImportExportMileDemoSeeder,
|
|
LoginAudienceMiddleware,
|
|
// Feeds position-TYPE grants to the synchronous permission checks — without
|
|
// it, staff whose permissions live on their position type resolve to none.
|
|
PositionTypePermissionsCache,
|
|
],
|
|
})
|
|
export class AppModule implements OnApplicationBootstrap {
|
|
constructor(
|
|
private readonly iamBaselineSeeder: IamBaselineSeeder,
|
|
private readonly edrOrgSeeder: EdrOrgSeeder,
|
|
private readonly freightPositionsSeeder: FreightPositionsSeeder,
|
|
private readonly fileUploadSettingsSeeder: FileUploadSettingsSeeder,
|
|
private readonly supportContentSeeder: SupportContentSeeder,
|
|
// private readonly yardFacilitiesSeeder: YardFacilitiesSeeder,
|
|
private readonly freightPermissionKeyMigrationSeeder: FreightPermissionKeyMigrationSeeder,
|
|
private readonly freightNotificationPermissionsSeeder: FreightNotificationPermissionsSeeder,
|
|
// Disabled seeds — injections commented out (imports/provider/run too):
|
|
// private readonly demoUsersSeeder: DemoUsersSeeder,
|
|
// private readonly freightStaffUsersSeeder: FreightStaffUsersSeeder,
|
|
// private readonly pricingDataSeeder: PricingDataSeeder,
|
|
// private readonly indodeFacilitySeeder: IndodeFacilitySeeder,
|
|
// private readonly batch14TestDataSeeder: Batch14TestDataSeeder,
|
|
// private readonly batch5TestDataSeeder: Batch5TestDataSeeder,
|
|
// private readonly batch7TestDataSeeder: Batch7TestDataSeeder,
|
|
// private readonly batch8TestDataSeeder: Batch8TestDataSeeder,
|
|
// private readonly warehouseDemoSeeder: WarehouseDemoSeeder,
|
|
// private readonly exportDjiboutiInterchangeDemoSeeder: ExportDjiboutiInterchangeDemoSeeder,
|
|
// private readonly marshallingDemoTrainsSeeder: MarshallingDemoTrainsSeeder,
|
|
// private readonly demoFreightDataSeeder: DemoFreightDataSeeder,
|
|
// private readonly govCompaniesSeeder: GovCompaniesSeeder,
|
|
) { }
|
|
|
|
async onApplicationBootstrap() {
|
|
// ── Enabled: permissions + file-upload settings (+ dropdown settings) only ──
|
|
// Everything else below is intentionally disabled. Seeders stay registered
|
|
// as providers and injected; only their .run() calls are commented out, so
|
|
// re-enabling any of them is a one-line uncomment.
|
|
|
|
// Permissions foundation — keep enabled:
|
|
// freightPermissionKeyMigration → renames legacy permission keys
|
|
// edrOrgSeeder → seeds org/unit + the Permission catalog
|
|
// iamBaselineSeeder → @edr/iam-seed: IAM app, roles, permissions,
|
|
// position types, organization types +
|
|
// default units, org/unit settings and the
|
|
// super-admin account. Replaces the package's
|
|
// DataSeeder, and is shared with
|
|
// edr-passenger-api so one writer owns the
|
|
// `iam` schema. Runs after edrOrgSeeder
|
|
// because the super admin attaches to the
|
|
// edr_freight org/unit.
|
|
// Writes nothing unless SEED_IAM_BASELINE=true.
|
|
// freightPositionsSeeder → seeds Position + PositionPermission rows
|
|
// (depends on edrOrgSeeder, must run after)
|
|
await this.freightPermissionKeyMigrationSeeder.run();
|
|
await this.edrOrgSeeder.run();
|
|
await this.iamBaselineSeeder.run();
|
|
await this.freightPositionsSeeder.run();
|
|
// freightNotificationPermissions → seeds the <module>:get_notification
|
|
// keys and backfills them onto whoever
|
|
// already holds each desk's anchor
|
|
// permission. Runs LAST in this block so
|
|
// it sees a freshly-seeded catalog and
|
|
// freshly-seeded positions. Unlike the
|
|
// seeders above it is NOT gated behind
|
|
// SEED_EDR_ORG — without it every staff
|
|
// notification resolves to no one.
|
|
await this.freightNotificationPermissionsSeeder.run();
|
|
|
|
// File upload settings — keep enabled.
|
|
await this.fileUploadSettingsSeeder.run();
|
|
|
|
// Portal help/FAQ/legal copy — keep enabled. Idempotent by emptiness, so
|
|
// it fills an empty table once and never touches admin edits afterwards.
|
|
await this.supportContentSeeder.run();
|
|
|
|
// Flags which yards can load/unload cargo (Indode, Sebeta, Modjo, Adama,
|
|
// Dire Dawa). Idempotent; creates no yards.
|
|
// await this.yardFacilitiesSeeder.run();
|
|
|
|
// Dropdown settings are not seeded on boot; run them with
|
|
// `pnpm seed:dropdown-settings` (src/scripts/seed-dropdown-settings.ts).
|
|
|
|
// ── Disabled: demo / test / reference data seeds ──
|
|
// Uncomment a line to re-enable that seed.
|
|
// await this.demoUsersSeeder.run();
|
|
// await this.freightStaffUsersSeeder.run();
|
|
// await this.pricingDataSeeder.run();
|
|
// IndodeFacilitySeeder keys its warehouses on INDODE_OPEN / INDODE_CLOSED, so
|
|
// it will not recognise a hand-created Indode warehouse and will seed a second
|
|
// one alongside it. Only enable it against an Indode that has no warehouse.
|
|
// await this.indodeFacilitySeeder.run();
|
|
// await this.batch14TestDataSeeder.run();
|
|
// await this.batch5TestDataSeeder.run();
|
|
// await this.batch7TestDataSeeder.run();
|
|
// await this.batch8TestDataSeeder.run();
|
|
// await this.warehouseDemoSeeder.run();
|
|
// await this.exportDjiboutiInterchangeDemoSeeder.run();
|
|
// await this.marshallingDemoTrainsSeeder.run();
|
|
// demoFreightDataSeeder seeds ONLY the 4 staff users (wagons + approval
|
|
// rules are already disabled inside the seeder).
|
|
// await this.demoFreightDataSeeder.run();
|
|
// Government entities (importer/exporter profiles) that government bookings
|
|
// bill to. Idempotent — keyed by fixed IDs.
|
|
// await this.govCompaniesSeeder.run();
|
|
}
|
|
|
|
configure(consumer: MiddlewareConsumer) {
|
|
// FIRST: opens the request log context every later middleware/guard/service
|
|
// writes into via logCtx(). Anything applied above it logs into the void.
|
|
consumer.apply(RequestLogMiddleware).forRoutes("*");
|
|
consumer
|
|
.apply(LoginAudienceMiddleware)
|
|
.forRoutes(
|
|
{ path: "auth/login", method: RequestMethod.POST },
|
|
{ path: "auth/mfa-verify", method: RequestMethod.POST },
|
|
);
|
|
}
|
|
}
|