Merge branch 'dev' into freight/nati-2

# Conflicts:
#	apps/edr-freight-api/src/app.module.ts
#	apps/edr-freight-api/src/seed/freight-permissions.registry.ts
#	apps/edr-freight-web/backoffice/src/components/layout/sidebar-sections.tsx
#	apps/edr-freight-web/backoffice/src/constants/URLS.ts
#	apps/edr-freight-web/backoffice/src/lib/permissions.ts
This commit is contained in:
Nathnael
2026-08-20 11:29:21 +00:00
287 changed files with 25453 additions and 2339 deletions

View File

@@ -0,0 +1,16 @@
import { ApiProperty } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsNumber, IsPositive, IsString, Length } from 'class-validator';
export class BillClearanceChargeDto {
@ApiProperty({ example: 12500.5 })
@Type(() => Number)
@IsNumber()
@IsPositive()
amount!: number;
@ApiProperty({ example: 'ETB' })
@IsString()
@Length(3, 8)
currency!: string;
}

View File

@@ -125,3 +125,59 @@ export class OperationReviewDto {
@IsString()
note?: string;
}
/**
* A staff decision applied to BOTH halves of a consolidated pair. The two
* bookings share a wagon, so they advance or cancel together — never one alone.
*/
export class PairedDecisionDto {
@ApiProperty({
enum: ["accept", "cancel", "operationAccept", "requestChanges"],
description: 'Which staff decision to apply to both bookings.',
})
@IsIn(["accept", "cancel", "operationAccept", "requestChanges"])
decision!: "accept" | "cancel" | "operationAccept" | "requestChanges";
@ApiPropertyOptional({ description: "Cancellation reason (decision=cancel)." })
@IsOptional()
@IsString()
reason?: string;
@ApiPropertyOptional({
description: "Message to the customer (decision=requestChanges).",
})
@IsOptional()
@IsString()
note?: string;
@ApiPropertyOptional({
description: "Contract validity window in days (decision=accept).",
})
@IsOptional()
@IsInt()
@Min(1)
validityDays?: number;
}
/** Approve a shared-wagon pairing. The note is optional context for the audit. */
export class ApproveConsolidationDto {
@ApiPropertyOptional({
description: "Optional note recorded with the approval.",
maxLength: 500,
})
@IsOptional()
@IsString()
note?: string;
}
/** Reject a shared-wagon pairing. A reason is mandatory — GL has to act on it. */
export class RejectConsolidationDto {
@ApiProperty({
description:
"Why the pairing is rejected. Sent back to GL on both bookings.",
maxLength: 500,
})
@IsString()
@MinLength(1)
reason!: string;
}