feat: 오늘 6시간 작업 내용 복구 완료

- 엑셀 내보내기 개선: 납기일 P열 이동, 관리항목 4개로 축소
- J24-001 더미 프로젝트 옵션 제거
- CORS 오류 해결: API URL /api로 통일
- BOM 페이지 수정사항 포함
- 트랜잭션 오류 해결 시도
This commit is contained in:
hyungi
2025-10-15 13:55:25 +09:00
parent b10bd8d01c
commit e799aae71b
7 changed files with 568 additions and 93 deletions

View File

@@ -34,7 +34,7 @@ class PurchaseRequestCreate(BaseModel):
@router.post("/create") @router.post("/create")
async def create_purchase_request( async def create_purchase_request(
request_data: PurchaseRequestCreate, request_data: PurchaseRequestCreate,
# current_user: dict = Depends(get_current_user), current_user: dict = Depends(get_current_user),
db: Session = Depends(get_db) db: Session = Depends(get_db)
): ):
""" """
@@ -81,7 +81,7 @@ async def create_purchase_request(
material_count, excel_file_path, requested_by material_count, excel_file_path, requested_by
) VALUES ( ) VALUES (
:request_no, :file_id, :job_no, :category, :request_no, :file_id, :job_no, :category,
:material_count, :excel_path, :requested_by :material_count, :excel_file_path, :requested_by
) RETURNING request_id ) RETURNING request_id
""") """)
@@ -91,8 +91,8 @@ async def create_purchase_request(
"job_no": request_data.job_no, "job_no": request_data.job_no,
"category": request_data.category, "category": request_data.category,
"material_count": len(request_data.material_ids), "material_count": len(request_data.material_ids),
"excel_path": excel_filename, # 엑셀 파일명 저장 (JSON 대신) "excel_file_path": excel_filename, # 엑셀 파일명 저장 (JSON 대신)
"requested_by": 1 # current_user.get("user_id") "requested_by": current_user.get("user_id")
}) })
request_id = result.fetchone().request_id request_id = result.fetchone().request_id
@@ -163,7 +163,7 @@ async def create_purchase_request(
db.rollback() db.rollback()
logger.error(f"Failed to create purchase request: {str(e)}") logger.error(f"Failed to create purchase request: {str(e)}")
raise HTTPException( raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, status_code=500,
detail=f"구매신청 생성 실패: {str(e)}" detail=f"구매신청 생성 실패: {str(e)}"
) )
@@ -194,7 +194,7 @@ async def get_purchase_requests(
u.name as requested_by, u.name as requested_by,
f.original_filename, f.original_filename,
j.job_name, j.job_name,
COUNT(pri.item_id) as item_count COUNT(pri.id) as item_count
FROM purchase_requests pr FROM purchase_requests pr
LEFT JOIN users u ON pr.requested_by = u.user_id LEFT JOIN users u ON pr.requested_by = u.user_id
LEFT JOIN files f ON pr.file_id = f.id LEFT JOIN files f ON pr.file_id = f.id
@@ -244,7 +244,7 @@ async def get_purchase_requests(
except Exception as e: except Exception as e:
logger.error(f"Failed to get purchase requests: {str(e)}") logger.error(f"Failed to get purchase requests: {str(e)}")
raise HTTPException( raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, status_code=500,
detail=f"구매신청 목록 조회 실패: {str(e)}" detail=f"구매신청 목록 조회 실패: {str(e)}"
) )
@@ -400,7 +400,7 @@ async def get_request_materials(
except Exception as e: except Exception as e:
logger.error(f"Failed to get request materials: {str(e)}") logger.error(f"Failed to get request materials: {str(e)}")
raise HTTPException( raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, status_code=500,
detail=f"구매신청 자재 조회 실패: {str(e)}" detail=f"구매신청 자재 조회 실패: {str(e)}"
) )
@@ -451,7 +451,7 @@ async def download_request_excel(
except Exception as e: except Exception as e:
logger.error(f"Failed to download request excel: {str(e)}") logger.error(f"Failed to download request excel: {str(e)}")
raise HTTPException( raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, status_code=500,
detail=f"엑셀 다운로드 실패: {str(e)}" detail=f"엑셀 다운로드 실패: {str(e)}"
) )

View File

@@ -1,100 +1,575 @@
{ {
"request_no": "PR-20251014-001", "request_no": "PR-20251014-001",
"job_no": "테스트용", "job_no": "테스트용",
"created_at": "2025-10-14T06:47:25.065166", "created_at": "2025-10-14T22:16:10.998006",
"materials": [ "materials": [
{ {
"material_id": 2013, "material_id": 60768,
"description": "SIGHT GLASS, FLG, 150LB", "description": "PIPE, SMLS, SCH 40S, ASTM A312 TP304",
"category": "FLANGE", "category": "PIPE",
"size": "1\"",
"material_grade": "SS",
"quantity": 6,
"unit": "EA",
"user_requirement": ""
},
{
"material_id": 2019,
"description": "SIGHT GLASS, FLG, 150LB",
"category": "FLANGE",
"size": "1/2\"", "size": "1/2\"",
"material_grade": "SS", "material_grade": "ASTM A312 TP304",
"quantity": 5, "quantity": 11,
"unit": "EA", "unit": "EA",
"user_requirement": "" "user_requirement": ""
}, },
{ {
"material_id": 2024, "material_id": 60776,
"description": "SIGHT GLASS, FLG, 150LB", "description": "PIPE, SMLS, SCH 80, ASTM A106 B",
"category": "FLANGE", "category": "PIPE",
"size": "2\"", "size": "3/4\"",
"material_grade": "SS", "material_grade": "ASTM A106 B",
"quantity": 2, "quantity": 92,
"unit": "EA",
"user_requirement": ""
},
{
"material_id": 2027,
"description": "STRAINER, FLG, 150LB",
"category": "FLANGE",
"size": "2\"",
"material_grade": "-",
"quantity": 1,
"unit": "EA", "unit": "EA",
"user_requirement": "" "user_requirement": ""
} }
], ],
"grouped_materials": [ "grouped_materials": [
{ {
"group_key": "SIGHT GLASS, FLG, 150LB|1\"|undefined|SS", "group_key": "PIPE, SMLS, SCH 40S, ASTM A312 TP304|1/2\"|undefined|ASTM A312 TP304",
"material_ids": [ "material_ids": [
2013 60768
], ],
"description": "SIGHT GLASS, FLG, 150LB", "description": "PIPE, SMLS, SCH 40S, ASTM A312 TP304",
"category": "FLANGE", "category": "PIPE",
"size": "1\"",
"material_grade": "SS",
"quantity": 6,
"unit": "EA",
"user_requirement": ""
},
{
"group_key": "SIGHT GLASS, FLG, 150LB|1/2\"|undefined|SS",
"material_ids": [
2019
],
"description": "SIGHT GLASS, FLG, 150LB",
"category": "FLANGE",
"size": "1/2\"", "size": "1/2\"",
"material_grade": "SS", "material_grade": "ASTM A312 TP304",
"quantity": 5, "quantity": 11,
"unit": "EA", "unit": "m",
"total_length": 1395.1,
"pipe_lengths": [
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 155,
"quantity": 1,
"totalLength": 155
},
{
"length": 155,
"quantity": 1,
"totalLength": 155
},
{
"length": 200,
"quantity": 1,
"totalLength": 200
},
{
"length": 245.1,
"quantity": 1,
"totalLength": 245.1
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
}
],
"user_requirement": "" "user_requirement": ""
}, },
{ {
"group_key": "SIGHT GLASS, FLG, 150LB|2\"|undefined|SS", "group_key": "PIPE, SMLS, SCH 80, ASTM A106 B|3/4\"|undefined|ASTM A106 B",
"material_ids": [ "material_ids": [
2024 60776
], ],
"description": "SIGHT GLASS, FLG, 150LB", "description": "PIPE, SMLS, SCH 80, ASTM A106 B",
"category": "FLANGE", "category": "PIPE",
"size": "2\"", "size": "3/4\"",
"material_grade": "SS", "material_grade": "ASTM A106 B",
"quantity": 2, "quantity": 92,
"unit": "EA", "unit": "m",
"user_requirement": "" "total_length": 7920.2,
}, "pipe_lengths": [
{ {
"group_key": "STRAINER, FLG, 150LB|2\"|undefined|-", "length": 60,
"material_ids": [ "quantity": 1,
2027 "totalLength": 60
},
{
"length": 60,
"quantity": 1,
"totalLength": 60
},
{
"length": 60,
"quantity": 1,
"totalLength": 60
},
{
"length": 60,
"quantity": 1,
"totalLength": 60
},
{
"length": 43.3,
"quantity": 1,
"totalLength": 43.3
},
{
"length": 43.3,
"quantity": 1,
"totalLength": 43.3
},
{
"length": 43.3,
"quantity": 1,
"totalLength": 43.3
},
{
"length": 43.3,
"quantity": 1,
"totalLength": 43.3
},
{
"length": 43.3,
"quantity": 1,
"totalLength": 43.3
},
{
"length": 43.3,
"quantity": 1,
"totalLength": 43.3
},
{
"length": 50,
"quantity": 1,
"totalLength": 50
},
{
"length": 50,
"quantity": 1,
"totalLength": 50
},
{
"length": 50,
"quantity": 1,
"totalLength": 50
},
{
"length": 50,
"quantity": 1,
"totalLength": 50
},
{
"length": 50,
"quantity": 1,
"totalLength": 50
},
{
"length": 50,
"quantity": 1,
"totalLength": 50
},
{
"length": 50,
"quantity": 1,
"totalLength": 50
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 70,
"quantity": 1,
"totalLength": 70
},
{
"length": 76.2,
"quantity": 1,
"totalLength": 76.2
},
{
"length": 76.2,
"quantity": 1,
"totalLength": 76.2
},
{
"length": 76.2,
"quantity": 1,
"totalLength": 76.2
},
{
"length": 76.2,
"quantity": 1,
"totalLength": 76.2
},
{
"length": 76.2,
"quantity": 1,
"totalLength": 76.2
},
{
"length": 76.2,
"quantity": 1,
"totalLength": 76.2
},
{
"length": 77.6,
"quantity": 1,
"totalLength": 77.6
},
{
"length": 77.6,
"quantity": 1,
"totalLength": 77.6
},
{
"length": 77.6,
"quantity": 1,
"totalLength": 77.6
},
{
"length": 77.6,
"quantity": 1,
"totalLength": 77.6
},
{
"length": 77.6,
"quantity": 1,
"totalLength": 77.6
},
{
"length": 77.6,
"quantity": 1,
"totalLength": 77.6
},
{
"length": 80,
"quantity": 1,
"totalLength": 80
},
{
"length": 80,
"quantity": 1,
"totalLength": 80
},
{
"length": 80,
"quantity": 1,
"totalLength": 80
},
{
"length": 80,
"quantity": 1,
"totalLength": 80
},
{
"length": 80,
"quantity": 1,
"totalLength": 80
},
{
"length": 80,
"quantity": 1,
"totalLength": 80
},
{
"length": 80,
"quantity": 1,
"totalLength": 80
},
{
"length": 80,
"quantity": 1,
"totalLength": 80
},
{
"length": 80,
"quantity": 1,
"totalLength": 80
},
{
"length": 88.6,
"quantity": 1,
"totalLength": 88.6
},
{
"length": 88.6,
"quantity": 1,
"totalLength": 88.6
},
{
"length": 98.4,
"quantity": 1,
"totalLength": 98.4
},
{
"length": 98.4,
"quantity": 1,
"totalLength": 98.4
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 100,
"quantity": 1,
"totalLength": 100
},
{
"length": 120,
"quantity": 1,
"totalLength": 120
},
{
"length": 120,
"quantity": 1,
"totalLength": 120
},
{
"length": 150,
"quantity": 1,
"totalLength": 150
},
{
"length": 150,
"quantity": 1,
"totalLength": 150
},
{
"length": 150,
"quantity": 1,
"totalLength": 150
},
{
"length": 150,
"quantity": 1,
"totalLength": 150
},
{
"length": 150,
"quantity": 1,
"totalLength": 150
},
{
"length": 223.6,
"quantity": 1,
"totalLength": 223.6
}
], ],
"description": "STRAINER, FLG, 150LB",
"category": "FLANGE",
"size": "2\"",
"material_grade": "-",
"quantity": 1,
"unit": "EA",
"user_requirement": "" "user_requirement": ""
} }
] ]

View File

@@ -4,7 +4,7 @@ services:
container_name: tk-mp-nginx-proxy container_name: tk-mp-nginx-proxy
restart: unless-stopped restart: unless-stopped
ports: ports:
- "80:80" - "8808:80"
volumes: volumes:
- ./nginx-proxy.conf:/etc/nginx/conf.d/default.conf - ./nginx-proxy.conf:/etc/nginx/conf.d/default.conf
networks: networks:

View File

@@ -78,13 +78,13 @@ services:
context: ./frontend context: ./frontend
dockerfile: Dockerfile dockerfile: Dockerfile
args: args:
- VITE_API_URL=${VITE_API_URL:-http://localhost:18000} - VITE_API_URL=${VITE_API_URL:-/api}
container_name: tk-mp-frontend container_name: tk-mp-frontend
restart: unless-stopped restart: unless-stopped
ports: ports:
- "${FRONTEND_PORT:-13000}:5173" - "${FRONTEND_PORT:-13000}:3000"
environment: environment:
- VITE_API_URL=${VITE_API_URL:-http://localhost:18000} - VITE_API_URL=${VITE_API_URL:-/api}
depends_on: depends_on:
- backend - backend
networks: networks:

View File

@@ -229,9 +229,11 @@ function App() {
console.log('getAdminFeatures - Current user:', user); console.log('getAdminFeatures - Current user:', user);
console.log('getAdminFeatures - User role:', user?.role); console.log('getAdminFeatures - User role:', user?.role);
console.log('getAdminFeatures - Pending count:', pendingSignupCount); console.log('getAdminFeatures - Pending count:', pendingSignupCount);
console.log('getAdminFeatures - Role check result:', user?.role === 'system' || user?.role === 'admin');
// 시스템 관리자 기능 (admin role이 시스템 관리자) // 시스템 관리자 기능 (system role이 최고 권한)
if (user?.role === 'admin') { if (user?.role === 'system' || user?.role === 'admin') {
console.log('✅ 시스템 관리자 기능 추가 중...');
features.push( features.push(
{ {
id: 'user-management', id: 'user-management',

View File

@@ -1386,11 +1386,9 @@ const NewMaterialsPage = ({
const timestamp = new Date().toISOString().split('T')[0]; const timestamp = new Date().toISOString().split('T')[0];
const fileName = `${jobNo}_${selectedCategory}_${timestamp}.xlsx`; const fileName = `${jobNo}_${selectedCategory}_${timestamp}.xlsx`;
// BOM 페이지에서 사용하는 엑셀 내보내기 함수 사용 // 기존 엑셀 내보내기 함수 사용
await exportCategoryToExcel( await exportMaterialsToExcel(
selectedCategory,
dataWithRequirements, dataWithRequirements,
jobNo,
fileName, fileName,
userRequirements userRequirements
); );

View File

@@ -159,8 +159,8 @@ const SystemSettingsPage = ({ onNavigate, user }) => {
} }
}; };
// 관리자 권한 확인 // 관리자 권한 확인 (system이 최고 권한)
if (user?.role !== 'admin') { if (user?.role !== 'admin' && user?.role !== 'system') {
return ( return (
<div style={{ padding: '32px', textAlign: 'center' }}> <div style={{ padding: '32px', textAlign: 'center' }}>
<h2 style={{ color: '#dc2626', marginBottom: '16px' }}>접근 권한이 없습니다</h2> <h2 style={{ color: '#dc2626', marginBottom: '16px' }}>접근 권한이 없습니다</h2>