File

src/lanes/dto/create-tariff.dto.ts

Description

A lane tariff. Every monetary field must be > 0 so an admin cannot accidentally save a negative rate that would invert revenue.

At least ONE of the rate fields (ratePerTrip / ratePerKm / ratePerTon) must be set — otherwise the tariff has no economic meaning.

effectiveTo, when present, must be strictly after effectiveFrom. The cross-field check is enforced in the service layer because class-validator does not have a clean built-in for date ordering.

Index

Properties

Properties

Optional currency
Type : string
Decorators :
@ApiPropertyOptional({example: 'KES', description: 'ISO-4217 currency code'})
@IsOptional()
@IsString()
@Length(3, 3, {message: 'currency must be a 3-letter ISO 4217 code'})
effectiveFrom
Type : string
Decorators :
@ApiProperty({example: '2026-01-01T00:00:00Z'})
@IsDateString({}, {message: 'effectiveFrom must be a valid ISO date'})
Optional effectiveTo
Type : string
Decorators :
@ApiPropertyOptional({example: '2026-12-31T23:59:59Z'})
@IsOptional()
@IsDateString({}, {message: 'effectiveTo must be a valid ISO date'})
@ValidateIf(o => )
Optional minCharge
Type : number
Decorators :
@ApiPropertyOptional({example: 15000, description: 'Minimum charge regardless of distance/weight'})
@IsOptional()
@Type(undefined)
@IsNumber({maxDecimalPlaces: 4})
@IsPositive({message: 'minCharge must be greater than 0'})
@Max(100000000, {message: 'minCharge is unrealistically large'})
Optional ratePerKm
Type : number
Decorators :
@ApiPropertyOptional({example: 120, description: 'Variable rate per km'})
@IsOptional()
@Type(undefined)
@IsNumber({maxDecimalPlaces: 4})
@IsPositive({message: 'ratePerKm must be greater than 0'})
@Max(1000000, {message: 'ratePerKm is unrealistically large'})
Optional ratePerTon
Type : number
Decorators :
@ApiPropertyOptional({example: 3500, description: 'Variable rate per ton'})
@IsOptional()
@Type(undefined)
@IsNumber({maxDecimalPlaces: 4})
@IsPositive({message: 'ratePerTon must be greater than 0'})
@Max(10000000, {message: 'ratePerTon is unrealistically large'})
Optional ratePerTrip
Type : number
Decorators :
@ApiPropertyOptional({example: 50000, description: 'Flat rate per trip'})
@IsOptional()
@Type(undefined)
@IsNumber({maxDecimalPlaces: 4})
@IsPositive({message: 'ratePerTrip must be greater than 0'})
@Max(100000000, {message: 'ratePerTrip is unrealistically large'})
Optional vehicleType
Type : VehicleType
Decorators :
@ApiPropertyOptional({enum: VehicleType})
@IsOptional()
@IsEnum(VehicleType)
import {
  IsOptional,
  IsNumber,
  IsString,
  IsEnum,
  IsDateString,
  IsPositive,
  Max,
  Length,
  ValidateIf,
} from 'class-validator';
import { Type } from 'class-transformer';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { VehicleType } from '@prisma/client';

/**
 * A lane tariff. Every monetary field must be > 0 so an admin cannot
 * accidentally save a negative rate that would invert revenue.
 *
 * At least ONE of the rate fields (ratePerTrip / ratePerKm / ratePerTon)
 * must be set — otherwise the tariff has no economic meaning.
 *
 * `effectiveTo`, when present, must be strictly after `effectiveFrom`.
 * The cross-field check is enforced in the service layer because
 * class-validator does not have a clean built-in for date ordering.
 */
export class CreateTariffDto {
  @ApiPropertyOptional({ enum: VehicleType })
  @IsOptional()
  @IsEnum(VehicleType)
  vehicleType?: VehicleType;

  @ApiPropertyOptional({ example: 50000, description: 'Flat rate per trip' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 4 })
  @IsPositive({ message: 'ratePerTrip must be greater than 0' })
  @Max(100_000_000, { message: 'ratePerTrip is unrealistically large' })
  ratePerTrip?: number;

  @ApiPropertyOptional({ example: 120, description: 'Variable rate per km' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 4 })
  @IsPositive({ message: 'ratePerKm must be greater than 0' })
  @Max(1_000_000, { message: 'ratePerKm is unrealistically large' })
  ratePerKm?: number;

  @ApiPropertyOptional({ example: 3500, description: 'Variable rate per ton' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 4 })
  @IsPositive({ message: 'ratePerTon must be greater than 0' })
  @Max(10_000_000, { message: 'ratePerTon is unrealistically large' })
  ratePerTon?: number;

  @ApiPropertyOptional({ example: 15000, description: 'Minimum charge regardless of distance/weight' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 4 })
  @IsPositive({ message: 'minCharge must be greater than 0' })
  @Max(100_000_000, { message: 'minCharge is unrealistically large' })
  minCharge?: number;

  @ApiPropertyOptional({ example: 'KES', description: 'ISO-4217 currency code' })
  @IsOptional()
  @IsString()
  @Length(3, 3, { message: 'currency must be a 3-letter ISO 4217 code' })
  currency?: string;

  @ApiProperty({ example: '2026-01-01T00:00:00Z' })
  @IsDateString({}, { message: 'effectiveFrom must be a valid ISO date' })
  effectiveFrom: string;

  @ApiPropertyOptional({ example: '2026-12-31T23:59:59Z' })
  @IsOptional()
  @IsDateString({}, { message: 'effectiveTo must be a valid ISO date' })
  // Class-validator can't compare two dates declaratively without a custom
  // decorator; the service layer does the strict ordering check.
  @ValidateIf((o) => !!o.effectiveTo)
  effectiveTo?: string;
}

results matching ""

    No results matching ""