feat: add hazardous goods declaration feature

- Introduced HazardDeclarationPanel component to display dangerous goods declaration details.
- Updated URL constants to include CLEARANCE_PROCEED endpoint for re-requesting operations.
- Enhanced permissions to include hazardous approval roles for contract approvals.
- Integrated HazardDeclarationPanel into ContractRequestDetailPage and ContractClearanceDetailPage.
- Added proceedToOperation method in bookings service for handling operation re-requests.
- Updated contract forms and schemas to include hazard class and UN number fields.
- Implemented validation for hazardous contracts in the contract creation flow.
- Added expiry notice functionality for contracts nearing validity end.
- Created tests for expiry notice calculations and labels.
- Updated UI components to reflect hazardous cargo information and validation errors.
This commit is contained in:
Marshal
2026-07-25 21:10:09 +00:00
parent fde5e6de4b
commit 9a1c8e5603
41 changed files with 1663 additions and 99 deletions

View File

@@ -1,4 +1,5 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { hazardClassLabel } from '@edr/types';
import { ContractsRepository } from '../modules/contracts/contracts.repository';
import {
@@ -143,6 +144,11 @@ export class ContractDocumentViewModelBuilder {
const hasCustomer = signatures.some((s) => s.role === 'CUSTOMER');
const hasStaff = signatures.some((s) => s.role === 'STAFF');
// Signed before company stamps were required — the customer has to sign
// again to attach one, otherwise EDR can never counter-sign the contract.
const customerStampMissing = signatures.some(
(s) => s.role === 'CUSTOMER' && !s.stampImageUrl,
);
const hasContractFile = Boolean(
contract.files?.some((f) => f.code === 'contract'),
);
@@ -185,7 +191,9 @@ export class ContractDocumentViewModelBuilder {
// Cast: contract signers (CUSTOMER|STAFF|DIRECTOR|CEO) widen the booking
// view-model's narrower CUSTOMER|STAFF role union.
signatures: signatures as unknown as ContractViewModel['signatures'],
canSignCustomer: contract.status === 'CONTRACT_READY' && !hasCustomer,
canSignCustomer:
(contract.status === 'CONTRACT_READY' && !hasCustomer) ||
(contract.status === 'SIGNED_CUSTOMER' && customerStampMissing),
canSignStaff:
contract.status === 'SIGNED_CUSTOMER' && hasCustomer && !hasStaff,
hasContractDocument: hasContractFile,
@@ -295,7 +303,16 @@ export class ContractDocumentViewModelBuilder {
cargoDescription: this.valueOrDash(cargoName),
totalWeightVgm: '—',
equipmentReturn: this.valueOrDash(contract.equipmentReturn),
hazardousLabel: contract.isHazardous ? 'Yes' : 'No',
// A hazardous contract names the declared class + UN number on the
// schedule — the flag alone is not a dangerous-goods declaration.
hazardousLabel: contract.isHazardous
? [
hazardClassLabel(contract.hazardClass) ?? 'Yes',
contract.unNumber ? `UN ${contract.unNumber}` : null,
]
.filter(Boolean)
.join(' · ')
: 'No',
firstMilePickupAddress: this.valueOrDash(contract.firstMilePickupAddress),
lastMileDeliveryAddress: this.valueOrDash(contract.lastMileDeliveryAddress),
};