import { CircuitDeviceRowRepository } from "../../db/repositories/circuit-device-row.repository.js"; import { CircuitListRepository } from "../../db/repositories/circuit-list.repository.js"; import { CircuitRepository } from "../../db/repositories/circuit.repository.js"; import { CircuitSectionRepository } from "../../db/repositories/circuit-section.repository.js"; import { ProjectDeviceRepository } from "../../db/repositories/project-device.repository.js"; import type { CreateCircuitDeviceRowInput, CreateCircuitInput, UpdateCircuitDeviceRowInput, UpdateCircuitInput, } from "../../shared/validation/circuit.schemas.js"; import { CircuitNumberingService } from "./circuit-numbering.service.js"; export class CircuitWriteService { private readonly circuitRepository: CircuitRepository; private readonly circuitSectionRepository: CircuitSectionRepository; private readonly circuitListRepository: CircuitListRepository; private readonly deviceRowRepository: CircuitDeviceRowRepository; private readonly projectDeviceRepository: ProjectDeviceRepository; private readonly numberingService: CircuitNumberingService; constructor(deps?: { circuitRepository?: CircuitRepository; circuitSectionRepository?: CircuitSectionRepository; circuitListRepository?: CircuitListRepository; deviceRowRepository?: CircuitDeviceRowRepository; projectDeviceRepository?: ProjectDeviceRepository; numberingService?: CircuitNumberingService; }) { this.circuitRepository = deps?.circuitRepository ?? new CircuitRepository(); this.circuitSectionRepository = deps?.circuitSectionRepository ?? new CircuitSectionRepository(); this.circuitListRepository = deps?.circuitListRepository ?? new CircuitListRepository(); this.deviceRowRepository = deps?.deviceRowRepository ?? new CircuitDeviceRowRepository(); this.projectDeviceRepository = deps?.projectDeviceRepository ?? new ProjectDeviceRepository(); this.numberingService = deps?.numberingService ?? new CircuitNumberingService(); } private async assertSectionInList(sectionId: string, circuitListId: string) { const section = await this.circuitSectionRepository.findById(sectionId); if (!section) { throw new Error("Invalid section id."); } if (section.circuitListId !== circuitListId) { throw new Error("Section does not belong to circuit list."); } return section; } private async assertUniqueEquipmentIdentifier( circuitListId: string, equipmentIdentifier: string, excludeCircuitId?: string ) { const exists = await this.circuitRepository.existsByEquipmentIdentifier( circuitListId, equipmentIdentifier, excludeCircuitId ); if (exists) { throw new Error("Duplicate equipmentIdentifier in circuit list."); } } private async assertValidLinkedProjectDevice(circuitId: string, linkedProjectDeviceId?: string) { if (!linkedProjectDeviceId) { return; } const circuit = await this.circuitRepository.findById(circuitId); if (!circuit) { throw new Error("Invalid circuit id."); } const list = await this.circuitListRepository.findByIdByListIdOnly(circuit.circuitListId); if (!list) { throw new Error("Circuit list not found."); } const device = await this.projectDeviceRepository.findById(list.projectId, linkedProjectDeviceId); if (!device) { throw new Error("Invalid linked project device id."); } } async createCircuit(projectId: string, circuitListId: string, input: CreateCircuitInput) { const list = await this.circuitListRepository.findById(projectId, circuitListId); if (!list) { throw new Error("Circuit list not found in project."); } await this.assertSectionInList(input.sectionId, circuitListId); await this.assertUniqueEquipmentIdentifier(circuitListId, input.equipmentIdentifier); const id = await this.circuitRepository.create({ circuitListId, sectionId: input.sectionId, equipmentIdentifier: input.equipmentIdentifier, displayName: input.displayName, sortOrder: input.sortOrder, protectionType: input.protectionType, protectionRatedCurrent: input.protectionRatedCurrent, protectionCharacteristic: input.protectionCharacteristic, cableType: input.cableType, cableCrossSection: input.cableCrossSection, cableLength: input.cableLength, rcdAssignment: input.rcdAssignment, terminalDesignation: input.terminalDesignation, voltage: input.voltage, status: input.status, isReserve: input.isReserve ?? true, remark: input.remark, }); return this.circuitRepository.findById(id); } async updateCircuit(circuitId: string, input: UpdateCircuitInput) { const current = await this.circuitRepository.findById(circuitId); if (!current) { throw new Error("Invalid circuit id."); } const sectionId = input.sectionId ?? current.sectionId; const equipmentIdentifier = input.equipmentIdentifier ?? current.equipmentIdentifier; const sortOrder = input.sortOrder ?? current.sortOrder; await this.assertSectionInList(sectionId, current.circuitListId); await this.assertUniqueEquipmentIdentifier(current.circuitListId, equipmentIdentifier, circuitId); await this.circuitRepository.update(circuitId, { sectionId, equipmentIdentifier, displayName: input.displayName ?? current.displayName ?? undefined, sortOrder, protectionType: input.protectionType ?? current.protectionType ?? undefined, protectionRatedCurrent: input.protectionRatedCurrent ?? current.protectionRatedCurrent ?? undefined, protectionCharacteristic: input.protectionCharacteristic ?? current.protectionCharacteristic ?? undefined, cableType: input.cableType ?? current.cableType ?? undefined, cableCrossSection: input.cableCrossSection ?? current.cableCrossSection ?? undefined, cableLength: input.cableLength ?? current.cableLength ?? undefined, rcdAssignment: input.rcdAssignment ?? current.rcdAssignment ?? undefined, terminalDesignation: input.terminalDesignation ?? current.terminalDesignation ?? undefined, voltage: input.voltage ?? current.voltage ?? undefined, status: input.status ?? current.status ?? undefined, isReserve: input.isReserve ?? Boolean(current.isReserve), remark: input.remark ?? current.remark ?? undefined, }); return this.circuitRepository.findById(circuitId); } async deleteCircuit(circuitId: string) { const current = await this.circuitRepository.findById(circuitId); if (!current) { throw new Error("Invalid circuit id."); } await this.circuitRepository.delete(circuitId); } async createDeviceRow(circuitId: string, input: CreateCircuitDeviceRowInput) { const circuit = await this.circuitRepository.findById(circuitId); if (!circuit) { throw new Error("Invalid circuit id."); } await this.assertValidLinkedProjectDevice(circuitId, input.linkedProjectDeviceId); const existingRows = await this.deviceRowRepository.countByCircuit(circuitId); const rowId = await this.deviceRowRepository.create({ circuitId, linkedProjectDeviceId: input.linkedProjectDeviceId, sortOrder: input.sortOrder ?? (existingRows + 1) * 10, name: input.name, displayName: input.displayName, phaseType: input.phaseType, connectionKind: input.connectionKind, costGroup: input.costGroup, category: input.category, level: input.level, roomId: input.roomId, roomNumberSnapshot: input.roomNumberSnapshot, roomNameSnapshot: input.roomNameSnapshot, quantity: input.quantity, powerPerUnit: input.powerPerUnit, simultaneityFactor: input.simultaneityFactor, cosPhi: input.cosPhi, remark: input.remark, overriddenFields: input.overriddenFields, }); if (Boolean(circuit.isReserve)) { await this.circuitRepository.update(circuit.id, { sectionId: circuit.sectionId, equipmentIdentifier: circuit.equipmentIdentifier, displayName: circuit.displayName ?? undefined, sortOrder: circuit.sortOrder, protectionType: circuit.protectionType ?? undefined, protectionRatedCurrent: circuit.protectionRatedCurrent ?? undefined, protectionCharacteristic: circuit.protectionCharacteristic ?? undefined, cableType: circuit.cableType ?? undefined, cableCrossSection: circuit.cableCrossSection ?? undefined, cableLength: circuit.cableLength ?? undefined, rcdAssignment: circuit.rcdAssignment ?? undefined, terminalDesignation: circuit.terminalDesignation ?? undefined, voltage: circuit.voltage ?? undefined, status: circuit.status ?? undefined, isReserve: false, remark: circuit.remark ?? undefined, }); } return this.deviceRowRepository.findById(rowId); } async updateDeviceRow(rowId: string, input: UpdateCircuitDeviceRowInput) { const current = await this.deviceRowRepository.findById(rowId); if (!current) { throw new Error("Invalid device row id."); } await this.assertValidLinkedProjectDevice(current.circuitId, input.linkedProjectDeviceId); await this.deviceRowRepository.update(rowId, { linkedProjectDeviceId: input.linkedProjectDeviceId ?? current.linkedProjectDeviceId ?? undefined, name: input.name ?? current.name, displayName: input.displayName ?? current.displayName, phaseType: input.phaseType ?? current.phaseType ?? undefined, connectionKind: input.connectionKind ?? current.connectionKind ?? undefined, costGroup: input.costGroup ?? current.costGroup ?? undefined, category: input.category ?? current.category ?? undefined, level: input.level ?? current.level ?? undefined, roomId: input.roomId ?? current.roomId ?? undefined, roomNumberSnapshot: input.roomNumberSnapshot ?? current.roomNumberSnapshot ?? undefined, roomNameSnapshot: input.roomNameSnapshot ?? current.roomNameSnapshot ?? undefined, quantity: input.quantity ?? current.quantity, powerPerUnit: input.powerPerUnit ?? current.powerPerUnit, simultaneityFactor: input.simultaneityFactor ?? current.simultaneityFactor, cosPhi: input.cosPhi ?? current.cosPhi ?? undefined, remark: input.remark ?? current.remark ?? undefined, overriddenFields: input.overriddenFields ?? current.overriddenFields ?? undefined, }); return this.deviceRowRepository.findById(rowId); } async deleteDeviceRow(rowId: string) { const current = await this.deviceRowRepository.findById(rowId); if (!current) { throw new Error("Invalid device row id."); } const circuit = await this.circuitRepository.findById(current.circuitId); if (!circuit) { throw new Error("Invalid circuit id."); } await this.deviceRowRepository.delete(rowId); const remaining = await this.deviceRowRepository.countByCircuit(current.circuitId); if (remaining === 0) { await this.circuitRepository.update(circuit.id, { sectionId: circuit.sectionId, equipmentIdentifier: circuit.equipmentIdentifier, displayName: circuit.displayName ?? undefined, sortOrder: circuit.sortOrder, protectionType: circuit.protectionType ?? undefined, protectionRatedCurrent: circuit.protectionRatedCurrent ?? undefined, protectionCharacteristic: circuit.protectionCharacteristic ?? undefined, cableType: circuit.cableType ?? undefined, cableCrossSection: circuit.cableCrossSection ?? undefined, cableLength: circuit.cableLength ?? undefined, rcdAssignment: circuit.rcdAssignment ?? undefined, terminalDesignation: circuit.terminalDesignation ?? undefined, voltage: circuit.voltage ?? undefined, status: circuit.status ?? undefined, isReserve: true, remark: circuit.remark ?? undefined, }); } } async getNextIdentifier(sectionId: string) { return this.numberingService.getNextIdentifier(sectionId); } async renumberSection(sectionId: string) { const section = await this.circuitSectionRepository.findById(sectionId); if (!section) { throw new Error("Invalid section id."); } const sectionCircuits = await this.circuitRepository.listBySection(sectionId); const otherCircuits = (await this.circuitRepository.listByCircuitList(section.circuitListId)).filter( (circuit) => circuit.sectionId !== sectionId ); const otherIdentifiers = new Set(otherCircuits.map((circuit) => circuit.equipmentIdentifier)); let index = 1; for (const circuit of sectionCircuits) { let candidate = `${section.prefix}${index}`; while (otherIdentifiers.has(candidate)) { index += 1; candidate = `${section.prefix}${index}`; } await this.circuitRepository.update(circuit.id, { sectionId: circuit.sectionId, equipmentIdentifier: candidate, displayName: circuit.displayName ?? undefined, sortOrder: circuit.sortOrder, protectionType: circuit.protectionType ?? undefined, protectionRatedCurrent: circuit.protectionRatedCurrent ?? undefined, protectionCharacteristic: circuit.protectionCharacteristic ?? undefined, cableType: circuit.cableType ?? undefined, cableCrossSection: circuit.cableCrossSection ?? undefined, cableLength: circuit.cableLength ?? undefined, rcdAssignment: circuit.rcdAssignment ?? undefined, terminalDesignation: circuit.terminalDesignation ?? undefined, voltage: circuit.voltage ?? undefined, status: circuit.status ?? undefined, isReserve: Boolean(circuit.isReserve), remark: circuit.remark ?? undefined, }); index += 1; } return this.circuitRepository.listBySection(sectionId); } }