mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 09:42:53 +00:00
implement Global Logistics booking process for customs contracts and enhance contract document handling
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
ForbiddenException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
@@ -191,15 +192,20 @@ export class ContractBookingService {
|
||||
*/
|
||||
private async assertGate(contract: Contract, isGlActor: boolean): Promise<string> {
|
||||
if (contract.customsClearingEnabled) {
|
||||
// Path B — the customer creates the booking once GL has finalized the
|
||||
// pre-booking clearance (GL "create booking" was removed; clearance ends
|
||||
// at finalize and hands the booking back to the customer).
|
||||
// Path B — Global Logistics creates the booking ON BEHALF OF the customer
|
||||
// once GL has finalized the pre-booking clearance. The customer never
|
||||
// books a customs contract himself.
|
||||
if (!isGlActor) {
|
||||
throw new ForbiddenException(
|
||||
'Customs-clearance contracts are booked by Global Logistics on behalf of the customer.',
|
||||
);
|
||||
}
|
||||
if (contract.clearanceStatus !== 'CLEARANCE_READY_FOR_BOOKING') {
|
||||
throw new BadRequestException(
|
||||
'Contract clearance is not ready for booking yet.',
|
||||
);
|
||||
}
|
||||
return isGlActor ? 'GL_ET' : 'CUSTOMER';
|
||||
return 'GL_ET';
|
||||
}
|
||||
|
||||
// Path A — customer (or staff) once the contract is executed.
|
||||
|
||||
@@ -8,7 +8,7 @@ import { InjectDataSource } from '@nestjs/typeorm';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { CompaniesService } from '../companies/companies.service';
|
||||
import { ProfileType } from '../companies/entities/company-profile.entity';
|
||||
import { CompanyProfile, ProfileType } from '../companies/entities/company-profile.entity';
|
||||
import { CompanyStatus } from '../companies/entities/company.entity';
|
||||
import { ServiceType } from '../rule-engine/entities/service-type.entity';
|
||||
import { FilesService } from '../files/files.service';
|
||||
@@ -220,9 +220,55 @@ export class ContractsService {
|
||||
}
|
||||
}
|
||||
|
||||
// Attach the company profile's onboarding / business-license documents to the
|
||||
// contract by reference. The separate "Documents" intake step was removed —
|
||||
// the profile documents are simply carried onto every contract automatically.
|
||||
await this.attachProfileDocuments(contract.id, companyProfileId);
|
||||
|
||||
return { contract: await this.findById(contract.id), warnings };
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a company profile's stored business-license / onboarding documents onto
|
||||
* a contract by reference (no byte re-upload). Codes are slugged from each
|
||||
* document name so they group under "Profile documents" on the contract detail
|
||||
* page. No-op when the contract has no profile or the profile has no documents.
|
||||
*/
|
||||
private async attachProfileDocuments(
|
||||
contractId: string,
|
||||
companyProfileId: string | null,
|
||||
): Promise<void> {
|
||||
if (!companyProfileId) return;
|
||||
const profile = await this.dataSource
|
||||
.getRepository(CompanyProfile)
|
||||
.findOne({ where: { id: companyProfileId } });
|
||||
const docs = profile?.businessLicenseFiles ?? [];
|
||||
if (docs.length === 0) return;
|
||||
|
||||
const slug = (name: string) =>
|
||||
name
|
||||
.toLowerCase()
|
||||
.replace(/\.[a-z0-9]+$/, '')
|
||||
.replace(/[^a-z0-9]+/g, '_')
|
||||
.replace(/^_+|_+$/g, '') || 'profile_document';
|
||||
|
||||
try {
|
||||
await this.filesService.attachExistingFiles(
|
||||
contractId,
|
||||
'contracts',
|
||||
docs.map((d, i) => ({
|
||||
code: `${slug(d.name)}_${i + 1}`,
|
||||
name: d.name,
|
||||
url: d.url,
|
||||
size: d.size,
|
||||
mimeType: d.mimeType,
|
||||
})),
|
||||
);
|
||||
} catch {
|
||||
// Non-fatal — the contract is still valid without the carried documents.
|
||||
}
|
||||
}
|
||||
|
||||
private async persistRoutes(
|
||||
contractId: string,
|
||||
routes: CreateContractDto['routes'],
|
||||
|
||||
@@ -77,6 +77,13 @@ export class WagonsService {
|
||||
async update(id: string, dto: UpdateWagonDto): Promise<Wagon> {
|
||||
const wagon = await this.findById(id);
|
||||
Object.assign(wagon, dto);
|
||||
// `findById` eager-loads `currentYard`; when the DTO changes the scalar FK
|
||||
// TypeORM otherwise re-derives `current_yard_id` from the STALE relation
|
||||
// object (the old yard) on save and silently reverts the change. Drop the
|
||||
// relation so the scalar `currentYardId` wins.
|
||||
if (dto.currentYardId !== undefined) {
|
||||
wagon.currentYard = null;
|
||||
}
|
||||
await this.wagonRepo.save(wagon);
|
||||
// Re-read with the relation so the response reflects the new yard label
|
||||
// instead of the stale relation object loaded before the assign.
|
||||
|
||||
Reference in New Issue
Block a user