mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 20:10:56 +00:00
149 lines
5.8 KiB
TypeScript
149 lines
5.8 KiB
TypeScript
// apps/edr-freight-api/src/modules/container-management/containers.service.ts
|
|
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { FindOptionsOrder, FindOptionsWhere, ILike, Repository } from 'typeorm';
|
|
import { CreateContainerDto } from './dto/create-container.dto';
|
|
import { UpdateContainerDto } from './dto/update-container.dto';
|
|
import { AssignContainerToWagonDto } from './dto/assign-container-to-wagon.dto';
|
|
import { Container } from './entities/container.entity';
|
|
import { Wagon } from '../wagons/entities/wagon.entity';
|
|
import { ContainerType } from '../rule-engine/entities/container-type.entity';
|
|
|
|
@Injectable()
|
|
export class ContainersService {
|
|
constructor(
|
|
@InjectRepository(Container)
|
|
private readonly containerRepo: Repository<Container>,
|
|
@InjectRepository(Wagon)
|
|
private readonly wagonRepo: Repository<Wagon>, // ✅ use raw repository
|
|
@InjectRepository(ContainerType)
|
|
private readonly containerTypeRepo: Repository<ContainerType>,
|
|
) {}
|
|
|
|
async create(dto: CreateContainerDto): Promise<Container> {
|
|
const existing = await this.containerRepo.findOne({
|
|
where: { containerNumber: dto.containerNumber },
|
|
});
|
|
if (existing) {
|
|
throw new ConflictException(`Container number "${dto.containerNumber}" already exists`);
|
|
}
|
|
|
|
const containerType = await this.containerTypeRepo.findOne({
|
|
where: { id: dto.containerTypeId, isActive: true },
|
|
});
|
|
if (!containerType) {
|
|
throw new NotFoundException(`Container type ${dto.containerTypeId} not found`);
|
|
}
|
|
|
|
if (dto.wagonId) {
|
|
const wagon = await this.wagonRepo.findOne({ where: { id: dto.wagonId } });
|
|
if (!wagon) throw new NotFoundException(`Wagon ${dto.wagonId} not found`);
|
|
}
|
|
|
|
const container = this.containerRepo.create(dto);
|
|
if (dto.wagonId === undefined) container.wagonId = null;
|
|
if (dto.position === undefined) container.position = null;
|
|
return this.containerRepo.save(container);
|
|
}
|
|
|
|
async findAll(query: Record<string, string | undefined> = {}): Promise<Container[]> {
|
|
const where: FindOptionsWhere<Container>[] | FindOptionsWhere<Container> = [];
|
|
const search = query.search?.trim();
|
|
const status = query.status?.trim();
|
|
const wagonId = query.wagonId?.trim();
|
|
|
|
if (search) {
|
|
where.push({
|
|
containerNumber: ILike(`%${search}%`),
|
|
...(status ? { status } : {}),
|
|
...(wagonId ? { wagonId } : {}),
|
|
});
|
|
}
|
|
|
|
const sortBy = ['containerNumber', 'tareWeight', 'maxGrossWeight', 'status', 'position'].includes(query.sortBy ?? '')
|
|
? (query.sortBy as keyof Container)
|
|
: 'containerNumber';
|
|
const sortOrder = query.sortOrder?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
|
|
|
|
return this.containerRepo.find({
|
|
where: search ? where : { ...(status ? { status } : {}), ...(wagonId ? { wagonId } : {}) },
|
|
order: { [sortBy]: sortOrder } as FindOptionsOrder<Container>,
|
|
skip: query.page && query.limit ? (Number(query.page) - 1) * Number(query.limit) : undefined,
|
|
take: query.limit ? Number(query.limit) : undefined,
|
|
});
|
|
}
|
|
|
|
async findById(id: string): Promise<Container> {
|
|
const container = await this.containerRepo.findOne({ where: { id } });
|
|
if (!container) throw new NotFoundException(`Container ${id} not found`);
|
|
return container;
|
|
}
|
|
|
|
async update(id: string, dto: UpdateContainerDto): Promise<Container> {
|
|
const container = await this.findById(id);
|
|
if (dto.containerNumber && dto.containerNumber !== container.containerNumber) {
|
|
const existing = await this.containerRepo.findOne({
|
|
where: { containerNumber: dto.containerNumber },
|
|
});
|
|
if (existing) {
|
|
throw new ConflictException(`Container number "${dto.containerNumber}" already exists`);
|
|
}
|
|
}
|
|
if (dto.containerTypeId) {
|
|
const containerType = await this.containerTypeRepo.findOne({
|
|
where: { id: dto.containerTypeId, isActive: true },
|
|
});
|
|
if (!containerType) {
|
|
throw new NotFoundException(`Container type ${dto.containerTypeId} not found`);
|
|
}
|
|
}
|
|
if (dto.wagonId) {
|
|
const wagon = await this.wagonRepo.findOne({ where: { id: dto.wagonId } });
|
|
if (!wagon) throw new NotFoundException(`Wagon ${dto.wagonId} not found`);
|
|
}
|
|
Object.assign(container, dto);
|
|
return this.containerRepo.save(container);
|
|
}
|
|
|
|
async remove(id: string): Promise<void> {
|
|
const container = await this.findById(id);
|
|
await this.containerRepo.remove(container);
|
|
}
|
|
|
|
async assignToWagon(containerId: string, dto: AssignContainerToWagonDto): Promise<Container> {
|
|
const container = await this.findById(containerId);
|
|
if (container.status === 'LOADED') {
|
|
throw new ConflictException('Cannot reassign a loaded container');
|
|
}
|
|
|
|
const wagon = await this.wagonRepo.findOne({ where: { id: dto.wagonId } });
|
|
if (!wagon) throw new NotFoundException(`Wagon ${dto.wagonId} not found`);
|
|
|
|
let position: number | null = dto.position ?? null;
|
|
if (position === null) {
|
|
const maxPos = await this.containerRepo
|
|
.createQueryBuilder('c')
|
|
.select('MAX(c.position)', 'max')
|
|
.where('c.wagonId = :wagonId', { wagonId: wagon.id })
|
|
.getRawOne();
|
|
position = (maxPos?.max ?? 0) + 1;
|
|
}
|
|
|
|
container.wagonId = wagon.id;
|
|
container.position = position;
|
|
container.status = 'AVAILABLE';
|
|
return this.containerRepo.save(container);
|
|
}
|
|
|
|
async unassignFromWagon(containerId: string): Promise<Container> {
|
|
const container = await this.findById(containerId);
|
|
if (container.status === 'LOADED') {
|
|
throw new ConflictException('Cannot unassign a loaded container');
|
|
}
|
|
container.wagonId = null;
|
|
container.position = null;
|
|
container.status = 'AVAILABLE';
|
|
return this.containerRepo.save(container);
|
|
}
|
|
}
|