mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 09:58:12 +00:00
GRN-<DIR>-<DATE>-<REF8> carried no owner, so a note couldn't be identified by who owns the cargo. Add an owner segment sourced from the booking's company at every generation point (import, export, facility, manual receive), keep REF8 for uniqueness, and label the GRN document row Owner's Name.
153 lines
5.9 KiB
TypeScript
153 lines
5.9 KiB
TypeScript
import { BadRequestException, ConflictException, Injectable, NotFoundException } from '@nestjs/common';
|
|
|
|
import { CargoType } from '../rule-engine/entities/cargo-type.entity';
|
|
import { CreateWarehouseYardDto } from './dto/create-warehouse-yard.dto';
|
|
import { UpdateWarehouseYardDto } from './dto/update-warehouse-yard.dto';
|
|
import { WarehouseYard } from './entities/warehouse-yard.entity';
|
|
import { WarehouseYardsRepository } from './warehouse-yards.repository';
|
|
import { WarehousesService } from './warehouses.service';
|
|
|
|
@Injectable()
|
|
export class WarehouseYardsService {
|
|
constructor(
|
|
private readonly yardsRepository: WarehouseYardsRepository,
|
|
private readonly warehousesService: WarehousesService,
|
|
) {}
|
|
|
|
findAll(): Promise<WarehouseYard[]> {
|
|
return this.yardsRepository.findAll({
|
|
relations: { warehouse: true, zones: true, cargoTypes: true },
|
|
order: { code: 'ASC' },
|
|
});
|
|
}
|
|
|
|
findByWarehouse(warehouseId: string): Promise<WarehouseYard[]> {
|
|
return this.yardsRepository.findAll({
|
|
where: { warehouseId },
|
|
relations: { zones: true, cargoTypes: true },
|
|
order: { code: 'ASC' },
|
|
});
|
|
}
|
|
|
|
async findById(id: string): Promise<WarehouseYard> {
|
|
const yard = await this.yardsRepository.findById(id, {
|
|
relations: { warehouse: true, zones: true, cargoTypes: true },
|
|
});
|
|
|
|
if (!yard) {
|
|
throw new NotFoundException(`Warehouse yard ${id} not found`);
|
|
}
|
|
|
|
return yard;
|
|
}
|
|
|
|
async create(warehouseId: string, dto: CreateWarehouseYardDto): Promise<WarehouseYard> {
|
|
// Ensure the parent warehouse exists.
|
|
await this.warehousesService.findById(warehouseId);
|
|
await this.assertCodeUnique(warehouseId, dto.code.trim());
|
|
await this.assertCapacityWithinWarehouse(warehouseId, dto.capacityWeight ?? null, dto.capacityContainers ?? null);
|
|
|
|
return this.yardsRepository.create({
|
|
warehouseId,
|
|
name: dto.name.trim(),
|
|
code: dto.code.trim(),
|
|
type: dto.type,
|
|
direction: dto.direction ?? null,
|
|
capacityWeight: dto.capacityWeight ?? null,
|
|
capacityContainers: dto.capacityContainers ?? null,
|
|
maxWeight: dto.maxWeight ?? dto.capacityWeight ?? null,
|
|
maxVolume: dto.maxVolume ?? null,
|
|
currentWeight: 0,
|
|
currentContainers: 0,
|
|
currentVolume: 0,
|
|
status: 'ACTIVE',
|
|
isActive: true,
|
|
// Join rows are written by the save (RESTRICT FK rejects unknown ids).
|
|
cargoTypes: (dto.cargoTypeIds ?? []).map((id) => ({ id }) as CargoType),
|
|
});
|
|
}
|
|
|
|
async update(id: string, dto: UpdateWarehouseYardDto): Promise<WarehouseYard> {
|
|
const existing = await this.findById(id);
|
|
|
|
if (dto.code && dto.code.trim() !== existing.code) {
|
|
await this.assertCodeUnique(existing.warehouseId, dto.code.trim(), id);
|
|
}
|
|
|
|
const newCapacityWeight = dto.capacityWeight ?? existing.capacityWeight ?? null;
|
|
const newCapacityContainers = dto.capacityContainers ?? existing.capacityContainers ?? null;
|
|
|
|
// Validate updated capacity doesn't exceed warehouse limits
|
|
if (newCapacityWeight !== (existing.capacityWeight ?? null) || newCapacityContainers !== (existing.capacityContainers ?? null)) {
|
|
await this.assertCapacityWithinWarehouse(existing.warehouseId, newCapacityWeight, newCapacityContainers, id);
|
|
}
|
|
|
|
const status = dto.status ?? existing.status;
|
|
|
|
const updated = await this.yardsRepository.update(id, {
|
|
name: dto.name?.trim() ?? existing.name,
|
|
code: dto.code?.trim() ?? existing.code,
|
|
type: dto.type ?? existing.type,
|
|
direction: dto.direction ?? existing.direction,
|
|
capacityWeight: newCapacityWeight,
|
|
capacityContainers: newCapacityContainers,
|
|
maxWeight: dto.maxWeight ?? existing.maxWeight,
|
|
maxVolume: dto.maxVolume ?? existing.maxVolume,
|
|
status,
|
|
isActive: status === 'ACTIVE',
|
|
...(dto.cargoTypeIds
|
|
? { cargoTypes: dto.cargoTypeIds.map((cargoTypeId) => ({ id: cargoTypeId }) as CargoType) }
|
|
: {}),
|
|
});
|
|
|
|
if (!updated) {
|
|
throw new NotFoundException(`Warehouse yard ${id} not found`);
|
|
}
|
|
|
|
return this.findById(id);
|
|
}
|
|
|
|
private async assertCodeUnique(warehouseId: string, code: string, ignoreId?: string): Promise<void> {
|
|
const [existing] = await this.yardsRepository.findAll({ where: { warehouseId, code } });
|
|
|
|
if (existing && existing.id !== ignoreId) {
|
|
throw new ConflictException(`Yard code ${code} already exists in this warehouse`);
|
|
}
|
|
}
|
|
|
|
private async assertCapacityWithinWarehouse(
|
|
warehouseId: string,
|
|
newCapacityWeight: number | null,
|
|
newCapacityContainers: number | null,
|
|
excludeYardId?: string,
|
|
): Promise<void> {
|
|
const warehouse = await this.warehousesService.findById(warehouseId);
|
|
const yards = await this.findByWarehouse(warehouseId);
|
|
|
|
// Sum existing yard capacities, excluding the yard being updated if provided
|
|
const otherYards = excludeYardId ? yards.filter((y) => y.id !== excludeYardId) : yards;
|
|
const totalExistingWeight = otherYards.reduce((sum, y) => sum + (y.capacityWeight ?? 0), 0);
|
|
const totalExistingContainers = otherYards.reduce((sum, y) => sum + (y.capacityContainers ?? 0), 0);
|
|
|
|
// Check weight capacity
|
|
if (newCapacityWeight !== null && warehouse.capacityWeight != null) {
|
|
const totalWeight = totalExistingWeight + newCapacityWeight;
|
|
if (totalWeight > warehouse.capacityWeight) {
|
|
throw new BadRequestException(
|
|
`Total yard weight capacity (${totalWeight}t) exceeds warehouse limit (${warehouse.capacityWeight}t)`,
|
|
);
|
|
}
|
|
}
|
|
|
|
// Check container capacity
|
|
if (newCapacityContainers !== null && warehouse.capacityContainers != null) {
|
|
const totalContainers = totalExistingContainers + newCapacityContainers;
|
|
if (totalContainers > warehouse.capacityContainers) {
|
|
throw new BadRequestException(
|
|
`Total yard container capacity (${totalContainers}) exceeds warehouse limit (${warehouse.capacityContainers})`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|