import { getPool } from "../database/db"; export class YardLayoutService { // 모든 야드 레이아웃 목록 조회 async getAllLayouts() { const query = ` SELECT yl.id, yl.name, yl.description, yl.created_by, yl.created_at, yl.updated_at, COUNT(ymp.id) as placement_count FROM yard_layout yl LEFT JOIN yard_material_placement ymp ON yl.id = ymp.yard_layout_id GROUP BY yl.id ORDER BY yl.updated_at DESC `; const pool = getPool(); const result = await pool.query(query); return result.rows; } // 특정 야드 레이아웃 상세 조회 async getLayoutById(id: number) { const query = ` SELECT id, name, description, created_by, created_at, updated_at FROM yard_layout WHERE id = $1 `; const pool = getPool(); const result = await pool.query(query, [id]); return result.rows[0] || null; } // 새 야드 레이아웃 생성 async createLayout(data: { name: string; description?: string; created_by?: string; }) { const query = ` INSERT INTO yard_layout (name, description, created_by) VALUES ($1, $2, $3) RETURNING * `; const pool = getPool(); const result = await pool.query(query, [ data.name, data.description || null, data.created_by || null, ]); return result.rows[0]; } // 야드 레이아웃 수정 (이름, 설명만) async updateLayout( id: number, data: { name?: string; description?: string } ) { const query = ` UPDATE yard_layout SET name = COALESCE($1, name), description = COALESCE($2, description), updated_at = CURRENT_TIMESTAMP WHERE id = $3 RETURNING * `; const pool = getPool(); const result = await pool.query(query, [ data.name || null, data.description || null, id, ]); return result.rows[0] || null; } // 야드 레이아웃 삭제 async deleteLayout(id: number) { const query = `DELETE FROM yard_layout WHERE id = $1 RETURNING *`; const pool = getPool(); const result = await pool.query(query, [id]); return result.rows[0] || null; } // 특정 야드의 모든 배치 자재 조회 async getPlacementsByLayoutId(layoutId: number) { const query = ` SELECT id, yard_layout_id, external_material_id, material_code, material_name, quantity, unit, position_x, position_y, position_z, size_x, size_y, size_z, color, memo, created_at, updated_at FROM yard_material_placement WHERE yard_layout_id = $1 ORDER BY created_at ASC `; const pool = getPool(); const result = await pool.query(query, [layoutId]); return result.rows; } // 야드에 자재 배치 추가 async addMaterialPlacement(layoutId: number, data: any) { const query = ` INSERT INTO yard_material_placement ( yard_layout_id, external_material_id, material_code, material_name, quantity, unit, position_x, position_y, position_z, size_x, size_y, size_z, color, memo ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) RETURNING * `; const pool = getPool(); const result = await pool.query(query, [ layoutId, data.external_material_id, data.material_code, data.material_name, data.quantity, data.unit, data.position_x || 0, data.position_y || 0, data.position_z || 0, data.size_x || 5, data.size_y || 5, data.size_z || 5, data.color || "#3b82f6", data.memo || null, ]); return result.rows[0]; } // 배치 정보 수정 (위치, 크기, 색상, 메모만) async updatePlacement(placementId: number, data: any) { const query = ` UPDATE yard_material_placement SET position_x = COALESCE($1, position_x), position_y = COALESCE($2, position_y), position_z = COALESCE($3, position_z), size_x = COALESCE($4, size_x), size_y = COALESCE($5, size_y), size_z = COALESCE($6, size_z), color = COALESCE($7, color), memo = COALESCE($8, memo), updated_at = CURRENT_TIMESTAMP WHERE id = $9 RETURNING * `; const pool = getPool(); const result = await pool.query(query, [ data.position_x, data.position_y, data.position_z, data.size_x, data.size_y, data.size_z, data.color, data.memo, placementId, ]); return result.rows[0] || null; } // 배치 해제 (자재는 삭제되지 않음) async removePlacement(placementId: number) { const query = `DELETE FROM yard_material_placement WHERE id = $1 RETURNING *`; const pool = getPool(); const result = await pool.query(query, [placementId]); return result.rows[0] || null; } // 여러 배치 일괄 업데이트 async batchUpdatePlacements(layoutId: number, placements: any[]) { const pool = getPool(); const client = await pool.connect(); try { await client.query("BEGIN"); const results = []; for (const placement of placements) { const query = ` UPDATE yard_material_placement SET position_x = $1, position_y = $2, position_z = $3, size_x = $4, size_y = $5, size_z = $6, updated_at = CURRENT_TIMESTAMP WHERE id = $7 AND yard_layout_id = $8 RETURNING * `; const result = await client.query(query, [ placement.position_x, placement.position_y, placement.position_z, placement.size_x, placement.size_y, placement.size_z, placement.id, layoutId, ]); if (result.rows[0]) { results.push(result.rows[0]); } } await client.query("COMMIT"); return results; } catch (error) { await client.query("ROLLBACK"); throw error; } finally { client.release(); } } // 야드 레이아웃 복제 async duplicateLayout(id: number, newName: string) { const pool = getPool(); const client = await pool.connect(); try { await client.query("BEGIN"); // 원본 레이아웃 조회 const layoutQuery = `SELECT * FROM yard_layout WHERE id = $1`; const layoutResult = await client.query(layoutQuery, [id]); const originalLayout = layoutResult.rows[0]; if (!originalLayout) { throw new Error("Layout not found"); } // 새 레이아웃 생성 const newLayoutQuery = ` INSERT INTO yard_layout (name, description, created_by) VALUES ($1, $2, $3) RETURNING * `; const newLayoutResult = await client.query(newLayoutQuery, [ newName, originalLayout.description, originalLayout.created_by, ]); const newLayout = newLayoutResult.rows[0]; // 배치 자재 복사 const placementsQuery = `SELECT * FROM yard_material_placement WHERE yard_layout_id = $1`; const placementsResult = await client.query(placementsQuery, [id]); for (const placement of placementsResult.rows) { await client.query( ` INSERT INTO yard_material_placement ( yard_layout_id, external_material_id, material_code, material_name, quantity, unit, position_x, position_y, position_z, size_x, size_y, size_z, color, memo ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) `, [ newLayout.id, placement.external_material_id, placement.material_code, placement.material_name, placement.quantity, placement.unit, placement.position_x, placement.position_y, placement.position_z, placement.size_x, placement.size_y, placement.size_z, placement.color, placement.memo, ] ); } await client.query("COMMIT"); return newLayout; } catch (error) { await client.query("ROLLBACK"); throw error; } finally { client.release(); } } } export default new YardLayoutService();