mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 11:55:42 +00:00
add booking request functionality for GENERAL customs contracts
- Create migration for booking_requests table with necessary fields and indexes. - Implement BookingRequestRepository for database operations related to booking requests. - Develop BookingRequestService to handle business logic for submitting, accepting, rejecting, and canceling booking requests. - Create DTOs for creating booking requests and reviewing them. - Define BookingRequest entity to map to the booking_requests table. - Add UI components for managing shipment requests, including detail and list pages. - Implement OperationDatePicker component for selecting available shipment days.
This commit is contained in:
@@ -16,6 +16,7 @@ import { FileRecord } from '../files/entities/file.entity';
|
||||
import { assertCanApproveBookingStep } from '../../common/freight-permission.util';
|
||||
import { ApprovalRulesService } from '../rule-engine/services/approval-rules.service';
|
||||
import { CargoTypesService } from '../rule-engine/services/cargo-types.service';
|
||||
import { DropdownSettingsService } from '../dropdown-settings/dropdown-settings.service';
|
||||
import { FilesService } from '../files/files.service';
|
||||
import { SignaturesService } from '../signatures/signatures.service';
|
||||
import { ContractPricingService } from './contract-pricing.service';
|
||||
@@ -27,6 +28,14 @@ import { Contract } from './entities/contract.entity';
|
||||
import { ContractSignerRole } from './entities/contract-signature.entity';
|
||||
import { SignContractDto } from './dto/sign-contract.dto';
|
||||
|
||||
/**
|
||||
* Dropdown-settings code holding the admin-configured contract validity options
|
||||
* (each option's `value` is a day count). The staff accept dialog reads the same
|
||||
* code, so accept can only use a configured duration. See the seed migration
|
||||
* `SeedContractValidityPeriods`.
|
||||
*/
|
||||
const CONTRACT_VALIDITY_PERIODS_CODE = 'contract_validity_periods';
|
||||
|
||||
/** Status-machine guard mirroring booking-status.util. */
|
||||
function assertContractStatus(contract: Contract, allowed: string[]): void {
|
||||
if (!allowed.includes(contract.status)) {
|
||||
@@ -46,6 +55,7 @@ export class ContractTransitionService {
|
||||
private readonly pricingService: ContractPricingService,
|
||||
private readonly approvalRulesService: ApprovalRulesService,
|
||||
private readonly cargoTypesService: CargoTypesService,
|
||||
private readonly dropdownSettingsService: DropdownSettingsService,
|
||||
private readonly filesService: FilesService,
|
||||
private readonly signaturesService: SignaturesService,
|
||||
private readonly milestoneService: ClearanceMilestoneService,
|
||||
@@ -101,6 +111,8 @@ export class ContractTransitionService {
|
||||
);
|
||||
}
|
||||
|
||||
await this.assertValidityDaysConfigured(validityDays);
|
||||
|
||||
const validFrom = new Date();
|
||||
const validUntil = new Date(validFrom);
|
||||
validUntil.setDate(validUntil.getDate() + validityDays);
|
||||
@@ -118,6 +130,37 @@ export class ContractTransitionService {
|
||||
return this.contractsService.findById(contractId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the chosen validity (days) is one of the admin-configured options in
|
||||
* the `contract_validity_periods` dropdown setting. If the setting is missing
|
||||
* or has no options yet, fall back to the DTO range check (already applied) so
|
||||
* acceptance is never hard-blocked before an admin configures the list.
|
||||
*/
|
||||
private async assertValidityDaysConfigured(validityDays: number): Promise<void> {
|
||||
let setting;
|
||||
try {
|
||||
setting = await this.dropdownSettingsService.getByCode(
|
||||
CONTRACT_VALIDITY_PERIODS_CODE,
|
||||
);
|
||||
} catch {
|
||||
// Not configured yet — keep the flow working with the DTO range only.
|
||||
return;
|
||||
}
|
||||
|
||||
const allowed = (setting.children ?? [])
|
||||
.map((o) => Number(o.value))
|
||||
.filter((n) => Number.isFinite(n));
|
||||
if (allowed.length === 0) return;
|
||||
|
||||
if (!allowed.includes(validityDays)) {
|
||||
throw new BadRequestException(
|
||||
`Validity ${validityDays} days is not a configured option. Allowed: ${allowed
|
||||
.sort((a, b) => a - b)
|
||||
.join(', ')} days.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build contract approval steps from the system approval_rules chain (US-06:
|
||||
* container → line staff + director; bulk → directors + CEO). Mirrors the
|
||||
@@ -520,7 +563,16 @@ export class ContractTransitionService {
|
||||
contract.customsClearingEnabled ?? false,
|
||||
);
|
||||
|
||||
if (clearanceCode) {
|
||||
// GENERAL + customs (Path B) runs clearance PER BOOKING, not at the contract
|
||||
// level: there is no contract clearance cycle. The contract just becomes
|
||||
// active; the customer then files shipment requests and GL books + clears
|
||||
// each one. ONE_TIME customs and Path A self-clearance keep the contract
|
||||
// cycle below.
|
||||
const isGeneralCustoms =
|
||||
contract.contractKind === 'GENERAL' &&
|
||||
Boolean(contract.customsClearingEnabled);
|
||||
|
||||
if (clearanceCode && !isGeneralCustoms) {
|
||||
// Open a clearance cycle, seed the pre-booking milestones, and route the
|
||||
// customer to upload. Path A is ops-reviewed; Path B is GL-reviewed — the
|
||||
// distinction is enforced at the review/finalize endpoints, not here.
|
||||
@@ -531,7 +583,8 @@ export class ContractTransitionService {
|
||||
updates.clearanceStatus = 'AWAITING_DOCUMENTS';
|
||||
updates.clearanceCycleNumber = cycleNumber;
|
||||
} else {
|
||||
// No clearance gate (DOMESTIC) — ready for the customer to book directly.
|
||||
// No contract-level clearance gate — DOMESTIC, or GENERAL+customs (which
|
||||
// clears per booking). Ready for shipment requests / direct booking.
|
||||
updates.status =
|
||||
contract.contractKind === 'GENERAL' ? 'CONTRACT_ACTIVE' : 'FULLY_EXECUTED';
|
||||
updates.clearanceStatus = 'NOT_APPLICABLE';
|
||||
|
||||
Reference in New Issue
Block a user