import { PaginatedResponse } from '@edr/types'; import { ConflictException, Inject, Injectable, NotFoundException } from '@nestjs/common'; import { generateCode } from '../../../common/utils/generate-code.util'; import { CreateYardDto } from '../dto/create-yard.dto'; import { ListYardsQueryDto } from '../dto/list-rule-engine-query.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 — standard paginated envelope with server-side search. */ async findAll(query: ListYardsQueryDto): Promise> { return this.repository.findPaged(query); } /** Get a yard by ID. */ async findById(id: string): Promise { const entity = await this.repository.findById(id); if (!entity) throw new NotFoundException(`Yard ${id} not found`); return entity; } /** Create a yard. */ async create(dto: CreateYardDto): Promise { const code = generateCode(dto.label).slice(0, 40); 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, hasFacility: dto.hasFacility ?? false, displayOrder, }); } /** Update a yard. */ async update(id: string, dto: UpdateYardDto): Promise { await this.findById(id); const updated = await this.repository.update(id, dto); if (!updated) throw new NotFoundException(`Yard ${id} not found`); return updated; } /** * Soft-delete a yard. The unique `code` (and the label) get a `@` * suffix first — e.g. SEBETA → SEBETA@1755612345678 — so a new yard with the * same name can be created later without tripping UQ_yards_code, which spans * soft-deleted rows too. */ async remove(id: string): Promise { const yard = await this.findById(id); const suffix = `@${Date.now()}`; await this.repository.update(id, { code: `${yard.code.slice(0, 40 - suffix.length)}${suffix}`, label: `${yard.label.slice(0, 100 - suffix.length)}${suffix}`, }); await this.repository.softDelete(id); } async reorder(dto: ReorderItemsDto): Promise { await this.displayOrder.reorderByIds(Yard, 'displayOrder', dto.ids); } async moveOrder(id: string, direction: 'up' | 'down'): Promise { await this.findById(id); await this.displayOrder.moveOne(Yard, 'displayOrder', id, direction); } }