mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 14:50:57 +00:00
Merge branch 'freight_feature/contrat' of github.com:Tria-plc/edr-platform into freight_feature/contrat
This commit is contained in:
@@ -121,6 +121,8 @@ export class BookingsRepository extends BaseRepository<Booking> {
|
||||
containerTypeId: string;
|
||||
quantity: number;
|
||||
vgmPerUnitTons: number;
|
||||
hazardousQuantity?: number;
|
||||
reeferQuantity?: number;
|
||||
weightResult: ContainerWeightResult;
|
||||
}>,
|
||||
): Promise<BookingContainer[]> {
|
||||
@@ -133,11 +135,16 @@ export class BookingsRepository extends BaseRepository<Booking> {
|
||||
const wagonsPerUnit = ct ? Number(ct.wagonsPerUnit) : 1;
|
||||
const totalVgm = item.quantity * item.vgmPerUnitTons;
|
||||
const wagonsRequired = Math.ceil(item.quantity * wagonsPerUnit);
|
||||
// A per-line breakdown can never exceed the line's own quantity.
|
||||
const clamp = (v?: number) =>
|
||||
Math.max(0, Math.min(item.quantity, Math.floor(Number(v ?? 0)) || 0));
|
||||
|
||||
const row = containerRepo.create({
|
||||
bookingId,
|
||||
containerTypeId: item.containerTypeId,
|
||||
quantity: item.quantity,
|
||||
hazardousQuantity: clamp(item.hazardousQuantity),
|
||||
reeferQuantity: clamp(item.reeferQuantity),
|
||||
vgmPerUnitTons: item.vgmPerUnitTons,
|
||||
totalVgmTons: totalVgm,
|
||||
wagonsRequired,
|
||||
|
||||
@@ -66,6 +66,17 @@ const NEEDS_ACTION_STATUSES = [
|
||||
'APPROVED_PENDING_SIGNATURE',
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Clamp a bulk hazardous/reefer amount into 0..cargoAmount: it can never exceed
|
||||
* the total cargo it's a portion of, and is never negative.
|
||||
*/
|
||||
function clampToCargo(value: number | undefined, cargoAmount: number): number {
|
||||
const v = Number(value ?? 0);
|
||||
if (!Number.isFinite(v) || v <= 0) return 0;
|
||||
const cap = Number.isFinite(cargoAmount) && cargoAmount > 0 ? cargoAmount : 0;
|
||||
return Math.min(v, cap);
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class BookingsService {
|
||||
constructor(
|
||||
@@ -505,6 +516,16 @@ export class BookingsService {
|
||||
// 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,
|
||||
// Bulk-only hazardous/reefer amount, clamped to the cargo amount. Container
|
||||
// freight tracks this per line, so these are 0 for CONTAINER.
|
||||
bulkHazardousQuantity:
|
||||
dto.freightType === 'BULK'
|
||||
? clampToCargo(dto.bulkHazardousQuantity, dto.cargoTotalWeightVgm)
|
||||
: 0,
|
||||
bulkReeferQuantity:
|
||||
dto.freightType === 'BULK'
|
||||
? clampToCargo(dto.bulkReeferQuantity, dto.cargoTotalWeightVgm)
|
||||
: 0,
|
||||
paymentCurrency: dto.paymentCurrency,
|
||||
pnrCode: dto.pnrCode,
|
||||
financialTerms: dto.financialTerms,
|
||||
@@ -527,6 +548,8 @@ export class BookingsService {
|
||||
containerTypeId: c.containerTypeId,
|
||||
quantity: c.quantity,
|
||||
vgmPerUnitTons: c.vgmPerUnitTons,
|
||||
hazardousQuantity: c.hazardousQuantity,
|
||||
reeferQuantity: c.reeferQuantity,
|
||||
weightResult: ruleResult.containerWeightResults[i],
|
||||
})),
|
||||
);
|
||||
@@ -664,6 +687,8 @@ export class BookingsService {
|
||||
containers,
|
||||
);
|
||||
|
||||
const cargoAmount =
|
||||
dto.cargoTotalWeightVgm ?? Number(existing.cargoTotalWeightVgm ?? 0);
|
||||
const updates: Record<string, unknown> = {
|
||||
...dto,
|
||||
freightType,
|
||||
@@ -674,6 +699,22 @@ export class BookingsService {
|
||||
freightType === 'BULK'
|
||||
? (dto.isReefer ?? existing.isReefer ?? false)
|
||||
: false,
|
||||
// Bulk-only hazardous/reefer amount, clamped to the cargo amount; 0 for
|
||||
// container freight (per-line on the containers instead).
|
||||
bulkHazardousQuantity:
|
||||
freightType === 'BULK'
|
||||
? clampToCargo(
|
||||
dto.bulkHazardousQuantity ?? Number(existing.bulkHazardousQuantity ?? 0),
|
||||
cargoAmount,
|
||||
)
|
||||
: 0,
|
||||
bulkReeferQuantity:
|
||||
freightType === 'BULK'
|
||||
? clampToCargo(
|
||||
dto.bulkReeferQuantity ?? Number(existing.bulkReeferQuantity ?? 0),
|
||||
cargoAmount,
|
||||
)
|
||||
: 0,
|
||||
priorityScore: ruleResult.priorityScore,
|
||||
tradeDirection,
|
||||
};
|
||||
@@ -720,6 +761,8 @@ export class BookingsService {
|
||||
containerTypeId: c.containerTypeId,
|
||||
quantity: c.quantity,
|
||||
vgmPerUnitTons: c.vgmPerUnitTons,
|
||||
hazardousQuantity: c.hazardousQuantity,
|
||||
reeferQuantity: c.reeferQuantity,
|
||||
weightResult: ruleResult.containerWeightResults[i],
|
||||
})),
|
||||
);
|
||||
|
||||
@@ -52,6 +52,28 @@ export class CreateBookingContainerDto {
|
||||
@Min(0)
|
||||
@Transform(({ value }) => Number(value))
|
||||
vgmPerUnitTons!: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'How many of this line are hazardous (0..quantity)',
|
||||
minimum: 0,
|
||||
default: 0,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Transform(({ value }) => Number(value ?? 0))
|
||||
hazardousQuantity?: number;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: 'How many of this line are refrigerated (0..quantity)',
|
||||
minimum: 0,
|
||||
default: 0,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
@Transform(({ value }) => Number(value ?? 0))
|
||||
reeferQuantity?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,6 +342,25 @@ export class CreateBookingDto {
|
||||
@Transform(({ value }) => value === 'true' || value === true)
|
||||
isReefer?: boolean;
|
||||
|
||||
/**
|
||||
* Bulk-only: how much of the cargo is hazardous / refrigerated, in the cargo's
|
||||
* unit of measure (tons for PER_TON, item count for PER_ITEM). Must not exceed
|
||||
* cargoTotalWeightVgm. Ignored for container freight (per-line on containers).
|
||||
*/
|
||||
@ApiPropertyOptional({ minimum: 0, default: 0 })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@Transform(({ value }) => Number(value ?? 0))
|
||||
bulkHazardousQuantity?: number;
|
||||
|
||||
@ApiPropertyOptional({ minimum: 0, default: 0 })
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@Min(0)
|
||||
@Transform(({ value }) => Number(value ?? 0))
|
||||
bulkReeferQuantity?: number;
|
||||
|
||||
@ApiProperty({ enum: PAYMENT_CURRENCIES })
|
||||
@IsIn([...PAYMENT_CURRENCIES])
|
||||
paymentCurrency!: string;
|
||||
|
||||
@@ -320,6 +320,19 @@ export class Booking extends BaseEntity {
|
||||
@Column({ name: 'is_reefer', type: 'boolean', default: false })
|
||||
isReefer!: boolean;
|
||||
|
||||
/**
|
||||
* Bulk-only hazardous / reefer amount, in the cargo's own unit of measure
|
||||
* (tons for PER_TON commodities, item count for PER_ITEM) — i.e. how much of
|
||||
* `cargoTotalWeightVgm` is hazardous / refrigerated. 0 when none. Container
|
||||
* freight carries this per line on `booking_container` instead, so these stay
|
||||
* 0 for CONTAINER bookings. The booleans above remain the surcharge trigger.
|
||||
*/
|
||||
@Column({ name: 'bulk_hazardous_quantity', type: 'numeric', precision: 12, scale: 3, default: 0 })
|
||||
bulkHazardousQuantity!: number;
|
||||
|
||||
@Column({ name: 'bulk_reefer_quantity', type: 'numeric', precision: 12, scale: 3, default: 0 })
|
||||
bulkReeferQuantity!: number;
|
||||
|
||||
@Column({ name: 'payment_currency', type: 'varchar', length: 5 })
|
||||
paymentCurrency!: string;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user