mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
176 lines
6.2 KiB
TypeScript
176 lines
6.2 KiB
TypeScript
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { FindOptionsOrder, FindOptionsWhere, ILike, Repository } from 'typeorm';
|
|
import { CreateCargoDto } from './dto/create-cargo.dto';
|
|
import { UpdateCargoDto } from './dto/update-cargo.dto';
|
|
import { LoadCargoDto } from './dto/load-cargo.dto';
|
|
import { DeliverCargoDto } from './dto/deliver-cargo.dto';
|
|
import { Cargo } from './entities/cargoes.entity';
|
|
import { Container } from '../container-management/entities/container.entity';
|
|
import { CargoType } from '../rule-engine/entities/cargo-type.entity';
|
|
|
|
@Injectable()
|
|
export class CargoesService {
|
|
constructor(
|
|
@InjectRepository(Cargo)
|
|
private readonly cargoRepo: Repository<Cargo>,
|
|
@InjectRepository(Container)
|
|
private readonly containerRepo: Repository<Container>,
|
|
@InjectRepository(CargoType)
|
|
private readonly cargoTypeRepo: Repository<CargoType>,
|
|
) {}
|
|
|
|
async create(dto: CreateCargoDto): Promise<Cargo> {
|
|
const existing = await this.cargoRepo.findOne({
|
|
where: { cargoReference: dto.cargoReference },
|
|
});
|
|
if (existing) {
|
|
throw new ConflictException(`Cargo reference "${dto.cargoReference}" already exists`);
|
|
}
|
|
|
|
const container = await this.containerRepo.findOne({ where: { id: dto.containerId } });
|
|
if (!container) {
|
|
throw new NotFoundException(`Container ${dto.containerId} not found`);
|
|
}
|
|
|
|
if (dto.cargoTypeId) {
|
|
const cargoType = await this.cargoTypeRepo.findOne({
|
|
where: { id: dto.cargoTypeId, isActive: true },
|
|
});
|
|
if (!cargoType) throw new NotFoundException(`Cargo type ${dto.cargoTypeId} not found`);
|
|
}
|
|
|
|
const cargo = this.cargoRepo.create(dto);
|
|
return this.cargoRepo.save(cargo);
|
|
}
|
|
|
|
async findAll(query: Record<string, string | undefined> = {}): Promise<Cargo[]> {
|
|
const where: FindOptionsWhere<Cargo>[] | FindOptionsWhere<Cargo> = [];
|
|
const search = query.search?.trim();
|
|
const status = query.status?.trim();
|
|
const containerId = query.containerId?.trim();
|
|
|
|
if (search) {
|
|
where.push({
|
|
cargoReference: ILike(`%${search}%`),
|
|
...(status ? { status } : {}),
|
|
...(containerId ? { containerId } : {}),
|
|
});
|
|
where.push({
|
|
description: ILike(`%${search}%`),
|
|
...(status ? { status } : {}),
|
|
...(containerId ? { containerId } : {}),
|
|
});
|
|
}
|
|
|
|
const sortBy = ['cargoReference', 'quantity', 'weight', 'volume', 'status'].includes(query.sortBy ?? '')
|
|
? (query.sortBy as keyof Cargo)
|
|
: 'cargoReference';
|
|
const sortOrder = query.sortOrder?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';
|
|
|
|
return this.cargoRepo.find({
|
|
where: search ? where : { ...(status ? { status } : {}), ...(containerId ? { containerId } : {}) },
|
|
order: { [sortBy]: sortOrder } as FindOptionsOrder<Cargo>,
|
|
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<Cargo> {
|
|
const cargo = await this.cargoRepo.findOne({ where: { id } });
|
|
if (!cargo) throw new NotFoundException(`Cargo ${id} not found`);
|
|
return cargo;
|
|
}
|
|
|
|
async update(id: string, dto: UpdateCargoDto): Promise<Cargo> {
|
|
const cargo = await this.findById(id);
|
|
if (dto.cargoReference && dto.cargoReference !== cargo.cargoReference) {
|
|
const existing = await this.cargoRepo.findOne({
|
|
where: { cargoReference: dto.cargoReference },
|
|
});
|
|
if (existing) {
|
|
throw new ConflictException(`Cargo reference "${dto.cargoReference}" already exists`);
|
|
}
|
|
}
|
|
if (dto.containerId) {
|
|
const container = await this.containerRepo.findOne({ where: { id: dto.containerId } });
|
|
if (!container) throw new NotFoundException(`Container ${dto.containerId} not found`);
|
|
}
|
|
if (dto.cargoTypeId) {
|
|
const cargoType = await this.cargoTypeRepo.findOne({
|
|
where: { id: dto.cargoTypeId, isActive: true },
|
|
});
|
|
if (!cargoType) throw new NotFoundException(`Cargo type ${dto.cargoTypeId} not found`);
|
|
}
|
|
Object.assign(cargo, dto);
|
|
return this.cargoRepo.save(cargo);
|
|
}
|
|
|
|
async remove(id: string): Promise<void> {
|
|
const cargo = await this.findById(id);
|
|
await this.cargoRepo.remove(cargo);
|
|
}
|
|
|
|
async loadCargo(id: string, dto: LoadCargoDto): Promise<Cargo> {
|
|
const cargo = await this.cargoRepo.findOne({
|
|
where: { id },
|
|
relations: { container: true }, // ✅ fixed
|
|
});
|
|
if (!cargo) throw new NotFoundException('Cargo not found');
|
|
if (cargo.status !== 'PENDING') {
|
|
throw new ConflictException('Cargo already loaded or delivered');
|
|
}
|
|
|
|
cargo.status = 'LOADED';
|
|
cargo.loadedAt = new Date();
|
|
cargo.quantity = dto.quantity;
|
|
cargo.weight = dto.weight;
|
|
cargo.volume = dto.volume ?? null;
|
|
if (dto.description) cargo.description = dto.description;
|
|
|
|
if (cargo.container) {
|
|
cargo.container.status = 'LOADED';
|
|
await this.containerRepo.save(cargo.container);
|
|
}
|
|
|
|
return this.cargoRepo.save(cargo);
|
|
}
|
|
|
|
async unloadCargo(id: string): Promise<Cargo> {
|
|
const cargo = await this.findById(id);
|
|
if (cargo.status !== 'LOADED') {
|
|
throw new ConflictException('Cargo is not loaded');
|
|
}
|
|
cargo.status = 'UNLOADED';
|
|
cargo.unloadedAt = new Date();
|
|
return this.cargoRepo.save(cargo);
|
|
}
|
|
|
|
async deliverCargo(id: string, dto?: DeliverCargoDto): Promise<Cargo> {
|
|
const cargo = await this.cargoRepo.findOne({
|
|
where: { id },
|
|
relations: { container: true }, // ✅ fixed
|
|
});
|
|
if (!cargo) throw new NotFoundException('Cargo not found');
|
|
if (cargo.status !== 'LOADED') {
|
|
throw new ConflictException('Only loaded cargo can be delivered');
|
|
}
|
|
|
|
cargo.status = 'DELIVERED';
|
|
if (dto?.deliveryRemarks) cargo.description = dto.deliveryRemarks;
|
|
|
|
const remaining =
|
|
cargo.containerId != null
|
|
? await this.cargoRepo.count({
|
|
where: { containerId: cargo.containerId, status: 'LOADED' },
|
|
})
|
|
: 0;
|
|
if (remaining === 0 && cargo.container) {
|
|
cargo.container.status = 'AVAILABLE';
|
|
await this.containerRepo.save(cargo.container);
|
|
}
|
|
|
|
return this.cargoRepo.save(cargo);
|
|
}
|
|
}
|