Merge pull request #43 from Tria-plc/freight_feature/booking/minio

allow customers booking
This commit is contained in:
yaschalew10
2026-05-28 10:37:24 +03:00
committed by GitHub
5 changed files with 56 additions and 16 deletions

View File

@@ -9,6 +9,7 @@ import {
Patch,
Post,
Query,
Request,
UploadedFiles,
UseInterceptors,
} from "@nestjs/common";
@@ -52,6 +53,7 @@ export class BookingsController {
create(
@Body() dto: CreateBookingDto,
@UploadedFiles() files: Express.Multer.File[],
@Request() req: any,
) {
console.log(
"[BookingsController] Files received:",
@@ -63,7 +65,8 @@ export class BookingsController {
mimetype: f.mimetype,
})),
);
return this.bookingsService.create(dto, files ?? []);
const userId: string | undefined = req.user?.id ?? req.user?.sub;
return this.bookingsService.create(dto, files ?? [], userId);
}
// ── 2. Update draft booking (multipart/form-data) ─────────────────────

View File

@@ -1,6 +1,7 @@
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { CustomersModule } from "../customers/customers.module";
import { FilesModule } from "../files/files.module";
import { MinioModule } from "../minio/minio.module";
import { BookingsController } from "./bookings.controller";
@@ -9,7 +10,7 @@ import { BookingsService } from "./bookings.service";
import { Booking } from "./entities/booking.entity";
@Module({
imports: [TypeOrmModule.forFeature([Booking]), FilesModule, MinioModule],
imports: [TypeOrmModule.forFeature([Booking]), FilesModule, MinioModule, CustomersModule],
controllers: [BookingsController],
providers: [BookingsService, BookingsRepository],
exports: [BookingsService],

View File

@@ -20,6 +20,18 @@ export class BookingsRepository extends BaseRepository<Booking> {
return this.repository.findOne({ where: { reference } });
}
/** Count bookings created in a specific year. */
async countByYear(year: number): Promise<number> {
const startDate = new Date(year, 0, 1);
const endDate = new Date(year + 1, 0, 1);
return this.repository
.createQueryBuilder("booking")
.where("booking.created_at >= :startDate", { startDate })
.andWhere("booking.created_at < :endDate", { endDate })
.getCount();
}
/** Find a booking by reference with associated files (polymorphic join). */
async findByReferenceWithFiles(reference: string): Promise<Booking | null> {
const booking = await this.repository

View File

@@ -6,6 +6,7 @@ import {
} from "@nestjs/common";
import { IsNull, Not } from "typeorm";
import { CustomersService } from "../customers/customers.service";
import { FilesService } from "../files/files.service";
import { MinioService } from "../minio/minio.service";
import { BookingsRepository } from "./bookings.repository";
@@ -32,10 +33,23 @@ export class BookingsService {
private readonly bookingsRepository: BookingsRepository,
private readonly filesService: FilesService,
private readonly minioService: MinioService,
private readonly customersService: CustomersService,
) {}
// ── helpers ──────────────────────────────────────────────────────────
/** Generate a unique booking reference number. */
private async generateReference(): Promise<string> {
const year = new Date().getFullYear();
const prefix = `BK-${year}`;
// Get the count of bookings created this year
const count = await this.bookingsRepository.countByYear(year);
const sequenceNumber = String(count + 1).padStart(6, '0');
return `${prefix}-${sequenceNumber}`;
}
/** Resolve auto-consolidation flag. */
private resolveConsolidation(
containers: Array<{ type: string; qty: number }> | undefined | null,
@@ -107,9 +121,23 @@ export class BookingsService {
async create(
dto: CreateBookingDto,
files: Express.Multer.File[],
userId?: string,
): Promise<{ booking: Booking; warnings: string[] }> {
const warnings: string[] = [];
// Resolve customerId: use provided value (admin) or look up by IAM userId
let customerId = dto.customerId;
if (!customerId) {
if (!userId) {
throw new BadRequestException('customerId is required or must be resolvable from auth token');
}
const customer = await this.customersService.findByUserId(userId);
customerId = customer.id;
}
// Generate reference if not provided
const reference = dto.reference || await this.generateReference();
const allowConsolidation = this.resolveConsolidation(
dto.containers,
dto.allowConsolidation,
@@ -131,6 +159,10 @@ export class BookingsService {
const booking = await this.bookingsRepository.create({
...dto,
reference,
customerId,
totalAmount: 0,
paymentStatus: "PENDING",
scheduledDate: new Date(dto.scheduledDate),
startDate: dto.startDate ? new Date(dto.startDate) : undefined,
endDate: dto.endDate ? new Date(dto.endDate) : undefined,

View File

@@ -69,14 +69,16 @@ export class ContainerItem {
export class CreateBookingDto {
// ── core ─────────────────────────────────────────────────────────────
@ApiProperty({ description: "Unique booking reference" })
@ApiPropertyOptional({ description: "Unique booking reference (auto-generated if not provided)" })
@IsOptional()
@IsString()
@Transform(({ value }) => (typeof value === "string" ? value.trim() : value))
reference!: string;
reference?: string;
@ApiProperty({ format: "uuid" })
@ApiPropertyOptional({ format: "uuid", description: "Admin only: target customer. Omit to resolve from auth token." })
@IsOptional()
@IsUUID()
customerId!: string;
customerId?: string;
@ApiPropertyOptional({ format: "uuid" })
@IsOptional()
@@ -87,16 +89,6 @@ export class CreateBookingDto {
@IsDateString()
scheduledDate!: string;
@ApiProperty({ minimum: 0 })
@IsNumber()
@Min(0)
@Transform(({ value }) => Number(value))
totalAmount!: number;
@ApiPropertyOptional({ enum: PAYMENT_STATUSES, default: "PENDING" })
@IsOptional()
@IsIn([...PAYMENT_STATUSES])
paymentStatus?: string;
// ── contract ─────────────────────────────────────────────────────────
@ApiProperty({ enum: CONTRACT_TYPES })