dynamic contract templates and companyId on bookings

This commit is contained in:
marshal
2026-06-04 16:53:14 +03:00
parent 324935a334
commit 8fea963ddb
26 changed files with 724 additions and 181 deletions

View File

@@ -1,7 +1,8 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CustomersModule } from '../customers/customers.module';
// import { CustomersModule } from '../customers/customers.module';
import { CompaniesModule } from '../companies/companies.module';
import { FilesModule } from '../files/files.module';
import { MinioModule } from '../minio/minio.module';
import { RuleEngineModule } from '../rule-engine/rule-engine.module';
@@ -41,7 +42,8 @@ import { ContractViewModelBuilder } from '../../contracts/contract-view-model.bu
]),
FilesModule,
MinioModule,
CustomersModule,
CompaniesModule,
// CustomersModule,
RuleEngineModule,
],
controllers: [BookingsController, PaymentsWebhookController],

View File

@@ -61,7 +61,8 @@ export class BookingsRepository extends BaseRepository<Booking> {
.createQueryBuilder('booking')
.leftJoinAndSelect('booking.bookingContainers', 'bc')
.leftJoinAndSelect('bc.containerType', 'ct')
.leftJoinAndSelect('booking.customer', 'customer')
.leftJoinAndSelect('booking.company', 'company')
// .leftJoinAndSelect('booking.customer', 'customer')
.leftJoinAndSelect('booking.train', 'train')
.leftJoinAndSelect('booking.serviceType', 'st')
.leftJoinAndSelect('booking.cargoType', 'cargo')
@@ -351,7 +352,8 @@ export class BookingsRepository extends BaseRepository<Booking> {
const qb = this.repository
.createQueryBuilder('booking')
.leftJoinAndSelect('booking.customer', 'customer')
.leftJoinAndSelect('booking.company', 'company')
// .leftJoinAndSelect('booking.customer', 'customer')
.leftJoinAndSelect('booking.cargoType', 'cargo')
.leftJoinAndSelect('booking.serviceType', 'serviceType')
.where('booking.status IN (:...statuses)', { statuses });

View File

@@ -6,7 +6,8 @@ import {
} from '@nestjs/common';
import { IsNull, Not } from 'typeorm';
import { CustomersService } from '../customers/customers.service';
// import { CustomersService } from '../customers/customers.service';
import { CompaniesService } from '../companies/companies.service';
import { FilesService } from '../files/files.service';
import { MinioService } from '../minio/minio.service';
import { ContainerTypesService } from '../rule-engine/services/container-types.service';
@@ -30,7 +31,8 @@ export class BookingsService {
private readonly bookingsRepository: BookingsRepository,
private readonly filesService: FilesService,
private readonly minioService: MinioService,
private readonly customersService: CustomersService,
// private readonly customersService: CustomersService,
private readonly companiesService: CompaniesService,
private readonly ruleEngineService: RuleEngineService,
private readonly containerTypesService: ContainerTypesService,
private readonly consolidationService: ConsolidationService,
@@ -154,15 +156,26 @@ export class BookingsService {
): Promise<{ booking: Booking; warnings: string[] }> {
const warnings: string[] = [];
let customerId = dto.customerId;
if (!customerId) {
// 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;
// }
let companyId = dto.companyId;
if (!companyId) {
if (!userId) {
throw new BadRequestException(
'customerId is required or must be resolvable from auth token',
'companyId is required or must be resolvable from auth token',
);
}
const customer = await this.customersService.findByUserId(userId);
customerId = customer.id;
const { company } = await this.companiesService.getCompanyInfoByUserId(userId);
companyId = company.id;
}
const reference = dto.reference || (await this.generateReference());
@@ -196,7 +209,7 @@ export class BookingsService {
const booking = await this.bookingsRepository.create({
reference,
customerId,
companyId,
trainId: dto.trainId,
contractType: dto.contractType,
previousContractId: dto.previousContractId,
@@ -375,7 +388,8 @@ export class BookingsService {
const where: Record<string, unknown> = {};
if (filter.status) where.status = filter.status;
if (filter.customerId) where.customerId = filter.customerId;
// if (filter.customerId) where.customerId = filter.customerId;
if (filter.companyId) where.companyId = filter.companyId;
if (filter.contractType) where.contractType = filter.contractType;
if (filter.serviceTypeId) where.serviceTypeId = filter.serviceTypeId;
if (filter.cargoTypeId) where.cargoTypeId = filter.cargoTypeId;
@@ -399,7 +413,8 @@ export class BookingsService {
skip: (page - 1) * pageSize,
take: pageSize,
order: { [sortField]: sortDir },
relations: ['customer', 'originYard', 'destinationYard', 'serviceType'],
relations: ['company', 'originYard', 'destinationYard', 'serviceType'],
// relations: ['customer', 'originYard', 'destinationYard', 'serviceType'],
});
return { items, total };
}

View File

@@ -61,10 +61,15 @@ export class CreateBookingDto {
@Transform(({ value }) => (typeof value === 'string' ? value.trim() : value))
reference?: string;
@ApiPropertyOptional({ format: 'uuid', description: 'Admin only: target customer' })
// @ApiPropertyOptional({ format: 'uuid', description: 'Admin only: target customer (legacy)' })
// @IsOptional()
// @IsUUID()
// customerId?: string;
@ApiPropertyOptional({ format: 'uuid', description: 'Admin only: target company' })
@IsOptional()
@IsUUID()
customerId?: string;
companyId?: string;
@ApiPropertyOptional({ format: 'uuid' })
@IsOptional()

View File

@@ -14,10 +14,15 @@ export class FilterBookingDto {
@IsIn([...BOOKING_STATUSES])
status?: string;
// @ApiPropertyOptional({ format: 'uuid' })
// @IsOptional()
// @IsUUID()
// customerId?: string;
@ApiPropertyOptional({ format: 'uuid' })
@IsOptional()
@IsUUID()
customerId?: string;
companyId?: string;
@ApiPropertyOptional()
@IsOptional()

View File

@@ -1,6 +1,7 @@
import { BaseEntity } from '@edr/api-common';
import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm';
import { Customer } from '../../customers/entities/customer.entity';
// import { Customer } from '../../customers/entities/customer.entity';
import { Company } from '../../companies/entities/company.entity';
import { CargoType } from '../../rule-engine/entities/cargo-type.entity';
import { ServiceType } from '../../rule-engine/entities/service-type.entity';
import { ShippingLine } from '../../rule-engine/entities/shipping-line.entity';
@@ -60,12 +61,19 @@ export class Booking extends BaseEntity {
@Column({ name: 'reference', type: 'varchar', length: 64, unique: true })
reference!: string;
@Column({ name: 'customer_id', type: 'uuid' })
customerId!: string;
// Legacy — superseded by companyId (column kept in DB)
// @Column({ name: 'customer_id', type: 'uuid' })
// customerId!: string;
// @ManyToOne(() => Customer)
// @JoinColumn({ name: 'customer_id' })
// customer?: Customer;
@ManyToOne(() => Customer)
@JoinColumn({ name: 'customer_id' })
customer?: Customer;
@Column({ name: 'company_id', type: 'uuid' })
companyId!: string;
@ManyToOne(() => Company)
@JoinColumn({ name: 'company_id' })
company?: Company;
@Column({ name: 'train_id', type: 'uuid', nullable: true })
trainId?: string | null;