mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 18:20:57 +00:00
change price logic on the ,rule engine ui, auto generate the contrat
This commit is contained in:
@@ -1,17 +1,20 @@
|
||||
import { Inject, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { CreateApprovalRuleDto } from '../dto/create-approval-rule.dto';
|
||||
import { ReorderItemsDto } from '../dto/reorder-items.dto';
|
||||
import { UpdateApprovalRuleDto } from '../dto/update-approval-rule.dto';
|
||||
import { ApprovalRule } from '../entities/approval-rule.entity';
|
||||
import {
|
||||
APPROVAL_RULES_REPOSITORY,
|
||||
IApprovalRulesRepository,
|
||||
} from '../interfaces/approval-rules.repository.interface';
|
||||
import { DisplayOrderService } from './display-order.service';
|
||||
|
||||
@Injectable()
|
||||
export class ApprovalRulesService {
|
||||
constructor(
|
||||
@Inject(APPROVAL_RULES_REPOSITORY)
|
||||
private readonly repository: IApprovalRulesRepository,
|
||||
private readonly displayOrder: DisplayOrderService,
|
||||
) {}
|
||||
|
||||
/** List approval rules. */
|
||||
@@ -21,7 +24,7 @@ export class ApprovalRulesService {
|
||||
pageSize?: number;
|
||||
}): Promise<{ data: ApprovalRule[]; meta: { total: number; page: number; pageSize: number; totalPages: number } }> {
|
||||
const page = filter.page ?? 1;
|
||||
const pageSize = filter.pageSize ?? 20;
|
||||
const pageSize = filter.pageSize ?? 10;
|
||||
const where: Record<string, unknown> = {};
|
||||
if (filter.requiresDirectorApproval !== undefined) {
|
||||
where.requiresDirectorApproval = filter.requiresDirectorApproval;
|
||||
@@ -50,9 +53,20 @@ export class ApprovalRulesService {
|
||||
|
||||
/** Create an approval rule step. */
|
||||
async create(dto: CreateApprovalRuleDto): Promise<ApprovalRule> {
|
||||
if (dto.stepOrder !== undefined && dto.insertAfterId) {
|
||||
throw new BadRequestException('Cannot set both stepOrder and insertAfterId');
|
||||
}
|
||||
|
||||
const scopeWhere = { requiresDirectorApproval: dto.requiresDirectorApproval };
|
||||
const stepOrder = await this.displayOrder.resolveCreateOrder(ApprovalRule, 'stepOrder', {
|
||||
explicitOrder: dto.stepOrder,
|
||||
insertAfterId: dto.insertAfterId,
|
||||
scopeWhere,
|
||||
});
|
||||
|
||||
return this.repository.create({
|
||||
requiresDirectorApproval: dto.requiresDirectorApproval,
|
||||
stepOrder: dto.stepOrder,
|
||||
stepOrder,
|
||||
requiredRole: dto.requiredRole,
|
||||
actionLabel: dto.actionLabel,
|
||||
blocksRole: dto.blocksRole,
|
||||
@@ -72,4 +86,20 @@ export class ApprovalRulesService {
|
||||
await this.findById(id);
|
||||
await this.repository.softDelete(id);
|
||||
}
|
||||
|
||||
async reorder(dto: ReorderItemsDto): Promise<void> {
|
||||
if (dto.requiresDirectorApproval === undefined) {
|
||||
throw new BadRequestException('requiresDirectorApproval is required for approval rule reorder');
|
||||
}
|
||||
await this.displayOrder.reorderByIds(ApprovalRule, 'stepOrder', dto.ids, {
|
||||
requiresDirectorApproval: dto.requiresDirectorApproval,
|
||||
});
|
||||
}
|
||||
|
||||
async moveOrder(id: string, direction: 'up' | 'down'): Promise<void> {
|
||||
const rule = await this.findById(id);
|
||||
await this.displayOrder.moveOne(ApprovalRule, 'stepOrder', id, direction, {
|
||||
requiresDirectorApproval: rule.requiresDirectorApproval,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,21 @@ import { ConflictException, Inject, Injectable, NotFoundException } from '@nestj
|
||||
import { ILike } from 'typeorm';
|
||||
import { generateCode } from '../../../common/utils/generate-code.util';
|
||||
import { CreateCargoTypeDto } from '../dto/create-cargo-type.dto';
|
||||
import { ReorderItemsDto } from '../dto/reorder-items.dto';
|
||||
import { UpdateCargoTypeDto } from '../dto/update-cargo-type.dto';
|
||||
import { CargoType } from '../entities/cargo-type.entity';
|
||||
import {
|
||||
CARGO_TYPES_REPOSITORY,
|
||||
ICargoTypesRepository,
|
||||
} from '../interfaces/cargo-types.repository.interface';
|
||||
import { DisplayOrderService } from './display-order.service';
|
||||
|
||||
@Injectable()
|
||||
export class CargoTypesService {
|
||||
constructor(
|
||||
@Inject(CARGO_TYPES_REPOSITORY)
|
||||
private readonly repository: ICargoTypesRepository,
|
||||
private readonly displayOrder: DisplayOrderService,
|
||||
) {}
|
||||
|
||||
/** List cargo types with pagination and optional filtering. */
|
||||
@@ -28,7 +31,7 @@ export class CargoTypesService {
|
||||
sortOrder?: 'ASC' | 'DESC';
|
||||
}): Promise<{ data: CargoType[]; meta: { total: number; page: number; pageSize: number; totalPages: number } }> {
|
||||
const page = filter.page ?? 1;
|
||||
const pageSize = filter.pageSize ?? 20;
|
||||
const pageSize = filter.pageSize ?? 10;
|
||||
const where: Record<string, unknown> = {};
|
||||
if (filter.isActive !== undefined) where.isActive = filter.isActive;
|
||||
if (filter.requiresDirectorApproval !== undefined) where.requiresDirectorApproval = filter.requiresDirectorApproval;
|
||||
@@ -66,6 +69,12 @@ export class CargoTypesService {
|
||||
const parent = await this.repository.findById(dto.parentGroupId);
|
||||
if (!parent) throw new NotFoundException(`Parent cargo type ${dto.parentGroupId} not found`);
|
||||
}
|
||||
|
||||
const displayOrder = await this.displayOrder.resolveCreateOrder(CargoType, 'displayOrder', {
|
||||
explicitOrder: dto.displayOrder,
|
||||
insertAfterId: dto.insertAfterId,
|
||||
});
|
||||
|
||||
return this.repository.create({
|
||||
code,
|
||||
cargoTypeName: dto.cargoTypeName,
|
||||
@@ -73,7 +82,7 @@ export class CargoTypesService {
|
||||
showFreeTextBox: dto.showFreeTextBox ?? false,
|
||||
requiresDirectorApproval: dto.requiresDirectorApproval ?? false,
|
||||
isActive: dto.isActive ?? true,
|
||||
displayOrder: dto.displayOrder ?? 1,
|
||||
displayOrder,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -95,4 +104,13 @@ export class CargoTypesService {
|
||||
await this.findById(id);
|
||||
await this.repository.softDelete(id);
|
||||
}
|
||||
|
||||
async reorder(dto: ReorderItemsDto): Promise<void> {
|
||||
await this.displayOrder.reorderByIds(CargoType, 'displayOrder', dto.ids);
|
||||
}
|
||||
|
||||
async moveOrder(id: string, direction: 'up' | 'down'): Promise<void> {
|
||||
await this.findById(id);
|
||||
await this.displayOrder.moveOne(CargoType, 'displayOrder', id, direction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import { ConflictException, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { generateCode } from '../../../common/utils/generate-code.util';
|
||||
import { CreateContainerTypeDto } from '../dto/create-container-type.dto';
|
||||
import { ReorderItemsDto } from '../dto/reorder-items.dto';
|
||||
import { UpdateContainerTypeDto } from '../dto/update-container-type.dto';
|
||||
import { ContainerType } from '../entities/container-type.entity';
|
||||
import {
|
||||
CONTAINER_TYPES_REPOSITORY,
|
||||
IContainerTypesRepository,
|
||||
} from '../interfaces/container-types.repository.interface';
|
||||
import { DisplayOrderService } from './display-order.service';
|
||||
|
||||
@Injectable()
|
||||
export class ContainerTypesService {
|
||||
constructor(
|
||||
@Inject(CONTAINER_TYPES_REPOSITORY)
|
||||
private readonly repository: IContainerTypesRepository,
|
||||
private readonly displayOrder: DisplayOrderService,
|
||||
) {}
|
||||
|
||||
/** List container types with pagination. */
|
||||
@@ -22,7 +25,7 @@ export class ContainerTypesService {
|
||||
pageSize?: number;
|
||||
}): Promise<{ data: ContainerType[]; meta: { total: number; page: number; pageSize: number; totalPages: number } }> {
|
||||
const page = filter.page ?? 1;
|
||||
const pageSize = filter.pageSize ?? 20;
|
||||
const pageSize = filter.pageSize ?? 10;
|
||||
const where: Record<string, unknown> = {};
|
||||
if (filter.isActive !== undefined) where.isActive = filter.isActive;
|
||||
|
||||
@@ -47,6 +50,12 @@ export class ContainerTypesService {
|
||||
const code = generateCode(dto.label);
|
||||
const existing = await this.repository.findByCode(code);
|
||||
if (existing) throw new ConflictException(`Container type with label "${dto.label}" conflicts with existing code "${code}"`);
|
||||
|
||||
const displayOrder = await this.displayOrder.resolveCreateOrder(ContainerType, 'displayOrder', {
|
||||
explicitOrder: dto.displayOrder,
|
||||
insertAfterId: dto.insertAfterId,
|
||||
});
|
||||
|
||||
return this.repository.create({
|
||||
code,
|
||||
label: dto.label,
|
||||
@@ -55,7 +64,7 @@ export class ContainerTypesService {
|
||||
isReefer: dto.isReefer ?? false,
|
||||
isOpenTop: dto.isOpenTop ?? false,
|
||||
isActive: dto.isActive ?? true,
|
||||
displayOrder: dto.displayOrder ?? 1,
|
||||
displayOrder,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -72,4 +81,13 @@ export class ContainerTypesService {
|
||||
await this.findById(id);
|
||||
await this.repository.softDelete(id);
|
||||
}
|
||||
|
||||
async reorder(dto: ReorderItemsDto): Promise<void> {
|
||||
await this.displayOrder.reorderByIds(ContainerType, 'displayOrder', dto.ids);
|
||||
}
|
||||
|
||||
async moveOrder(id: string, direction: 'up' | 'down'): Promise<void> {
|
||||
await this.findById(id);
|
||||
await this.displayOrder.moveOne(ContainerType, 'displayOrder', id, direction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { DataSource, EntityTarget, FindOptionsWhere, ObjectLiteral } from 'typeorm';
|
||||
|
||||
export type OrderField = 'displayOrder' | 'stepOrder';
|
||||
|
||||
@Injectable()
|
||||
export class DisplayOrderService {
|
||||
constructor(private readonly dataSource: DataSource) {}
|
||||
|
||||
async getMaxOrder<T extends ObjectLiteral>(
|
||||
entity: EntityTarget<T>,
|
||||
field: OrderField,
|
||||
where?: FindOptionsWhere<T>,
|
||||
): Promise<number> {
|
||||
const repo = this.dataSource.getRepository(entity);
|
||||
const qb = repo.createQueryBuilder('e').select(`MAX(e.${field})`, 'max');
|
||||
if (where) {
|
||||
Object.entries(where).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
qb.andWhere(`e.${key} = :${key}`, { [key]: value });
|
||||
}
|
||||
});
|
||||
}
|
||||
const row = await qb.getRawOne<{ max: string | null }>();
|
||||
return row?.max ? Number(row.max) : 0;
|
||||
}
|
||||
|
||||
async resolveCreateOrder<T extends ObjectLiteral>(
|
||||
entity: EntityTarget<T>,
|
||||
field: OrderField,
|
||||
options: {
|
||||
explicitOrder?: number;
|
||||
insertAfterId?: string;
|
||||
scopeWhere?: FindOptionsWhere<T>;
|
||||
},
|
||||
): Promise<number> {
|
||||
const { explicitOrder, insertAfterId, scopeWhere } = options;
|
||||
|
||||
if (insertAfterId) {
|
||||
if (explicitOrder !== undefined) {
|
||||
throw new BadRequestException('Cannot set both explicit order and insertAfterId');
|
||||
}
|
||||
const repo = this.dataSource.getRepository(entity);
|
||||
const after = await repo.findOne({
|
||||
where: { id: insertAfterId, ...scopeWhere } as unknown as FindOptionsWhere<T>,
|
||||
});
|
||||
if (!after) {
|
||||
throw new NotFoundException(`Record ${insertAfterId} not found in scope`);
|
||||
}
|
||||
const afterOrder = Number((after as Record<string, unknown>)[field]);
|
||||
await this.shiftOrdersFrom(entity, field, afterOrder + 1, 1, scopeWhere);
|
||||
return afterOrder + 1;
|
||||
}
|
||||
|
||||
if (explicitOrder !== undefined) {
|
||||
return explicitOrder;
|
||||
}
|
||||
|
||||
const max = await this.getMaxOrder(entity, field, scopeWhere);
|
||||
return max + 1;
|
||||
}
|
||||
|
||||
async reorderByIds<T extends ObjectLiteral>(
|
||||
entity: EntityTarget<T>,
|
||||
field: OrderField,
|
||||
ids: string[],
|
||||
scopeWhere?: FindOptionsWhere<T>,
|
||||
): Promise<void> {
|
||||
const repo = this.dataSource.getRepository(entity);
|
||||
const existing = await repo.find({
|
||||
where: scopeWhere,
|
||||
order: { [field]: 'ASC' } as never,
|
||||
});
|
||||
|
||||
const scopedIds = new Set(existing.map((row) => String(row.id)));
|
||||
if (ids.length !== scopedIds.size) {
|
||||
throw new BadRequestException('Reorder list must include every item in scope exactly once');
|
||||
}
|
||||
for (const id of ids) {
|
||||
if (!scopedIds.has(id)) {
|
||||
throw new BadRequestException(`ID ${id} is not in the reorder scope`);
|
||||
}
|
||||
}
|
||||
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
try {
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
await queryRunner.manager.update(entity, ids[i], { [field]: -(i + 1) } as never);
|
||||
}
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
await queryRunner.manager.update(entity, ids[i], { [field]: i + 1 } as never);
|
||||
}
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (err) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw err;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
async moveOne<T extends ObjectLiteral>(
|
||||
entity: EntityTarget<T>,
|
||||
field: OrderField,
|
||||
id: string,
|
||||
direction: 'up' | 'down',
|
||||
scopeWhere?: FindOptionsWhere<T>,
|
||||
): Promise<void> {
|
||||
const repo = this.dataSource.getRepository(entity);
|
||||
const items = await repo.find({
|
||||
where: scopeWhere,
|
||||
order: { [field]: 'ASC' } as never,
|
||||
});
|
||||
|
||||
const index = items.findIndex((row) => String(row.id) === id);
|
||||
if (index === -1) {
|
||||
throw new NotFoundException(`Record ${id} not found in scope`);
|
||||
}
|
||||
|
||||
const targetIndex = direction === 'up' ? index - 1 : index + 1;
|
||||
if (targetIndex < 0 || targetIndex >= items.length) {
|
||||
throw new BadRequestException(`Cannot move ${direction}`);
|
||||
}
|
||||
|
||||
const current = items[index] as Record<string, unknown>;
|
||||
const neighbor = items[targetIndex] as Record<string, unknown>;
|
||||
const currentOrder = Number(current[field]);
|
||||
const neighborOrder = Number(neighbor[field]);
|
||||
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
try {
|
||||
await queryRunner.manager.update(entity, String(current.id), { [field]: -1 } as never);
|
||||
await queryRunner.manager.update(entity, String(neighbor.id), { [field]: -2 } as never);
|
||||
await queryRunner.manager.update(entity, String(current.id), { [field]: neighborOrder } as never);
|
||||
await queryRunner.manager.update(entity, String(neighbor.id), { [field]: currentOrder } as never);
|
||||
await queryRunner.commitTransaction();
|
||||
} catch (err) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw err;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
private async shiftOrdersFrom<T extends ObjectLiteral>(
|
||||
entity: EntityTarget<T>,
|
||||
field: OrderField,
|
||||
fromOrder: number,
|
||||
delta: number,
|
||||
scopeWhere?: FindOptionsWhere<T>,
|
||||
): Promise<void> {
|
||||
const repo = this.dataSource.getRepository(entity);
|
||||
const orderColumn = repo.metadata.findColumnWithPropertyName(field)?.databaseName ?? field;
|
||||
const qb = repo
|
||||
.createQueryBuilder()
|
||||
.update()
|
||||
.set({ [field]: () => `"${orderColumn}" + ${delta}` } as never)
|
||||
.where(`"${orderColumn}" >= :fromOrder`, { fromOrder });
|
||||
|
||||
if (scopeWhere) {
|
||||
Object.entries(scopeWhere).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
const col = repo.metadata.findColumnWithPropertyName(key)?.databaseName ?? key;
|
||||
qb.andWhere(`"${col}" = :scope_${key}`, { [`scope_${key}`]: value });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await qb.execute();
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ export class RatesService {
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
});
|
||||
return { data, meta: { total, page, pageSize, totalPages: Math.ceil(total / pageSize) } };
|
||||
return { data, meta: { total, page, pageSize, totalPages: Math.max(1, Math.ceil(total / pageSize)) } };
|
||||
}
|
||||
|
||||
/** Return all currently LIVE rates. */
|
||||
|
||||
@@ -2,18 +2,21 @@ import { ConflictException, Inject, Injectable, NotFoundException } from '@nestj
|
||||
import { ILike } from 'typeorm';
|
||||
import { generateCode } from '../../../common/utils/generate-code.util';
|
||||
import { CreateServiceTypeDto } from '../dto/create-service-type.dto';
|
||||
import { ReorderItemsDto } from '../dto/reorder-items.dto';
|
||||
import { UpdateServiceTypeDto } from '../dto/update-service-type.dto';
|
||||
import { ServiceType } from '../entities/service-type.entity';
|
||||
import {
|
||||
IServiceTypesRepository,
|
||||
SERVICE_TYPES_REPOSITORY,
|
||||
} from '../interfaces/service-types.repository.interface';
|
||||
import { DisplayOrderService } from './display-order.service';
|
||||
|
||||
@Injectable()
|
||||
export class ServiceTypesService {
|
||||
constructor(
|
||||
@Inject(SERVICE_TYPES_REPOSITORY)
|
||||
private readonly repository: IServiceTypesRepository,
|
||||
private readonly displayOrder: DisplayOrderService,
|
||||
) {}
|
||||
|
||||
/** List service types with pagination and optional filtering. */
|
||||
@@ -27,7 +30,7 @@ export class ServiceTypesService {
|
||||
sortOrder?: 'ASC' | 'DESC';
|
||||
}): Promise<{ data: ServiceType[]; meta: { total: number; page: number; pageSize: number; totalPages: number } }> {
|
||||
const page = filter.page ?? 1;
|
||||
const pageSize = filter.pageSize ?? 20;
|
||||
const pageSize = filter.pageSize ?? 10;
|
||||
const where: Record<string, unknown> = {};
|
||||
if (filter.isActive !== undefined) where.isActive = filter.isActive;
|
||||
if (filter.canBeBookedAlone !== undefined) where.canBeBookedAlone = filter.canBeBookedAlone;
|
||||
@@ -59,6 +62,12 @@ export class ServiceTypesService {
|
||||
const code = generateCode(dto.serviceName);
|
||||
const existing = await this.repository.findByCode(code);
|
||||
if (existing) throw new ConflictException(`Service type with name "${dto.serviceName}" conflicts with existing code "${code}"`);
|
||||
|
||||
const displayOrder = await this.displayOrder.resolveCreateOrder(ServiceType, 'displayOrder', {
|
||||
explicitOrder: dto.displayOrder,
|
||||
insertAfterId: dto.insertAfterId,
|
||||
});
|
||||
|
||||
return this.repository.create({
|
||||
code,
|
||||
serviceName: dto.serviceName,
|
||||
@@ -69,7 +78,7 @@ export class ServiceTypesService {
|
||||
includesCustoms: dto.includesCustoms ?? false,
|
||||
priorityBonusPoints: dto.priorityBonusPoints ?? 0,
|
||||
isActive: dto.isActive ?? true,
|
||||
displayOrder: dto.displayOrder ?? 1,
|
||||
displayOrder,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -87,4 +96,13 @@ export class ServiceTypesService {
|
||||
await this.findById(id);
|
||||
await this.repository.softDelete(id);
|
||||
}
|
||||
|
||||
async reorder(dto: ReorderItemsDto): Promise<void> {
|
||||
await this.displayOrder.reorderByIds(ServiceType, 'displayOrder', dto.ids);
|
||||
}
|
||||
|
||||
async moveOrder(id: string, direction: 'up' | 'down'): Promise<void> {
|
||||
await this.findById(id);
|
||||
await this.displayOrder.moveOne(ServiceType, 'displayOrder', id, direction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { ConflictException, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { generateCode } from '../../../common/utils/generate-code.util';
|
||||
import { CreateYardDto } from '../dto/create-yard.dto';
|
||||
import { ReorderItemsDto } from '../dto/reorder-items.dto';
|
||||
import { UpdateYardDto } from '../dto/update-yard.dto';
|
||||
import { Yard } from '../entities/yard.entity';
|
||||
import { IYardsRepository, YARDS_REPOSITORY } from '../interfaces/yards.repository.interface';
|
||||
import { DisplayOrderService } from './display-order.service';
|
||||
|
||||
@Injectable()
|
||||
export class YardsService {
|
||||
constructor(
|
||||
@Inject(YARDS_REPOSITORY)
|
||||
private readonly repository: IYardsRepository,
|
||||
private readonly displayOrder: DisplayOrderService,
|
||||
) {}
|
||||
|
||||
/** List yards with pagination. */
|
||||
@@ -20,7 +23,7 @@ export class YardsService {
|
||||
pageSize?: number;
|
||||
}): Promise<{ data: Yard[]; meta: { total: number; page: number; pageSize: number; totalPages: number } }> {
|
||||
const page = filter.page ?? 1;
|
||||
const pageSize = filter.pageSize ?? 20;
|
||||
const pageSize = filter.pageSize ?? 10;
|
||||
const where: Record<string, unknown> = {};
|
||||
if (filter.isActive !== undefined) where.isActive = filter.isActive;
|
||||
if (filter.country) where.country = filter.country;
|
||||
@@ -46,12 +49,18 @@ export class YardsService {
|
||||
const code = generateCode(dto.label);
|
||||
const existing = await this.repository.findByCode(code);
|
||||
if (existing) throw new ConflictException(`Yard with label "${dto.label}" conflicts with existing code "${code}"`);
|
||||
|
||||
const displayOrder = await this.displayOrder.resolveCreateOrder(Yard, 'displayOrder', {
|
||||
explicitOrder: dto.displayOrder,
|
||||
insertAfterId: dto.insertAfterId,
|
||||
});
|
||||
|
||||
return this.repository.create({
|
||||
code,
|
||||
label: dto.label,
|
||||
country: dto.country,
|
||||
isActive: dto.isActive ?? true,
|
||||
displayOrder: dto.displayOrder ?? 1,
|
||||
displayOrder,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,4 +77,13 @@ export class YardsService {
|
||||
await this.findById(id);
|
||||
await this.repository.softDelete(id);
|
||||
}
|
||||
|
||||
async reorder(dto: ReorderItemsDto): Promise<void> {
|
||||
await this.displayOrder.reorderByIds(Yard, 'displayOrder', dto.ids);
|
||||
}
|
||||
|
||||
async moveOrder(id: string, direction: 'up' | 'down'): Promise<void> {
|
||||
await this.findById(id);
|
||||
await this.displayOrder.moveOne(Yard, 'displayOrder', id, direction);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user