mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 12:30:58 +00:00
feat: enhance cargo details handling and add document clearance features
- Improved handling of commodity selection in the cargo details form to prevent unwanted resets on re-renders. - Added `isReefer` flag to the CreateBookingDto interface for booking-level refrigerated status. - Introduced a new configuration file for clearance tabs to manage document clearance views. - Implemented DocumentClearanceDetailPage for detailed review and management of clearance documents. - Created DocumentClearanceListPage for listing and filtering clearance bookings with enhanced UI components.
This commit is contained in:
@@ -276,6 +276,12 @@ export class BookingPricingService {
|
||||
allowConsolidation,
|
||||
shippingLineId: booking.shippingLineId,
|
||||
totalWagons,
|
||||
// Bulk tonnage scales PER_TON surcharges (e.g. the bulk reefer surcharge).
|
||||
// Container freight carries 0 here — its surcharges scale by container count.
|
||||
bulkTons:
|
||||
booking.freightType === 'BULK'
|
||||
? Number(booking.cargoTotalWeightVgm ?? 0)
|
||||
: 0,
|
||||
containers,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -126,8 +126,10 @@ export class BookingsService {
|
||||
paymentCurrency: string;
|
||||
tradeDirection: string;
|
||||
isHazardous?: boolean;
|
||||
isReefer?: boolean;
|
||||
isGovernment?: boolean;
|
||||
shippingLineId?: string | null;
|
||||
bulkTons?: number;
|
||||
containers: CreateBookingContainerDto[];
|
||||
}): Promise<BookingEvaluationInput> {
|
||||
const containerLines =
|
||||
@@ -166,10 +168,14 @@ export class BookingsService {
|
||||
paymentCurrency: dto.paymentCurrency,
|
||||
tradeDirection: dto.tradeDirection,
|
||||
isHazardous: dto.isHazardous ?? false,
|
||||
// Bulk reefer comes from the customer toggle; container reefer is derived
|
||||
// from the container type and ORed in by the engine.
|
||||
isReefer: dto.freightType === 'BULK' ? (dto.isReefer ?? false) : false,
|
||||
isGovernment: dto.isGovernment ?? false,
|
||||
allowConsolidation,
|
||||
shippingLineId: dto.shippingLineId,
|
||||
totalWagons,
|
||||
bulkTons: dto.freightType === 'BULK' ? Number(dto.bulkTons ?? 0) : 0,
|
||||
containers,
|
||||
};
|
||||
}
|
||||
@@ -387,8 +393,10 @@ export class BookingsService {
|
||||
paymentCurrency: dto.paymentCurrency,
|
||||
tradeDirection,
|
||||
isHazardous: dto.isHazardous,
|
||||
isReefer: dto.isReefer,
|
||||
isGovernment,
|
||||
shippingLineId: dto.shippingLineId,
|
||||
bulkTons: dto.cargoTotalWeightVgm,
|
||||
containers,
|
||||
});
|
||||
const ruleResult = await this.ruleEngineService.evaluate(evalInput);
|
||||
@@ -425,6 +433,10 @@ export class BookingsService {
|
||||
shippingLineId: dto.shippingLineId,
|
||||
cargoTotalWeightVgm: dto.cargoTotalWeightVgm,
|
||||
isHazardous: dto.isHazardous ?? false,
|
||||
// Bulk reefer is the customer's toggle; container reefer is derived from
|
||||
// the container type at pricing time, so the booking-level flag stays off
|
||||
// for container freight to avoid double-counting.
|
||||
isReefer: dto.freightType === 'BULK' ? (dto.isReefer ?? false) : false,
|
||||
paymentCurrency: dto.paymentCurrency,
|
||||
pnrCode: dto.pnrCode,
|
||||
financialTerms: dto.financialTerms,
|
||||
@@ -586,7 +598,9 @@ export class BookingsService {
|
||||
paymentCurrency: dto.paymentCurrency ?? existing.paymentCurrency,
|
||||
tradeDirection,
|
||||
isHazardous: dto.isHazardous ?? existing.isHazardous,
|
||||
isReefer: dto.isReefer ?? existing.isReefer,
|
||||
shippingLineId: dto.shippingLineId ?? existing.shippingLineId ?? undefined,
|
||||
bulkTons: dto.cargoTotalWeightVgm ?? Number(existing.cargoTotalWeightVgm ?? 0),
|
||||
containers,
|
||||
});
|
||||
|
||||
@@ -606,6 +620,12 @@ export class BookingsService {
|
||||
...dto,
|
||||
freightType,
|
||||
cargoTypeId: freightType === 'BULK' ? cargoTypeId : null,
|
||||
// Booking-level reefer is only meaningful for bulk; container reefer is
|
||||
// derived from the container type at pricing time.
|
||||
isReefer:
|
||||
freightType === 'BULK'
|
||||
? (dto.isReefer ?? existing.isReefer ?? false)
|
||||
: false,
|
||||
priorityScore: ruleResult.priorityScore,
|
||||
tradeDirection,
|
||||
};
|
||||
|
||||
@@ -306,6 +306,17 @@ export class CreateBookingDto {
|
||||
@Transform(({ value }) => value === 'true' || value === true)
|
||||
isHazardous?: boolean;
|
||||
|
||||
/**
|
||||
* Booking-level refrigerated flag. For bulk freight this is the customer's
|
||||
* reefer choice (containers derive reefer from the container type instead).
|
||||
* ORed with per-container reefer when the REEFER surcharge is evaluated.
|
||||
*/
|
||||
@ApiPropertyOptional({ default: false })
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Transform(({ value }) => value === 'true' || value === true)
|
||||
isReefer?: boolean;
|
||||
|
||||
@ApiProperty({ enum: PAYMENT_CURRENCIES })
|
||||
@IsIn([...PAYMENT_CURRENCIES])
|
||||
paymentCurrency!: string;
|
||||
|
||||
@@ -57,6 +57,12 @@ export interface BookingEvaluationInput {
|
||||
allowConsolidation?: boolean;
|
||||
shippingLineId?: string | null;
|
||||
totalWagons: number;
|
||||
/**
|
||||
* Total bulk tonnage on the booking (cargoTotalWeightVgm). Used to scale
|
||||
* PER_TON surcharges (e.g. the bulk reefer surcharge). 0/undefined for
|
||||
* container freight, which is scaled by container count instead.
|
||||
*/
|
||||
bulkTons?: number;
|
||||
containers: BookingContainerEvalInput[];
|
||||
}
|
||||
|
||||
@@ -224,16 +230,46 @@ export class RuleEngineService {
|
||||
});
|
||||
if (!triggered) continue;
|
||||
|
||||
let triggerValue: number | null = null;
|
||||
let calculatedAmount = Number(rate.rateValue);
|
||||
// Surcharges scale by their own rateUnit, so the same trigger can bill the
|
||||
// right way per freight shape — e.g. a PER_TON reefer rate multiplies the
|
||||
// bulk tonnage, while a PER_CONTAINER reefer rate multiplies the container
|
||||
// count. triggerValue records the quantity billed (shown on the breakdown).
|
||||
const rateValue = Number(rate.rateValue);
|
||||
const containerCount = input.containers.reduce(
|
||||
(sum, c) => sum + Number(c.quantity || 0),
|
||||
0,
|
||||
);
|
||||
const overweightExcessTons = containerWeightResults.reduce(
|
||||
(sum, r) => sum + (r.overweightExcessTons ?? 0),
|
||||
0,
|
||||
);
|
||||
|
||||
// Per-ton surcharges (typically OVERWEIGHT) bill against the excess tons.
|
||||
if (rate.rateUnit === 'PER_TON' && rate.trigger === 'OVERWEIGHT') {
|
||||
triggerValue = containerWeightResults.reduce(
|
||||
(sum, r) => sum + (r.overweightExcessTons ?? 0),
|
||||
0,
|
||||
);
|
||||
calculatedAmount = triggerValue * Number(rate.rateValue);
|
||||
let triggerValue: number | null = null;
|
||||
let calculatedAmount: number;
|
||||
|
||||
switch (rate.rateUnit) {
|
||||
case 'PER_TON':
|
||||
// OVERWEIGHT bills the excess tons; every other PER_TON surcharge
|
||||
// (e.g. bulk reefer) bills the full bulk tonnage.
|
||||
triggerValue =
|
||||
rate.trigger === 'OVERWEIGHT'
|
||||
? overweightExcessTons
|
||||
: Number(input.bulkTons ?? 0);
|
||||
calculatedAmount = triggerValue * rateValue;
|
||||
break;
|
||||
case 'PER_CONTAINER':
|
||||
triggerValue = containerCount;
|
||||
calculatedAmount = triggerValue * rateValue;
|
||||
break;
|
||||
case 'PER_WAGON':
|
||||
triggerValue = input.totalWagons;
|
||||
calculatedAmount = triggerValue * rateValue;
|
||||
break;
|
||||
case 'FLAT':
|
||||
default:
|
||||
// FLAT (and any unknown unit) bills once.
|
||||
calculatedAmount = rateValue;
|
||||
break;
|
||||
}
|
||||
|
||||
// Safety guard: never include a surcharge with a non-positive amount (a
|
||||
|
||||
Reference in New Issue
Block a user