mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
Merge pull request #176 from Tria-plc/freight_feature/priority
Freight feature/priority
This commit is contained in:
@@ -46,6 +46,7 @@ import { PaymentModule } from "./modules/payment/payment.module";
|
||||
import { DemoBookingsSeeder } from "./seed/demo-bookings.seeder";
|
||||
import { PricingDataSeeder } from "./seed/pricing-data.seeder";
|
||||
import { FileUploadSettingsSeeder } from "./seed/file-upload-settings.seeder";
|
||||
import { FreightPermissionKeyMigrationSeeder } from "./seed/freight-permission-key-migration.seeder";
|
||||
//New Trains, Wagons, Container and Cargo management modules
|
||||
import { TrainsModule } from "./modules/trains/trains.module";
|
||||
import { WagonsModule } from './modules/wagons/wagons.module';
|
||||
@@ -110,7 +111,15 @@ import { OverviewModule } from './modules/overview/overview.module';
|
||||
RoutesModule,
|
||||
OverviewModule,
|
||||
],
|
||||
providers: [EdrOrgSeeder, DemoUsersSeeder,FreightStaffUsersSeeder, DemoBookingsSeeder, PricingDataSeeder, FileUploadSettingsSeeder],
|
||||
providers: [
|
||||
EdrOrgSeeder,
|
||||
DemoUsersSeeder,
|
||||
FreightStaffUsersSeeder,
|
||||
DemoBookingsSeeder,
|
||||
PricingDataSeeder,
|
||||
FileUploadSettingsSeeder,
|
||||
FreightPermissionKeyMigrationSeeder,
|
||||
],
|
||||
})
|
||||
export class AppModule implements OnApplicationBootstrap {
|
||||
constructor(
|
||||
@@ -121,9 +130,11 @@ export class AppModule implements OnApplicationBootstrap {
|
||||
private readonly demoBookingsSeeder: DemoBookingsSeeder,
|
||||
private readonly pricingDataSeeder: PricingDataSeeder,
|
||||
private readonly fileUploadSettingsSeeder: FileUploadSettingsSeeder,
|
||||
private readonly freightPermissionKeyMigrationSeeder: FreightPermissionKeyMigrationSeeder,
|
||||
) { }
|
||||
|
||||
async onApplicationBootstrap() {
|
||||
await this.freightPermissionKeyMigrationSeeder.run();
|
||||
await this.seeder.run();
|
||||
await this.edrOrgSeeder.run();
|
||||
await this.demoUsersSeeder.run();
|
||||
|
||||
@@ -128,6 +128,20 @@ export class BookingsController {
|
||||
return this.bookingsService.getListSummary(filter);
|
||||
}
|
||||
|
||||
@Get('my')
|
||||
@ApiOperation({
|
||||
summary: "List the current customer's bookings ready for payment",
|
||||
description:
|
||||
'Bookings owned by the authenticated user\'s company that are payable ' +
|
||||
'(FULLY_EXECUTED, SELECTED_FOR_BATCH, AWAITING_PAYMENT) and not yet PAID.',
|
||||
})
|
||||
findMyPayable(
|
||||
@CurrentUser() user: AuthUserPayload,
|
||||
@Query() filter: FilterBookingDto,
|
||||
) {
|
||||
return this.bookingsService.findMyPayable(resolveAuthUserId(user), filter);
|
||||
}
|
||||
|
||||
@Get('queues/:queue')
|
||||
@ApiOperation({
|
||||
summary: 'List bookings for a dashboard queue',
|
||||
|
||||
@@ -31,6 +31,8 @@ export interface BookingListFilterOptions {
|
||||
freightType?: string;
|
||||
tradeDirection?: string;
|
||||
paymentCurrency?: string;
|
||||
paymentStatus?: string;
|
||||
excludePaymentStatus?: string;
|
||||
allowConsolidation?: boolean;
|
||||
consolidationPaired?: string;
|
||||
}
|
||||
@@ -570,6 +572,16 @@ export class BookingsRepository extends BaseRepository<Booking> {
|
||||
paymentCurrency: options.paymentCurrency,
|
||||
});
|
||||
}
|
||||
if (options.paymentStatus) {
|
||||
qb.andWhere('booking.payment_status = :paymentStatus', {
|
||||
paymentStatus: options.paymentStatus,
|
||||
});
|
||||
}
|
||||
if (options.excludePaymentStatus) {
|
||||
qb.andWhere('booking.payment_status != :excludePaymentStatus', {
|
||||
excludePaymentStatus: options.excludePaymentStatus,
|
||||
});
|
||||
}
|
||||
if (options.allowConsolidation !== undefined) {
|
||||
qb.andWhere('booking.allow_consolidation = :allowConsolidation', {
|
||||
allowConsolidation: options.allowConsolidation,
|
||||
|
||||
@@ -552,6 +552,7 @@ export class BookingsService {
|
||||
freightType: filter.freightType,
|
||||
tradeDirection: filter.tradeDirection,
|
||||
paymentCurrency: filter.paymentCurrency,
|
||||
paymentStatus: filter.paymentStatus,
|
||||
allowConsolidation: filter.allowConsolidation,
|
||||
consolidationPaired: filter.consolidationPaired,
|
||||
sortBy: filter.sortBy,
|
||||
@@ -559,6 +560,35 @@ export class BookingsService {
|
||||
});
|
||||
}
|
||||
|
||||
/** Booking statuses at which a customer can pay (mirrors booking-payment.service). */
|
||||
private static readonly PAYABLE_STATUSES = [
|
||||
'FULLY_EXECUTED',
|
||||
'SELECTED_FOR_BATCH',
|
||||
'AWAITING_PAYMENT',
|
||||
];
|
||||
|
||||
/**
|
||||
* List the current customer's bookings that are ready for payment:
|
||||
* payable status AND not yet PAID. Company scope is derived from the
|
||||
* authenticated user and cannot be widened by the caller.
|
||||
*/
|
||||
async findMyPayable(
|
||||
userId: string,
|
||||
filter: FilterBookingDto,
|
||||
): Promise<{ items: Booking[]; total: number }> {
|
||||
const { company } = await this.companiesService.getCompanyInfoByUserId(userId);
|
||||
|
||||
return this.bookingsRepository.findAllPaginated({
|
||||
page: filter.page ?? 1,
|
||||
pageSize: filter.pageSize ?? 20,
|
||||
statuses: BookingsService.PAYABLE_STATUSES,
|
||||
excludePaymentStatus: 'PAID',
|
||||
companyId: company.id,
|
||||
sortBy: filter.sortBy,
|
||||
sortOrder: filter.sortOrder,
|
||||
});
|
||||
}
|
||||
|
||||
/** Aggregate metrics and tab counts for the backoffice booking list. */
|
||||
async getListSummary(filter: FilterBookingDto): Promise<BookingListSummaryDto> {
|
||||
const page = filter.page ?? 1;
|
||||
@@ -573,6 +603,7 @@ export class BookingsService {
|
||||
freightType: filter.freightType,
|
||||
tradeDirection: filter.tradeDirection,
|
||||
paymentCurrency: filter.paymentCurrency,
|
||||
paymentStatus: filter.paymentStatus,
|
||||
allowConsolidation: filter.allowConsolidation,
|
||||
consolidationPaired: filter.consolidationPaired,
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
PAYMENT_CURRENCIES,
|
||||
TRADE_DIRECTIONS,
|
||||
} from './create-booking.dto';
|
||||
import { PAYMENT_STATUSES } from '../entities/booking.entity';
|
||||
|
||||
export class FilterBookingDto {
|
||||
@ApiPropertyOptional({ enum: BOOKING_STATUSES })
|
||||
@@ -65,6 +66,11 @@ export class FilterBookingDto {
|
||||
@IsIn([...PAYMENT_CURRENCIES])
|
||||
paymentCurrency?: string;
|
||||
|
||||
@ApiPropertyOptional({ enum: PAYMENT_STATUSES })
|
||||
@IsOptional()
|
||||
@IsIn([...PAYMENT_STATUSES])
|
||||
paymentStatus?: string;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
@IsOptional()
|
||||
@Transform(({ value }) => value === 'true' || value === true)
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { Permission } from '@tria-plc/iamapi-common';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
/** Renamed rule-engine resources: old key -> new key (same permission id). */
|
||||
const PERMISSION_KEY_RENAMES: ReadonlyArray<{ from: string; to: string }> = [
|
||||
{
|
||||
from: 'edr_freight_app:rule_engine:priority_rules:view',
|
||||
to: 'edr_freight_app:rule_engine:priority_configs:view',
|
||||
},
|
||||
{
|
||||
from: 'edr_freight_app:rule_engine:priority_rules:manage',
|
||||
to: 'edr_freight_app:rule_engine:priority_configs:manage',
|
||||
},
|
||||
];
|
||||
|
||||
@Injectable()
|
||||
export class FreightPermissionKeyMigrationSeeder {
|
||||
private readonly logger = new Logger(FreightPermissionKeyMigrationSeeder.name);
|
||||
|
||||
constructor(private readonly dataSource: DataSource) {}
|
||||
|
||||
async run() {
|
||||
const permissionRepository = this.dataSource.getRepository(Permission);
|
||||
|
||||
for (const { from, to } of PERMISSION_KEY_RENAMES) {
|
||||
const existing = await permissionRepository.findOne({
|
||||
where: { key: from },
|
||||
select: { id: true, key: true },
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const targetExists = await permissionRepository.existsBy({ key: to });
|
||||
if (targetExists) {
|
||||
this.logger.warn(
|
||||
`Skipping permission key rename ${from} -> ${to}: target key already exists`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
await permissionRepository.update({ id: existing.id }, { key: to });
|
||||
this.logger.log(`Renamed permission key ${from} -> ${to}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --port 5183",
|
||||
"build": "cd ./user-management-config && npm run build && tsc -b && vite build",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --port 5183",
|
||||
"lint": "eslint src",
|
||||
"test": "vitest run",
|
||||
@@ -17,10 +17,10 @@
|
||||
"dependencies": {
|
||||
"@edr/types": "workspace:*",
|
||||
"@edr/ui-common": "workspace:*",
|
||||
"@hello-pangea/dnd": "^18.0.1",
|
||||
"@mantine/core": "^9.3.0",
|
||||
"@mantine/hooks": "^9.3.0",
|
||||
"@tabler/icons-react": "^3.44.0",
|
||||
"@hello-pangea/dnd": "^18.0.1",
|
||||
"@tanstack/react-query": "^5.100.11",
|
||||
"@tria-plc/iamui-common": "1.1.2",
|
||||
"axios": "^1.7.7",
|
||||
@@ -36,6 +36,7 @@
|
||||
"recharts": "^3.8.1",
|
||||
"sonner": "^2.0.7",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
"tinymce": "^8.6.0",
|
||||
"zustand": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
12
apps/edr-freight-web/backoffice/user-management-config/package-lock.json
generated
Normal file
12
apps/edr-freight-web/backoffice/user-management-config/package-lock.json
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "user-management-host",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "user-management-host",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -249,6 +249,9 @@ importers:
|
||||
tailwind-merge:
|
||||
specifier: ^3.6.0
|
||||
version: 3.6.0
|
||||
tinymce:
|
||||
specifier: ^8.6.0
|
||||
version: 8.6.0
|
||||
zustand:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.14(@types/react@18.3.31)(immer@11.1.8)(react@19.2.6)(use-sync-external-store@1.6.0(react@19.2.6))
|
||||
@@ -11690,6 +11693,9 @@ packages:
|
||||
tinymce@7.9.3:
|
||||
resolution: {integrity: sha512-Mtm54U5YJ6Pyo/GaAx+JSHXTGEuxrg2AowVWCD9zy1eBolp5Ub7S1rTtsyQdxhPegfhLuR3VLiTKGw1tacv09g==}
|
||||
|
||||
tinymce@8.6.0:
|
||||
resolution: {integrity: sha512-qODYoNL4cPIzNFpkEEQDm3DWI78I/nQXIPwUMHRN+q/LyT9Wzt7xis2Nfhk88kV6sibp6MJXPSaUDVNe02ZB/w==}
|
||||
|
||||
tinypool@1.1.1:
|
||||
resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
@@ -26602,6 +26608,8 @@ snapshots:
|
||||
|
||||
tinymce@7.9.3: {}
|
||||
|
||||
tinymce@8.6.0: {}
|
||||
|
||||
tinypool@1.1.1: {}
|
||||
|
||||
tinyrainbow@1.2.0: {}
|
||||
|
||||
Reference in New Issue
Block a user