mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Merge pull request #43 from Tria-plc/freight_feature/booking/minio
allow customers booking
This commit is contained in:
@@ -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) ─────────────────────
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 })
|
||||
|
||||
Reference in New Issue
Block a user