# ๐Ÿ”ง PIPE Cutting Plan & ์ด์Šˆ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ ๊ฐ€์ด๋“œ ## ๐Ÿ“‹ ๋ชฉ์ฐจ 1. [์‹œ์Šคํ…œ ๊ฐœ์š”](#์‹œ์Šคํ…œ-๊ฐœ์š”) 2. [์ „์ฒด ์›Œํฌํ”Œ๋กœ์šฐ](#์ „์ฒด-์›Œํฌํ”Œ๋กœ์šฐ) 3. [๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„](#๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค-์„ค๊ณ„) 4. [ํŽ˜์ด์ง€๋ณ„ ์ƒ์„ธ ์„ค๊ณ„](#ํŽ˜์ด์ง€๋ณ„-์ƒ์„ธ-์„ค๊ณ„) 5. [API ์—”๋“œํฌ์ธํŠธ](#api-์—”๋“œํฌ์ธํŠธ) 6. [๋ฆฌ๋น„์ „ ๊ด€๋ฆฌ ๋กœ์ง](#๋ฆฌ๋น„์ „-๊ด€๋ฆฌ-๋กœ์ง) 7. [๊ฐœ๋ฐœ ์šฐ์„ ์ˆœ์œ„](#๊ฐœ๋ฐœ-์šฐ์„ ์ˆœ์œ„) 8. [๊ธฐ์ˆ  ์Šคํƒ](#๊ธฐ์ˆ -์Šคํƒ) --- ## ๐ŸŽฏ ์‹œ์Šคํ…œ ๊ฐœ์š” ### **ํ•ต์‹ฌ ๋ชฉ์ ** - PIPE ์ž์žฌ์˜ ์ฒด๊ณ„์ ์ธ Cutting Plan ๊ด€๋ฆฌ - ๊ตฌ์—ญ๋ณ„/๋„๋ฉด๋ณ„ ๋‹จ๊ด€ ์ •๋ณด ๊ด€๋ฆฌ - ๋ฆฌ๋น„์ „ ์‹œ ๋ณ€๊ฒฝ์‚ฌํ•ญ ์ถ”์  ๋ฐ ๋น„๊ต - ํ˜„์žฅ ์ด์Šˆ ๋ฐ ๋ฌธ์ œ์  ์ฒด๊ณ„์  ๊ธฐ๋ก ### **์ฃผ์š” ํŠน์ง•** - **2๋‹จ๊ณ„ ์›Œํฌํ”Œ๋กœ์šฐ**: ๊ตฌ์—ญ ํ• ๋‹น โ†’ ๋ผ์ธ๋ฒˆํ˜ธ ์ž…๋ ฅ - **์Šค๋งˆํŠธ ๋ฆฌ๋น„์ „**: ๊ธฐ์กด ๋ฐ์ดํ„ฐ์™€ ์‹ ๊ทœ BOM ์ž๋™ ๋น„๊ต - **ํ˜„์žฅ ์ด์Šˆ ๊ด€๋ฆฌ**: ๋„๋ฉด๋ณ„/๋‹จ๊ด€๋ณ„ ๋ฌธ์ œ์  ์ถ”์  - **Excel ์—ฐ๋™**: ์™„์ „ํ•œ ๋ฐ์ดํ„ฐ ๋‚ด๋ณด๋‚ด๊ธฐ ์ง€์› --- ## ๐Ÿ”„ ์ „์ฒด ์›Œํฌํ”Œ๋กœ์šฐ ```mermaid graph TD A[BOM ์—…๋กœ๋“œ] --> B{PIPE ๋ฐ์ดํ„ฐ ์ถ”์ถœ} B --> C[1๋‹จ๊ณ„: ๊ตฌ์—ญ๋ณ„ ๋„๋ฉด ํ• ๋‹น] C --> D[2๋‹จ๊ณ„: ๋ผ์ธ๋ฒˆํ˜ธ ์ž…๋ ฅ] D --> E[3๋‹จ๊ณ„: ๋‹จ๊ด€ ์ •๋ณด ํ™•์ธ] E --> F[Cutting Plan ์ €์žฅ] F --> G{์ƒˆ๋กœ์šด ๋ฆฌ๋น„์ „?} G -->|Yes| H[4๋‹จ๊ณ„: ๋ฆฌ๋น„์ „ ๋น„๊ต] G -->|No| I[์ด์Šˆ ๊ด€๋ฆฌ ํŽ˜์ด์ง€] H --> J[๋ณ€๊ฒฝ์‚ฌํ•ญ ํ™•์ธ/์ˆ˜์ •] J --> K[5๋‹จ๊ณ„: ๋ณ€๊ฒฝ ๋„๋ฉด ์š”์•ฝ] K --> L[ํŒŒ์ดํ”„ ๊ตฌ๋งค๋Ÿ‰ ์žฌ๊ณ„์‚ฐ] L --> I I --> M[ํ˜„์žฅ ์ด์Šˆ ์ž…๋ ฅ] M --> N[Excel ๋‚ด๋ณด๋‚ด๊ธฐ] ``` --- ## ๐Ÿ—„๏ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„ ### **1. pipe_cutting_plans** (๋‹จ๊ด€ ์ •๋ณด) ```sql CREATE TABLE pipe_cutting_plans ( id SERIAL PRIMARY KEY, job_no VARCHAR(50) NOT NULL, file_id INTEGER REFERENCES files(id), -- ๊ธฐ๋ณธ ์ •๋ณด area VARCHAR(10), -- #01, #02 ๋“ฑ drawing_name VARCHAR(100) NOT NULL, -- P&ID-001 line_no VARCHAR(50) NOT NULL, -- LINE-A-001 -- ํŒŒ์ดํ”„ ์ •๋ณด material_grade VARCHAR(50), -- A106 GR.B schedule_spec VARCHAR(20), -- SCH40, SCH80 nominal_size VARCHAR(50), -- 4", 6", 8" length_mm DECIMAL(10,3) NOT NULL, -- 1500.0 -- ๋๋‹จ ์ •๋ณด end_preparation VARCHAR(20), -- ๋ฌด๊ฐœ์„ , ํ•œ๊ฐœ์„ , ์–‘๊ฐœ์„  -- ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_by VARCHAR(100), -- ์ธ๋ฑ์Šค INDEX idx_cutting_plans_job (job_no), INDEX idx_cutting_plans_area_drawing (area, drawing_name), INDEX idx_cutting_plans_material (material_grade, nominal_size) ); ``` ### **2. pipe_revision_comparisons** (๋ฆฌ๋น„์ „ ๋น„๊ต) ```sql CREATE TABLE pipe_revision_comparisons ( id SERIAL PRIMARY KEY, job_no VARCHAR(50) NOT NULL, current_file_id INTEGER REFERENCES files(id), previous_cutting_plan_id INTEGER REFERENCES pipe_cutting_plans(id), -- ๋น„๊ต ๊ฒฐ๊ณผ comparison_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, total_drawings INTEGER, -- ์ „์ฒด ๋„๋ฉด ์ˆ˜ changed_drawings INTEGER, -- ๋ณ€๊ฒฝ๋œ ๋„๋ฉด ์ˆ˜ unchanged_drawings INTEGER, -- ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๋„๋ฉด ์ˆ˜ -- ๋‹จ๊ด€ ๋ณ€๊ฒฝ ํ†ต๊ณ„ total_segments INTEGER, -- ์ „์ฒด ๋‹จ๊ด€ ์ˆ˜ added_segments INTEGER, -- ์ถ”๊ฐ€๋œ ๋‹จ๊ด€ removed_segments INTEGER, -- ์‚ญ์ œ๋œ ๋‹จ๊ด€ modified_segments INTEGER, -- ์ˆ˜์ •๋œ ๋‹จ๊ด€ unchanged_segments INTEGER, -- ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ๋‹จ๊ด€ -- ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ created_by VARCHAR(100), is_applied BOOLEAN DEFAULT FALSE, applied_at TIMESTAMP, applied_by VARCHAR(100) ); ``` ### **3. pipe_revision_changes** (๋ฆฌ๋น„์ „ ๋ณ€๊ฒฝ ์ƒ์„ธ) ```sql CREATE TABLE pipe_revision_changes ( id SERIAL PRIMARY KEY, comparison_id INTEGER REFERENCES pipe_revision_comparisons(id), -- ๋ณ€๊ฒฝ ์ •๋ณด drawing_name VARCHAR(100) NOT NULL, change_type VARCHAR(20) NOT NULL, -- 'added', 'removed', 'modified', 'unchanged' -- ์ด์ „ ๋ฐ์ดํ„ฐ (์ˆ˜์ •/์‚ญ์ œ์˜ ๊ฒฝ์šฐ) old_line_no VARCHAR(50), old_material_grade VARCHAR(50), old_schedule_spec VARCHAR(20), old_nominal_size VARCHAR(50), old_length_mm DECIMAL(10,3), old_end_preparation VARCHAR(20), -- ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ (์ถ”๊ฐ€/์ˆ˜์ •์˜ ๊ฒฝ์šฐ) new_line_no VARCHAR(50), new_material_grade VARCHAR(50), new_schedule_spec VARCHAR(20), new_nominal_size VARCHAR(50), new_length_mm DECIMAL(10,3), new_end_preparation VARCHAR(20), -- ๋ณ€๊ฒฝ ์‚ฌ์œ  change_reason TEXT, -- ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ### **4. pipe_drawing_issues** (๋„๋ฉด ์ „๋ฐ˜ ์ด์Šˆ) ```sql CREATE TABLE pipe_drawing_issues ( id SERIAL PRIMARY KEY, job_no VARCHAR(50) NOT NULL, area VARCHAR(10) NOT NULL, drawing_name VARCHAR(100) NOT NULL, -- ์ด์Šˆ ์ •๋ณด issue_description TEXT NOT NULL, severity VARCHAR(20) DEFAULT 'medium', -- 'low', 'medium', 'high', 'critical' status VARCHAR(20) DEFAULT 'open', -- 'open', 'in_progress', 'resolved' -- ํ•ด๊ฒฐ ์ •๋ณด resolution_notes TEXT, resolved_by VARCHAR(100), resolved_at TIMESTAMP, -- ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ reported_by VARCHAR(100), reported_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ### **5. pipe_segment_issues** (๋‹จ๊ด€๋ณ„ ์ด์Šˆ) ```sql CREATE TABLE pipe_segment_issues ( id SERIAL PRIMARY KEY, job_no VARCHAR(50) NOT NULL, area VARCHAR(10) NOT NULL, drawing_name VARCHAR(100) NOT NULL, line_no VARCHAR(50) NOT NULL, -- ์›๋ณธ ์ •๋ณด original_material_info VARCHAR(100), original_length DECIMAL(10,3), -- ์ด์Šˆ ์ •๋ณด issue_description TEXT NOT NULL, issue_type VARCHAR(50), -- 'cutting', 'installation', 'material', 'routing', 'other' -- ๋ณ€๊ฒฝ์‚ฌํ•ญ (์žˆ๋Š” ๊ฒฝ์šฐ) length_change DECIMAL(10,3), -- +/- ๋ณ€๊ฒฝ๋Ÿ‰ new_length DECIMAL(10,3), -- ์ตœ์ข… ๊ธธ์ด material_change VARCHAR(100), -- ์žฌ์งˆ ๋ณ€๊ฒฝ ์ •๋ณด -- ์ด์Šˆ ๊ด€๋ฆฌ severity VARCHAR(20) DEFAULT 'medium', status VARCHAR(20) DEFAULT 'open', -- ํ•ด๊ฒฐ ์ •๋ณด resolution_notes TEXT, resolved_by VARCHAR(100), resolved_at TIMESTAMP, -- ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ cutting_plan_id INTEGER REFERENCES pipe_cutting_plans(id), reported_by VARCHAR(100), reported_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` --- ## ๐Ÿ“„ ํŽ˜์ด์ง€๋ณ„ ์ƒ์„ธ ์„ค๊ณ„ ### **1. PIPE Cutting Plan ๋ฉ”์ธ ํŽ˜์ด์ง€** **ํŒŒ์ผ**: `frontend/src/pages/revision/PipeCuttingPlanPage.jsx` #### **1๋‹จ๊ณ„: ๊ตฌ์—ญ๋ณ„ ๋„๋ฉด ํ• ๋‹น** ```javascript // UI ๊ตฌ์กฐ

๐Ÿ“‚ 1๋‹จ๊ณ„: ๊ตฌ์—ญ๋ณ„ ๋„๋ฉด ํ• ๋‹น

{/* ๊ตฌ์—ญ ์ž…๋ ฅ */}
setCurrentArea(e.target.value)} />
{/* ๋„๋ฉด ์„ ํƒ */}

๐Ÿ“‹ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋„๋ฉด ๋ชฉ๋ก

{availableDrawings.map(drawing => ( ))}
{/* ๊ตฌ์—ญ๋ณ„ ํ• ๋‹น ํ˜„ํ™ฉ */}
{areas.map(area => (

{area.name}

{area.drawings.map(drawing => ( {drawing} ))}
))}
``` #### **2๋‹จ๊ณ„: ๋ผ์ธ๋ฒˆํ˜ธ ์ž…๋ ฅ** ```javascript

๐Ÿ”ข 2๋‹จ๊ณ„: ๋ผ์ธ๋ฒˆํ˜ธ ์ž…๋ ฅ

{/* ๊ตฌ์—ญ/๋„๋ฉด ์„ ํƒ */}
{/* ๋‹จ๊ด€ ์ •๋ณด ํ…Œ์ด๋ธ” */}
{pipeSegments.map((segment, index) => ( ))}
๋ผ์ธ๋ฒˆํ˜ธ ์žฌ์งˆ ๊ทœ๊ฒฉ ๊ธธ์ด(mm) ๋๋‹จ๊ฐ€๊ณต ์•ก์…˜
updateSegmentLineNo(index, e.target.value)} placeholder="LINE-A-001" /> {segment.material_grade} {segment.schedule_spec} {segment.nominal_size} {segment.length_mm}
``` #### **3๋‹จ๊ณ„: ๋‹จ๊ด€ ์ •๋ณด ํ™•์ธ** ```javascript

โœ… 3๋‹จ๊ณ„: ๋‹จ๊ด€ ์ •๋ณด ํ™•์ธ

{/* ์ „์ฒด ์š”์•ฝ */}

๐Ÿ“Š ์ „์ฒด ํ˜„ํ™ฉ

์ด ๊ตฌ์—ญ: {areas.length}๊ฐœ

์ด ๋„๋ฉด: {totalDrawings}๊ฐœ

์ด ๋‹จ๊ด€: {totalSegments}๊ฐœ

{/* ๊ตฌ์—ญ๋ณ„ ์ƒ์„ธ */} {areas.map(area => (

๐Ÿ—๏ธ {area.name} ๊ตฌ์—ญ

{area.drawings.map(drawing => (
๐Ÿ“‹ {drawing}
{getSegmentsForDrawing(area.name, drawing).map(segment => ( ))}
๋ผ์ธ๋ฒˆํ˜ธ ์žฌ์งˆ/๊ทœ๊ฒฉ ๊ธธ์ด ๋๋‹จ๊ฐ€๊ณต
{segment.line_no} {segment.material_grade} {segment.schedule_spec} {segment.nominal_size} {segment.length_mm}mm {segment.end_preparation}
))}
))}
``` ### **2. ๋ฆฌ๋น„์ „ ๋น„๊ต ํŽ˜์ด์ง€** **ํŒŒ์ผ**: `frontend/src/pages/revision/PipeRevisionComparisonPage.jsx` #### **4๋‹จ๊ณ„: ๋ฆฌ๋น„์ „ ๋น„๊ต** ```javascript

๐Ÿ”„ 4๋‹จ๊ณ„: ๋ฆฌ๋น„์ „ ๋น„๊ต

{/* ๋น„๊ต ์š”์•ฝ */}

โž• ์ถ”๊ฐ€๋œ ๋‹จ๊ด€

{comparisonResult.added_segments}

โž– ์‚ญ์ œ๋œ ๋‹จ๊ด€

{comparisonResult.removed_segments}

๐Ÿ”ง ์ˆ˜์ •๋œ ๋‹จ๊ด€

{comparisonResult.modified_segments}

โœ… ๋ณ€๊ฒฝ์—†์Œ

{comparisonResult.unchanged_segments}
{/* ๋ณ€๊ฒฝ๋œ ๋„๋ฉด๋งŒ ํ‘œ์‹œ */}

๐Ÿ“‹ ๋ณ€๊ฒฝ๋œ ๋„๋ฉด ๋ชฉ๋ก

{changedDrawings.map(drawing => (
๐Ÿ“„ {drawing.name}
{drawing.segments.map(segment => ( ))}
์ƒํƒœ ๋ผ์ธ๋ฒˆํ˜ธ ์žฌ์งˆ/๊ทœ๊ฒฉ ๊ธธ์ด ๋๋‹จ๊ฐ€๊ณต ๋ณ€๊ฒฝ์‚ฌํ•ญ
{getStatusLabel(segment.change_type)} {segment.line_no} {segment.material_info} {segment.length_mm}mm {segment.end_preparation} {segment.change_type !== 'unchanged' && (
{segment.changes.map(change => (
{change.field}: {change.old_value} โ†’ {change.new_value}
))}
)}
))}
``` #### **5๋‹จ๊ณ„: ๋ณ€๊ฒฝ ๋„๋ฉด ์š”์•ฝ** ```javascript

๐Ÿ“Š 5๋‹จ๊ณ„: ๋ณ€๊ฒฝ ๋„๋ฉด ์š”์•ฝ

{/* ๋ณ€๊ฒฝ ๋„๋ฉด๋งŒ ์ง‘์ค‘ ํ‘œ์‹œ */}

โš ๏ธ ๊ฒ€ํ† ๊ฐ€ ํ•„์š”ํ•œ ๋„๋ฉด๋“ค

{changedDrawingsOnly.map(drawing => (
๐Ÿ“„ {drawing.name}
+{drawing.added_count} -{drawing.removed_count} ~{drawing.modified_count}
๐Ÿ“ˆ ํŒŒ์ดํ”„ ๊ตฌ๋งค๋Ÿ‰ ์˜ํ–ฅ
{drawing.material_impacts.map(impact => (
{impact.material} {impact.length_change > 0 ? '+' : ''}{impact.length_change}m ({impact.percentage_change > 0 ? '+' : ''}{impact.percentage_change}%)
))}
))}
{/* ์ „์ฒด ๊ตฌ๋งค๋Ÿ‰ ์žฌ๊ณ„์‚ฐ */}

๐Ÿ’ฐ ํŒŒ์ดํ”„ ๊ตฌ๋งค๋Ÿ‰ ์žฌ๊ณ„์‚ฐ

์ด์ „ ๊ตฌ๋งค ๊ณ„ํš
{previousPurchasePlan.map(item => (
{item.material} {item.total_length}m
))}
์‹ ๊ทœ ๊ตฌ๋งค ๊ณ„ํš
{newPurchasePlan.map(item => (
{item.material} {item.total_length}m {item.additional_needed > 0 && ( (+{item.additional_needed}m ์ถ”๊ฐ€ ํ•„์š”) )}
))}
``` ### **3. ๋‹จ๊ด€ ์ด์Šˆ ๊ด€๋ฆฌ ํŽ˜์ด์ง€** **ํŒŒ์ผ**: `frontend/src/pages/PipeIssueManagementPage.jsx` ```javascript

๐Ÿ› ๏ธ ๋‹จ๊ด€ ์ด์Šˆ ๊ด€๋ฆฌ

ํ˜„์žฅ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ํŒŒ์ดํ”„ ๊ด€๋ จ ์ด์Šˆ๋ฅผ ์ฒด๊ณ„์ ์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค

{/* ๊ฒ€์ƒ‰/ํ•„ํ„ฐ ์„น์…˜ */}
{/* ๋„๋ฉด ์ „๋ฐ˜ ์ด์Šˆ ์„น์…˜ */}

๐Ÿ“‹ ๋„๋ฉด ์ „๋ฐ˜ ์ด์Šˆ

{/* ๊ธฐ์กด ์ด์Šˆ ๋ชฉ๋ก */}
{drawingIssues.map(issue => (
{issue.severity.toUpperCase()} {issue.status.toUpperCase()}

{issue.issue_description}

๋ณด๊ณ ์ž: {issue.reported_by} | {issue.reported_at}
{issue.status === 'open' && ( )}
))}
{/* ์ƒˆ ์ด์Šˆ ์ถ”๊ฐ€ */}

โž• ์ƒˆ ๋„๋ฉด ์ด์Šˆ ์ถ”๊ฐ€