# 견적관리 API 요청서 > **작성일**: 2025-12-04 > **목적**: 견적관리 기능을 위한 백엔드 API 요청 > **참조**: sam-design/QuoteManagement3Write.tsx, QuoteManagement3Detail.tsx --- ## 1. 개요 ### 1.1 기능 요약 견적관리 시스템은 다음 기능을 지원해야 합니다: - 견적 CRUD (등록, 조회, 수정, 삭제) - 견적 상태 관리 (임시저장 → 확정 → 수주전환) - 견적 수정 이력 관리 (버전 관리) - 견적 품목(BOM) 관리 - 자동 견적 산출 (수식 기반 계산) ← **백엔드 구현** ### 1.2 특이사항 - **자동 견적 산출 로직**은 백엔드에서 구현 예정 (수식 계산 엔진) - 프론트엔드는 입력값을 전달하고 계산 결과를 받아서 표시 --- ## 2. 데이터 모델 ### 2.1 Quote (견적) - 메인 엔티티 ```typescript interface Quote { // === 기본 정보 === id: number; tenant_id: number; quote_number: string; // 견적번호 (예: KD-SC-251204-01) registration_date: string; // 등록일 (YYYY-MM-DD) receipt_date: string; // 접수일 author: string; // 작성자 // === 발주처 정보 === client_id: number | null; // 거래처 ID (FK) client_name: string; // 거래처명 (직접입력 대응) manager: string | null; // 담당자 contact: string | null; // 연락처 // === 현장 정보 === site_id: number | null; // 현장 ID (FK, 별도 테이블 필요시) site_name: string | null; // 현장명 site_code: string | null; // 현장코드 // === 제품 정보 === product_category: 'SCREEN' | 'STEEL'; // 제품 카테고리 product_id: number | null; // 선택된 제품 ID (품목마스터 FK) product_code: string | null; // 제품코드 product_name: string | null; // 제품명 // === 규격 정보 === open_size_width: number | null; // 오픈사이즈 폭 (mm) open_size_height: number | null; // 오픈사이즈 높이 (mm) quantity: number; // 수량 (기본값: 1) unit_symbol: string | null; // 부호 floors: string | null; // 층수 // === 금액 정보 === material_cost: number; // 재료비 합계 labor_cost: number; // 노무비 install_cost: number; // 설치비 subtotal: number; // 소계 discount_rate: number; // 할인율 (%) discount_amount: number; // 할인금액 total_amount: number; // 최종 금액 // === 상태 관리 === status: 'draft' | 'sent' | 'approved' | 'rejected' | 'finalized' | 'converted'; current_revision: number; // 현재 수정 차수 (0부터 시작) is_final: boolean; // 최종확정 여부 finalized_at: string | null; // 확정일시 finalized_by: number | null; // 확정자 ID // === 기타 정보 === completion_date: string | null; // 납기일 remarks: string | null; // 비고 memo: string | null; // 메모 notes: string | null; // 특이사항 // === 시스템 필드 === created_at: string; updated_at: string; created_by: number | null; updated_by: number | null; deleted_at: string | null; // Soft Delete } ``` ### 2.2 QuoteItem (견적 품목) - BOM 계산 결과 ```typescript interface QuoteItem { id: number; quote_id: number; // 견적 ID (FK) tenant_id: number; // === 품목 정보 === item_id: number | null; // 품목마스터 ID (FK) item_code: string; // 품목코드 item_name: string; // 품명 specification: string | null; // 규격 unit: string; // 단위 // === 수량/금액 === base_quantity: number; // 기본수량 calculated_quantity: number; // 계산된 수량 unit_price: number; // 단가 total_price: number; // 금액 (수량 × 단가) // === 수식 정보 === formula: string | null; // 수식 (예: "W/1000 + 0.1") formula_source: string | null; // 수식 출처 (BOM템플릿, 제품BOM 등) formula_category: string | null; // 수식 카테고리 data_source: string | null; // 데이터 출처 // === 기타 === delivery_date: string | null; // 품목별 납기일 note: string | null; // 비고 sort_order: number; // 정렬순서 created_at: string; updated_at: string; } ``` ### 2.3 QuoteRevision (견적 수정 이력) ```typescript interface QuoteRevision { id: number; quote_id: number; // 견적 ID (FK) tenant_id: number; revision_number: number; // 수정 차수 (1, 2, 3...) revision_date: string; // 수정일 revision_by: number; // 수정자 ID revision_by_name: string; // 수정자 이름 revision_reason: string | null; // 수정 사유 // 이전 버전 데이터 (JSON) previous_data: object; // 수정 전 견적 전체 데이터 (스냅샷) created_at: string; } ``` --- ## 3. API 엔드포인트 ### 3.1 견적 CRUD | Method | Endpoint | 설명 | 비고 | |--------|----------|------|------| | `GET` | `/api/v1/quotes` | 목록 조회 | 페이지네이션, 필터, 검색 | | `GET` | `/api/v1/quotes/{id}` | 단건 조회 | 품목(items), 이력(revisions) 포함 | | `POST` | `/api/v1/quotes` | 생성 | 품목 배열 포함 | | `PUT` | `/api/v1/quotes/{id}` | 수정 | 수정이력 자동 생성 | | `DELETE` | `/api/v1/quotes/{id}` | 삭제 | Soft Delete | | `DELETE` | `/api/v1/quotes` | 일괄 삭제 | `ids[]` 파라미터 | ### 3.2 견적 상태 관리 | Method | Endpoint | 설명 | 비고 | |--------|----------|------|------| | `PATCH` | `/api/v1/quotes/{id}/finalize` | 최종확정 | status → 'finalized', is_final → true | | `PATCH` | `/api/v1/quotes/{id}/convert-to-order` | 수주전환 | status → 'converted', 수주 데이터 생성 | | `PATCH` | `/api/v1/quotes/{id}/cancel-finalize` | 확정취소 | is_final → false (조건부) | ### 3.3 자동 견적 산출 (핵심 기능) | Method | Endpoint | 설명 | 비고 | |--------|----------|------|------| | `POST` | `/api/v1/quotes/calculate` | 자동 산출 | **수식 계산 엔진** | | `POST` | `/api/v1/quotes/{id}/recalculate` | 재계산 | 기존 견적 재산출 | ### 3.4 견적 문서 출력 | Method | Endpoint | 설명 | 비고 | |--------|----------|------|------| | `GET` | `/api/v1/quotes/{id}/document/quote` | 견적서 PDF | | | `GET` | `/api/v1/quotes/{id}/document/calculation` | 산출내역서 PDF | | | `GET` | `/api/v1/quotes/{id}/document/purchase-order` | 발주서 PDF | | ### 3.5 문서 발송 API ⭐ 신규 요청 | Method | Endpoint | 설명 | 비고 | |--------|----------|------|------| | `POST` | `/api/v1/quotes/{id}/send/email` | 이메일 발송 | 첨부파일 포함 | | `POST` | `/api/v1/quotes/{id}/send/fax` | 팩스 발송 | 팩스 서비스 연동 | | `POST` | `/api/v1/quotes/{id}/send/kakao` | 카카오톡 발송 | 알림톡/친구톡 | ### 3.6 견적번호 생성 | Method | Endpoint | 설명 | 비고 | |--------|----------|------|------| | `GET` | `/api/v1/quotes/generate-number` | 견적번호 생성 | `?category=SCREEN` | --- ## 4. 상세 API 명세 ### 4.1 목록 조회 `GET /api/v1/quotes` **Query Parameters:** ``` page: number (default: 1) size: number (default: 20) q: string (검색어 - 견적번호, 발주처, 담당자, 현장명) status: string (상태 필터) product_category: string (제품 카테고리) client_id: number (발주처 ID) date_from: string (등록일 시작) date_to: string (등록일 종료) sort_by: string (정렬 컬럼) sort_order: 'asc' | 'desc' ``` **Response:** ```json { "success": true, "data": { "current_page": 1, "data": [ { "id": 1, "quote_number": "KD-SC-251204-01", "registration_date": "2025-12-04", "client_name": "ABC건설", "site_name": "강남 오피스텔 현장", "product_category": "SCREEN", "product_name": "전동스크린 A형", "quantity": 10, "total_amount": 15000000, "status": "draft", "current_revision": 0, "is_final": false, "created_at": "2025-12-04T10:00:00Z" } ], "last_page": 5, "per_page": 20, "total": 100 } } ``` ### 4.2 단건 조회 `GET /api/v1/quotes/{id}` **Response:** ```json { "success": true, "data": { "id": 1, "quote_number": "KD-SC-251204-01", "registration_date": "2025-12-04", "receipt_date": "2025-12-04", "author": "김철수", "client_id": 10, "client_name": "ABC건설", "manager": "이영희", "contact": "010-1234-5678", "site_id": 5, "site_name": "강남 오피스텔 현장", "site_code": "PJ-20251204-01", "product_category": "SCREEN", "product_id": 100, "product_code": "SCR-001", "product_name": "전동스크린 A형", "open_size_width": 2000, "open_size_height": 3000, "quantity": 10, "unit_symbol": "A", "floors": "3층", "material_cost": 12000000, "labor_cost": 1500000, "install_cost": 1500000, "subtotal": 15000000, "discount_rate": 0, "discount_amount": 0, "total_amount": 15000000, "status": "draft", "current_revision": 2, "is_final": false, "completion_date": "2025-12-31", "remarks": "급하게 진행 필요", "memo": "", "notes": "", "items": [ { "id": 1, "item_code": "SCR-MOTOR-001", "item_name": "스크린 모터", "specification": "220V, 1/4HP", "unit": "EA", "base_quantity": 1, "calculated_quantity": 10, "unit_price": 150000, "total_price": 1500000, "formula": "Q", "formula_source": "제품BOM", "sort_order": 1 } ], "revisions": [ { "revision_number": 2, "revision_date": "2025-12-04", "revision_by_name": "김철수", "revision_reason": "고객 요청으로 수량 변경" }, { "revision_number": 1, "revision_date": "2025-12-03", "revision_by_name": "김철수", "revision_reason": "단가 조정" } ], "created_at": "2025-12-04T10:00:00Z", "updated_at": "2025-12-04T15:30:00Z" } } ``` ### 4.3 생성 `POST /api/v1/quotes` **Request Body:** ```json { "registration_date": "2025-12-04", "receipt_date": "2025-12-04", "client_id": 10, "client_name": "ABC건설", "manager": "이영희", "contact": "010-1234-5678", "site_id": 5, "site_name": "강남 오피스텔 현장", "site_code": "PJ-20251204-01", "product_category": "SCREEN", "product_id": 100, "open_size_width": 2000, "open_size_height": 3000, "quantity": 10, "unit_symbol": "A", "floors": "3층", "completion_date": "2025-12-31", "remarks": "급하게 진행 필요", "items": [ { "item_id": 50, "item_code": "SCR-MOTOR-001", "item_name": "스크린 모터", "unit": "EA", "base_quantity": 1, "calculated_quantity": 10, "unit_price": 150000, "total_price": 1500000, "formula": "Q", "sort_order": 1 } ] } ``` ### 4.4 자동 산출 `POST /api/v1/quotes/calculate` ⭐ 핵심 **Request Body:** ```json { "product_id": 100, "product_category": "SCREEN", "open_size_width": 2000, "open_size_height": 3000, "quantity": 10, "floors": "3층", "unit_symbol": "A", "options": { "guide_rail_install_type": "벽부형", "motor_power": "1/4HP", "controller": "표준형", "edge_wing_size": 50, "inspection_fee": 0 } } ``` **Response:** ```json { "success": true, "data": { "product_id": 100, "product_name": "전동스크린 A형", "product_category": "SCREEN", "open_size": { "width": 2000, "height": 3000 }, "quantity": 10, "items": [ { "item_id": 50, "item_code": "SCR-MOTOR-001", "item_name": "스크린 모터", "specification": "220V, 1/4HP", "unit": "EA", "base_quantity": 1, "calculated_quantity": 10, "unit_price": 150000, "total_price": 1500000, "formula": "Q", "formula_result": "10 × 1 = 10", "formula_source": "제품BOM: 전동스크린 A형", "data_source": "품목마스터 [SCR-MOTOR-001]" }, { "item_id": 51, "item_code": "SCR-RAIL-001", "item_name": "가이드레일", "specification": "알루미늄", "unit": "M", "base_quantity": 1, "calculated_quantity": 60, "unit_price": 15000, "total_price": 900000, "formula": "H/1000 × 2 × Q", "formula_result": "(3000/1000) × 2 × 10 = 60", "formula_source": "BOM템플릿: 스크린_표준", "data_source": "품목마스터 [SCR-RAIL-001]" } ], "summary": { "material_cost": 12000000, "labor_cost": 1500000, "install_cost": 1500000, "subtotal": 15000000, "total_amount": 15000000 }, "calculation_info": { "bom_template_used": "스크린_표준", "formula_variables": { "W": 2000, "H": 3000, "Q": 10 }, "calculated_at": "2025-12-04T10:00:00Z" } } } ``` --- ## 5. 수식 계산 엔진 (백엔드 구현 요청) ### 5.1 수식 변수 | 변수 | 설명 | 예시 | |------|------|------| | `W` | 오픈사이즈 폭 (mm) | 2000 | | `H` | 오픈사이즈 높이 (mm) | 3000 | | `Q` | 수량 | 10 | ### 5.2 수식 예시 ``` 수량 그대로: Q 높이 기반: H/1000 폭+높이: (W + H) / 1000 가이드레일: H/1000 × 2 × Q 스크린원단: (W/1000 + 0.1) × (H/1000 + 0.3) × Q ``` ### 5.3 반올림 규칙 | 규칙 | 설명 | |------|------| | `ceil` | 올림 | | `floor` | 내림 | | `round` | 반올림 | ### 5.4 BOM 템플릿 연동 - 제품별 BOM 템플릿에서 수식 조회 - 템플릿이 없으면 품목마스터 BOM 사용 - 수식 + 단가로 자동 금액 계산 --- ## 6. 상태 흐름도 ``` [신규등록] ↓ [draft] 임시저장 ↓ (최종확정) [finalized] 확정 ↓ (수주전환) [converted] 수주전환 ``` ### 6.1 상태별 제약 | 상태 | 수정 가능 | 삭제 가능 | 비고 | |------|----------|----------|------| | `draft` | O | O | 자유롭게 수정 | | `sent` | O | O | 발송 후 수정 가능 (이력 기록) | | `finalized` | X | X | 확정 후 수정 불가 | | `converted` | X | X | 수주전환 후 불변 | --- ## 7. 프론트엔드 구현 현황 (2025-12-04 업데이트) ### 7.1 구현 완료된 파일 | 파일 | 설명 | 상태 | |------|------|------| | `quote-management/page.tsx` | 견적 목록 페이지 | ✅ 완료 (샘플 데이터) | | `quote-management/new/page.tsx` | 견적 등록 페이지 | ✅ 완료 | | `quote-management/[id]/page.tsx` | 견적 상세 페이지 | ✅ 완료 | | `quote-management/[id]/edit/page.tsx` | 견적 수정 페이지 | ✅ 완료 | | `components/quotes/QuoteRegistration.tsx` | 견적 등록/수정 컴포넌트 | ✅ 완료 | | `components/quotes/QuoteDocument.tsx` | 견적서 문서 컴포넌트 | ✅ 완료 | | `components/quotes/QuoteCalculationReport.tsx` | 산출내역서 문서 컴포넌트 | ✅ 완료 | | `components/quotes/PurchaseOrderDocument.tsx` | 발주서 문서 컴포넌트 | ✅ 완료 | ### 7.2 UI 기능 구현 현황 | 기능 | 상태 | 비고 | |------|------|------| | 견적 목록 조회 | ✅ UI 완료 | 샘플 데이터, API 연동 필요 | | 견적 검색/필터 | ✅ UI 완료 | 로컬 필터링, API 연동 필요 | | 견적 등록 폼 | ✅ UI 완료 | API 연동 필요 | | 견적 상세 페이지 | ✅ UI 완료 | API 연동 필요 | | 견적 수정 폼 | ✅ UI 완료 | API 연동 필요 | | 견적 삭제 | ✅ UI 완료 | 로컬 상태, API 연동 필요 | | 견적 일괄 삭제 | ✅ UI 완료 | 로컬 상태, API 연동 필요 | | 자동 견적 산출 | ⏳ 버튼만 | 백엔드 수식 엔진 필요 | | 발주처 선택 | ⏳ 샘플 데이터 | `/api/v1/clients` 연동 필요 | | 현장 선택 | ⏳ 샘플 데이터 | 발주처 연동 후 현장 API 필요 | | 제품 선택 | ⏳ 샘플 데이터 | `/api/v1/item-masters` 연동 필요 | | **견적서 모달** | ✅ UI 완료 | PDF/이메일/팩스/카톡 버튼, **발송 API 필요** | | **산출내역서 모달** | ✅ UI 완료 | PDF/이메일/팩스/카톡 버튼, **발송 API 필요** | | **발주서 모달** | ✅ UI 완료 | PDF/이메일/팩스/카톡 버튼, **발송 API 필요** | | 최종확정 버튼 | ✅ UI 완료 | API 연동 필요 | ### 7.3 견적 등록/수정 폼 필드 (구현 완료) **기본 정보 섹션:** - 등록일 (readonly, 오늘 날짜) - 작성자 (readonly, 로그인 사용자) - 발주처 선택 * (필수) - 현장명 (발주처 선택 시 연동) - 발주 담당자 - 연락처 - 납기일 - 비고 **자동 견적 산출 섹션 (동적 항목):** - 층수 - 부호 - 제품 카테고리 (PC) * - 제품명 * - 오픈사이즈 (W0) * - 오픈사이즈 (H0) * - 가이드레일 설치 유형 (GT) * - 모터 전원 (MP) * - 연동제어기 (CT) * - 수량 (QTY) * - 마구리 날개치수 (WS) - 검사비 (INSP) **기능:** - 견적 항목 추가/복사/삭제 - 자동 견적 산출 버튼 - 샘플 데이터 생성 버튼 ### 7.4 다음 단계 (API 연동) ```typescript // useQuoteList 훅 (목록) const { quotes, pagination, isLoading, fetchQuotes, deleteQuote, bulkDelete } = useQuoteList(); // useQuote 훅 (단건 CRUD) const { quote, isLoading, fetchQuote, createQuote, updateQuote, finalizeQuote, convertToOrder } = useQuote(); // useQuoteCalculation 훅 (자동 산출) const { calculationResult, isCalculating, calculate, recalculate } = useQuoteCalculation(); ``` --- ## 8. 관련 참조 ### 8.1 거래처 API 연동 - 발주처 선택 시 `/api/v1/clients` 연동 - 직접입력 시 자동 등록 가능 ### 8.2 현장 API (추후) - 현장 선택 시 `/api/v1/sites` 연동 (별도 API 필요시) ### 8.3 품목마스터 연동 - 제품 선택 시 `/api/v1/item-masters` 연동 - BOM 조회 시 품목마스터 BOM 활용 --- ## 9. 요청 우선순위 | 순위 | API | 설명 | |------|-----|------| | P1 | 견적 CRUD | 기본 목록/등록/수정/삭제 | | P1 | 자동 산출 | 수식 계산 엔진 (핵심) | | P1 | 견적번호 생성 | 자동 채번 | | P2 | 상태 관리 | 확정/수주전환 | | P2 | 수정 이력 | 버전 관리 | | P3 | 문서 출력 | PDF 생성 | --- ## 10. 질문사항 1. **현장(Site) 테이블**: 별도 테이블로 관리할지? (거래처 하위 개념) 2. **수식 계산**: BOM 템플릿 테이블 구조는? 3. **문서 출력**: PDF 라이브러리 선정 (TCPDF, Dompdf 등) 4. **알림**: 견적 발송 시 이메일/카카오톡 연동 계획?