fix issue

This commit is contained in:
Marshal
2026-07-16 00:33:31 +00:00
parent 234a74e812
commit 41fe04652f
51 changed files with 1895 additions and 206 deletions

View File

@@ -1,6 +1,6 @@
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
import { Injectable, NotFoundException, ConflictException, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { FindOptionsOrder, FindOptionsWhere, ILike, Repository } from 'typeorm';
import { FindOptionsOrder, FindOptionsWhere, ILike, Not, Repository } from 'typeorm';
import { CreateCargoDto } from './dto/create-cargo.dto';
import { UpdateCargoDto } from './dto/update-cargo.dto';
import { LoadCargoDto } from './dto/load-cargo.dto';
@@ -32,6 +32,7 @@ export class CargoesService {
if (!container) {
throw new NotFoundException(`Container ${dto.containerId} not found`);
}
await this.assertContainerCapacity(container, dto.weight);
if (dto.cargoTypeId) {
const cargoType = await this.cargoTypeRepo.findOne({
@@ -121,6 +122,10 @@ export class CargoesService {
throw new ConflictException('Cargo already loaded or delivered');
}
if (cargo.container) {
await this.assertContainerCapacity(cargo.container, dto.weight, cargo.id);
}
cargo.status = 'LOADED';
cargo.loadedAt = new Date();
cargo.quantity = dto.quantity;
@@ -137,13 +142,31 @@ export class CargoesService {
}
async unloadCargo(id: string): Promise<Cargo> {
const cargo = await this.findById(id);
const cargo = await this.cargoRepo.findOne({
where: { id },
relations: { container: true },
});
if (!cargo) throw new NotFoundException('Cargo not found');
if (cargo.status !== 'LOADED') {
throw new ConflictException('Cargo is not loaded');
}
cargo.status = 'UNLOADED';
cargo.unloadedAt = new Date();
return this.cargoRepo.save(cargo);
const saved = await this.cargoRepo.save(cargo);
// loadCargo flips the container to LOADED; on unload, free it back to
// AVAILABLE once no other LOADED cargo still references the container.
if (cargo.containerId != null && cargo.container) {
const remaining = await this.cargoRepo.count({
where: { containerId: cargo.containerId, status: 'LOADED', id: Not(cargo.id) },
});
if (remaining === 0) {
cargo.container.status = 'AVAILABLE';
await this.containerRepo.save(cargo.container);
}
}
return saved;
}
async deliverCargo(id: string, dto?: DeliverCargoDto): Promise<Cargo> {
@@ -161,10 +184,13 @@ export class CargoesService {
if (dto?.receiverName) cargo.receiverName = dto.receiverName;
if (dto?.deliveryRemarks) cargo.deliveryRemarks = dto.deliveryRemarks;
// Exclude the cargo being delivered — it is still LOADED in the DB until the
// save below, so counting it would keep `remaining` > 0 and never free the
// container.
const remaining =
cargo.containerId != null
? await this.cargoRepo.count({
where: { containerId: cargo.containerId, status: 'LOADED' },
where: { containerId: cargo.containerId, status: 'LOADED', id: Not(cargo.id) },
})
: 0;
if (remaining === 0 && cargo.container) {
@@ -174,4 +200,34 @@ export class CargoesService {
return this.cargoRepo.save(cargo);
}
/**
* Reject when placing `newWeightKg` on the container would exceed its max gross
* weight. All values are kilograms: cargoes.weight is kg (entity), and the
* container's tare_weight / max_gross_weight are kg (entity). Capacity check is
* tare + already-LOADED cargo + new cargo <= max gross weight.
*/
private async assertContainerCapacity(
container: Container,
newWeightKg: number,
excludeCargoId?: string,
): Promise<void> {
const qb = this.cargoRepo
.createQueryBuilder('c')
.select('COALESCE(SUM(c.weight), 0)', 'sum')
.where('c.containerId = :containerId', { containerId: container.id })
.andWhere('c.status = :status', { status: 'LOADED' });
if (excludeCargoId) qb.andWhere('c.id != :excludeCargoId', { excludeCargoId });
const raw = await qb.getRawOne<{ sum: string }>();
const loadedKg = Number(raw?.sum ?? 0);
const tareKg = Number(container.tareWeight);
const maxGrossKg = Number(container.maxGrossWeight);
if (tareKg + loadedKg + newWeightKg > maxGrossKg) {
throw new BadRequestException(
`Cargo weight exceeds container capacity: tare ${tareKg}kg + loaded ${loadedKg}kg + ` +
`new ${newWeightKg}kg > max gross ${maxGrossKg}kg`,
);
}
}
}