add quantity cap for GENERAL contracts and implement capacity tracking

- Updated ContractClearanceService and ContractsController to remove region parameter from queue method.
- Enhanced ContractsRepository to attach contract files for download and added attachContractFiles method.
- Modified ContractsService to persist cargo scope with quantity cap based on contract kind.
- Introduced quantityCap field in CreateContractCargoScopeDto and ContractCargoScope entity.
- Implemented capacity tracking in the frontend with ContractCapacityNotice component to display remaining bookable quantities.
- Updated various components and services to support new capacity features, including hooks and API calls.
- Added migration to include quantity_cap column in contract_cargo_scope table.
This commit is contained in:
Marshal
2026-06-28 17:32:18 +00:00
parent e1d54746c2
commit 11c7f1bb74
27 changed files with 512 additions and 74 deletions

View File

@@ -210,7 +210,7 @@ export class ContractsService {
} as never);
await this.persistRoutes(contract.id, dto.routes);
await this.persistCargoScope(contract.id, dto.cargoScope);
await this.persistCargoScope(contract.id, dto.cargoScope, contract.contractKind);
if (files.length > 0) {
try {
@@ -244,8 +244,12 @@ export class ContractsService {
private async persistCargoScope(
contractId: string,
cargoScope: CreateContractDto['cargoScope'],
contractKind: string,
): Promise<void> {
const repo = this.dataSource.getRepository(ContractCargoScope);
// A quantity cap only governs GENERAL contracts (multi-shipment draw-down).
// ONE_TIME allows a single booking, so any cap on it is meaningless → null.
const isGeneral = contractKind === 'GENERAL';
await repo.save(
cargoScope.map((c) =>
repo.create({
@@ -253,6 +257,7 @@ export class ContractsService {
containerSize: c.containerSize ?? null,
cargoTypeId: c.cargoTypeId ?? null,
cargoFreeText: c.cargoFreeText ?? null,
quantityCap: isGeneral ? (c.quantityCap ?? null) : null,
}),
),
);
@@ -318,7 +323,7 @@ export class ContractsService {
}
if (dto.cargoScope) {
await this.dataSource.getRepository(ContractCargoScope).delete({ contractId: id });
await this.persistCargoScope(id, dto.cargoScope);
await this.persistCargoScope(id, dto.cargoScope, existing.contractKind);
}
if (files.length > 0) {