diff --git a/.pnpm-store/v11/.pnpm-needs-build-marker b/.pnpm-store/v11/.pnpm-needs-build-marker new file mode 100644 index 000000000..e69de29bb diff --git a/.pnpm-store/v11/.tmp/pnpm-11.1.1-1781268424966/package.json b/.pnpm-store/v11/.tmp/pnpm-11.1.1-1781268424966/package.json new file mode 100644 index 000000000..1d4a3e729 --- /dev/null +++ b/.pnpm-store/v11/.tmp/pnpm-11.1.1-1781268424966/package.json @@ -0,0 +1 @@ +{"dependencies":{"pnpm":"11.1.1"}} \ No newline at end of file diff --git a/.pnpm-store/v11/index.db b/.pnpm-store/v11/index.db new file mode 100644 index 000000000..8fdf9e7d3 Binary files /dev/null and b/.pnpm-store/v11/index.db differ diff --git a/apps/edr-freight-api/package.json b/apps/edr-freight-api/package.json index c3efffb3d..7270d97f7 100644 --- a/apps/edr-freight-api/package.json +++ b/apps/edr-freight-api/package.json @@ -13,13 +13,8 @@ "lint": "eslint src", "test": "jest", "test:e2e": "jest --config ./test/jest-e2e.json", -<<<<<<< HEAD "seed:wagons": "ts-node -r tsconfig-paths/register src/scripts/seed-edr-wagons.ts", "type-check": "tsc --noEmit" -======= - "type-check": "tsc --noEmit", - "seed:demo-scheduling": "ts-node -r tsconfig-paths/register src/scripts/seed-demo-scheduling.ts" ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db }, "dependencies": { "@edr/api-common": "workspace:*", diff --git a/apps/edr-freight-api/src/app.module.ts b/apps/edr-freight-api/src/app.module.ts index a248ca58f..3083c0e78 100644 --- a/apps/edr-freight-api/src/app.module.ts +++ b/apps/edr-freight-api/src/app.module.ts @@ -50,11 +50,8 @@ 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'; -<<<<<<< HEAD import { WarehousesModule } from './modules/warehouses/warehouses.module'; -======= import { OverviewModule } from './modules/overview/overview.module'; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db @Module({ imports: [ @@ -109,11 +106,8 @@ import { OverviewModule } from './modules/overview/overview.module'; ContainersModule, CargoesModule, RoutesModule, -<<<<<<< HEAD WarehousesModule, -======= OverviewModule, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db ], providers: [EdrOrgSeeder, DemoUsersSeeder,FreightStaffUsersSeeder, DemoBookingsSeeder, PricingDataSeeder, FileUploadSettingsSeeder], }) diff --git a/apps/edr-freight-api/src/modules/payment/payment.service.ts b/apps/edr-freight-api/src/modules/payment/payment.service.ts index e4168370c..ccc0e8833 100644 --- a/apps/edr-freight-api/src/modules/payment/payment.service.ts +++ b/apps/edr-freight-api/src/modules/payment/payment.service.ts @@ -12,7 +12,6 @@ import * as fs from "fs"; import * as path from "path"; import * as Handlebars from "handlebars"; import { ConfigService } from "@nestjs/config"; -import { SchedulingStatus } from "@edr/types"; import { Booking } from "../bookings/entities/booking.entity"; import { @@ -62,107 +61,6 @@ export class PaymentService { const result = await this.telebirrProvider.initiate(input); -<<<<<<< HEAD - const queryRunner = this.datasource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); - - console.log(paymentResp.expiresAt); - try { - const resp = await cb(queryRunner); - const payment = await this.paymentRepo.createTr(queryRunner, { - amount, - currency, - method, - refId: resp.id, - type: resp.type, - merchantOrderId: orderId, - rawInitiation: paymentResp.rawInitiation, - clientAction: paymentResp.clientAction, - expiresAt: paymentResp.expiresAt, - reason, - }); - await queryRunner.commitTransaction(); - return { - refId: payment.refId, - clientAction: paymentResp.clientAction, - status: payment.status, - paidAt: payment.paidAt?.toISOString(), - failureCode: payment.failerCode ?? undefined, - failureMessage: payment.failureMessage ?? undefined, - }; - } catch (err) { - await queryRunner.rollbackTransaction(); - throw new Error("payment failed"); - } finally { - await queryRunner.release(); - } - } - - async getActivePaymentByRefIdAndMethod( - refId: string, - method: PaymentEntity["method"], - ): Promise { - return this.paymentRepo.getActivePaymentByRefIdAndMethod(refId, method); - } - - async genReceiptHtml(orderId: string) { - const payment = await this.paymentRepo.findOneBy({ - merchantOrderId: orderId, - status: "success", - }); - if (!payment) { - throw new BadRequestException(); - } - - const filePath = path.join(__dirname, "templates", "receipt.hbs"); - if (!fs.existsSync(filePath)) { - throw new InternalServerErrorException(); - } - const source = fs.readFileSync(filePath, "utf8"); - const template = Handlebars.compile(source); - - const html = template({ - vendorName: "Ethio Djibouti Railway Ticket Booking", - vendorAddress: "Addis Ababa", - receiptDate: payment.paidAt, - paymentMethod: payment?.method, - subtotal: payment?.amount.toString(), - total: payment?.amount.toString(), - currency: payment?.currency, - reason: payment?.reason, - }); - - return html; - } - - async checkStatusAndUpdate(orderId: string) { - const resp = await this.paymentRepo.findOneBy({ merchantOrderId: orderId }); - if (!resp) { - throw new NotFoundException("order id not found"); - } - - try { - const result = await this.telebirrPaymentStategy.queryStatus( - resp.merchantOrderId, - ); - const bizContent = result.rawResponse.biz_content as { - order_status: string; - }; - - const ordersStatus = bizContent.order_status; - if (ordersStatus == "PAY_SUCCESS") { - await this.datasource.transaction(async (mg) => { - const now = new Date(); - const holdExpires = new Date(now.getTime() + 3 * 60 * 60 * 1000); - await mg.update(Booking, { id: resp.refId }, { - status: "PAID", - schedulingStatus: SchedulingStatus.Holding, - holdStartedAt: now, - holdExpiresAt: holdExpires, - }); - await mg.update(PaymentEntity, { id: resp.id }, { status: "success" }); -======= const payment = await this.paymentRepo.create({ amount: amount, currency: DEFAULT_CURRENCY, @@ -174,7 +72,6 @@ export class PaymentService { clientAction: result.clientAction as Record, expiresAt: result.expiresAt, reason: `Payment for booking`, ->>>>>>> eda21e22d872344b74c0c72308f87ce7435b299f }); return { diff --git a/apps/edr-freight-api/src/modules/train-schedules/entities/train-schedule.entity.ts b/apps/edr-freight-api/src/modules/train-schedules/entities/train-schedule.entity.ts index a449d0487..feb7fa318 100644 --- a/apps/edr-freight-api/src/modules/train-schedules/entities/train-schedule.entity.ts +++ b/apps/edr-freight-api/src/modules/train-schedules/entities/train-schedule.entity.ts @@ -8,22 +8,11 @@ import { TrainSet } from '../../train-sets/entities/train-set.entity'; import { TrainScheduleBooking } from './train-schedule-booking.entity'; export const TRAIN_SCHEDULE_STATUSES = [ -<<<<<<< HEAD - 'DRAFT', - 'READY', - 'PUBLISHED', - 'DEPARTED', - 'IN_TRANSIT', - 'ARRIVED', - 'COMPLETED', - 'CANCELLED', -======= TrainScheduleStatusEnum.Draft, TrainScheduleStatusEnum.Scheduled, TrainScheduleStatusEnum.Dispatched, TrainScheduleStatusEnum.Arrived, TrainScheduleStatusEnum.Cancelled, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db ] as const; export type TrainScheduleStatus = (typeof TRAIN_SCHEDULE_STATUSES)[number]; diff --git a/apps/edr-freight-api/src/modules/train-scheduling/dto/create-container-train-schedule.dto.ts b/apps/edr-freight-api/src/modules/train-scheduling/dto/create-container-train-schedule.dto.ts index 4f3d5ed6c..2562d1b88 100644 --- a/apps/edr-freight-api/src/modules/train-scheduling/dto/create-container-train-schedule.dto.ts +++ b/apps/edr-freight-api/src/modules/train-scheduling/dto/create-container-train-schedule.dto.ts @@ -1,11 +1,6 @@ -<<<<<<< HEAD -import { ApiProperty } from '@nestjs/swagger'; -import { ArrayMinSize, IsArray, IsDateString, IsIn, IsOptional, IsUUID } from 'class-validator'; -======= import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsDateString, IsInt, IsNumber, IsOptional, IsUUID, Min } from 'class-validator'; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db export class CreateContainerTrainScheduleDto { @ApiProperty({ format: 'uuid' }) @@ -25,26 +20,6 @@ export class CreateContainerTrainScheduleDto { @IsUUID() locomotiveId!: string; -<<<<<<< HEAD - @ApiProperty({ enum: ['CONTAINER', 'BULK'], default: 'CONTAINER' }) - @IsOptional() - @IsIn(['CONTAINER', 'BULK']) - assignmentType?: 'CONTAINER' | 'BULK'; - - @ApiProperty({ type: [String] }) - @IsOptional() - @IsArray() - @ArrayMinSize(1) - @IsUUID('4', { each: true }) - bookingIds?: string[]; - - @ApiProperty({ type: [String], required: false }) - @IsOptional() - @IsArray() - @ArrayMinSize(1) - @IsUUID('4', { each: true }) - wagonIds?: string[]; -======= @ApiPropertyOptional({ description: 'Maximum total booking weight allowed on this train' }) @IsOptional() @Type(() => Number) @@ -65,5 +40,4 @@ export class CreateContainerTrainScheduleDto { @IsInt() @Min(1) maxWagonsPerTrain?: number; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db } diff --git a/apps/edr-freight-api/src/modules/train-scheduling/dto/get-eligible-container-bookings.dto.ts b/apps/edr-freight-api/src/modules/train-scheduling/dto/get-eligible-container-bookings.dto.ts index cb3dd5b52..e33327f38 100644 --- a/apps/edr-freight-api/src/modules/train-scheduling/dto/get-eligible-container-bookings.dto.ts +++ b/apps/edr-freight-api/src/modules/train-scheduling/dto/get-eligible-container-bookings.dto.ts @@ -1,9 +1,5 @@ import { ApiPropertyOptional } from '@nestjs/swagger'; -<<<<<<< HEAD -import { IsDateString, IsIn, IsOptional, IsUUID } from 'class-validator'; -======= import { IsOptional, IsUUID } from 'class-validator'; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db export class GetEligibleContainerBookingsDto { @ApiPropertyOptional({ format: 'uuid' }) @@ -18,20 +14,5 @@ export class GetEligibleContainerBookingsDto { @ApiPropertyOptional({ example: 'HOLDING' }) @IsOptional() -<<<<<<< HEAD - @IsDateString() - scheduleDate?: string; - - @ApiPropertyOptional({ enum: ['CONTAINER', 'BULK'] }) - @IsOptional() - @IsIn(['CONTAINER', 'BULK']) - assignmentType?: 'CONTAINER' | 'BULK'; - - @ApiPropertyOptional({ enum: ['IMPORT', 'EXPORT', 'DOMESTIC'] }) - @IsOptional() - @IsIn(['IMPORT', 'EXPORT', 'DOMESTIC']) - tradeDirection?: 'IMPORT' | 'EXPORT' | 'DOMESTIC'; -======= schedulingStatus?: string; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db } diff --git a/apps/edr-freight-api/src/modules/train-scheduling/dto/preview-container-train-schedule.dto.ts b/apps/edr-freight-api/src/modules/train-scheduling/dto/preview-container-train-schedule.dto.ts index c12b3c157..28ee62070 100644 --- a/apps/edr-freight-api/src/modules/train-scheduling/dto/preview-container-train-schedule.dto.ts +++ b/apps/edr-freight-api/src/modules/train-scheduling/dto/preview-container-train-schedule.dto.ts @@ -1,33 +1,3 @@ -<<<<<<< HEAD -import { ApiProperty } from '@nestjs/swagger'; -import { ArrayMinSize, IsArray, IsDateString, IsIn, IsOptional, IsUUID } from 'class-validator'; - -export class PreviewContainerTrainScheduleDto { - @ApiProperty({ type: [String] }) - @IsArray() - @ArrayMinSize(1) - @IsUUID('4', { each: true }) - bookingIds!: string[]; - - @ApiProperty({ example: '2026-06-20T08:00:00.000Z' }) - @IsDateString() - scheduleDate!: string; - - @ApiProperty({ format: 'uuid' }) - @IsUUID() - originStationId!: string; - - @ApiProperty({ format: 'uuid' }) - @IsUUID() - destinationStationId!: string; - - @ApiProperty({ enum: ['CONTAINER', 'BULK'], default: 'CONTAINER' }) - @IsOptional() - @IsIn(['CONTAINER', 'BULK']) - assignmentType?: 'CONTAINER' | 'BULK'; -} -======= import { PreviewTrainScheduleDto } from './preview-train-schedule.dto'; export class PreviewContainerTrainScheduleDto extends PreviewTrainScheduleDto {} ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db diff --git a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.controller.ts b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.controller.ts index def3a188d..320efb859 100644 --- a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.controller.ts +++ b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.controller.ts @@ -196,17 +196,10 @@ export class TrainSchedulingController { return this.trainSchedulingService.cancelTrainSchedule(id); } -<<<<<<< HEAD - @Post('container/schedules/:id/publish') - @ApiOperation({ summary: 'Publish container train schedule' }) - publishTrainSchedule(@Param('id', ParseUUIDPipe) id: string) { - return this.trainSchedulingService.publishTrainSchedule(id); -======= @Post('bulk/schedules/:id/cancel') @TrainSchedulingManage() @ApiOperation({ summary: 'Cancel bulk train schedule' }) cancelBulkTrainSchedule(@Param('id', ParseUUIDPipe) id: string) { return this.trainSchedulingService.cancelTrainSchedule(id); ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db } } diff --git a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.module.ts b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.module.ts index bb8a8330a..dce9d07a8 100644 --- a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.module.ts +++ b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.module.ts @@ -14,18 +14,7 @@ import { TrainSchedulesModule } from '../train-schedules/train-schedules.module' import { WagonType } from '../wagon-types/entities/wagon-type.entity'; import { WagonTypesModule } from '../wagon-types/wagon-types.module'; import { Wagon } from '../wagons/entities/wagon.entity'; -<<<<<<< HEAD -import { TrainSet } from '../train-sets/entities/train-set.entity'; -import { TrainSetWagon } from '../train-sets/entities/train-set-wagon.entity'; -import { TrainSetsModule } from '../train-sets/train-sets.module'; -import { TrainScheduleBooking } from '../train-schedules/entities/train-schedule-booking.entity'; -import { TrainSchedule } from '../train-schedules/entities/train-schedule.entity'; -import { WagonBookingAllocation } from '../train-schedules/entities/wagon-booking-allocation.entity'; -import { TrainSchedulesModule } from '../train-schedules/train-schedules.module'; -import { Yard } from '../rule-engine/entities/yard.entity'; -======= import { TrainSchedulingGlobalRules } from './entities/train-scheduling-global-rules.entity'; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db import { TrainSchedulingController } from './train-scheduling.controller'; import { TrainSchedulingService } from './train-scheduling.service'; diff --git a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.spec.ts b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.spec.ts index 9c664e75b..c932c9c97 100644 --- a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.spec.ts +++ b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.spec.ts @@ -58,15 +58,10 @@ const makeBooking = ( scheduledDate: new Date(scheduledDate), originYardId, destinationYardId, -<<<<<<< HEAD - status: 'APPROVED', - customer: { companyName: 'Demo Customer' }, -======= status: 'PAID', schedulingStatus: 'HOLDING', holdExpiresAt: new Date(Date.now() + 60 * 60 * 1000), company: { companyName: 'Demo Customer' }, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db originYard: { label: 'Djibouti', code: 'DJIBOUTI' }, destinationYard: { label: 'Addis Ababa', code: 'ADDIS_ABABA' }, bookingContainers: [ @@ -352,14 +347,7 @@ describe('TrainSchedulingService', () => { it('rejects bookings that are not in assignable status', async () => { const bookings = [ -<<<<<<< HEAD - { - ...makeBooking('b7', 'BKG-CONT-007', 120, 2, '40FT'), - status: 'PAID', - }, -======= { ...makeBooking('b7', 'BKG-CONT-007', 120, 2, '40FT', 2), status: 'APPROVED' }, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db ]; wagonTypesRepository.findAll.mockResolvedValue([nw5]); diff --git a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.ts b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.ts index 3ea2d492e..bbf2f4f95 100644 --- a/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.ts +++ b/apps/edr-freight-api/src/modules/train-scheduling/train-scheduling.service.ts @@ -46,73 +46,6 @@ import { PreviewTrainScheduleDto } from './dto/preview-train-schedule.dto'; import { TrainSchedulingGlobalRules } from './entities/train-scheduling-global-rules.entity'; import { UpdateTrainSchedulingGlobalRulesDto } from './dto/update-train-scheduling-global-rules.dto'; import { -<<<<<<< HEAD - Locomotive, - type LocomotiveStatus, -} from "../locomotives/entities/locomotive.entity"; -import { LocomotivesRepository } from "../locomotives/locomotives.repository"; -import { TrainSetWagon } from "../train-sets/entities/train-set-wagon.entity"; -import { TrainSet } from "../train-sets/entities/train-set.entity"; -import { Route } from "../routes/entities/route.entity"; -import { TrainScheduleBooking } from "../train-schedules/entities/train-schedule-booking.entity"; -import { TrainSchedule } from "../train-schedules/entities/train-schedule.entity"; -import { WagonBookingAllocation } from "../train-schedules/entities/wagon-booking-allocation.entity"; -import { Wagon } from "../wagons/entities/wagon.entity"; -import { WagonType } from "../wagon-types/entities/wagon-type.entity"; -import { WagonTypesRepository } from "../wagon-types/wagon-types.repository"; -import { CreateContainerTrainScheduleDto } from "./dto/create-container-train-schedule.dto"; -import { GetEligibleContainerBookingsDto } from "./dto/get-eligible-container-bookings.dto"; -import { PreviewContainerTrainScheduleDto } from "./dto/preview-container-train-schedule.dto"; - -const DEFAULT_WAGON_TYPE_CODE = "NW5"; -const MAX_TRAIN_WEIGHT_TONS = 3500; -const MAX_TRAIN_LENGTH_METERS = 760; -const ASSIGNABLE_BOOKING_STATUSES = ["APPROVED", "READY_FOR_ASSIGNMENT"] as const; -const EXCLUDED_BOOKING_STATUSES = ["CANCELLED", "COMPLETED", "IN_TRANSIT", "ARRIVED"] as const; -const MAX_CONTAINER_WAGONS = 53; -const MAX_BULK_WAGONS = 37; - -type EligibleBookingItem = { - id: string; - reference: string; - customer: string; - containerType: string; - quantity: number; - weightTons: number; - origin: string; - destination: string; - preferredDepartureDate: string; - status: string; -}; - -type WagonAllocationRecord = { - bookingId: string; - bookingReference: string; - allocatedWeightTons: number; -}; - -type WagonPlanRecord = { - sequenceNo: number; - capacityTons: number; - lengthMeters: number; - assignedWeightTons: number; - allocations: WagonAllocationRecord[]; -}; - -type ValidationResult = { - valid: boolean; - violations: string[]; - bookings: Booking[]; - wagonType: WagonType; - summary: { - totalBookings: number; - totalWeightTons: number; - wagonType: string; - wagonsNeeded: number; - totalLengthMeters: number; - }; - wagonPlan: WagonPlanRecord[]; -======= buildCappedWagonPlan, computeFleetAvailability, selectBookingsWithinFleetCap, @@ -150,7 +83,6 @@ const DEFAULT_TRAIN_LIMITS: Required = { maxWagonsPerTrain: 53, max20ftContainerWeightTons: 30, max20ftPairWeightDiffTons: 10, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db }; @Injectable() @@ -180,36 +112,12 @@ export class TrainSchedulingService { } async getEligibleContainerBookings(query: GetEligibleContainerBookingsDto) { -<<<<<<< HEAD - const bookingRepository = this.dataSource.getRepository(Booking); - const queryBuilder = bookingRepository - .createQueryBuilder("booking") - .leftJoinAndSelect("booking.company", "company") - .leftJoinAndSelect("booking.originYard", "originYard") - .leftJoinAndSelect("booking.destinationYard", "destinationYard") - .leftJoinAndSelect("booking.bookingContainers", "bookingContainer") - .leftJoinAndSelect("bookingContainer.containerType", "containerType") - .leftJoin( - TrainScheduleBooking, - "scheduleBooking", - "scheduleBooking.booking_id = booking.id", - ) - .where("booking.freightType = :freightType", { - freightType: query.assignmentType ?? "CONTAINER", - }) - .andWhere("scheduleBooking.id IS NULL"); - - queryBuilder.andWhere("booking.status IN (:...assignableStatuses)", { - assignableStatuses: ASSIGNABLE_BOOKING_STATUSES, - }); -======= return this.getEligibleBookings({ ...query, freightType: 'CONTAINER' }); } async getEligibleBulkBookings(query: GetEligibleBulkBookingsDto) { return this.getEligibleBookings({ ...query, freightType: 'BULK' }); } ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db async getTrainSchedulingGlobalRules() { return this.loadGlobalRulesRow(); @@ -226,84 +134,12 @@ export class TrainSchedulingService { if (dto.max20ftContainerWeightTons != null) { row.max20ftContainerWeightTons = dto.max20ftContainerWeightTons; } -<<<<<<< HEAD - - if (query.tradeDirection === "IMPORT") { - queryBuilder.andWhere( - `( - lower(originYard.country) IN ('djibouti', 'djoubti', 'dj') - OR lower(originYard.code) LIKE '%djib%' - OR lower(originYard.label) LIKE '%djib%' - )`, - ); - } - - if (query.tradeDirection === "EXPORT") { - queryBuilder.andWhere( - `( - lower(destinationYard.country) IN ('djibouti', 'djoubti', 'dj') - OR lower(destinationYard.code) LIKE '%djib%' - OR lower(destinationYard.label) LIKE '%djib%' - )`, - ); - } - - if (query.scheduleDate) { - queryBuilder.andWhere( - `DATE(booking.scheduled_date AT TIME ZONE 'UTC') = :scheduleDate`, - { scheduleDate: this.toUtcDateKey(query.scheduleDate) }, - ); -======= if (dto.max20ftPairWeightDiffTons != null) { row.max20ftPairWeightDiffTons = dto.max20ftPairWeightDiffTons; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db } return this.dataSource.getRepository(TrainSchedulingGlobalRules).save(row); } -<<<<<<< HEAD - const bookings = await queryBuilder - .orderBy("booking.scheduled_date", "ASC") - .addOrderBy("booking.created_at", "ASC") - .getMany(); - - const items: EligibleBookingItem[] = bookings.map((booking) => ({ - id: booking.id, - reference: booking.reference, - customer: - booking.company?.name ?? booking.company?.email ?? "Unknown customer", - containerType: - booking.bookingContainers - ?.map( - (container) => - container.containerType?.label ?? - container.containerType?.code ?? - "Container", - ) - .join(", ") ?? (booking.freightType === "BULK" ? "Bulk cargo" : "Container"), - quantity: - booking.bookingContainers?.reduce( - (sum, container) => sum + Number(container.quantity ?? 0), - 0, - ) ?? 0, - weightTons: this.roundTons(booking.cargoTotalWeightVgm), - origin: - booking.originYard?.label ?? - booking.originYard?.code ?? - "Unknown origin", - destination: - booking.destinationYard?.label ?? - booking.destinationYard?.code ?? - "Unknown destination", - preferredDepartureDate: booking.scheduledDate.toISOString(), - status: booking.status, - })); - - return { - count: items.length, - items, - }; -======= async previewTrainSchedule(dto: PreviewTrainScheduleDto) { const limits = await this.resolveTrainLimitConfig(dto); return this.buildPreviewResponse( @@ -317,7 +153,6 @@ export class TrainSchedulingService { dto.targetScheduleId, ), ); ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db } async previewContainerTrainSchedule(dto: PreviewContainerTrainScheduleDto) { @@ -370,29 +205,6 @@ export class TrainSchedulingService { async createContainerTrainSchedule(dto: CreateContainerTrainScheduleDto) { const route = await this.getActiveRoute(dto.routeId); -<<<<<<< HEAD - const validation = dto.bookingIds?.length - ? await this.validateContainerBookingsForScheduling({ - bookingIds: dto.bookingIds, - scheduleDate: dto.scheduleDate, - originStationId: route.originYardId, - destinationStationId: route.destinationYardId, - assignmentType: dto.assignmentType ?? "CONTAINER", - }) - : null; - - if (validation && !validation.valid) { - throw new BadRequestException({ - message: "Train schedule assignment is invalid", - violations: validation.violations, - }); - } - - const locomotive = await this.selectOrValidateLocomotive( - dto.locomotiveId, - validation?.summary.totalWeightTons ?? 0, - validation?.summary.totalLengthMeters ?? 0, -======= const locomotive = await this.selectOrValidateLocomotive(dto.locomotiveId, 0, 0); const createdScheduleId = await this.dataSource.transaction(async (manager) => { @@ -467,7 +279,6 @@ export class TrainSchedulingService { true, limits, scheduleId, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db ); if (!validation.valid) { @@ -671,42 +482,8 @@ export class TrainSchedulingService { ); } -<<<<<<< HEAD - const selectedPhysicalWagons = validation - ? await this.lockSelectedWagonsForSchedule( - manager, - dto.wagonIds ?? [], - validation.wagonPlan.length, - route, - dto.assignmentType ?? "CONTAINER", - ) - : []; - - const trainSetResult = validation - ? await this.buildTrainSet( - manager, - lockedLocomotive, - validation.wagonType, - validation.summary.totalWeightTons, - validation.summary.totalLengthMeters, - validation.wagonPlan, - selectedPhysicalWagons, - ) - : { trainSet: await this.buildEmptyTrainSet(manager, lockedLocomotive), wagons: [] }; - const { trainSet, wagons } = trainSetResult; - - const schedule = manager.getRepository(TrainSchedule).create({ - trainSetId: trainSet.id, - routeId: route.id, - originStationId: route.originYardId, - destinationStationId: route.destinationYardId, - scheduledDepartureDate: new Date(dto.scheduleDate), - scheduledArrivalDate: dto.arrivalDate ? new Date(dto.arrivalDate) : null, - status: validation ? "READY" : "DRAFT", -======= const physicalWagon = await manager.getRepository(Wagon).findOne({ where: { id: assignment.physicalWagonId }, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db }); if (!physicalWagon) { throw new NotFoundException(`Wagon ${assignment.physicalWagonId} not found`); @@ -725,55 +502,9 @@ export class TrainSchedulingService { ); } -<<<<<<< HEAD - const savedSchedule = await manager - .getRepository(TrainSchedule) - .save(schedule); - - if (validation) { - await manager.getRepository(TrainScheduleBooking).save( - validation.bookings.map((booking) => - manager.getRepository(TrainScheduleBooking).create({ - trainScheduleId: savedSchedule.id, - bookingId: booking.id, - }), - ), - ); - - const wagonBySequence = new Map(wagons.map((wagon) => [wagon.sequenceNo, wagon])); - const allocations = validation.wagonPlan.flatMap((wagonPlan) => { - const savedWagon = wagonBySequence.get(wagonPlan.sequenceNo); - if (!savedWagon) return []; - - return wagonPlan.allocations.map((allocation) => - manager.getRepository(WagonBookingAllocation).create({ - trainSetWagonId: savedWagon.id, - bookingId: allocation.bookingId, - allocatedWeightTons: allocation.allocatedWeightTons, - }), - ); - }); - - if (allocations.length > 0) { - await manager.getRepository(WagonBookingAllocation).save(allocations); - } - - await manager.getRepository(Booking).update( - { id: In(validation.bookings.map((booking) => booking.id)) }, - { - status: "INVOICED", - paymentStatus: "PENDING", - }, - ); - } - - await locomotiveRepository.update(lockedLocomotive.id, { - status: "ASSIGNED", -======= await manager.getRepository(TrainSetWagon).update(assignment.trainSetWagonId, { physicalWagonId: assignment.physicalWagonId, status: 'RESERVED', ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db }); await manager.getRepository(Wagon).update(assignment.physicalWagonId, { trainSetWagonId: assignment.trainSetWagonId, @@ -950,10 +681,6 @@ export class TrainSchedulingService { violations.push('One or more selected bookings are already assigned to a train schedule'); } -<<<<<<< HEAD - const nonContainerBookings = bookings.filter( - (booking) => booking.freightType !== (dto.assignmentType ?? "CONTAINER"), -======= const bookingTypes = new Set(bookings.map((b) => b.freightType)); const isMixed = bookingTypes.size > 1; const resolvedMode: 'CONTAINER' | 'BULK' | 'MIXED' = @@ -968,117 +695,15 @@ export class TrainSchedulingService { const invalidStatus = bookings.filter( (b) => !SCHEDULABLE_BOOKING_STATUSES.includes(b.status as 'PAID'), ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db ); if (invalidStatus.length) { const statuses = [...new Set(invalidStatus.map((b) => b.status))]; violations.push( -<<<<<<< HEAD - `Only ${dto.assignmentType ?? "CONTAINER"} bookings are supported for this assignment`, - ); - } - - const invalidStatusBookings = bookings.filter( - (booking) => - !ASSIGNABLE_BOOKING_STATUSES.includes(booking.status as (typeof ASSIGNABLE_BOOKING_STATUSES)[number]), - ); - if (invalidStatusBookings.length > 0) { - const invalidStatuses = [...new Set(invalidStatusBookings.map((booking) => booking.status))]; - violations.push( - `Only ${ASSIGNABLE_BOOKING_STATUSES.join(", ")} bookings can be assigned; received: ${invalidStatuses.join(", ")}`, - ); - } - - const excludedStatusBookings = bookings.filter((booking) => - EXCLUDED_BOOKING_STATUSES.includes(booking.status as (typeof EXCLUDED_BOOKING_STATUSES)[number]), - ); - if (excludedStatusBookings.length > 0) { - violations.push(`Cancelled, completed, in-transit, or arrived bookings cannot be assigned`); - } - - const scheduleDateKey = this.toUtcDateKey(dto.scheduleDate); - const routeMismatch = bookings.some( - (booking) => - booking.originYardId !== dto.originStationId || - booking.destinationYardId !== dto.destinationStationId, - ); - if (routeMismatch) { - violations.push( - "Selected bookings must share the same origin and destination as the schedule", - ); - } - - const dateMismatch = bookings.some( - (booking) => this.toUtcDateKey(booking.scheduledDate) !== scheduleDateKey, - ); - if (dateMismatch) { - violations.push("Selected bookings must share the same schedule date"); - } - - const uniqueOriginCount = new Set( - bookings.map((booking) => booking.originYardId), - ).size; - if (uniqueOriginCount > 1) { - violations.push("Selected bookings must share the same origin station"); - } - - const uniqueDestinationCount = new Set( - bookings.map((booking) => booking.destinationYardId), - ).size; - if (uniqueDestinationCount > 1) { - violations.push( - "Selected bookings must share the same destination station", - ); - } - - const uniqueDateCount = new Set( - bookings.map((booking) => this.toUtcDateKey(booking.scheduledDate)), - ).size; - if (uniqueDateCount > 1) { - violations.push( - "Selected bookings must share the same preferred departure date", - ); - } - - const totalWeightTons = this.roundTons( - bookings.reduce( - (sum, booking) => sum + Number(booking.cargoTotalWeightVgm ?? 0), - 0, - ), - ); - - const wagonPlan = this.allocateBookingsToWagons( - bookings, - this.calculateNW5WagonPlan(totalWeightTons, wagonType), - ); - const totalLengthMeters = this.roundTons( - wagonPlan.reduce((sum, wagon) => sum + wagon.lengthMeters, 0), - ); - - if (totalWeightTons > MAX_TRAIN_WEIGHT_TONS) { - violations.push( - `Total booking weight ${totalWeightTons}T exceeds max train weight ${MAX_TRAIN_WEIGHT_TONS}T`, - ); - } - - if (totalLengthMeters > MAX_TRAIN_LENGTH_METERS) { - violations.push( - `Total wagon length ${totalLengthMeters}m exceeds max train length ${MAX_TRAIN_LENGTH_METERS}m`, -======= `Only ${SCHEDULABLE_BOOKING_STATUSES.join(', ')} bookings can be scheduled; received: ${statuses.join(', ')}`, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db ); } if ( -<<<<<<< HEAD - wagonPlan.length > - (dto.assignmentType === "BULK" ? MAX_BULK_WAGONS : MAX_CONTAINER_WAGONS) - ) { - violations.push( - `Wagon count ${wagonPlan.length} exceeds ${dto.assignmentType === "BULK" ? "bulk" : "container"} limit ${dto.assignmentType === "BULK" ? MAX_BULK_WAGONS : MAX_CONTAINER_WAGONS}`, - ); -======= bookings.some( (b) => b.originYardId !== dto.originStationId || @@ -1086,7 +711,6 @@ export class TrainSchedulingService { ) ) { violations.push('Selected bookings must share the same origin and destination as the schedule'); ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db } if (!forceAssign) { @@ -1644,139 +1268,7 @@ export class TrainSchedulingService { return locomotive; } -<<<<<<< HEAD - async lockSelectedWagonsForSchedule( - manager: EntityManager, - wagonIds: string[], - requiredCount: number, - route: Route, - assignmentType: "CONTAINER" | "BULK", - ) { - const uniqueWagonIds = [...new Set(wagonIds)]; - - if (uniqueWagonIds.length < requiredCount) { - throw new BadRequestException( - `Select at least ${requiredCount} available wagons for this schedule`, - ); - } - - const wagons = await manager - .getRepository(Wagon) - .createQueryBuilder("wagon") - .leftJoinAndSelect("wagon.wagonType", "wagonType") - .where("wagon.id IN (:...wagonIds)", { wagonIds: uniqueWagonIds }) - .setLock("pessimistic_write") - .getMany(); - - if (wagons.length !== uniqueWagonIds.length) { - throw new BadRequestException("One or more selected wagons were not found"); - } - - const expectedStatus = this.expectedWagonStatusForRoute(route); - const allowedStatuses = new Set([ - expectedStatus, - "AVAILABLE", - ...(expectedStatus === "EXPORT_READY" ? ["IMPORT_READY"] : []), - ]); - const invalidWagon = wagons.find( - (wagon) => - wagon.trainId || - wagon.status === "ASSIGNED" || - wagon.currentLocationYardId !== route.originYardId || - !allowedStatuses.has(wagon.status) || - !this.wagonTypeSupportsAssignment(wagon, assignmentType), - ); - - if (invalidWagon) { - throw new BadRequestException( - `Wagon ${invalidWagon.wagonNumber} is not at the route origin or is not ready for this ${this.routeDirection(route).toLowerCase()} route`, - ); - } - - const wagonById = new Map(wagons.map((wagon) => [wagon.id, wagon])); - return uniqueWagonIds.slice(0, requiredCount).map((wagonId) => wagonById.get(wagonId)!); - } - - private wagonTypeSupportsAssignment(wagon: Wagon, assignmentType: "CONTAINER" | "BULK") { - const supportedLoadTypes = wagon.wagonType?.supportedLoadTypes ?? []; - const normalized = supportedLoadTypes.map((loadType) => loadType.trim().toUpperCase()); - return normalized.includes(assignmentType); - } - - private routeDirection(route: Route) { - const originCountry = route.originYard?.country?.trim().toLowerCase(); - const destinationCountry = route.destinationYard?.country?.trim().toLowerCase(); - const isOriginEthiopia = originCountry === "ethiopia" || originCountry === "et"; - const isDestinationEthiopia = destinationCountry === "ethiopia" || destinationCountry === "et"; - - if (!isOriginEthiopia && isDestinationEthiopia) return "IMPORT"; - if (isOriginEthiopia && !isDestinationEthiopia) return "EXPORT"; - return "DOMESTIC"; - } - - private expectedWagonStatusForRoute(route: Route) { - const direction = this.routeDirection(route); - if (direction === "IMPORT") return "IMPORT_READY"; - if (direction === "EXPORT" || direction === "DOMESTIC") return "EXPORT_READY"; - return "AVAILABLE"; - } - - async buildTrainSet( - manager: EntityManager, - locomotive: Locomotive, - wagonType: WagonType, - totalWeightTons: number, - totalLengthMeters: number, - wagonPlan: WagonPlanRecord[], - physicalWagons: Wagon[] = [], - ) { - const trainSet = manager.getRepository(TrainSet).create({ - locomotiveId: locomotive.id, - totalWeightTons, - totalLengthMeters, - wagonCount: wagonPlan.length, - status: "ASSIGNED", - }); - const savedTrainSet = await manager.getRepository(TrainSet).save(trainSet); - - const wagons = wagonPlan.map((wagon, index) => { - const physicalWagon = physicalWagons[index]; - const selectedWagonType = physicalWagon?.wagonType ?? wagonType; - - return manager.getRepository(TrainSetWagon).create({ - trainSetId: savedTrainSet.id, - wagonTypeId: selectedWagonType.id, - physicalWagonId: physicalWagon?.id ?? null, - sequenceNo: wagon.sequenceNo, - capacityTons: Number(selectedWagonType.capacityTons), - lengthMeters: Number(selectedWagonType.lengthMeters), - assignedWeightTons: wagon.assignedWeightTons, - }); - }); - - const savedWagons = await manager.getRepository(TrainSetWagon).save(wagons); - - if (physicalWagons.length > 0) { - await Promise.all( - physicalWagons.map((wagon, index) => - manager.getRepository(Wagon).update(wagon.id, { - status: "ASSIGNED", - sequenceNumber: index + 1, - }), - ), - ); - } - - return { trainSet: savedTrainSet, wagons: savedWagons }; - } - - async buildEmptyTrainSet( - manager: EntityManager, - locomotive: Locomotive, - ) { -======= private async buildEmptyTrainSet(manager: EntityManager, locomotive: Locomotive) { ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db const trainSet = manager.getRepository(TrainSet).create({ locomotiveId: locomotive.id, totalWeightTons: 0, @@ -1787,313 +1279,6 @@ export class TrainSchedulingService { return manager.getRepository(TrainSet).save(trainSet); } -<<<<<<< HEAD - allocateBookingsToWagons( - bookings: Booking[], - baseWagonPlan: WagonPlanRecord[], - ): WagonPlanRecord[] { - const remaining = bookings.map((booking) => ({ - bookingId: booking.id, - bookingReference: booking.reference, - remainingWeightTons: this.roundTons( - Number(booking.cargoTotalWeightVgm ?? 0), - ), - })); - let bookingIndex = 0; - - return baseWagonPlan.map((wagon) => { - let wagonRemaining = this.roundTons(wagon.capacityTons); - const allocations: WagonAllocationRecord[] = []; - let assignedWeightTons = 0; - - while (wagonRemaining > 0 && bookingIndex < remaining.length) { - const booking = remaining[bookingIndex]; - const allocatedWeightTons = this.roundTons( - Math.min(wagonRemaining, booking.remainingWeightTons), - ); - - if (allocatedWeightTons <= 0) { - bookingIndex += 1; - continue; - } - - allocations.push({ - bookingId: booking.bookingId, - bookingReference: booking.bookingReference, - allocatedWeightTons, - }); - booking.remainingWeightTons = this.roundTons( - booking.remainingWeightTons - allocatedWeightTons, - ); - wagonRemaining = this.roundTons(wagonRemaining - allocatedWeightTons); - assignedWeightTons = this.roundTons( - assignedWeightTons + allocatedWeightTons, - ); - - if (booking.remainingWeightTons <= 0) { - bookingIndex += 1; - } - } - - return { - ...wagon, - assignedWeightTons, - allocations, - }; - }); - } - - async getContainerTrainSchedules() { - const schedules = await this.dataSource.getRepository(TrainSchedule).find({ - relations: { - trainSet: { locomotive: true }, - route: true, - originStation: true, - destinationStation: true, - scheduleBookings: true, - }, - order: { scheduledDepartureDate: "DESC", createdAt: "DESC" }, - }); - - return schedules.map((schedule) => ({ - id: schedule.id, - scheduleDate: schedule.scheduledDepartureDate, - routeName: schedule.route?.name ?? null, - origin: - schedule.originStation?.label ?? schedule.originStation?.code ?? null, - destination: - schedule.destinationStation?.label ?? - schedule.destinationStation?.code ?? - null, - locomotive: schedule.trainSet?.locomotive - ? { - id: schedule.trainSet.locomotive.id, - code: schedule.trainSet.locomotive.code, - name: schedule.trainSet.locomotive.name ?? null, - } - : null, - wagonCount: schedule.trainSet?.wagonCount ?? 0, - totalWeightTons: this.roundTons( - Number(schedule.trainSet?.totalWeightTons ?? 0), - ), - totalLengthMeters: this.roundTons( - Number(schedule.trainSet?.totalLengthMeters ?? 0), - ), - bookingsCount: schedule.scheduleBookings?.length ?? 0, - status: schedule.status, - })); - } - - async getContainerTrainScheduleById(id: string) { - const schedule = await this.dataSource - .getRepository(TrainSchedule) - .findOne({ - where: { id }, - relations: { - route: true, - trainSet: { - locomotive: true, - wagons: { wagonType: true, physicalWagon: true, allocations: { booking: true } }, - }, - originStation: true, - destinationStation: true, - scheduleBookings: { - booking: { company: true, originYard: true, destinationYard: true }, - }, - }, - }); - - if (!schedule) { - throw new NotFoundException(`Train schedule ${id} not found`); - } - - return { - id: schedule.id, - status: schedule.status, - route: schedule.route - ? { - id: schedule.route.id, - name: schedule.route.name, - } - : null, - scheduledDepartureDate: schedule.scheduledDepartureDate, - scheduledArrivalDate: schedule.scheduledArrivalDate, - originStation: schedule.originStation, - destinationStation: schedule.destinationStation, - trainSet: schedule.trainSet - ? { - id: schedule.trainSet.id, - status: schedule.trainSet.status, - wagonCount: schedule.trainSet.wagonCount, - totalWeightTons: this.roundTons( - Number(schedule.trainSet.totalWeightTons), - ), - totalLengthMeters: this.roundTons( - Number(schedule.trainSet.totalLengthMeters), - ), - locomotive: schedule.trainSet.locomotive - ? { - id: schedule.trainSet.locomotive.id, - code: schedule.trainSet.locomotive.code, - name: schedule.trainSet.locomotive.name, - status: schedule.trainSet.locomotive.status, - maxPullWeightTons: this.roundTons( - Number(schedule.trainSet.locomotive.maxPullWeightTons), - ), - maxTrainLengthMeters: this.roundTons( - Number(schedule.trainSet.locomotive.maxTrainLengthMeters), - ), - } - : null, - wagons: [...(schedule.trainSet.wagons ?? [])] - .sort((left, right) => left.sequenceNo - right.sequenceNo) - .map((wagon) => ({ - id: wagon.id, - sequenceNo: wagon.sequenceNo, - capacityTons: this.roundTons(Number(wagon.capacityTons)), - lengthMeters: this.roundTons(Number(wagon.lengthMeters)), - assignedWeightTons: this.roundTons( - Number(wagon.assignedWeightTons), - ), - wagonType: wagon.wagonType - ? { - id: wagon.wagonType.id, - code: wagon.wagonType.code, - name: wagon.wagonType.name, - } - : null, - physicalWagon: wagon.physicalWagon - ? { - id: wagon.physicalWagon.id, - wagonNumber: wagon.physicalWagon.wagonNumber, - status: wagon.physicalWagon.status, - } - : null, - allocations: - wagon.allocations?.map((allocation) => ({ - id: allocation.id, - bookingId: allocation.bookingId, - bookingReference: allocation.booking?.reference ?? null, - allocatedWeightTons: this.roundTons( - Number(allocation.allocatedWeightTons), - ), - })) ?? [], - })), - } - : null, - bookings: - schedule.scheduleBookings?.map((scheduleBooking) => ({ - id: scheduleBooking.booking?.id ?? scheduleBooking.bookingId, - reference: scheduleBooking.booking?.reference ?? null, - customer: - scheduleBooking.booking?.company?.name ?? - scheduleBooking.booking?.company?.email ?? - null, - weightTons: this.roundTons( - Number(scheduleBooking.booking?.cargoTotalWeightVgm ?? 0), - ), - status: scheduleBooking.booking?.status ?? null, - })) ?? [], - }; - } - - async cancelTrainSchedule(id: string) { - const schedule = await this.dataSource - .getRepository(TrainSchedule) - .findOne({ - where: { id }, - relations: { trainSet: { locomotive: true, wagons: true } }, - }); - - if (!schedule) { - throw new NotFoundException(`Train schedule ${id} not found`); - } - - await this.dataSource.transaction(async (manager) => { - await manager.getRepository(TrainSchedule).update(schedule.id, { - status: "CANCELLED", - }); - - if (schedule.trainSetId) { - await manager.getRepository(TrainSet).update(schedule.trainSetId, { - status: "CANCELLED", - }); - } - - if (schedule.trainSet?.locomotiveId) { - await manager - .getRepository(Locomotive) - .update(schedule.trainSet.locomotiveId, { - status: "AVAILABLE", - }); - } - - const physicalWagonIds = - schedule.trainSet?.wagons - ?.map((wagon) => wagon.physicalWagonId) - .filter((wagonId): wagonId is string => Boolean(wagonId)) ?? []; - - if (physicalWagonIds.length > 0) { - const physicalWagons = await manager.getRepository(Wagon).find({ - where: { id: In(physicalWagonIds) }, - relations: { currentLocationYard: true }, - }); - - await Promise.all( - physicalWagons.map((wagon) => - manager.getRepository(Wagon).update(wagon.id, { - status: this.expectedWagonStatusForYard(wagon.currentLocationYard), - sequenceNumber: null, - }), - ), - ); - } - }); - - return this.getContainerTrainScheduleById(id); - } - - async publishTrainSchedule(id: string) { - const schedule = await this.dataSource.getRepository(TrainSchedule).findOne({ - where: { id }, - relations: { trainSet: true, scheduleBookings: true }, - }); - - if (!schedule) { - throw new NotFoundException(`Train schedule ${id} not found`); - } - - if (schedule.status === "CANCELLED") { - throw new BadRequestException("Cancelled schedules cannot be published"); - } - - if (!schedule.trainSet || schedule.trainSet.wagonCount <= 0) { - throw new BadRequestException("Allocate wagons before publishing the schedule"); - } - - if ((schedule.scheduleBookings?.length ?? 0) === 0) { - throw new BadRequestException("Assign bookings before publishing the schedule"); - } - - await this.dataSource.getRepository(TrainSchedule).update(id, { status: "PUBLISHED" }); - return this.getContainerTrainScheduleById(id); - } - - private async loadBookingsForScheduling(bookingIds: string[]) { - return this.dataSource.getRepository(Booking).find({ - where: { id: In(bookingIds) }, - relations: { - company: true, - originYard: true, - destinationYard: true, - bookingContainers: { containerType: true }, - }, - order: { createdAt: "ASC" }, - }); - } - -======= ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db private async getActiveRoute(routeId: string) { const route = await this.dataSource.getRepository(Route).findOne({ where: { id: routeId }, @@ -2104,18 +1289,6 @@ export class TrainSchedulingService { return route; } -<<<<<<< HEAD - private expectedWagonStatusForYard(yard?: { country?: string } | null) { - const country = yard?.country?.trim().toLowerCase(); - if (country === "ethiopia" || country === "et") return "EXPORT_READY"; - if (country === "djibouti" || country === "djoubti" || country === "dj") return "IMPORT_READY"; - return "AVAILABLE"; - } - - private toUtcDateKey(value: Date | string) { - const date = value instanceof Date ? value : new Date(value); - return date.toISOString().slice(0, 10); -======= private mapEligibleBooking(booking: Booking) { return { id: booking.id, @@ -2137,7 +1310,6 @@ export class TrainSchedulingService { preferredDepartureDate: booking.scheduledDate.toISOString(), status: booking.status, }; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db } private resolveScheduleFreightType( diff --git a/apps/edr-freight-api/src/modules/train-sets/entities/train-set-wagon.entity.ts b/apps/edr-freight-api/src/modules/train-sets/entities/train-set-wagon.entity.ts index b505a5643..a220218e0 100644 --- a/apps/edr-freight-api/src/modules/train-sets/entities/train-set-wagon.entity.ts +++ b/apps/edr-freight-api/src/modules/train-sets/entities/train-set-wagon.entity.ts @@ -52,13 +52,6 @@ export class TrainSetWagon extends BaseEntity { @Column({ name: 'assigned_weight_tons', type: 'numeric', precision: 10, scale: 3, default: 0 }) assignedWeightTons!: number; - @Column({ name: 'physical_wagon_id', type: 'uuid', nullable: true }) - physicalWagonId?: string | null; - - @ManyToOne(() => Wagon, { nullable: true, onDelete: 'SET NULL' }) - @JoinColumn({ name: 'physical_wagon_id' }) - physicalWagon?: Wagon | null; - @Column({ name: 'status', type: 'varchar', length: 20, default: 'PLANNED' }) status!: string; diff --git a/apps/edr-freight-api/src/modules/wagon-types/dto/create-wagon-type.dto.ts b/apps/edr-freight-api/src/modules/wagon-types/dto/create-wagon-type.dto.ts index 64bfc973e..6de5debda 100644 --- a/apps/edr-freight-api/src/modules/wagon-types/dto/create-wagon-type.dto.ts +++ b/apps/edr-freight-api/src/modules/wagon-types/dto/create-wagon-type.dto.ts @@ -28,10 +28,7 @@ const toStringArray = ({ value }: { value: unknown }) => { if (Array.isArray(value)) { return value.map((entry) => String(entry).trim()).filter(Boolean); } -<<<<<<< HEAD -======= ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db if (typeof value !== 'string') return []; return value @@ -46,40 +43,24 @@ export class CreateWagonTypeDto { @MaxLength(32) code!: string; -<<<<<<< HEAD - @ApiProperty({ description: 'Display name, e.g. "Flat Wagon"', maxLength: 100 }) -======= @ApiProperty({ maxLength: 100, example: 'Flat wagon container' }) ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db @IsString() @MaxLength(100) name!: string; -<<<<<<< HEAD - @ApiProperty({ description: 'Maximum payload capacity in metric tons', example: 60 }) -======= @ApiProperty({ description: 'Maximum payload capacity in metric tons', example: 70 }) ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db @Transform(toNumber) @IsNumber() @Min(0.001) capacityTons!: number; -<<<<<<< HEAD - @ApiProperty({ description: 'Wagon length in meters', example: 14.2 }) -======= @ApiProperty({ description: 'Wagon length in meters', example: 14 }) ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db @Transform(toNumber) @IsNumber() @Min(0.001) lengthMeters!: number; -<<<<<<< HEAD - @ApiPropertyOptional({ description: 'Maximum wagons of this type per train', example: 45 }) -======= @ApiPropertyOptional({ description: 'Maximum wagons of this type per train', example: 53 }) ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db @IsOptional() @Transform(toOptionalNumber) @IsInt() diff --git a/apps/edr-freight-api/src/modules/wagon-types/wagon-types.controller.ts b/apps/edr-freight-api/src/modules/wagon-types/wagon-types.controller.ts index 92a55a438..827338a1a 100644 --- a/apps/edr-freight-api/src/modules/wagon-types/wagon-types.controller.ts +++ b/apps/edr-freight-api/src/modules/wagon-types/wagon-types.controller.ts @@ -28,9 +28,6 @@ export class WagonTypesController { @RuleEngineView('wagon-types') @ApiOperation({ summary: 'List wagon types' }) findAll(@Query() query: Record) { -<<<<<<< HEAD - return this.wagonTypesService.findAll(query); -======= return this.wagonTypesService.findAll({ isActive: query.isActive === 'all' @@ -43,7 +40,6 @@ export class WagonTypesController { sortBy: query.sortBy, sortOrder: query.sortOrder, }); ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db } @Get(':id') diff --git a/apps/edr-freight-api/src/modules/wagon-types/wagon-types.service.ts b/apps/edr-freight-api/src/modules/wagon-types/wagon-types.service.ts index 6e9784667..9308ddcf0 100644 --- a/apps/edr-freight-api/src/modules/wagon-types/wagon-types.service.ts +++ b/apps/edr-freight-api/src/modules/wagon-types/wagon-types.service.ts @@ -6,44 +6,18 @@ import { UpdateWagonTypeDto } from './dto/update-wagon-type.dto'; import { WagonType } from './entities/wagon-type.entity'; import { WagonTypesRepository } from './wagon-types.repository'; -<<<<<<< HEAD -type WagonTypeListResponse = { - data: WagonType[]; - meta: { total: number; page: number; pageSize: number; totalPages: number }; -======= type WagonTypeListFilter = { isActive?: boolean; page?: number; pageSize?: number; sortBy?: string; sortOrder?: string; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db }; @Injectable() export class WagonTypesService { constructor(private readonly wagonTypesRepository: WagonTypesRepository) {} -<<<<<<< HEAD - async findAll(query: Record = {}): Promise { - const page = Math.max(1, Number(query.page) || 1); - const pageSize = Math.max(1, Number(query.pageSize) || 20); - const isActive = - query.isActive === 'all' - ? undefined - : query.isActive === undefined - ? true - : query.isActive === 'true'; - const sortBy = ['code', 'name', 'capacityTons', 'lengthMeters', 'isActive'].includes( - query.sortBy ?? '', - ) - ? (query.sortBy as keyof WagonType) - : 'code'; - const sortOrder = query.sortOrder?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC'; - - const [data, total] = await this.wagonTypesRepository.findAndCount({ - where: isActive === undefined ? {} : { isActive }, -======= async findAll(filter: WagonTypeListFilter = {}): Promise<{ data: WagonType[]; meta: { total: number; page: number; pageSize: number; totalPages: number }; @@ -59,7 +33,6 @@ export class WagonTypesService { const [data, total] = await this.wagonTypesRepository.findAndCount({ where: filter.isActive === undefined ? {} : { isActive: filter.isActive }, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db order: { [sortBy]: sortOrder } as FindOptionsOrder, skip: (page - 1) * pageSize, take: pageSize, @@ -71,21 +44,14 @@ export class WagonTypesService { total, page, pageSize, -<<<<<<< HEAD - totalPages: Math.ceil(total / pageSize), -======= totalPages: Math.max(1, Math.ceil(total / pageSize)), ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db }, }; } async findById(id: string): Promise { const wagonType = await this.wagonTypesRepository.findById(id); -<<<<<<< HEAD -======= ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db if (!wagonType) { throw new NotFoundException(`Wagon type ${id} not found`); } @@ -103,25 +69,16 @@ export class WagonTypesService { async create(dto: CreateWagonTypeDto): Promise { const code = dto.code.trim().toUpperCase(); const existing = await this.wagonTypesRepository.findByCode(code); -<<<<<<< HEAD -======= ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db if (existing) { throw new ConflictException(`Wagon type code "${code}" already exists`); } return this.wagonTypesRepository.create({ -<<<<<<< HEAD - ...dto, - code, - name: dto.name.trim(), -======= code, name: dto.name.trim(), capacityTons: dto.capacityTons, lengthMeters: dto.lengthMeters, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db maxWagonsPerTrain: dto.maxWagonsPerTrain ?? null, supportedLoadTypes: dto.supportedLoadTypes ?? [], isActive: dto.isActive ?? true, @@ -143,12 +100,9 @@ export class WagonTypesService { ...dto, ...(nextCode ? { code: nextCode } : {}), ...(dto.name ? { name: dto.name.trim() } : {}), -<<<<<<< HEAD -======= maxWagonsPerTrain: dto.maxWagonsPerTrain === undefined ? undefined : dto.maxWagonsPerTrain ?? null, supportedLoadTypes: dto.supportedLoadTypes ?? undefined, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db }); if (!updated) { diff --git a/apps/edr-freight-api/src/modules/wagons/dto/create-wagon.dto.ts b/apps/edr-freight-api/src/modules/wagons/dto/create-wagon.dto.ts index d50c3a2ab..7ceb3cf2d 100644 --- a/apps/edr-freight-api/src/modules/wagons/dto/create-wagon.dto.ts +++ b/apps/edr-freight-api/src/modules/wagons/dto/create-wagon.dto.ts @@ -30,17 +30,12 @@ export class CreateWagonDto { maxPayloadWeight!: number; @IsOptional() -<<<<<<< HEAD - @IsIn(['AVAILABLE', 'IMPORT_READY', 'EXPORT_READY', 'ASSIGNED', 'MAINTENANCE', 'RETIRED']) - status?: string; -======= @IsEnum(WagonStatus) status?: WagonStatus; @IsOptional() @IsEnum(WagonReadiness) readiness?: WagonReadiness; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db @IsOptional() @IsString() diff --git a/apps/edr-freight-api/src/modules/wagons/entities/wagon.entity.ts b/apps/edr-freight-api/src/modules/wagons/entities/wagon.entity.ts index 90b2d281d..bd08fadd5 100644 --- a/apps/edr-freight-api/src/modules/wagons/entities/wagon.entity.ts +++ b/apps/edr-freight-api/src/modules/wagons/entities/wagon.entity.ts @@ -12,6 +12,8 @@ import { WagonType } from '../../wagon-types/entities/wagon-type.entity'; export const WAGON_STATUSES = [ WagonStatus.Available, WagonStatus.Assigned, + WagonStatus.ImportReady, + WagonStatus.ExportReady, WagonStatus.Maintenance, WagonStatus.Retired, ] as const; @@ -56,16 +58,11 @@ export class Wagon extends BaseEntity { @Column({ name: 'max_payload_weight', type: 'decimal', precision: 10, scale: 2 }) maxPayloadWeight!: number; -<<<<<<< HEAD - @Column({ type: 'varchar', default: 'AVAILABLE' }) - status!: string; // AVAILABLE, IMPORT_READY, EXPORT_READY, ASSIGNED, MAINTENANCE, RETIRED -======= @Column({ type: 'varchar', length: 20, default: WagonStatus.Available }) status!: WagonStatusType; @Column({ type: 'varchar', length: 20, default: WagonReadiness.ImportReady }) readiness!: WagonReadinessType; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db @Column({ type: 'text', nullable: true }) notes!: string | null; @@ -84,7 +81,7 @@ export class Wagon extends BaseEntity { @JoinColumn({ name: 'current_train_schedule_id' }) currentTrainSchedule?: TrainSchedule | null; - /** Fleet master consist grouping — separate from operational train_schedules. */ + /** Fleet master consist grouping — separate from operational train_schedules. */ @ManyToOne(() => Train, (train) => train.wagons, { onDelete: 'SET NULL' }) @JoinColumn({ name: 'train_id' }) train!: Train | null; diff --git a/apps/edr-freight-api/src/modules/wagons/wagons.service.ts b/apps/edr-freight-api/src/modules/wagons/wagons.service.ts index 9d905aaf6..af2725353 100644 --- a/apps/edr-freight-api/src/modules/wagons/wagons.service.ts +++ b/apps/edr-freight-api/src/modules/wagons/wagons.service.ts @@ -41,26 +41,16 @@ export class WagonsService { const status = query.status?.trim(); const readiness = query.readiness?.trim(); const trainId = query.trainId?.trim(); -<<<<<<< HEAD - const currentLocationYardId = query.currentLocationYardId?.trim(); -======= const filters = { ...(status ? { status: status as Wagon['status'] } : {}), ...(readiness ? { readiness: readiness as Wagon['readiness'] } : {}), ...(trainId ? { trainId } : {}), }; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db if (search) { where.push({ wagonNumber: ILike(`%${search}%`), -<<<<<<< HEAD - ...(status ? { status } : {}), - ...(trainId ? { trainId } : {}), - ...(currentLocationYardId ? { currentLocationYardId } : {}), -======= ...filters, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db }); } @@ -70,12 +60,7 @@ export class WagonsService { const sortOrder = query.sortOrder?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC'; return this.wagonRepo.find({ -<<<<<<< HEAD - where: search ? where : { ...(status ? { status } : {}), ...(trainId ? { trainId } : {}), ...(currentLocationYardId ? { currentLocationYardId } : {}) }, - relations: { currentLocationYard: true, wagonType: true }, -======= where: search ? where : filters, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db order: { [sortBy]: sortOrder } as FindOptionsOrder, skip: query.page && query.limit ? (Number(query.page) - 1) * Number(query.limit) : undefined, take: query.limit ? Number(query.limit) : undefined, @@ -131,22 +116,23 @@ export class WagonsService { const wagon = await this.findById(wagonId); wagon.trainId = null; wagon.sequenceNumber = null; -<<<<<<< HEAD - wagon.status = await this.statusForLocation(wagon.currentLocationYardId, 'AVAILABLE'); -======= wagon.status = WagonStatus.Available; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db return this.wagonRepo.save(wagon); } - private async statusForLocation(yardId?: string | null, fallback = 'AVAILABLE') { + private async statusForLocation( + yardId?: string | null, + fallback: Wagon['status'] = WagonStatus.Available, + ): Promise { if (!yardId) return fallback; const yard = await this.yardRepo.findOne({ where: { id: yardId } }); const country = yard?.country?.trim().toLowerCase(); - if (country === 'ethiopia' || country === 'et') return 'EXPORT_READY'; - if (country === 'djibouti' || country === 'djoubti' || country === 'dj') return 'IMPORT_READY'; + if (country === 'ethiopia' || country === 'et') return WagonStatus.ExportReady; + if (country === 'djibouti' || country === 'djoubti' || country === 'dj') { + return WagonStatus.ImportReady; + } return fallback; } diff --git a/apps/edr-freight-web/backoffice/src/App.tsx b/apps/edr-freight-web/backoffice/src/App.tsx index 687ad1814..ddc85f10b 100644 --- a/apps/edr-freight-web/backoffice/src/App.tsx +++ b/apps/edr-freight-web/backoffice/src/App.tsx @@ -33,24 +33,14 @@ import UserManagementPage from "./pages/dashboard/user-management/UserManagement import UsersPage from "./pages/dashboard/user-management/UsersPage"; import DropdownSettingsPage from "./pages/dropdown_settings/DropdownSettingsPage"; import FileUploadSettingsPage from "./pages/documents/FileUploadSettingsPage"; -import RuleEngineLegacyRedirect from "./pages/ruleEngine/RuleEngineLegacyRedirect"; -import RuleEngineResourcePage from "./pages/ruleEngine/RuleEngineResourcePage"; -import TrainsPage from "./pages/trains/TrainsPage"; -<<<<<<< HEAD -import { - CargoesCrudPage, - ContainersCrudPage, - LocomotivesCrudPage, - TrainMasterDataPage, - WagonsCrudPage, -} from "./pages/fleet/FleetCrudPages"; -======= -import TrainScheduleV2ListPage from "./pages/trainScheduling/TrainScheduleV2ListPage"; -import TrainScheduleV2DetailPage from "./pages/trainScheduling/TrainScheduleV2DetailPage"; -import TrainSchedulingGlobalRulesPage from "./pages/trainScheduling/TrainSchedulingGlobalRulesPage"; -import FleetResourcePage from "./pages/fleet/FleetResourcePage"; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db -import { getCategorySidebarChildren } from "./pages/ruleEngine/config/resources"; +import RuleEngineLegacyRedirect from "./pages/ruleEngine/RuleEngineLegacyRedirect"; +import RuleEngineResourcePage from "./pages/ruleEngine/RuleEngineResourcePage"; +import TrainsPage from "./pages/trains/TrainsPage"; +import TrainScheduleV2ListPage from "./pages/trainScheduling/TrainScheduleV2ListPage"; +import TrainScheduleV2DetailPage from "./pages/trainScheduling/TrainScheduleV2DetailPage"; +import TrainSchedulingGlobalRulesPage from "./pages/trainScheduling/TrainSchedulingGlobalRulesPage"; +import FleetResourcePage from "./pages/fleet/FleetResourcePage"; +import { getCategorySidebarChildren } from "./pages/ruleEngine/config/resources"; import TrainDetailPage from "./pages/trains/TrainDetailPage"; import RoutesPage from "./pages/fleet/RoutesPage"; import WarehouseListPage from "./pages/warehouses/WarehouseListPage"; @@ -289,35 +279,24 @@ const App = () => { path="booking-requests/:id/contract" element={} /> - } /> -<<<<<<< HEAD - } /> - } /> -======= - } - /> - } - /> ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db - } /> - } /> - } /> - } /> -<<<<<<< HEAD - } /> - } /> - } /> -======= - } /> - } /> - } /> ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db - - } /> + } /> + } + /> + } + /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + } /> } /> } /> } /> diff --git a/apps/edr-freight-web/backoffice/src/services/wagon-types.service.ts b/apps/edr-freight-web/backoffice/src/services/wagon-types.service.ts index 4a80b9b8a..0532abb62 100644 --- a/apps/edr-freight-web/backoffice/src/services/wagon-types.service.ts +++ b/apps/edr-freight-web/backoffice/src/services/wagon-types.service.ts @@ -19,11 +19,7 @@ const asList = (payload: ListResponse): T[] => export const wagonTypesService = { async getWagonTypes() { const response = await api.get>('/wagon-types', { -<<<<<<< HEAD - params: { isActive: 'all' }, -======= params: { isActive: 'all', pageSize: 500 }, ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db }); return asList(response.data); }, diff --git a/apps/edr-freight-web/backoffice/src/types/trainScheduling.ts b/apps/edr-freight-web/backoffice/src/types/trainScheduling.ts index 593609b19..c44b6c305 100644 --- a/apps/edr-freight-web/backoffice/src/types/trainScheduling.ts +++ b/apps/edr-freight-web/backoffice/src/types/trainScheduling.ts @@ -136,20 +136,15 @@ export interface LocomotiveRecord { name?: string | null; maxPullWeightTons: number; maxTrainLengthMeters: number; -<<<<<<< HEAD status: - | 'AVAILABLE' - | 'UNAVAILABLE' - | 'IMPORT_READY' - | 'EXPORT_READY' - | 'ASSIGNED' - | 'MAINTENANCE' - | 'OUT_OF_SERVICE'; - locomotiveType?: 'DIESEL' | 'ELECTRIC'; -======= - status: "AVAILABLE" | "ASSIGNED" | "MAINTENANCE" | "OUT_OF_SERVICE"; + | "AVAILABLE" + | "UNAVAILABLE" + | "IMPORT_READY" + | "EXPORT_READY" + | "ASSIGNED" + | "MAINTENANCE" + | "OUT_OF_SERVICE"; locomotiveType?: "DIESEL" | "ELECTRIC"; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db } export interface TrainScheduleListItem { @@ -250,21 +245,12 @@ export interface TrainScheduleDetail { code: string; name: string; } | null; -<<<<<<< HEAD physicalWagon?: { id: string; wagonNumber: string; status: string; } | null; - allocations: Array<{ - id: string; - bookingId: string; - bookingReference: string | null; - allocatedWeightTons: number; - }>; -======= allocations: TrainScheduleWagonAllocation[]; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db }>; } | null; bookings: Array<{ @@ -281,13 +267,10 @@ export interface TrainScheduleDetail { export interface TrainScheduleFilters { originStationId?: string; destinationStationId?: string; -<<<<<<< HEAD scheduleDate?: string; assignmentType?: AssignmentType; tradeDirection?: TradeDirection; -======= schedulingStatus?: SchedulingStatus; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db } export interface TrainSchedulePreviewPayload { @@ -295,9 +278,7 @@ export interface TrainSchedulePreviewPayload { scheduleDate: string; originStationId: string; destinationStationId: string; -<<<<<<< HEAD assignmentType?: AssignmentType; -======= targetScheduleId?: string; maxTrainWeightTons?: number; maxTrainLengthMeters?: number; @@ -320,7 +301,6 @@ export interface ReschedulePlan { readmitted: RescheduleBookingSummary[]; finalBookingIds: string[]; warnings: string[]; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db } export interface CreateTrainSchedulePayload { @@ -328,11 +308,9 @@ export interface CreateTrainSchedulePayload { scheduleDate: string; arrivalDate?: string; locomotiveId: string; -<<<<<<< HEAD assignmentType?: AssignmentType; bookingIds: string[]; wagonIds?: string[]; -======= maxTrainWeightTons?: number; maxTrainLengthMeters?: number; maxWagonsPerTrain?: number; @@ -354,5 +332,4 @@ export interface PinWagonAssignment { export interface PinWagonsPayload { assignments: PinWagonAssignment[]; ->>>>>>> 10b011de8feafce5c0bae21594df5b36596587db } diff --git a/packages/types/src/freight/index.ts b/packages/types/src/freight/index.ts index 0bd2a5ed7..4051d64d1 100644 --- a/packages/types/src/freight/index.ts +++ b/packages/types/src/freight/index.ts @@ -135,6 +135,8 @@ export enum TrainSetWagonStatus { export enum WagonStatus { Available = "AVAILABLE", Assigned = "ASSIGNED", + ImportReady = "IMPORT_READY", + ExportReady = "EXPORT_READY", Maintenance = "MAINTENANCE", Retired = "RETIRED", } @@ -335,7 +337,7 @@ export interface IInvoice extends BaseEntity { dueAt: string; } -// ── Reference Data (booking form catalog) ────────────────────────────────────── +// ── Reference Data (booking form catalog) ────────────────────────────────────── export interface BookingReferenceYard { id: string; @@ -391,7 +393,7 @@ export interface BookingReferenceData { cargo_type: BookingReferenceCargoTypeGroup[]; } -// ── DTOs ─────────────────────────────────────────────────────────────────────── +// ── DTOs ─────────────────────────────────────────────────────────────────────── export interface CreateBookingContainerDto { containerTypeId: string; diff --git a/turbo.json b/turbo.json index 548b6d7a9..ad10d0c80 100644 --- a/turbo.json +++ b/turbo.json @@ -1,5 +1,6 @@ { "$schema": "https://turbo.build/schema.json", + "concurrency": "11", "tasks": { "build": { "dependsOn": ["^build"],