Files
edr-platform/apps/edr-freight-api/src/modules/bookings/bookings.repository.ts

95 lines
3.1 KiB
TypeScript

import { BaseRepository } from "@edr/api-common";
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { In, IsNull, Not, Repository } from "typeorm";
import { Booking } from "./entities/booking.entity";
import { FileRecord } from "../files/entities/file.entity";
@Injectable()
export class BookingsRepository extends BaseRepository<Booking> {
constructor(
@InjectRepository(Booking)
repository: Repository<Booking>,
) {
super(repository);
}
/** Find a booking by its human-readable reference number. */
findByReference(reference: string): Promise<Booking | null> {
return this.repository.findOne({ where: { reference } });
}
/** Find a booking by reference with associated files (polymorphic join). */
async findByReferenceWithFiles(reference: string): Promise<Booking | null> {
const booking = await this.repository
.createQueryBuilder("booking")
.where("booking.reference = :reference", { reference })
.leftJoinAndMapMany(
"booking.files",
FileRecord,
"file",
"file.resource_id = booking.id AND file.resource = 'bookings'"
)
.getOne();
return booking ?? null;
}
/** Find a booking by ID with associated files (polymorphic join). */
async findByIdWithFiles(id: string): Promise<Booking | null> {
const booking = await this.repository
.createQueryBuilder("booking")
.where("booking.id = :id", { id })
.leftJoinAndMapMany(
"booking.files",
FileRecord,
"file",
"file.resource_id = booking.id AND file.resource = 'bookings'"
)
.getOne();
return booking ?? null;
}
/** Find a compatible consolidation partner for the given booking. */
async findConsolidationPartner(booking: Booking): Promise<Booking | null> {
return this.repository.findOne({
where: {
allowConsolidation: true,
// Check if containers JSONB contains at least one 20FT entry with odd qty
containers: Not(IsNull()),
originStation: booking.originStation,
destinationStation: booking.destinationStation,
tradeDirection: booking.tradeDirection,
consolidationPartnerId: IsNull(),
status: In(["DRAFT", "PENDING_CONSOLIDATION"]),
id: Not(booking.id),
},
order: { createdAt: "ASC" },
});
}
/** Pair two bookings for consolidation. */
async pairConsolidation(bookingId: string, partnerId: string): Promise<void> {
await this.repository.update(bookingId, {
consolidationPartnerId: partnerId,
status: "CONSOLIDATED",
} as never);
await this.repository.update(partnerId, {
consolidationPartnerId: bookingId,
status: "CONSOLIDATED",
} as never);
}
/** Un-pair a consolidation. Returns both booking IDs. */
async unpairConsolidation(bookingId: string, partnerId: string): Promise<void> {
await this.repository.update(bookingId, {
consolidationPartnerId: null,
status: "PENDING_CONSOLIDATION",
} as never);
await this.repository.update(partnerId, {
consolidationPartnerId: null,
status: "PENDING_CONSOLIDATION",
} as never);
}
}