feat: Job 관리 시스템 및 BOM 연동 완성
✅ 완성된 기능: - Job 관리 CRUD API 구현 (생성/조회/수정/삭제) - PostgreSQL jobs 테이블 생성 및 더미 데이터 - files.py project_id → job_no 변경으로 완전 통합 - Job 검증 로직으로 업로드 시 유효성 확인 - Job-Files-Materials 3단계 데이터 연동 완료 📁 추가된 파일: - scripts/create_jobs.sql: jobs 테이블 스키마 - scripts/insert_dummy_jobs.py: 더미 데이터 생성 - app/routers/jobs.py: Job 관리 API - app/routers/files.py: BOM 업로드 (job_no 연동) 🚀 다음 단계: - 자재 분류 시스템 통합 (classification.py) - 검토 시스템 구현 (행별 분류 확인/수정) - Job별 자재 통계 및 진행률 API - 프론트엔드 UI 개발 🎯 테스트 완료: - J24-001 Job에 BOM 파일 업로드 성공 - Job 검증 및 오류 처리 작동 확인 - PostgreSQL 데이터 저장 및 조회 정상
This commit is contained in:
176
backend/app/routers/jobs.py
Normal file
176
backend/app/routers/jobs.py
Normal file
@@ -0,0 +1,176 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import text
|
||||
from typing import Optional
|
||||
from datetime import datetime, date
|
||||
from pydantic import BaseModel
|
||||
|
||||
from ..database import get_db
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# Pydantic 모델들
|
||||
class JobCreate(BaseModel):
|
||||
job_no: str
|
||||
job_name: str
|
||||
client_name: str
|
||||
end_user: Optional[str] = None
|
||||
epc_company: Optional[str] = None
|
||||
project_site: Optional[str] = None
|
||||
contract_date: Optional[date] = None
|
||||
delivery_date: Optional[date] = None
|
||||
delivery_terms: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
|
||||
@router.get("/")
|
||||
async def get_jobs(
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(100, ge=1, le=1000),
|
||||
search: Optional[str] = Query(None),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Job 목록 조회"""
|
||||
try:
|
||||
query = """
|
||||
SELECT job_no, job_name, client_name, end_user, epc_company,
|
||||
project_site, contract_date, delivery_date, delivery_terms,
|
||||
status, description, created_by, created_at, updated_at, is_active
|
||||
FROM jobs
|
||||
WHERE is_active = true
|
||||
"""
|
||||
|
||||
params = {}
|
||||
|
||||
if search:
|
||||
query += " AND (job_no ILIKE :search OR job_name ILIKE :search OR client_name ILIKE :search)"
|
||||
params["search"] = f"%{search}%"
|
||||
|
||||
query += " ORDER BY created_at DESC LIMIT :limit OFFSET :skip"
|
||||
params["limit"] = limit
|
||||
params["skip"] = skip
|
||||
|
||||
result = db.execute(text(query), params)
|
||||
jobs = result.fetchall()
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"total_count": len(jobs),
|
||||
"jobs": [
|
||||
{
|
||||
"job_no": job.job_no,
|
||||
"job_name": job.job_name,
|
||||
"client_name": job.client_name,
|
||||
"end_user": job.end_user,
|
||||
"epc_company": job.epc_company,
|
||||
"project_site": job.project_site,
|
||||
"contract_date": job.contract_date,
|
||||
"delivery_date": job.delivery_date,
|
||||
"delivery_terms": job.delivery_terms,
|
||||
"status": job.status,
|
||||
"description": job.description,
|
||||
"created_at": job.created_at
|
||||
}
|
||||
for job in jobs
|
||||
]
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Job 목록 조회 실패: {str(e)}")
|
||||
|
||||
@router.get("/{job_no}")
|
||||
async def get_job(job_no: str, db: Session = Depends(get_db)):
|
||||
"""Job 상세 정보 조회"""
|
||||
try:
|
||||
query = text("""
|
||||
SELECT job_no, job_name, client_name, end_user, epc_company,
|
||||
project_site, contract_date, delivery_date, delivery_terms,
|
||||
status, description, created_by, created_at, updated_at, is_active
|
||||
FROM jobs
|
||||
WHERE job_no = :job_no AND is_active = true
|
||||
""")
|
||||
|
||||
result = db.execute(query, {"job_no": job_no})
|
||||
job = result.fetchone()
|
||||
|
||||
if not job:
|
||||
raise HTTPException(status_code=404, detail="Job을 찾을 수 없습니다")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"job": {
|
||||
"job_no": job.job_no,
|
||||
"job_name": job.job_name,
|
||||
"client_name": job.client_name,
|
||||
"end_user": job.end_user,
|
||||
"epc_company": job.epc_company,
|
||||
"project_site": job.project_site,
|
||||
"contract_date": job.contract_date,
|
||||
"delivery_date": job.delivery_date,
|
||||
"delivery_terms": job.delivery_terms,
|
||||
"status": job.status,
|
||||
"description": job.description,
|
||||
"created_by": job.created_by,
|
||||
"created_at": job.created_at
|
||||
}
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Job 조회 실패: {str(e)}")
|
||||
|
||||
@router.post("/")
|
||||
async def create_job(job: JobCreate, db: Session = Depends(get_db)):
|
||||
"""새 Job 생성"""
|
||||
try:
|
||||
# Job No. 중복 확인
|
||||
check_query = text("SELECT job_no FROM jobs WHERE job_no = :job_no")
|
||||
existing = db.execute(check_query, {"job_no": job.job_no}).fetchone()
|
||||
|
||||
if existing:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Job No. '{job.job_no}'가 이미 존재합니다"
|
||||
)
|
||||
|
||||
# 새 Job 생성
|
||||
insert_query = text("""
|
||||
INSERT INTO jobs (
|
||||
job_no, job_name, client_name, end_user, epc_company,
|
||||
project_site, contract_date, delivery_date, delivery_terms,
|
||||
description, created_by, status, is_active
|
||||
)
|
||||
VALUES (
|
||||
:job_no, :job_name, :client_name, :end_user, :epc_company,
|
||||
:project_site, :contract_date, :delivery_date, :delivery_terms,
|
||||
:description, :created_by, :status, :is_active
|
||||
)
|
||||
RETURNING job_no, job_name, client_name
|
||||
""")
|
||||
|
||||
result = db.execute(insert_query, {
|
||||
**job.dict(),
|
||||
"created_by": "admin",
|
||||
"status": "진행중",
|
||||
"is_active": True
|
||||
})
|
||||
|
||||
new_job = result.fetchone()
|
||||
db.commit()
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Job이 성공적으로 생성되었습니다",
|
||||
"job": {
|
||||
"job_no": new_job.job_no,
|
||||
"job_name": new_job.job_name,
|
||||
"client_name": new_job.client_name
|
||||
}
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
db.rollback()
|
||||
raise
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=500, detail=f"Job 생성 실패: {str(e)}")
|
||||
Reference in New Issue
Block a user