mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 22:18:12 +00:00
refactor: rm the unused ff client stuff
This commit is contained in:
@@ -1,22 +1,34 @@
|
|||||||
import { Controller, Get, Post, Patch, Delete, Body, Param, Query, ParseUUIDPipe, HttpCode, HttpStatus, UseInterceptors, UploadedFiles } from '@nestjs/common';
|
import {
|
||||||
import { AnyFilesInterceptor } from '@nestjs/platform-express';
|
Controller,
|
||||||
import { ApiOperation, ApiTags, ApiConsumes } from '@nestjs/swagger';
|
Get,
|
||||||
import { CurrentUser } from '@edr/api-common';
|
Post,
|
||||||
import { FreightAdmin } from '../../common/booking-guards';
|
Patch,
|
||||||
import { FilesService } from '../files/files.service';
|
Delete,
|
||||||
import { CompaniesService } from './companies.service';
|
Body,
|
||||||
import { CreateCompanyDto } from './dto/create-company.dto';
|
Param,
|
||||||
import { UpdateCompanyDto } from './dto/update-company.dto';
|
Query,
|
||||||
import { CreateExternalProfileDto } from './dto/create-external-profile.dto';
|
ParseUUIDPipe,
|
||||||
import { CreateFFClientDto } from './dto/create-ff-client.dto';
|
HttpCode,
|
||||||
import { CreateCompanyWithProfileDto } from './dto/create-company-with-profile.dto';
|
HttpStatus,
|
||||||
import { ResponseCompanyDto } from './dto/response-company.dto';
|
UseInterceptors,
|
||||||
import { ResponseExternalProfileDto } from './dto/response-external-profile.dto';
|
UploadedFiles,
|
||||||
import { ResponseFFClientDto } from './dto/response-ff-client.dto';
|
} from "@nestjs/common";
|
||||||
import { CompanyInfoResponseDto } from './dto/company-info-response.dto';
|
import { AnyFilesInterceptor } from "@nestjs/platform-express";
|
||||||
import { UpdateProfileDto } from './dto/update-profile.dto';
|
import { ApiOperation, ApiTags, ApiConsumes } from "@nestjs/swagger";
|
||||||
import { ProfileResponseDto } from './dto/profile-response.dto';
|
import { CurrentUser } from "@edr/api-common";
|
||||||
import { DashboardSummaryResponseDto } from './dto/dashboard-summary-response.dto';
|
import { FreightAdmin } from "../../common/booking-guards";
|
||||||
|
import { FilesService } from "../files/files.service";
|
||||||
|
import { CompaniesService } from "./companies.service";
|
||||||
|
import { CreateCompanyDto } from "./dto/create-company.dto";
|
||||||
|
import { UpdateCompanyDto } from "./dto/update-company.dto";
|
||||||
|
import { CreateExternalProfileDto } from "./dto/create-external-profile.dto";
|
||||||
|
import { CreateCompanyWithProfileDto } from "./dto/create-company-with-profile.dto";
|
||||||
|
import { ResponseCompanyDto } from "./dto/response-company.dto";
|
||||||
|
import { ResponseExternalProfileDto } from "./dto/response-external-profile.dto";
|
||||||
|
import { CompanyInfoResponseDto } from "./dto/company-info-response.dto";
|
||||||
|
import { UpdateProfileDto } from "./dto/update-profile.dto";
|
||||||
|
import { ProfileResponseDto } from "./dto/profile-response.dto";
|
||||||
|
import { DashboardSummaryResponseDto } from "./dto/dashboard-summary-response.dto";
|
||||||
|
|
||||||
interface CurrentIamUser {
|
interface CurrentIamUser {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -25,36 +37,47 @@ interface CurrentIamUser {
|
|||||||
phoneNumber?: string;
|
phoneNumber?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiTags('Companies')
|
@ApiTags("Companies")
|
||||||
@Controller('companies')
|
@Controller("companies")
|
||||||
export class CompaniesController {
|
export class CompaniesController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly companiesService: CompaniesService,
|
private readonly companiesService: CompaniesService,
|
||||||
private readonly filesService: FilesService,
|
private readonly filesService: FilesService,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
@Get('getInfo')
|
@Get("getInfo")
|
||||||
@ApiOperation({ summary: 'Get company info for the current user' })
|
@ApiOperation({ summary: "Get company info for the current user" })
|
||||||
async getInfo(@CurrentUser() user: CurrentIamUser): Promise<CompanyInfoResponseDto> {
|
async getInfo(
|
||||||
const { profile, company } = await this.companiesService.getCompanyInfoByUserId(user.id);
|
@CurrentUser() user: CurrentIamUser,
|
||||||
|
): Promise<CompanyInfoResponseDto> {
|
||||||
|
const { profile, company } =
|
||||||
|
await this.companiesService.getCompanyInfoByUserId(user.id);
|
||||||
return new CompanyInfoResponseDto(profile, company);
|
return new CompanyInfoResponseDto(profile, company);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('profile')
|
@Get("profile")
|
||||||
@ApiOperation({ summary: 'Get flattened profile for the settings page' })
|
@ApiOperation({ summary: "Get flattened profile for the settings page" })
|
||||||
async getProfile(@CurrentUser() user: CurrentIamUser): Promise<ProfileResponseDto> {
|
async getProfile(
|
||||||
const { profile, company } = await this.companiesService.getCompanyInfoByUserId(user.id);
|
@CurrentUser() user: CurrentIamUser,
|
||||||
|
): Promise<ProfileResponseDto> {
|
||||||
|
const { profile, company } =
|
||||||
|
await this.companiesService.getCompanyInfoByUserId(user.id);
|
||||||
return new ProfileResponseDto(profile, company);
|
return new ProfileResponseDto(profile, company);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('dashboard')
|
@Get("dashboard")
|
||||||
@ApiOperation({ summary: 'Get portal dashboard KPIs (delivered, spend, freight volume) for the current user' })
|
@ApiOperation({
|
||||||
async getDashboard(@CurrentUser() user: CurrentIamUser): Promise<DashboardSummaryResponseDto> {
|
summary:
|
||||||
|
"Get portal dashboard KPIs (delivered, spend, freight volume) for the current user",
|
||||||
|
})
|
||||||
|
async getDashboard(
|
||||||
|
@CurrentUser() user: CurrentIamUser,
|
||||||
|
): Promise<DashboardSummaryResponseDto> {
|
||||||
return this.companiesService.getDashboardSummary(user.id);
|
return this.companiesService.getDashboardSummary(user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch('profile')
|
@Patch("profile")
|
||||||
@ApiOperation({ summary: 'Update profile (flattened settings page)' })
|
@ApiOperation({ summary: "Update profile (flattened settings page)" })
|
||||||
async updateProfile(
|
async updateProfile(
|
||||||
@CurrentUser() user: CurrentIamUser,
|
@CurrentUser() user: CurrentIamUser,
|
||||||
@Body() dto: UpdateProfileDto,
|
@Body() dto: UpdateProfileDto,
|
||||||
@@ -62,145 +85,134 @@ export class CompaniesController {
|
|||||||
return this.companiesService.updateProfile(user.id, dto);
|
return this.companiesService.updateProfile(user.id, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('create')
|
@Post("create")
|
||||||
@ApiOperation({ summary: 'Create a company with its associated external profile (onboarding)' })
|
@ApiOperation({
|
||||||
|
summary:
|
||||||
|
"Create a company with its associated external profile (onboarding)",
|
||||||
|
})
|
||||||
async createWithProfile(
|
async createWithProfile(
|
||||||
@CurrentUser() user: CurrentIamUser,
|
@CurrentUser() user: CurrentIamUser,
|
||||||
@Body() dto: CreateCompanyWithProfileDto,
|
@Body() dto: CreateCompanyWithProfileDto,
|
||||||
): Promise<CompanyInfoResponseDto> {
|
): Promise<CompanyInfoResponseDto> {
|
||||||
const nameParts = (user.name?.en ?? '').split(' ');
|
const nameParts = (user.name?.en ?? "").split(" ");
|
||||||
const { profile, company } = await this.companiesService.createCompanyWithProfile(
|
const { profile, company } =
|
||||||
{
|
await this.companiesService.createCompanyWithProfile(
|
||||||
userId: user.id,
|
{
|
||||||
firstName: nameParts[0] || '',
|
userId: user.id,
|
||||||
lastName: nameParts.slice(-1)[0] || '',
|
firstName: nameParts[0] || "",
|
||||||
email: user.email ?? '',
|
lastName: nameParts.slice(-1)[0] || "",
|
||||||
phone: user.phoneNumber ?? '',
|
email: user.email ?? "",
|
||||||
},
|
phone: user.phoneNumber ?? "",
|
||||||
dto,
|
},
|
||||||
);
|
dto,
|
||||||
|
);
|
||||||
return new CompanyInfoResponseDto(profile, company);
|
return new CompanyInfoResponseDto(profile, company);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@FreightAdmin()
|
@FreightAdmin()
|
||||||
@ApiOperation({ summary: 'Create a new company (customer, forwarder, transporter, broker)' })
|
@ApiOperation({
|
||||||
|
summary: "Create a new company (customer, forwarder, transporter, broker)",
|
||||||
|
})
|
||||||
async create(@Body() dto: CreateCompanyDto): Promise<ResponseCompanyDto> {
|
async create(@Body() dto: CreateCompanyDto): Promise<ResponseCompanyDto> {
|
||||||
const company = await this.companiesService.createCompany(dto);
|
const company = await this.companiesService.createCompany(dto);
|
||||||
return new ResponseCompanyDto(company);
|
return new ResponseCompanyDto(company);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: 'List all companies' })
|
@ApiOperation({ summary: "List all companies" })
|
||||||
async findAll(): Promise<ResponseCompanyDto[]> {
|
async findAll(): Promise<ResponseCompanyDto[]> {
|
||||||
const companies = await this.companiesService.findAllCompanies();
|
const companies = await this.companiesService.findAllCompanies();
|
||||||
return companies.map((c) => new ResponseCompanyDto(c));
|
return companies.map((c) => new ResponseCompanyDto(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('type/:type')
|
@Get("type/:type")
|
||||||
@ApiOperation({ summary: 'Find companies by type' })
|
@ApiOperation({ summary: "Find companies by type" })
|
||||||
async findByType(@Param('type') type: string): Promise<ResponseCompanyDto[]> {
|
async findByType(@Param("type") type: string): Promise<ResponseCompanyDto[]> {
|
||||||
const companies = await this.companiesService.findAllCompanies();
|
const companies = await this.companiesService.findAllCompanies();
|
||||||
return companies.filter((c) => c.type === type).map((c) => new ResponseCompanyDto(c));
|
return companies
|
||||||
|
.filter((c) => c.type === type)
|
||||||
|
.map((c) => new ResponseCompanyDto(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('search')
|
@Get("search")
|
||||||
@ApiOperation({ summary: 'Search companies by name' })
|
@ApiOperation({ summary: "Search companies by name" })
|
||||||
async search(@Query('name') name: string): Promise<ResponseCompanyDto[]> {
|
async search(@Query("name") name: string): Promise<ResponseCompanyDto[]> {
|
||||||
const companies = await this.companiesService.findAllCompanies();
|
const companies = await this.companiesService.findAllCompanies();
|
||||||
return companies
|
return companies
|
||||||
.filter((c) => c.name.toLowerCase().includes(name.toLowerCase()))
|
.filter((c) => c.name.toLowerCase().includes(name.toLowerCase()))
|
||||||
.map((c) => new ResponseCompanyDto(c));
|
.map((c) => new ResponseCompanyDto(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(":id")
|
||||||
@ApiOperation({ summary: 'Get company by ID' })
|
@ApiOperation({ summary: "Get company by ID" })
|
||||||
async findById(@Param('id', ParseUUIDPipe) id: string): Promise<ResponseCompanyDto> {
|
async findById(
|
||||||
|
@Param("id", ParseUUIDPipe) id: string,
|
||||||
|
): Promise<ResponseCompanyDto> {
|
||||||
const company = await this.companiesService.findCompanyById(id);
|
const company = await this.companiesService.findCompanyById(id);
|
||||||
return new ResponseCompanyDto(company);
|
return new ResponseCompanyDto(company);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch(':id')
|
@Patch(":id")
|
||||||
@FreightAdmin()
|
@FreightAdmin()
|
||||||
@ApiOperation({ summary: 'Update a company' })
|
@ApiOperation({ summary: "Update a company" })
|
||||||
async update(
|
async update(
|
||||||
@Param('id', ParseUUIDPipe) id: string,
|
@Param("id", ParseUUIDPipe) id: string,
|
||||||
@Body() dto: UpdateCompanyDto,
|
@Body() dto: UpdateCompanyDto,
|
||||||
): Promise<ResponseCompanyDto> {
|
): Promise<ResponseCompanyDto> {
|
||||||
const company = await this.companiesService.updateCompany(id, dto);
|
const company = await this.companiesService.updateCompany(id, dto);
|
||||||
return new ResponseCompanyDto(company);
|
return new ResponseCompanyDto(company);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(":id")
|
||||||
@FreightAdmin()
|
@FreightAdmin()
|
||||||
@ApiOperation({ summary: 'Soft-delete a company' })
|
@ApiOperation({ summary: "Soft-delete a company" })
|
||||||
@HttpCode(HttpStatus.NO_CONTENT)
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
async remove(@Param('id', ParseUUIDPipe) id: string): Promise<void> {
|
async remove(@Param("id", ParseUUIDPipe) id: string): Promise<void> {
|
||||||
await this.companiesService.deleteCompany(id);
|
await this.companiesService.deleteCompany(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post(':companyId/documents')
|
@Post(":companyId/documents")
|
||||||
@UseInterceptors(AnyFilesInterceptor())
|
@UseInterceptors(AnyFilesInterceptor())
|
||||||
@ApiConsumes('multipart/form-data')
|
@ApiConsumes("multipart/form-data")
|
||||||
@ApiOperation({ summary: 'Upload documents for a company (onboarding)' })
|
@ApiOperation({ summary: "Upload documents for a company (onboarding)" })
|
||||||
async uploadDocuments(
|
async uploadDocuments(
|
||||||
@Param('companyId', ParseUUIDPipe) companyId: string,
|
@Param("companyId", ParseUUIDPipe) companyId: string,
|
||||||
@UploadedFiles() files: Array<Express.Multer.File>,
|
@UploadedFiles() files: Array<Express.Multer.File>,
|
||||||
) {
|
) {
|
||||||
return this.filesService.uploadMany(companyId, 'companies', files);
|
return this.filesService.uploadMany(companyId, "companies", files);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post(':companyId/profiles')
|
@Post(":companyId/profiles")
|
||||||
@FreightAdmin()
|
@FreightAdmin()
|
||||||
@ApiOperation({ summary: 'Add a profile (employee) to a company' })
|
@ApiOperation({ summary: "Add a profile (employee) to a company" })
|
||||||
async createProfile(
|
async createProfile(
|
||||||
@Param('companyId', ParseUUIDPipe) companyId: string,
|
@Param("companyId", ParseUUIDPipe) companyId: string,
|
||||||
@Body() dto: CreateExternalProfileDto,
|
@Body() dto: CreateExternalProfileDto,
|
||||||
): Promise<ResponseExternalProfileDto> {
|
): Promise<ResponseExternalProfileDto> {
|
||||||
const profile = await this.companiesService.createProfile({ ...dto, companyId });
|
const profile = await this.companiesService.createProfile({
|
||||||
|
...dto,
|
||||||
|
companyId,
|
||||||
|
});
|
||||||
return new ResponseExternalProfileDto(profile);
|
return new ResponseExternalProfileDto(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':companyId/profiles')
|
@Get(":companyId/profiles")
|
||||||
@ApiOperation({ summary: 'List profiles for a company' })
|
@ApiOperation({ summary: "List profiles for a company" })
|
||||||
async listProfiles(
|
async listProfiles(
|
||||||
@Param('companyId', ParseUUIDPipe) companyId: string,
|
@Param("companyId", ParseUUIDPipe) companyId: string,
|
||||||
): Promise<ResponseExternalProfileDto[]> {
|
): Promise<ResponseExternalProfileDto[]> {
|
||||||
const profiles = await this.companiesService.findProfilesByCompany(companyId);
|
const profiles =
|
||||||
|
await this.companiesService.findProfilesByCompany(companyId);
|
||||||
return profiles.map((p) => new ResponseExternalProfileDto(p));
|
return profiles.map((p) => new ResponseExternalProfileDto(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('profile/user/:userId')
|
@Get("profile/user/:userId")
|
||||||
@ApiOperation({ summary: 'Get profile by IAM user ID' })
|
@ApiOperation({ summary: "Get profile by IAM user ID" })
|
||||||
async findProfileByUser(
|
async findProfileByUser(
|
||||||
@Param('userId', ParseUUIDPipe) userId: string,
|
@Param("userId", ParseUUIDPipe) userId: string,
|
||||||
): Promise<ResponseExternalProfileDto> {
|
): Promise<ResponseExternalProfileDto> {
|
||||||
const profile = await this.companiesService.findProfileByUserId(userId);
|
const profile = await this.companiesService.findProfileByUserId(userId);
|
||||||
return new ResponseExternalProfileDto(profile);
|
return new ResponseExternalProfileDto(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('ff-clients')
|
|
||||||
@FreightAdmin()
|
|
||||||
@ApiOperation({ summary: 'Link a forwarder to a client company' })
|
|
||||||
async createFFClient(@Body() dto: CreateFFClientDto): Promise<ResponseFFClientDto> {
|
|
||||||
const client = await this.companiesService.createFFClient(dto);
|
|
||||||
return new ResponseFFClientDto(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get(':forwarderCompanyId/clients')
|
|
||||||
@ApiOperation({ summary: 'List clients of a forwarder' })
|
|
||||||
async listFFClients(
|
|
||||||
@Param('forwarderCompanyId', ParseUUIDPipe) forwarderCompanyId: string,
|
|
||||||
): Promise<ResponseFFClientDto[]> {
|
|
||||||
const clients = await this.companiesService.findForwarderClients(forwarderCompanyId);
|
|
||||||
return clients.map((c) => new ResponseFFClientDto(c));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Delete('ff-clients/:id')
|
|
||||||
@FreightAdmin()
|
|
||||||
@ApiOperation({ summary: 'Remove a forwarder-client relationship' })
|
|
||||||
@HttpCode(HttpStatus.NO_CONTENT)
|
|
||||||
async removeFFClient(@Param('id', ParseUUIDPipe) id: string): Promise<void> {
|
|
||||||
await this.companiesService.deleteFFClient(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,27 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from "@nestjs/common";
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
import { FilesModule } from '../files/files.module';
|
import { FilesModule } from "../files/files.module";
|
||||||
import { CompaniesController } from './companies.controller';
|
import { CompaniesController } from "./companies.controller";
|
||||||
import { CompaniesService } from './companies.service';
|
import { CompaniesService } from "./companies.service";
|
||||||
import { CompaniesRepository } from './companies.repository';
|
import { CompaniesRepository } from "./companies.repository";
|
||||||
import { ExternalProfileRepository } from './external-profile.repository';
|
import { ExternalProfileRepository } from "./external-profile.repository";
|
||||||
import { FFClientRepository } from './ff-client.repository';
|
import { CompanyDashboardRepository } from "./company-dashboard.repository";
|
||||||
import { CompanyDashboardRepository } from './company-dashboard.repository';
|
import { Company } from "./entities/company.entity";
|
||||||
import { Company } from './entities/company.entity';
|
import { ExternalProfile } from "./entities/external-profile.entity";
|
||||||
import { ExternalProfile } from './entities/external-profile.entity';
|
import { Booking } from "../bookings/entities/booking.entity";
|
||||||
import { FFClient } from './entities/ff-client.entity';
|
|
||||||
import { Booking } from '../bookings/entities/booking.entity';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([Company, ExternalProfile, FFClient, Booking]), FilesModule],
|
imports: [
|
||||||
|
TypeOrmModule.forFeature([Company, ExternalProfile, Booking]),
|
||||||
|
FilesModule,
|
||||||
|
],
|
||||||
controllers: [CompaniesController],
|
controllers: [CompaniesController],
|
||||||
providers: [CompaniesService, CompaniesRepository, ExternalProfileRepository, FFClientRepository, CompanyDashboardRepository],
|
providers: [
|
||||||
|
CompaniesService,
|
||||||
|
CompaniesRepository,
|
||||||
|
ExternalProfileRepository,
|
||||||
|
CompanyDashboardRepository,
|
||||||
|
],
|
||||||
exports: [CompaniesService],
|
exports: [CompaniesService],
|
||||||
})
|
})
|
||||||
export class CompaniesModule {}
|
export class CompaniesModule { }
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
|
import {
|
||||||
import { CompaniesRepository } from './companies.repository';
|
Injectable,
|
||||||
import { ExternalProfileRepository } from './external-profile.repository';
|
NotFoundException,
|
||||||
import { FFClientRepository } from './ff-client.repository';
|
ConflictException,
|
||||||
import { CompanyDashboardRepository } from './company-dashboard.repository';
|
} from "@nestjs/common";
|
||||||
import { CreateCompanyDto } from './dto/create-company.dto';
|
import { CompaniesRepository } from "./companies.repository";
|
||||||
import { UpdateCompanyDto } from './dto/update-company.dto';
|
import { ExternalProfileRepository } from "./external-profile.repository";
|
||||||
import { CreateExternalProfileDto } from './dto/create-external-profile.dto';
|
import { CompanyDashboardRepository } from "./company-dashboard.repository";
|
||||||
import { CreateFFClientDto } from './dto/create-ff-client.dto';
|
import { CreateCompanyDto } from "./dto/create-company.dto";
|
||||||
import { CreateCompanyWithProfileDto } from './dto/create-company-with-profile.dto';
|
import { UpdateCompanyDto } from "./dto/update-company.dto";
|
||||||
import { UpdateProfileDto } from './dto/update-profile.dto';
|
import { CreateExternalProfileDto } from "./dto/create-external-profile.dto";
|
||||||
import { ProfileResponseDto } from './dto/profile-response.dto';
|
import { CreateCompanyWithProfileDto } from "./dto/create-company-with-profile.dto";
|
||||||
import { DashboardSummaryResponseDto } from './dto/dashboard-summary-response.dto';
|
import { UpdateProfileDto } from "./dto/update-profile.dto";
|
||||||
import { Company } from './entities/company.entity';
|
import { ProfileResponseDto } from "./dto/profile-response.dto";
|
||||||
import { ExternalProfile } from './entities/external-profile.entity';
|
import { DashboardSummaryResponseDto } from "./dto/dashboard-summary-response.dto";
|
||||||
import { FFClient } from './entities/ff-client.entity';
|
import { Company } from "./entities/company.entity";
|
||||||
|
import { ExternalProfile } from "./entities/external-profile.entity";
|
||||||
|
|
||||||
export interface UserIdentity {
|
export interface UserIdentity {
|
||||||
userId: string;
|
userId: string;
|
||||||
@@ -28,9 +29,8 @@ export class CompaniesService {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly companiesRepo: CompaniesRepository,
|
private readonly companiesRepo: CompaniesRepository,
|
||||||
private readonly profilesRepo: ExternalProfileRepository,
|
private readonly profilesRepo: ExternalProfileRepository,
|
||||||
private readonly ffClientsRepo: FFClientRepository,
|
|
||||||
private readonly dashboardRepo: CompanyDashboardRepository,
|
private readonly dashboardRepo: CompanyDashboardRepository,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
async createCompany(dto: CreateCompanyDto): Promise<Company> {
|
async createCompany(dto: CreateCompanyDto): Promise<Company> {
|
||||||
const exists = await this.companiesRepo.existsByTin(dto.tin);
|
const exists = await this.companiesRepo.existsByTin(dto.tin);
|
||||||
@@ -40,27 +40,34 @@ export class CompaniesService {
|
|||||||
return this.companiesRepo.create(dto);
|
return this.companiesRepo.create(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createCompanyWithProfile(identity: UserIdentity, dto: CreateCompanyWithProfileDto): Promise<{ company: Company; profile: ExternalProfile }> {
|
async createCompanyWithProfile(
|
||||||
|
identity: UserIdentity,
|
||||||
|
dto: CreateCompanyWithProfileDto,
|
||||||
|
): Promise<{ company: Company; profile: ExternalProfile }> {
|
||||||
if (dto.tin) {
|
if (dto.tin) {
|
||||||
const exists = await this.companiesRepo.existsByTin(dto.tin);
|
const exists = await this.companiesRepo.existsByTin(dto.tin);
|
||||||
if (exists) {
|
if (exists) {
|
||||||
throw new ConflictException(`Company with TIN ${dto.tin} already exists`);
|
throw new ConflictException(
|
||||||
|
`Company with TIN ${dto.tin} already exists`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingProfile = await this.profilesRepo.findByEmail(identity.email);
|
const existingProfile = await this.profilesRepo.findByEmail(identity.email);
|
||||||
if (existingProfile) {
|
if (existingProfile) {
|
||||||
throw new ConflictException(`Profile with email ${identity.email} already exists`);
|
throw new ConflictException(
|
||||||
|
`Profile with email ${identity.email} already exists`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const company = await this.companiesRepo.create({
|
const company = await this.companiesRepo.create({
|
||||||
name: dto.companyName,
|
name: dto.companyName,
|
||||||
type: dto.companyType,
|
type: dto.companyType,
|
||||||
tin: dto.tin ?? '',
|
tin: dto.tin ?? "",
|
||||||
vatNumber: dto.vatNumber ?? null,
|
vatNumber: dto.vatNumber ?? null,
|
||||||
businessLicense: dto.fanNumber ?? null,
|
businessLicense: dto.fanNumber ?? null,
|
||||||
fanNumber: dto.fanNumber ?? null,
|
fanNumber: dto.fanNumber ?? null,
|
||||||
country: dto.companyLocation ?? 'Ethiopia',
|
country: dto.companyLocation ?? "Ethiopia",
|
||||||
address: dto.companyAddress ?? null,
|
address: dto.companyAddress ?? null,
|
||||||
phone: dto.companyPhone ?? null,
|
phone: dto.companyPhone ?? null,
|
||||||
email: dto.companyEmail ?? null,
|
email: dto.companyEmail ?? null,
|
||||||
@@ -82,7 +89,7 @@ export class CompaniesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async findAllCompanies(): Promise<Company[]> {
|
async findAllCompanies(): Promise<Company[]> {
|
||||||
return this.companiesRepo.findAll({ order: { name: 'ASC' as any } });
|
return this.companiesRepo.findAll({ order: { name: "ASC" as any } });
|
||||||
}
|
}
|
||||||
|
|
||||||
async findCompanyById(id: string): Promise<Company> {
|
async findCompanyById(id: string): Promise<Company> {
|
||||||
@@ -91,12 +98,18 @@ export class CompaniesService {
|
|||||||
return company;
|
return company;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCompanyInfoByUserId(userId: string): Promise<{ profile: ExternalProfile; company: Company }> {
|
async getCompanyInfoByUserId(
|
||||||
|
userId: string,
|
||||||
|
): Promise<{ profile: ExternalProfile; company: Company }> {
|
||||||
const profile = await this.profilesRepo.findByUserId(userId);
|
const profile = await this.profilesRepo.findByUserId(userId);
|
||||||
if (!profile) throw new NotFoundException(`Profile for user ${userId} not found`);
|
if (!profile)
|
||||||
|
throw new NotFoundException(`Profile for user ${userId} not found`);
|
||||||
|
|
||||||
const company = profile.company;
|
const company = profile.company;
|
||||||
if (!company) throw new NotFoundException(`Company for profile ${profile.id} not found`);
|
if (!company)
|
||||||
|
throw new NotFoundException(
|
||||||
|
`Company for profile ${profile.id} not found`,
|
||||||
|
);
|
||||||
|
|
||||||
return { profile, company };
|
return { profile, company };
|
||||||
}
|
}
|
||||||
@@ -114,7 +127,9 @@ export class CompaniesService {
|
|||||||
* column, so "delivered YTD" counts bookings created this year that reached a
|
* column, so "delivered YTD" counts bookings created this year that reached a
|
||||||
* delivered/completed status.
|
* delivered/completed status.
|
||||||
*/
|
*/
|
||||||
async getDashboardSummary(userId: string): Promise<DashboardSummaryResponseDto> {
|
async getDashboardSummary(
|
||||||
|
userId: string,
|
||||||
|
): Promise<DashboardSummaryResponseDto> {
|
||||||
// A user without a company profile has no bookings — return an empty summary
|
// A user without a company profile has no bookings — return an empty summary
|
||||||
// rather than 404, so the portal home still renders.
|
// rather than 404, so the portal home still renders.
|
||||||
const profile = await this.profilesRepo.findByUserId(userId);
|
const profile = await this.profilesRepo.findByUserId(userId);
|
||||||
@@ -125,7 +140,9 @@ export class CompaniesService {
|
|||||||
const yearStart = new Date(now.getFullYear(), 0, 1);
|
const yearStart = new Date(now.getFullYear(), 0, 1);
|
||||||
const prevYearStart = new Date(now.getFullYear() - 1, 0, 1);
|
const prevYearStart = new Date(now.getFullYear() - 1, 0, 1);
|
||||||
// Same point in the previous year, so YoY compares like-for-like windows.
|
// Same point in the previous year, so YoY compares like-for-like windows.
|
||||||
const prevYearToDate = new Date(prevYearStart.getTime() + (now.getTime() - yearStart.getTime()));
|
const prevYearToDate = new Date(
|
||||||
|
prevYearStart.getTime() + (now.getTime() - yearStart.getTime()),
|
||||||
|
);
|
||||||
|
|
||||||
const [
|
const [
|
||||||
deliveredThis,
|
deliveredThis,
|
||||||
@@ -139,20 +156,36 @@ export class CompaniesService {
|
|||||||
this.dashboardRepo.countDelivered(companyId, yearStart, now),
|
this.dashboardRepo.countDelivered(companyId, yearStart, now),
|
||||||
this.dashboardRepo.countCommitted(companyId, yearStart, now),
|
this.dashboardRepo.countCommitted(companyId, yearStart, now),
|
||||||
this.dashboardRepo.sumPaidSpendByCurrency(companyId, yearStart, now),
|
this.dashboardRepo.sumPaidSpendByCurrency(companyId, yearStart, now),
|
||||||
this.dashboardRepo.sumPaidSpendByCurrency(companyId, prevYearStart, prevYearToDate),
|
this.dashboardRepo.sumPaidSpendByCurrency(
|
||||||
|
companyId,
|
||||||
|
prevYearStart,
|
||||||
|
prevYearToDate,
|
||||||
|
),
|
||||||
this.dashboardRepo.sumCommittedTonnage(companyId, yearStart, now),
|
this.dashboardRepo.sumCommittedTonnage(companyId, yearStart, now),
|
||||||
this.dashboardRepo.sumCommittedTonnage(companyId, prevYearStart, prevYearToDate),
|
this.dashboardRepo.sumCommittedTonnage(
|
||||||
this.dashboardRepo.monthlyCommittedTonnage(companyId, this.monthsAgo(now, 5), now),
|
companyId,
|
||||||
|
prevYearStart,
|
||||||
|
prevYearToDate,
|
||||||
|
),
|
||||||
|
this.dashboardRepo.monthlyCommittedTonnage(
|
||||||
|
companyId,
|
||||||
|
this.monthsAgo(now, 5),
|
||||||
|
now,
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Spend can span currencies; report the dominant one (prefer ETB on ties).
|
// Spend can span currencies; report the dominant one (prefer ETB on ties).
|
||||||
const spend = this.pickCurrencyTotal(spendThisByCcy);
|
const spend = this.pickCurrencyTotal(spendThisByCcy);
|
||||||
const spendPrev = spendPrevByCcy.find((c) => c.currency === spend.currency)?.total ?? 0;
|
const spendPrev =
|
||||||
|
spendPrevByCcy.find((c) => c.currency === spend.currency)?.total ?? 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
deliveredCount: deliveredThis,
|
deliveredCount: deliveredThis,
|
||||||
// Share of committed bookings that reached delivered/completed.
|
// Share of committed bookings that reached delivered/completed.
|
||||||
completionRate: committedThis > 0 ? Math.round((deliveredThis / committedThis) * 100) : 0,
|
completionRate:
|
||||||
|
committedThis > 0
|
||||||
|
? Math.round((deliveredThis / committedThis) * 100)
|
||||||
|
: 0,
|
||||||
spendYtd: spend.total,
|
spendYtd: spend.total,
|
||||||
spendCurrency: spend.currency,
|
spendCurrency: spend.currency,
|
||||||
spendYtdChangePct: this.changePct(spend.total, spendPrev),
|
spendYtdChangePct: this.changePct(spend.total, spendPrev),
|
||||||
@@ -172,12 +205,12 @@ export class CompaniesService {
|
|||||||
deliveredCount: 0,
|
deliveredCount: 0,
|
||||||
completionRate: 0,
|
completionRate: 0,
|
||||||
spendYtd: 0,
|
spendYtd: 0,
|
||||||
spendCurrency: 'ETB',
|
spendCurrency: "ETB",
|
||||||
spendYtdChangePct: 0,
|
spendYtdChangePct: 0,
|
||||||
freightVolume: {
|
freightVolume: {
|
||||||
totalTonnes: 0,
|
totalTonnes: 0,
|
||||||
totalValue: 0,
|
totalValue: 0,
|
||||||
currency: 'ETB',
|
currency: "ETB",
|
||||||
ytdChangePct: 0,
|
ytdChangePct: 0,
|
||||||
monthly: this.buildMonthlySeries(now, []),
|
monthly: this.buildMonthlySeries(now, []),
|
||||||
},
|
},
|
||||||
@@ -190,8 +223,11 @@ export class CompaniesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Pick the currency with the largest total, preferring ETB on ties / when empty. */
|
/** Pick the currency with the largest total, preferring ETB on ties / when empty. */
|
||||||
private pickCurrencyTotal(totals: { currency: string; total: number }[]): { currency: string; total: number } {
|
private pickCurrencyTotal(totals: { currency: string; total: number }[]): {
|
||||||
if (totals.length === 0) return { currency: 'ETB', total: 0 };
|
currency: string;
|
||||||
|
total: number;
|
||||||
|
} {
|
||||||
|
if (totals.length === 0) return { currency: "ETB", total: 0 };
|
||||||
return totals.reduce((best, cur) => (cur.total > best.total ? cur : best));
|
return totals.reduce((best, cur) => (cur.total > best.total ? cur : best));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,13 +242,29 @@ export class CompaniesService {
|
|||||||
now: Date,
|
now: Date,
|
||||||
rows: { year: number; month: number; tonnes: number }[],
|
rows: { year: number; month: number; tonnes: number }[],
|
||||||
): { month: string; tonnes: number }[] {
|
): { month: string; tonnes: number }[] {
|
||||||
const labels = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
const labels = [
|
||||||
|
"Jan",
|
||||||
|
"Feb",
|
||||||
|
"Mar",
|
||||||
|
"Apr",
|
||||||
|
"May",
|
||||||
|
"Jun",
|
||||||
|
"Jul",
|
||||||
|
"Aug",
|
||||||
|
"Sep",
|
||||||
|
"Oct",
|
||||||
|
"Nov",
|
||||||
|
"Dec",
|
||||||
|
];
|
||||||
const byKey = new Map(rows.map((r) => [`${r.year}-${r.month}`, r.tonnes]));
|
const byKey = new Map(rows.map((r) => [`${r.year}-${r.month}`, r.tonnes]));
|
||||||
const series: { month: string; tonnes: number }[] = [];
|
const series: { month: string; tonnes: number }[] = [];
|
||||||
for (let i = 5; i >= 0; i--) {
|
for (let i = 5; i >= 0; i--) {
|
||||||
const d = new Date(now.getFullYear(), now.getMonth() - i, 1);
|
const d = new Date(now.getFullYear(), now.getMonth() - i, 1);
|
||||||
const key = `${d.getFullYear()}-${d.getMonth() + 1}`;
|
const key = `${d.getFullYear()}-${d.getMonth() + 1}`;
|
||||||
series.push({ month: labels[d.getMonth()], tonnes: Math.round(byKey.get(key) ?? 0) });
|
series.push({
|
||||||
|
month: labels[d.getMonth()],
|
||||||
|
tonnes: Math.round(byKey.get(key) ?? 0),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return series;
|
return series;
|
||||||
}
|
}
|
||||||
@@ -224,7 +276,10 @@ export class CompaniesService {
|
|||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateProfile(userId: string, dto: UpdateProfileDto): Promise<ProfileResponseDto> {
|
async updateProfile(
|
||||||
|
userId: string,
|
||||||
|
dto: UpdateProfileDto,
|
||||||
|
): Promise<ProfileResponseDto> {
|
||||||
const { profile, company } = await this.getCompanyInfoByUserId(userId);
|
const { profile, company } = await this.getCompanyInfoByUserId(userId);
|
||||||
|
|
||||||
const companyUpdates: Record<string, any> = {};
|
const companyUpdates: Record<string, any> = {};
|
||||||
@@ -233,8 +288,10 @@ export class CompaniesService {
|
|||||||
if (dto.companyName !== undefined) companyUpdates.name = dto.companyName;
|
if (dto.companyName !== undefined) companyUpdates.name = dto.companyName;
|
||||||
if (dto.companyEmail !== undefined) companyUpdates.email = dto.companyEmail;
|
if (dto.companyEmail !== undefined) companyUpdates.email = dto.companyEmail;
|
||||||
if (dto.companyPhone !== undefined) companyUpdates.phone = dto.companyPhone;
|
if (dto.companyPhone !== undefined) companyUpdates.phone = dto.companyPhone;
|
||||||
if (dto.companyLocation !== undefined) companyUpdates.country = dto.companyLocation;
|
if (dto.companyLocation !== undefined)
|
||||||
if (dto.companyAddress !== undefined) companyUpdates.address = dto.companyAddress;
|
companyUpdates.country = dto.companyLocation;
|
||||||
|
if (dto.companyAddress !== undefined)
|
||||||
|
companyUpdates.address = dto.companyAddress;
|
||||||
if (dto.tin !== undefined) companyUpdates.tin = dto.tin;
|
if (dto.tin !== undefined) companyUpdates.tin = dto.tin;
|
||||||
if (dto.vatNumber !== undefined) companyUpdates.vatNumber = dto.vatNumber;
|
if (dto.vatNumber !== undefined) companyUpdates.vatNumber = dto.vatNumber;
|
||||||
if (dto.fanNumber !== undefined) {
|
if (dto.fanNumber !== undefined) {
|
||||||
@@ -242,21 +299,28 @@ export class CompaniesService {
|
|||||||
companyUpdates.fanNumber = dto.fanNumber;
|
companyUpdates.fanNumber = dto.fanNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dto.contactPersonName !== undefined) attrUpdates.contactPersonName = dto.contactPersonName;
|
if (dto.contactPersonName !== undefined)
|
||||||
if (dto.contactPersonPhone !== undefined) attrUpdates.contactPersonPhone = dto.contactPersonPhone;
|
attrUpdates.contactPersonName = dto.contactPersonName;
|
||||||
if (dto.generalManagerName !== undefined) attrUpdates.generalManagerName = dto.generalManagerName;
|
if (dto.contactPersonPhone !== undefined)
|
||||||
if (dto.generalManagerEmail !== undefined) attrUpdates.generalManagerEmail = dto.generalManagerEmail;
|
attrUpdates.contactPersonPhone = dto.contactPersonPhone;
|
||||||
if (dto.generalManagerPhone !== undefined) attrUpdates.generalManagerPhone = dto.generalManagerPhone;
|
if (dto.generalManagerName !== undefined)
|
||||||
|
attrUpdates.generalManagerName = dto.generalManagerName;
|
||||||
|
if (dto.generalManagerEmail !== undefined)
|
||||||
|
attrUpdates.generalManagerEmail = dto.generalManagerEmail;
|
||||||
|
if (dto.generalManagerPhone !== undefined)
|
||||||
|
attrUpdates.generalManagerPhone = dto.generalManagerPhone;
|
||||||
if (dto.poaName !== undefined) attrUpdates.poaName = dto.poaName;
|
if (dto.poaName !== undefined) attrUpdates.poaName = dto.poaName;
|
||||||
if (dto.poaPhone !== undefined) attrUpdates.poaPhone = dto.poaPhone;
|
if (dto.poaPhone !== undefined) attrUpdates.poaPhone = dto.poaPhone;
|
||||||
if (dto.poaEmail !== undefined) attrUpdates.poaEmail = dto.poaEmail;
|
if (dto.poaEmail !== undefined) attrUpdates.poaEmail = dto.poaEmail;
|
||||||
if (dto.poaLocation !== undefined) attrUpdates.poaLocation = dto.poaLocation;
|
if (dto.poaLocation !== undefined)
|
||||||
|
attrUpdates.poaLocation = dto.poaLocation;
|
||||||
if (dto.poaAddress !== undefined) attrUpdates.poaAddress = dto.poaAddress;
|
if (dto.poaAddress !== undefined) attrUpdates.poaAddress = dto.poaAddress;
|
||||||
|
|
||||||
companyUpdates.attributes = attrUpdates;
|
companyUpdates.attributes = attrUpdates;
|
||||||
|
|
||||||
const updated = await this.companiesRepo.update(company.id, companyUpdates);
|
const updated = await this.companiesRepo.update(company.id, companyUpdates);
|
||||||
if (!updated) throw new NotFoundException(`Company ${company.id} not found`);
|
if (!updated)
|
||||||
|
throw new NotFoundException(`Company ${company.id} not found`);
|
||||||
return new ProfileResponseDto(profile, updated);
|
return new ProfileResponseDto(profile, updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +334,9 @@ export class CompaniesService {
|
|||||||
|
|
||||||
const existing = await this.profilesRepo.findByEmail(dto.email);
|
const existing = await this.profilesRepo.findByEmail(dto.email);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
throw new ConflictException(`Profile with email ${dto.email} already exists`);
|
throw new ConflictException(
|
||||||
|
`Profile with email ${dto.email} already exists`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.profilesRepo.create(dto);
|
return this.profilesRepo.create(dto);
|
||||||
@@ -278,36 +344,12 @@ export class CompaniesService {
|
|||||||
|
|
||||||
async findProfileByUserId(userId: string): Promise<ExternalProfile> {
|
async findProfileByUserId(userId: string): Promise<ExternalProfile> {
|
||||||
const profile = await this.profilesRepo.findByUserId(userId);
|
const profile = await this.profilesRepo.findByUserId(userId);
|
||||||
if (!profile) throw new NotFoundException(`Profile for user ${userId} not found`);
|
if (!profile)
|
||||||
|
throw new NotFoundException(`Profile for user ${userId} not found`);
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findProfilesByCompany(companyId: string): Promise<ExternalProfile[]> {
|
async findProfilesByCompany(companyId: string): Promise<ExternalProfile[]> {
|
||||||
return this.profilesRepo.findByCompanyId(companyId);
|
return this.profilesRepo.findByCompanyId(companyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createFFClient(dto: CreateFFClientDto): Promise<FFClient> {
|
|
||||||
await this.findCompanyById(dto.forwarderCompanyId);
|
|
||||||
await this.findCompanyById(dto.clientCompanyId);
|
|
||||||
|
|
||||||
const existing = await this.ffClientsRepo.findRelationship(
|
|
||||||
dto.forwarderCompanyId,
|
|
||||||
dto.clientCompanyId,
|
|
||||||
);
|
|
||||||
if (existing) {
|
|
||||||
throw new ConflictException('This forwarder-client relationship already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.ffClientsRepo.create(dto);
|
|
||||||
}
|
|
||||||
|
|
||||||
async findForwarderClients(forwarderCompanyId: string): Promise<FFClient[]> {
|
|
||||||
return this.ffClientsRepo.findByForwarder(forwarderCompanyId);
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteFFClient(id: string): Promise<void> {
|
|
||||||
const client = await this.ffClientsRepo.findById(id);
|
|
||||||
if (!client) throw new NotFoundException(`FFClient ${id} not found`);
|
|
||||||
await this.ffClientsRepo.softDelete(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
import { IsUUID, IsNotEmpty, IsOptional, IsBoolean, IsEnum } from 'class-validator';
|
|
||||||
import { FFClientRelationship } from '../entities/ff-client.entity';
|
|
||||||
|
|
||||||
export class CreateFFClientDto {
|
|
||||||
@IsUUID()
|
|
||||||
@IsNotEmpty()
|
|
||||||
forwarderCompanyId!: string;
|
|
||||||
|
|
||||||
@IsUUID()
|
|
||||||
@IsNotEmpty()
|
|
||||||
clientCompanyId!: string;
|
|
||||||
|
|
||||||
@IsOptional()
|
|
||||||
@IsEnum(FFClientRelationship)
|
|
||||||
relationshipType?: FFClientRelationship;
|
|
||||||
|
|
||||||
@IsOptional()
|
|
||||||
@IsBoolean()
|
|
||||||
canBookOnBehalf?: boolean;
|
|
||||||
|
|
||||||
@IsOptional()
|
|
||||||
@IsBoolean()
|
|
||||||
canViewDocuments?: boolean;
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { FFClient, FFClientRelationship } from '../entities/ff-client.entity';
|
|
||||||
|
|
||||||
export class ResponseFFClientDto {
|
|
||||||
id: string;
|
|
||||||
forwarderCompanyId: string;
|
|
||||||
clientCompanyId: string;
|
|
||||||
relationshipType: FFClientRelationship;
|
|
||||||
canBookOnBehalf: boolean;
|
|
||||||
canViewDocuments: boolean;
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
|
|
||||||
constructor(client: FFClient) {
|
|
||||||
this.id = client.id;
|
|
||||||
this.forwarderCompanyId = client.forwarderCompanyId;
|
|
||||||
this.clientCompanyId = client.clientCompanyId;
|
|
||||||
this.relationshipType = client.relationshipType;
|
|
||||||
this.canBookOnBehalf = client.canBookOnBehalf;
|
|
||||||
this.canViewDocuments = client.canViewDocuments;
|
|
||||||
this.createdAt = client.createdAt;
|
|
||||||
this.updatedAt = client.updatedAt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import { PartialType } from '@nestjs/mapped-types';
|
|
||||||
import { CreateFFClientDto } from './create-ff-client.dto';
|
|
||||||
|
|
||||||
export class UpdateFFClientDto extends PartialType(CreateFFClientDto) {}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { BaseEntity } from '@edr/api-common';
|
|
||||||
import { Column, Entity, Index, ManyToOne, JoinColumn, Unique } from 'typeorm';
|
|
||||||
import { Company } from './company.entity';
|
|
||||||
|
|
||||||
export enum FFClientRelationship {
|
|
||||||
ManagedAccount = 'managed_account',
|
|
||||||
SubAgent = 'sub_agent',
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity({ schema: 'freight', name: 'ff_clients' })
|
|
||||||
@Unique(['forwarderCompanyId', 'clientCompanyId'])
|
|
||||||
@Index(['forwarderCompanyId'])
|
|
||||||
@Index(['clientCompanyId'])
|
|
||||||
export class FFClient extends BaseEntity {
|
|
||||||
@Column({ name: 'forwarder_company_id', type: 'uuid' })
|
|
||||||
forwarderCompanyId!: string;
|
|
||||||
|
|
||||||
@ManyToOne(() => Company)
|
|
||||||
@JoinColumn({ name: 'forwarder_company_id' })
|
|
||||||
forwarderCompany!: Company;
|
|
||||||
|
|
||||||
@Column({ name: 'client_company_id', type: 'uuid' })
|
|
||||||
clientCompanyId!: string;
|
|
||||||
|
|
||||||
@ManyToOne(() => Company)
|
|
||||||
@JoinColumn({ name: 'client_company_id' })
|
|
||||||
clientCompany!: Company;
|
|
||||||
|
|
||||||
@Column({ name: 'relationship_type', type: 'varchar', length: 32, default: FFClientRelationship.ManagedAccount })
|
|
||||||
relationshipType!: FFClientRelationship;
|
|
||||||
|
|
||||||
@Column({ name: 'can_book_on_behalf', type: 'boolean', default: true })
|
|
||||||
canBookOnBehalf!: boolean;
|
|
||||||
|
|
||||||
@Column({ name: 'can_view_documents', type: 'boolean', default: true })
|
|
||||||
canViewDocuments!: boolean;
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
import { BaseRepository } from '@edr/api-common';
|
|
||||||
import { FFClient } from './entities/ff-client.entity';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class FFClientRepository extends BaseRepository<FFClient> {
|
|
||||||
constructor(
|
|
||||||
@InjectRepository(FFClient)
|
|
||||||
repo: Repository<FFClient>,
|
|
||||||
) {
|
|
||||||
super(repo);
|
|
||||||
}
|
|
||||||
|
|
||||||
async findByForwarder(forwarderCompanyId: string): Promise<FFClient[]> {
|
|
||||||
return this.repository.find({ where: { forwarderCompanyId } as any });
|
|
||||||
}
|
|
||||||
|
|
||||||
async findByClient(clientCompanyId: string): Promise<FFClient[]> {
|
|
||||||
return this.repository.find({ where: { clientCompanyId } as any });
|
|
||||||
}
|
|
||||||
|
|
||||||
async findRelationship(
|
|
||||||
forwarderCompanyId: string,
|
|
||||||
clientCompanyId: string,
|
|
||||||
): Promise<FFClient | null> {
|
|
||||||
return this.repository.findOne({
|
|
||||||
where: { forwarderCompanyId, clientCompanyId } as any,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user