mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-01 03:07:37 +00:00
fix schule issue and contianer type issue
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectDataSource } from '@nestjs/typeorm';
|
||||
import { DataSource } from 'typeorm';
|
||||
import {
|
||||
NotificationAudience,
|
||||
NotificationPriority,
|
||||
@@ -9,6 +11,7 @@ import {
|
||||
import { Booking } from '../bookings/entities/booking.entity';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
import { NotificationInboxService } from '../notification-inbox/notification-inbox.service';
|
||||
import { resolveCompanyNotifyPhone } from '../notifications/resolve-company-phone.util';
|
||||
import { TrainSchedulesRepository } from '../train-schedules/train-schedules.repository';
|
||||
import { BATCH_TIMEZONE } from './booking-batch.constants';
|
||||
|
||||
@@ -20,6 +23,8 @@ export class BookingNotifierService {
|
||||
private readonly notifications: NotificationsService,
|
||||
private readonly inbox: NotificationInboxService,
|
||||
private readonly trainSchedules: TrainSchedulesRepository,
|
||||
@InjectDataSource()
|
||||
private readonly dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -60,7 +65,9 @@ export class BookingNotifierService {
|
||||
logLabel: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(`${logLabel} — ${this.ref(b)}`);
|
||||
const phone = b.company?.contactPersonPhone ?? b.company?.phone ?? null;
|
||||
const phone = b.companyId
|
||||
? await resolveCompanyNotifyPhone(this.dataSource, b.companyId)
|
||||
: null;
|
||||
const email = b.company?.email ?? b.company?.generalManagerEmail ?? null;
|
||||
|
||||
if (phone) {
|
||||
|
||||
@@ -13,6 +13,10 @@ import { TrainSchedule } from '../train-schedules/entities/train-schedule.entity
|
||||
import { TrainSchedulesRepository } from '../train-schedules/train-schedules.repository';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
import { NotificationInboxService } from '../notification-inbox/notification-inbox.service';
|
||||
import {
|
||||
companyNotifyPhoneExpr,
|
||||
primaryContactUserJoin,
|
||||
} from '../notifications/resolve-company-phone.util';
|
||||
import { BookingBatchService } from './booking-batch.service';
|
||||
import { BookingWindowGateway } from './booking-window.gateway';
|
||||
import { TrainSchedulingService, effectiveWindowConfig } from './train-scheduling.service';
|
||||
@@ -527,7 +531,7 @@ export class BookingWindowService implements OnModuleInit {
|
||||
}> = await this.dataSource.query(
|
||||
`SELECT DISTINCT
|
||||
c.company_id,
|
||||
COALESCE(co.contact_person_phone, co.phone) AS phone,
|
||||
${companyNotifyPhoneExpr('co')} AS phone,
|
||||
COALESCE(co.email, co.general_manager_email) AS email
|
||||
FROM freight.contract_routes cr
|
||||
JOIN freight.contracts c
|
||||
@@ -535,6 +539,7 @@ export class BookingWindowService implements OnModuleInit {
|
||||
AND c.status IN ('CONTRACT_ACTIVE', 'FULLY_EXECUTED')
|
||||
AND c.deleted_at IS NULL
|
||||
JOIN freight.companies co ON co.id = c.company_id
|
||||
${primaryContactUserJoin('co')}
|
||||
WHERE cr.origin_yard_id = $1
|
||||
AND cr.destination_yard_id = $2
|
||||
AND cr.deleted_at IS NULL`,
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { InjectDataSource } from '@nestjs/typeorm';
|
||||
import { SCHEDULE_BOOKINGS_CTE } from '../../common/schedule-bookings.sql';
|
||||
import {
|
||||
DataSource,
|
||||
EntityManager,
|
||||
@@ -2037,6 +2038,58 @@ export class TrainSchedulingService {
|
||||
return this.getTrainScheduleById(scheduleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* EXPORT ONLY. An export train must not leave carrying nothing while its cargo
|
||||
* sits in the shed: the goods are received into the origin warehouse, GRN'd and
|
||||
* loaded onto the wagons allocated to the booking, so anything still in the
|
||||
* warehouse at dispatch is being left behind. Blocks dispatch when an allocated
|
||||
* booking has warehouse inventory that never made it onto a wagon (received /
|
||||
* stored / ready but not LOADED) — either load it from the Load-to-Train queue,
|
||||
* or drop the booking's wagon allocation so it rides a later train.
|
||||
*
|
||||
* Import/domestic are untouched: their cargo isn't loaded out of an origin
|
||||
* warehouse, so warehouse inventory says nothing about what's aboard.
|
||||
*
|
||||
* Bookings with no warehouse inventory at all are NOT blocked — allocating a
|
||||
* wagon before the goods arrive is normal planning; they simply aren't aboard.
|
||||
*/
|
||||
private async assertAllocatedCargoLoaded(scheduleId: string): Promise<void> {
|
||||
const [route]: Array<{ originCountry: string | null; destinationCountry: string | null }> =
|
||||
await this.dataSource.query(
|
||||
`SELECT oy.country AS "originCountry", dy.country AS "destinationCountry"
|
||||
FROM freight.train_schedules ts
|
||||
LEFT JOIN freight.yards oy ON oy.id = ts.origin_station_id
|
||||
LEFT JOIN freight.yards dy ON dy.id = ts.destination_station_id
|
||||
WHERE ts.id = $1 AND ts.deleted_at IS NULL`,
|
||||
[scheduleId],
|
||||
);
|
||||
if (!route) return;
|
||||
const direction = deriveTradeDirection(
|
||||
{ country: route.originCountry },
|
||||
{ country: route.destinationCountry },
|
||||
);
|
||||
if (direction !== 'EXPORT') return;
|
||||
|
||||
const rows: Array<{ reference: string | null; status: string }> = await this.dataSource.query(
|
||||
`WITH ${SCHEDULE_BOOKINGS_CTE}
|
||||
SELECT DISTINCT b.reference AS "reference", inv.status AS "status"
|
||||
FROM sched_bookings sb
|
||||
JOIN freight.bookings b ON b.id = sb.booking_id AND b.deleted_at IS NULL
|
||||
JOIN freight.warehouse_inventory inv
|
||||
ON inv.booking_id = b.id AND inv.deleted_at IS NULL
|
||||
WHERE sb.schedule_id = $1
|
||||
AND inv.status IN ('RECEIVED', 'STORED', 'READY_FOR_LOADING')`,
|
||||
[scheduleId],
|
||||
);
|
||||
if (rows.length) {
|
||||
const refs = [...new Set(rows.map((r) => r.reference ?? '?'))].join(', ');
|
||||
throw new BadRequestException(
|
||||
`Cannot dispatch: cargo for booking(s) ${refs} is in the warehouse but not loaded onto a wagon. ` +
|
||||
`Load it from the warehouse Load-to-Train queue, or remove the booking's wagon allocation so it travels on a later train.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async dispatchSchedule(scheduleId: string) {
|
||||
const schedule = await this.trainSchedulesRepository.findByIdWithFullGraph(scheduleId);
|
||||
if (!schedule) {
|
||||
@@ -2046,6 +2099,8 @@ export class TrainSchedulingService {
|
||||
throw new BadRequestException('Only SCHEDULED trains can be dispatched');
|
||||
}
|
||||
await this.assertImportDjiboutiMayDepart(schedule);
|
||||
// Export only: don't leave received cargo behind in the warehouse.
|
||||
await this.assertAllocatedCargoLoaded(scheduleId);
|
||||
// A locomotive may sit on many future schedules, but it can only pull one train
|
||||
// at a time — block dispatch while any set locomotive is out on a dispatched train.
|
||||
const setLocomotiveIds = this.locomotivesOfTrainSet(schedule.trainSet).map((l) => l.id);
|
||||
|
||||
Reference in New Issue
Block a user