feat: 리비전 관리 시스템 및 구매확정 기능 구현
- 리비전 관리 라우터 및 서비스 추가 (revision_management.py, revision_comparison_service.py, revision_session_service.py) - 구매확정 기능 구현: materials 테이블에 purchase_confirmed 필드 추가 및 업데이트 로직 - 리비전 비교 로직 구현: 구매확정된 자재 기반으로 신규/변경 자재 자동 분류 - 데이터베이스 스키마 확장: revision_sessions, revision_material_changes, inventory_transfers 테이블 추가 - 구매신청 생성 시 자재 상세 정보 저장 및 purchase_confirmed 자동 업데이트 - 프론트엔드: 리비전 관리 컴포넌트 및 hooks 추가 - 파일 목록 조회 API 추가 (/files/list) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -189,6 +189,62 @@ def add_missing_columns(cursor):
|
||||
print(f"✅ material_purchase_tracking.{column_name} 컬럼 추가 완료")
|
||||
else:
|
||||
print(f"✅ material_purchase_tracking.{column_name} 컬럼이 이미 존재합니다")
|
||||
|
||||
# purchase_requests 테이블에 누락된 컬럼들 확인 및 추가
|
||||
purchase_requests_columns = {
|
||||
'file_id': 'INTEGER REFERENCES files(id)',
|
||||
'category': 'VARCHAR(50)',
|
||||
'material_count': 'INTEGER DEFAULT 0',
|
||||
'excel_file_path': 'VARCHAR(500)'
|
||||
}
|
||||
|
||||
for column_name, column_type in purchase_requests_columns.items():
|
||||
cursor.execute("""
|
||||
SELECT column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'purchase_requests' AND column_name = %s;
|
||||
""", (column_name,))
|
||||
|
||||
if not cursor.fetchone():
|
||||
print(f"➕ purchase_requests 테이블에 {column_name} 컬럼 추가 중...")
|
||||
cursor.execute(f"""
|
||||
ALTER TABLE purchase_requests ADD COLUMN {column_name} {column_type};
|
||||
""")
|
||||
print(f"✅ purchase_requests.{column_name} 컬럼 추가 완료")
|
||||
else:
|
||||
print(f"✅ purchase_requests.{column_name} 컬럼이 이미 존재합니다")
|
||||
|
||||
# purchase_request_items 테이블에 누락된 컬럼들 확인 및 추가
|
||||
purchase_request_items_columns = {
|
||||
'user_requirement': 'TEXT',
|
||||
'description': 'TEXT',
|
||||
'category': 'VARCHAR(50)',
|
||||
'subcategory': 'VARCHAR(100)',
|
||||
'material_grade': 'VARCHAR(50)',
|
||||
'size_spec': 'VARCHAR(50)',
|
||||
'drawing_name': 'VARCHAR(100)',
|
||||
'notes': 'TEXT',
|
||||
'is_ordered': 'BOOLEAN DEFAULT FALSE',
|
||||
'is_received': 'BOOLEAN DEFAULT FALSE'
|
||||
}
|
||||
|
||||
for column_name, column_type in purchase_request_items_columns.items():
|
||||
cursor.execute("""
|
||||
SELECT column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = 'purchase_request_items' AND column_name = %s;
|
||||
""", (column_name,))
|
||||
|
||||
if not cursor.fetchone():
|
||||
print(f"➕ purchase_request_items 테이블에 {column_name} 컬럼 추가 중...")
|
||||
cursor.execute(f"""
|
||||
ALTER TABLE purchase_request_items ADD COLUMN {column_name} {column_type};
|
||||
""")
|
||||
print(f"✅ purchase_request_items.{column_name} 컬럼 추가 완료")
|
||||
else:
|
||||
print(f"✅ purchase_request_items.{column_name} 컬럼이 이미 존재합니다")
|
||||
|
||||
print("✅ 모든 누락된 컬럼 추가 완료!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠️ 컬럼 추가 실패: {e}")
|
||||
@@ -328,7 +384,11 @@ def create_missing_tables():
|
||||
CREATE TABLE purchase_requests (
|
||||
request_id SERIAL PRIMARY KEY,
|
||||
request_no VARCHAR(50) UNIQUE NOT NULL,
|
||||
file_id INTEGER REFERENCES files(id),
|
||||
job_no VARCHAR(50) NOT NULL,
|
||||
category VARCHAR(50),
|
||||
material_count INTEGER DEFAULT 0,
|
||||
excel_file_path VARCHAR(500),
|
||||
project_name VARCHAR(200),
|
||||
requested_by INTEGER REFERENCES users(user_id),
|
||||
requested_by_username VARCHAR(100),
|
||||
@@ -387,6 +447,163 @@ def create_missing_tables():
|
||||
print("✅ purchase_request_items 테이블 생성 완료")
|
||||
else:
|
||||
print("✅ purchase_request_items 테이블 이미 존재")
|
||||
|
||||
# 5. revision_sessions 테이블
|
||||
print("📋 5. revision_sessions 테이블 확인...")
|
||||
cursor.execute("""
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_name = 'revision_sessions'
|
||||
);
|
||||
""")
|
||||
if not cursor.fetchone()[0]:
|
||||
print("➕ revision_sessions 테이블 생성 중...")
|
||||
cursor.execute("""
|
||||
CREATE TABLE revision_sessions (
|
||||
id SERIAL PRIMARY KEY,
|
||||
job_no VARCHAR(50) NOT NULL,
|
||||
current_file_id INTEGER REFERENCES files(id),
|
||||
previous_file_id INTEGER REFERENCES files(id),
|
||||
current_revision VARCHAR(20) NOT NULL,
|
||||
previous_revision VARCHAR(20) NOT NULL,
|
||||
|
||||
status VARCHAR(20) DEFAULT 'processing',
|
||||
total_materials INTEGER DEFAULT 0,
|
||||
processed_materials INTEGER DEFAULT 0,
|
||||
|
||||
added_count INTEGER DEFAULT 0,
|
||||
removed_count INTEGER DEFAULT 0,
|
||||
changed_count INTEGER DEFAULT 0,
|
||||
unchanged_count INTEGER DEFAULT 0,
|
||||
|
||||
purchase_cancel_count INTEGER DEFAULT 0,
|
||||
inventory_transfer_count INTEGER DEFAULT 0,
|
||||
additional_purchase_count INTEGER DEFAULT 0,
|
||||
|
||||
created_by VARCHAR(100),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
completed_at TIMESTAMP
|
||||
);
|
||||
CREATE INDEX idx_revision_sessions_job_no ON revision_sessions(job_no);
|
||||
CREATE INDEX idx_revision_sessions_status ON revision_sessions(status);
|
||||
""")
|
||||
print("✅ revision_sessions 테이블 생성 완료")
|
||||
else:
|
||||
print("✅ revision_sessions 테이블 이미 존재")
|
||||
|
||||
# 6. revision_material_changes 테이블
|
||||
print("📋 6. revision_material_changes 테이블 확인...")
|
||||
cursor.execute("""
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_name = 'revision_material_changes'
|
||||
);
|
||||
""")
|
||||
if not cursor.fetchone()[0]:
|
||||
print("➕ revision_material_changes 테이블 생성 중...")
|
||||
cursor.execute("""
|
||||
CREATE TABLE revision_material_changes (
|
||||
id SERIAL PRIMARY KEY,
|
||||
session_id INTEGER REFERENCES revision_sessions(id) ON DELETE CASCADE,
|
||||
|
||||
material_id INTEGER REFERENCES materials(id),
|
||||
previous_material_id INTEGER,
|
||||
material_description TEXT NOT NULL,
|
||||
category VARCHAR(50) NOT NULL,
|
||||
|
||||
change_type VARCHAR(20) NOT NULL,
|
||||
previous_quantity NUMERIC(10,3),
|
||||
current_quantity NUMERIC(10,3),
|
||||
quantity_difference NUMERIC(10,3),
|
||||
|
||||
purchase_status VARCHAR(20) NOT NULL,
|
||||
purchase_confirmed_at TIMESTAMP,
|
||||
|
||||
revision_action VARCHAR(30),
|
||||
action_status VARCHAR(20) DEFAULT 'pending',
|
||||
|
||||
processed_by VARCHAR(100),
|
||||
processed_at TIMESTAMP,
|
||||
processing_notes TEXT,
|
||||
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX idx_revision_changes_session ON revision_material_changes(session_id);
|
||||
CREATE INDEX idx_revision_changes_action ON revision_material_changes(revision_action);
|
||||
CREATE INDEX idx_revision_changes_status ON revision_material_changes(action_status);
|
||||
""")
|
||||
print("✅ revision_material_changes 테이블 생성 완료")
|
||||
else:
|
||||
print("✅ revision_material_changes 테이블 이미 존재")
|
||||
|
||||
# 7. inventory_transfers 테이블
|
||||
print("📋 7. inventory_transfers 테이블 확인...")
|
||||
cursor.execute("""
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_name = 'inventory_transfers'
|
||||
);
|
||||
""")
|
||||
if not cursor.fetchone()[0]:
|
||||
print("➕ inventory_transfers 테이블 생성 중...")
|
||||
cursor.execute("""
|
||||
CREATE TABLE inventory_transfers (
|
||||
id SERIAL PRIMARY KEY,
|
||||
revision_change_id INTEGER REFERENCES revision_material_changes(id),
|
||||
|
||||
material_description TEXT NOT NULL,
|
||||
category VARCHAR(50) NOT NULL,
|
||||
quantity NUMERIC(10,3) NOT NULL,
|
||||
unit VARCHAR(10) NOT NULL,
|
||||
|
||||
inventory_location VARCHAR(100),
|
||||
storage_notes TEXT,
|
||||
|
||||
transferred_by VARCHAR(100) NOT NULL,
|
||||
transferred_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
status VARCHAR(20) DEFAULT 'transferred'
|
||||
);
|
||||
CREATE INDEX idx_inventory_transfers_material ON inventory_transfers(material_description);
|
||||
CREATE INDEX idx_inventory_transfers_date ON inventory_transfers(transferred_at);
|
||||
""")
|
||||
print("✅ inventory_transfers 테이블 생성 완료")
|
||||
else:
|
||||
print("✅ inventory_transfers 테이블 이미 존재")
|
||||
|
||||
# 8. revision_action_logs 테이블
|
||||
print("📋 8. revision_action_logs 테이블 확인...")
|
||||
cursor.execute("""
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_name = 'revision_action_logs'
|
||||
);
|
||||
""")
|
||||
if not cursor.fetchone()[0]:
|
||||
print("➕ revision_action_logs 테이블 생성 중...")
|
||||
cursor.execute("""
|
||||
CREATE TABLE revision_action_logs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
session_id INTEGER REFERENCES revision_sessions(id),
|
||||
revision_change_id INTEGER REFERENCES revision_material_changes(id),
|
||||
|
||||
action_type VARCHAR(30) NOT NULL,
|
||||
action_description TEXT,
|
||||
|
||||
executed_by VARCHAR(100) NOT NULL,
|
||||
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
result VARCHAR(20) NOT NULL,
|
||||
result_message TEXT,
|
||||
result_data JSONB
|
||||
);
|
||||
CREATE INDEX idx_revision_logs_session ON revision_action_logs(session_id);
|
||||
CREATE INDEX idx_revision_logs_type ON revision_action_logs(action_type);
|
||||
CREATE INDEX idx_revision_logs_date ON revision_action_logs(executed_at);
|
||||
""")
|
||||
print("✅ revision_action_logs 테이블 생성 완료")
|
||||
else:
|
||||
print("✅ revision_action_logs 테이블 이미 존재")
|
||||
|
||||
# 변경사항 커밋
|
||||
conn.commit()
|
||||
|
||||
Reference in New Issue
Block a user