diff --git a/front/erp-api-detail.md b/front/erp-api-detail.md new file mode 100644 index 0000000..a3f4bbc --- /dev/null +++ b/front/erp-api-detail.md @@ -0,0 +1,1442 @@ +# ERP API 상세 스펙 + +> **작성일**: 2025-12-19 +> **기준 문서**: [erp-api-development-plan.md](../plans/erp-api-development-plan.md) +> **Swagger UI**: http://sam.kr/api-docs/index.html + +--- + +## 📋 공통 사항 + +### 인증 +모든 API는 다음 인증이 필요합니다: +- **API Key**: Header `X-Api-Key` 필수 +- **Bearer Token**: Header `Authorization: Bearer {token}` (인증 필요 API) + +### 공통 Response 형식 +```json +{ + "success": true, + "message": "message.key", + "data": { ... } +} +``` + +### 페이지네이션 Response +```json +{ + "success": true, + "message": "message.fetched", + "data": { + "items": [...], + "meta": { + "current_page": 1, + "per_page": 20, + "total": 100, + "last_page": 5 + } + } +} +``` + +### 공통 Query Parameters (LIST API) +| 파라미터 | 타입 | 설명 | 기본값 | +|----------|------|------|--------| +| page | integer | 페이지 번호 | 1 | +| per_page | integer | 페이지당 개수 | 20 | +| sort | string | 정렬 필드 | created_at | +| order | string | 정렬 방향 (asc/desc) | desc | +| search | string | 검색어 | - | + +--- + +## 1. 휴가 관리 (Leaves) + +### 1.1 휴가 목록 조회 +``` +GET /v1/leaves +``` + +**Query Parameters:** +| 파라미터 | 타입 | 필수 | 설명 | +|----------|------|------|------| +| status | string | - | pending/approved/rejected/cancelled | +| leave_type | string | - | annual/sick/personal/special | +| user_id | integer | - | 특정 사용자 필터 | +| start_date | date | - | 시작일 필터 (from) | +| end_date | date | - | 종료일 필터 (to) | + +**Response:** +```json +{ + "success": true, + "data": { + "items": [ + { + "id": 1, + "user_id": 10, + "user_name": "홍길동", + "leave_type": "annual", + "leave_type_name": "연차", + "start_date": "2025-12-20", + "end_date": "2025-12-22", + "days": 3, + "reason": "개인 사유", + "status": "pending", + "created_at": "2025-12-19T10:00:00Z" + } + ], + "meta": {...} + } +} +``` + +### 1.2 휴가 신청 +``` +POST /v1/leaves +``` + +**Request Body:** +```json +{ + "leave_type": "annual", + "start_date": "2025-12-20", + "end_date": "2025-12-22", + "reason": "개인 사유" +} +``` + +### 1.3 잔여휴가 조회 +``` +GET /v1/leaves/balance +``` + +**Response:** +```json +{ + "success": true, + "data": { + "annual": { "total": 15, "used": 5, "remaining": 10 }, + "sick": { "total": 3, "used": 0, "remaining": 3 }, + "special": { "total": 5, "used": 2, "remaining": 3 } + } +} +``` + +--- + +## 2. 근무/출퇴근 설정 + +### 2.1 근무 설정 조회/수정 +``` +GET /v1/settings/work +PUT /v1/settings/work +``` + +**Request/Response:** +```json +{ + "work_start_time": "09:00", + "work_end_time": "18:00", + "lunch_start_time": "12:00", + "lunch_end_time": "13:00", + "work_days": ["mon", "tue", "wed", "thu", "fri"], + "overtime_rate": 1.5, + "night_rate": 2.0 +} +``` + +### 2.2 출퇴근 설정 조회/수정 +``` +GET /v1/settings/attendance +PUT /v1/settings/attendance +``` + +**Request/Response:** +```json +{ + "gps_required": true, + "gps_radius": 100, + "late_threshold_minutes": 10, + "early_leave_threshold_minutes": 30, + "allow_remote_check": false +} +``` + +### 2.3 현장 관리 (Sites) + +#### 현장 목록 +``` +GET /v1/sites +``` + +**Query Parameters:** +| 파라미터 | 타입 | 설명 | +|----------|------|------| +| is_active | boolean | 활성 상태 필터 | +| search | string | 현장명 검색 | + +#### 현장 등록 +``` +POST /v1/sites +``` + +**Request Body:** +```json +{ + "name": "강남 현장", + "address": "서울시 강남구 ...", + "latitude": 37.5172, + "longitude": 127.0473, + "radius": 100, + "is_active": true, + "manager_id": 5, + "description": "신축 공사 현장" +} +``` + +--- + +## 3. 카드/계좌 관리 + +### 3.1 카드 등록 +``` +POST /v1/cards +``` + +**Request Body:** +```json +{ + "card_name": "법인카드1", + "card_number": "1234-5678-9012-3456", + "card_company": "신한카드", + "holder_name": "홍길동", + "expiry_date": "2028-12", + "limit_amount": 5000000, + "is_active": true +} +``` + +**Note:** 카드번호는 암호화되어 저장됩니다 (Laravel Crypt) + +### 3.2 계좌 등록 +``` +POST /v1/bank-accounts +``` + +**Request Body:** +```json +{ + "bank_name": "신한은행", + "account_number": "110-123-456789", + "account_holder": "주식회사 SAM", + "account_type": "business", + "balance": 10000000, + "is_primary": false, + "is_active": true +} +``` + +### 3.3 대표계좌 설정 +``` +PATCH /v1/bank-accounts/{id}/set-primary +``` + +**Note:** 기존 대표계좌는 자동으로 해제됩니다. + +--- + +## 4. 입금/출금 관리 + +### 4.1 입금 등록 +``` +POST /v1/deposits +``` + +**Request Body:** +```json +{ + "deposit_date": "2025-12-19", + "amount": 1000000, + "client_id": 5, + "bank_account_id": 1, + "payment_method": "transfer", + "description": "12월 매출 입금", + "reference_no": "DEP-2025-001" +} +``` + +**payment_method 옵션:** transfer(계좌이체), cash(현금), card(카드), check(수표) + +### 4.2 입금 요약 조회 +``` +GET /v1/deposits/summary +``` + +**Query Parameters:** +| 파라미터 | 타입 | 설명 | +|----------|------|------| +| start_date | date | 조회 시작일 | +| end_date | date | 조회 종료일 | + +**Response:** +```json +{ + "success": true, + "data": { + "total_amount": 50000000, + "by_payment_method": { + "transfer": 30000000, + "cash": 15000000, + "card": 5000000 + }, + "count": 45 + } +} +``` + +### 4.3 출금 등록 +``` +POST /v1/withdrawals +``` + +**Request Body:** +```json +{ + "withdrawal_date": "2025-12-19", + "amount": 500000, + "client_id": 10, + "bank_account_id": 1, + "payment_method": "transfer", + "category": "expense", + "description": "사무용품 구입", + "reference_no": "WDR-2025-001" +} +``` + +--- + +## 5. 매출/매입 관리 + +### 5.1 매출 등록 +``` +POST /v1/sales +``` + +**Request Body:** +```json +{ + "sale_date": "2025-12-19", + "client_id": 5, + "items": [ + { + "item_name": "상품A", + "quantity": 10, + "unit_price": 50000, + "tax_rate": 10 + } + ], + "supply_amount": 500000, + "tax_amount": 50000, + "total_amount": 550000, + "status": "draft", + "due_date": "2025-12-30", + "description": "12월 매출" +} +``` + +### 5.2 매출 확정 +``` +POST /v1/sales/{id}/confirm +``` + +**Note:** 확정 후에는 수정/삭제가 제한됩니다. + +### 5.3 거래명세서 조회 +``` +GET /v1/sales/{id}/statement +``` + +**Response:** +```json +{ + "success": true, + "data": { + "statement_number": "STSL202512190001", + "issued_at": null, + "sale": { "id": 1, "sale_number": "SL202512190001", ... }, + "seller": { + "name": "(주)테스트회사", + "business_number": "123-45-67890", + "representative": "홍길동", + "address": "서울시 강남구", + "tel": "02-1234-5678", + "email": "company@example.com" + }, + "buyer": { + "name": "(주)거래처", + "business_number": "987-65-43210", + "representative": "김철수", + "address": "서울시 서초구", + "tel": "02-9876-5432", + "email": "client@example.com" + }, + "items": [ + { + "description": "12월 매출", + "quantity": 1, + "unit_price": 1000000, + "supply_amount": 1000000, + "tax_amount": 100000, + "total_amount": 1100000 + } + ], + "summary": { + "supply_amount": 1000000, + "tax_amount": 100000, + "total_amount": 1100000 + } + } +} +``` + +### 5.4 거래명세서 발행 +``` +POST /v1/sales/{id}/statement/issue +``` + +**Note:** 확정(confirmed) 상태의 매출만 발행 가능합니다. + +**Response:** +```json +{ + "success": true, + "message": "message.sale.statement_issued", + "data": { + "statement_number": "STSL202512190001", + "issued_at": "2025-12-19T10:30:00+09:00" + } +} +``` + +### 5.5 거래명세서 이메일 발송 +``` +POST /v1/sales/{id}/statement/send +``` + +**Request Body:** +```json +{ + "email": "buyer@example.com", + "message": "거래명세서를 발송합니다." +} +``` + +**Note:** email 미입력 시 거래처 이메일로 발송됩니다. + +**Response:** +```json +{ + "success": true, + "message": "message.sale.statement_sent", + "data": { + "sent_to": "buyer@example.com", + "sent_at": "2025-12-19T10:35:00+09:00", + "statement_number": "STSL202512190001" + } +} +``` + +### 5.6 매출 요약 +``` +GET /v1/sales/summary +``` + +**Response:** +```json +{ + "success": true, + "data": { + "total_supply_amount": 10000000, + "total_tax_amount": 1000000, + "total_amount": 11000000, + "confirmed_amount": 8000000, + "pending_amount": 3000000, + "by_client": [ + { "client_id": 5, "client_name": "거래처A", "total": 5000000 } + ] + } +} +``` + +--- + +## 6. 보고서 + +### 6.1 일일 일보 +``` +GET /v1/reports/daily +``` + +**Query Parameters:** +| 파라미터 | 타입 | 필수 | 설명 | +|----------|------|------|------| +| date | date | O | 조회 기준일 | + +**Response:** +```json +{ + "success": true, + "data": { + "date": "2025-12-19", + "previous_balance": 50000000, + "deposits": { + "total": 5000000, + "items": [...] + }, + "withdrawals": { + "total": 2000000, + "items": [...] + }, + "current_balance": 53000000 + } +} +``` + +### 6.2 일일 일보 엑셀 다운로드 +``` +GET /v1/reports/daily/export?date=2025-12-19 +``` + +**Response:** Excel 파일 다운로드 + +### 6.3 지출 예상 내역서 +``` +GET /v1/reports/expense-estimate +``` + +**Query Parameters:** +| 파라미터 | 타입 | 설명 | +|----------|------|------| +| year | integer | 조회 연도 | +| month | integer | 조회 월 | + +--- + +## 7. 전자결재 + +### 7.1 결재 양식 스키마 +```json +{ + "id": 1, + "name": "휴가신청서", + "code": "LEAVE", + "category": "hr", + "fields": [ + { "key": "leave_type", "label": "휴가유형", "type": "select", "required": true }, + { "key": "start_date", "label": "시작일", "type": "date", "required": true }, + { "key": "end_date", "label": "종료일", "type": "date", "required": true }, + { "key": "reason", "label": "사유", "type": "textarea", "required": true } + ], + "is_active": true +} +``` + +### 7.2 결재 문서 작성 +``` +POST /v1/approvals +``` + +**Request Body:** +```json +{ + "form_id": 1, + "title": "연차 휴가 신청", + "content": { + "leave_type": "annual", + "start_date": "2025-12-20", + "end_date": "2025-12-22", + "reason": "개인 사유" + }, + "approval_line": [ + { "user_id": 10, "type": "approval", "order": 1 }, + { "user_id": 20, "type": "approval", "order": 2 }, + { "user_id": 30, "type": "reference", "order": 3 } + ] +} +``` + +### 7.3 결재 상신 +``` +POST /v1/approvals/{id}/submit +``` + +### 7.4 결재 승인/반려 +``` +POST /v1/approvals/{id}/approve +POST /v1/approvals/{id}/reject +``` + +**Request Body:** +```json +{ + "comment": "승인합니다" // 또는 "반려 사유" +} +``` + +### 7.5 결재 상태 흐름 +``` +draft → pending → approved/rejected + ↓ + cancelled (회수) +``` + +--- + +## 8. 급여 관리 + +### 8.1 급여 등록 +``` +POST /v1/payrolls +``` + +**Request Body:** +```json +{ + "user_id": 10, + "pay_year": 2025, + "pay_month": 12, + "base_salary": 3500000, + "allowances": { + "position": 200000, + "meal": 100000, + "transport": 100000 + }, + "deductions": { + "health_insurance": 125000, + "long_term_care": 16000, + "national_pension": 157500, + "employment_insurance": 32000, + "income_tax": 85000, + "local_tax": 8500 + } +} +``` + +### 8.2 급여 일괄 계산 +``` +POST /v1/payrolls/calculate +``` + +**Request Body:** +```json +{ + "pay_year": 2025, + "pay_month": 12, + "user_ids": [10, 11, 12] // 빈 배열이면 전체 직원 +} +``` + +### 8.3 급여명세서 조회 +``` +GET /v1/payrolls/{id}/payslip +``` + +**Response:** +```json +{ + "success": true, + "data": { + "employee": { + "name": "홍길동", + "department": "개발팀", + "position": "대리" + }, + "pay_period": "2025-12", + "earnings": { + "base_salary": 3500000, + "allowances": [...] + }, + "deductions": { + "insurance": [...], + "tax": [...] + }, + "gross_pay": 3900000, + "total_deductions": 424000, + "net_pay": 3476000 + } +} +``` + +### 8.4 급여 설정 (4대보험 요율) +``` +GET /v1/settings/payroll +PUT /v1/settings/payroll +``` + +**Request/Response:** +```json +{ + "health_insurance_rate": 3.545, + "long_term_care_rate": 12.81, + "national_pension_rate": 4.5, + "employment_insurance_rate": 0.9, + "income_tax_table": "2025" +} +``` + +--- + +## 9. 대시보드 + +### 9.1 대시보드 요약 +``` +GET /v1/dashboard/summary +``` + +**Response:** +```json +{ + "success": true, + "data": { + "today": { + "attendance_count": 45, + "leave_count": 3, + "late_count": 2 + }, + "finance": { + "current_balance": 150000000, + "today_deposits": 5000000, + "today_withdrawals": 2000000 + }, + "sales": { + "month_total": 50000000, + "month_confirmed": 40000000 + }, + "approvals": { + "pending": 5, + "my_turn": 2 + } + } +} +``` + +### 9.2 차트 데이터 +``` +GET /v1/dashboard/charts +``` + +**Query Parameters:** +| 파라미터 | 타입 | 설명 | +|----------|------|------| +| period | string | week/month/quarter/year | + +**Response:** +```json +{ + "success": true, + "data": { + "cash_flow": [ + { "date": "2025-12-13", "deposits": 1000000, "withdrawals": 500000 }, + { "date": "2025-12-14", "deposits": 2000000, "withdrawals": 800000 } + ], + "top_clients": [ + { "client_name": "거래처A", "total": 10000000 }, + { "client_name": "거래처B", "total": 8000000 } + ] + } +} +``` + +--- + +## 10. AI 리포트 + +### 10.1 AI 리포트 생성 +``` +POST /v1/reports/ai/generate +``` + +**Request Body:** +```json +{ + "report_type": "financial_analysis", + "analysis_areas": ["revenue", "expense", "cash_flow"], + "period": { + "start_date": "2025-12-01", + "end_date": "2025-12-31" + } +} +``` + +**report_type 옵션:** +- `financial_analysis` - 재무 분석 +- `cash_flow_analysis` - 현금 흐름 분석 +- `revenue_analysis` - 매출 분석 + +**Response:** +```json +{ + "success": true, + "data": { + "id": 1, + "status": "completed", + "report_type": "financial_analysis", + "content": { + "summary": "12월 재무 현황 분석 결과...", + "insights": [...], + "recommendations": [...] + }, + "created_at": "2025-12-19T10:00:00Z" + } +} +``` + +--- + +## 11. 가지급금 관리 + +### 11.1 가지급금 등록 +``` +POST /v1/loans +``` + +**Request Body:** +```json +{ + "user_id": 10, + "loan_date": "2025-12-19", + "amount": 5000000, + "purpose": "업무 관련 선지급", + "due_date": "2026-01-31", + "interest_rate": 4.6, + "status": "active" +} +``` + +### 11.2 인정이자 계산 +``` +POST /v1/loans/calculate-interest +``` + +**Request Body:** +```json +{ + "loan_ids": [1, 2, 3], + "calculation_date": "2025-12-31" +} +``` + +**Response:** +```json +{ + "success": true, + "data": [ + { + "loan_id": 1, + "principal": 5000000, + "days": 12, + "interest_rate": 4.6, + "calculated_interest": 7562 + } + ] +} +``` + +### 11.3 연도별 인정이자 리포트 +``` +GET /v1/loans/interest-report/2025 +``` + +--- + +## 12. 바로빌 세금계산서 + +### 12.1 바로빌 설정 +``` +PUT /v1/barobill-settings +``` + +**Request Body:** +```json +{ + "corp_num": "1234567890", + "cert_key": "encrypted_key", + "is_test_mode": false, + "auto_issue": true, + "default_email": "tax@company.com" +} +``` + +### 12.2 세금계산서 발행 +``` +POST /v1/tax-invoices/{id}/issue +``` + +**Response:** +```json +{ + "success": true, + "message": "message.tax_invoice.issued", + "data": { + "id": 1, + "issue_no": "20251219-12345678", + "status": "issued", + "nts_status": "pending", + "issued_at": "2025-12-19T10:00:00Z" + } +} +``` + +### 12.3 국세청 전송 상태 조회 +``` +GET /v1/tax-invoices/{id}/check-status +``` + +**Response:** +```json +{ + "success": true, + "data": { + "id": 1, + "nts_status": "transmitted", + "nts_message": "국세청 전송 완료", + "checked_at": "2025-12-19T11:00:00Z" + } +} +``` + +**nts_status 옵션:** +- `pending` - 전송 대기 +- `transmitted` - 전송 완료 +- `failed` - 전송 실패 + +--- + +## 13. 사용자 초대 (Invitations) + +> Phase 5에서 추가된 API입니다. + +### 13.1 초대 목록 조회 +``` +GET /v1/users/invitations +``` + +**Query Parameters:** +| 파라미터 | 타입 | 필수 | 설명 | +|----------|------|------|------| +| status | string | - | pending/accepted/expired/cancelled | +| search | string | - | 이메일 검색 | +| sort_by | string | - | created_at/expires_at/email | +| sort_dir | string | - | asc/desc | +| per_page | integer | - | 페이지당 개수 (기본 20) | +| page | integer | - | 페이지 번호 | + +**Response:** +```json +{ + "success": true, + "data": { + "items": [ + { + "id": 1, + "email": "invited@example.com", + "role_id": 2, + "role_name": "일반 사용자", + "message": "팀에 초대합니다", + "status": "pending", + "invited_by": 10, + "invited_by_name": "홍길동", + "expires_at": "2025-12-26T00:00:00Z", + "created_at": "2025-12-19T10:00:00Z" + } + ], + "meta": {...} + } +} +``` + +**status 옵션:** +- `pending` - 대기 중 +- `accepted` - 수락됨 +- `expired` - 만료됨 +- `cancelled` - 취소됨 + +### 13.2 사용자 초대 발송 +``` +POST /v1/users/invite +``` + +**Request Body:** +```json +{ + "email": "newuser@example.com", + "role_id": 2, + "message": "우리 팀에 초대합니다!", + "expires_days": 7 +} +``` + +| 필드 | 타입 | 필수 | 설명 | +|------|------|------|------| +| email | string | ✓ | 초대할 이메일 | +| role_id | integer | - | 부여할 역할 ID | +| message | string | - | 초대 메시지 (최대 1000자) | +| expires_days | integer | - | 만료일 (1~30일, 기본 7일) | + +**Response:** +```json +{ + "success": true, + "message": "message.invitation.sent", + "data": { + "id": 1, + "email": "newuser@example.com", + "token": "abc123xyz", + "expires_at": "2025-12-26T10:00:00Z" + } +} +``` + +### 13.3 초대 수락 +``` +POST /v1/users/invitations/{token}/accept +``` + +**Request Body:** +```json +{ + "name": "김철수", + "password": "securePassword123", + "password_confirmation": "securePassword123", + "phone": "010-1234-5678" +} +``` + +| 필드 | 타입 | 필수 | 설명 | +|------|------|------|------| +| name | string | ✓ | 사용자 이름 (최대 100자) | +| password | string | ✓ | 비밀번호 (최소 8자) | +| password_confirmation | string | ✓ | 비밀번호 확인 | +| phone | string | - | 전화번호 (최대 20자) | + +**Response:** +```json +{ + "success": true, + "message": "message.invitation.accepted", + "data": { + "user_id": 25, + "tenant_id": 1, + "email": "newuser@example.com", + "token": "sanctum_access_token..." + } +} +``` + +### 13.4 초대 취소 +``` +DELETE /v1/users/invitations/{id} +``` + +**Response:** +```json +{ + "success": true, + "message": "message.invitation.cancelled", + "data": { + "id": 1, + "cancelled_at": "2025-12-19T15:00:00Z" + } +} +``` + +### 13.5 초대 재발송 +``` +POST /v1/users/invitations/{id}/resend +``` + +**Response:** +```json +{ + "success": true, + "message": "message.invitation.resent", + "data": { + "id": 1, + "new_expires_at": "2025-12-26T15:00:00Z", + "resent_at": "2025-12-19T15:00:00Z" + } +} +``` + +--- + +## 14. 알림 설정 (Notification Settings) + +> Phase 5에서 추가된 API입니다. 모든 API는 `auth:sanctum` 인증이 필요합니다. + +### 14.1 알림 설정 조회 +``` +GET /v1/users/me/notification-settings +``` + +**Response:** +```json +{ + "success": true, + "data": { + "settings": [ + { + "notification_type": "approval", + "type_label": "전자결재", + "push_enabled": true, + "email_enabled": false, + "sms_enabled": false, + "in_app_enabled": true, + "kakao_enabled": false, + "settings": {} + }, + { + "notification_type": "order", + "type_label": "수주", + "push_enabled": true, + "email_enabled": false, + "sms_enabled": false, + "in_app_enabled": true, + "kakao_enabled": false, + "settings": {} + } + ], + "types": { + "approval": "전자결재", + "order": "수주", + "deposit": "입금", + "withdrawal": "출금", + "notice": "공지사항", + "system": "시스템", + "marketing": "마케팅", + "security": "보안" + }, + "channels": { + "push": "푸시 알림", + "email": "이메일", + "sms": "SMS", + "in_app": "인앱 알림", + "kakao": "카카오 알림톡" + } + } +} +``` + +### 14.2 알림 설정 수정 (단일) +``` +PUT /v1/users/me/notification-settings +``` + +**Request Body:** +```json +{ + "notification_type": "marketing", + "push_enabled": true, + "email_enabled": true, + "sms_enabled": false, + "in_app_enabled": true, + "kakao_enabled": false, + "settings": { + "frequency": "daily" + } +} +``` + +| 필드 | 타입 | 필수 | 설명 | +|------|------|------|------| +| notification_type | string | ✓ | 알림 유형 | +| push_enabled | boolean | - | 푸시 알림 | +| email_enabled | boolean | - | 이메일 알림 | +| sms_enabled | boolean | - | SMS 알림 | +| in_app_enabled | boolean | - | 인앱 알림 | +| kakao_enabled | boolean | - | 카카오 알림톡 | +| settings | object | - | 추가 설정 | + +**notification_type 옵션:** +- `approval` - 전자결재 +- `order` - 수주 +- `deposit` - 입금 +- `withdrawal` - 출금 +- `notice` - 공지사항 +- `system` - 시스템 +- `marketing` - 마케팅 +- `security` - 보안 + +**Response:** +```json +{ + "success": true, + "message": "message.updated", + "data": { + "notification_type": "marketing", + "push_enabled": true, + "email_enabled": true, + "sms_enabled": false, + "in_app_enabled": true, + "kakao_enabled": false, + "settings": { + "frequency": "daily" + } + } +} +``` + +### 14.3 알림 일괄 설정 +``` +PUT /v1/users/me/notification-settings/bulk +``` + +**Request Body:** +```json +{ + "settings": [ + { + "notification_type": "approval", + "push_enabled": true, + "email_enabled": true, + "in_app_enabled": true + }, + { + "notification_type": "marketing", + "push_enabled": false, + "email_enabled": false, + "in_app_enabled": false + } + ] +} +``` + +**Response:** +```json +{ + "success": true, + "message": "message.bulk_upsert", + "data": { + "updated_count": 2, + "settings": [...] + } +} +``` + +--- + +## 15. 계정 관리 (Account) + +> Phase 5에서 추가된 API입니다. 모든 API는 `auth:sanctum` 인증이 필요합니다. + +### 15.1 회원 탈퇴 (SAM 완전 탈퇴) +``` +POST /v1/account/withdraw +``` + +**Request Body:** +```json +{ + "password": "currentPassword123", + "reason": "not_using", + "detail": "서비스를 더 이상 사용할 계획이 없습니다" +} +``` + +| 필드 | 타입 | 필수 | 설명 | +|------|------|------|------| +| password | string | ✓ | 현재 비밀번호 (확인용) | +| reason | string | - | 탈퇴 사유 코드 | +| detail | string | - | 상세 사유 (최대 500자) | + +**reason 옵션:** +- `not_using` - 서비스를 더 이상 사용하지 않음 +- `difficult` - 사용하기 어려움 +- `alternative` - 다른 서비스 이용 +- `privacy` - 개인정보 보호 우려 +- `other` - 기타 + +**Response:** +```json +{ + "success": true, + "message": "message.account.withdrawn", + "data": { + "withdrawn_at": "2025-12-19T16:00:00Z" + } +} +``` + +> ⚠️ **주의**: 회원 탈퇴 시 모든 테넌트에서 탈퇴되며, 계정은 Soft Delete 처리됩니다. + +### 15.2 사용 중지 (특정 테넌트에서만 탈퇴) +``` +POST /v1/account/suspend +``` + +**Response:** +```json +{ + "success": true, + "message": "message.account.suspended", + "data": { + "suspended": true, + "new_default_tenant_id": 2 + } +} +``` + +> 다른 활성 테넌트가 있으면 해당 테넌트가 기본 테넌트로 설정됩니다. + +### 15.3 약관 동의 정보 조회 +``` +GET /v1/account/agreements +``` + +**Response:** +```json +{ + "success": true, + "data": { + "agreements": { + "terms": { + "type": "terms", + "label": "이용약관", + "required": true, + "agreed": true, + "agreed_at": "2025-01-01T00:00:00Z" + }, + "privacy": { + "type": "privacy", + "label": "개인정보 처리방침", + "required": true, + "agreed": true, + "agreed_at": "2025-01-01T00:00:00Z" + }, + "marketing": { + "type": "marketing", + "label": "마케팅 정보 수신", + "required": false, + "agreed": false, + "agreed_at": null + }, + "push": { + "type": "push", + "label": "푸시 알림 수신", + "required": false, + "agreed": true, + "agreed_at": "2025-06-15T10:00:00Z" + }, + "email": { + "type": "email", + "label": "이메일 수신", + "required": false, + "agreed": false, + "agreed_at": null + }, + "sms": { + "type": "sms", + "label": "SMS 수신", + "required": false, + "agreed": false, + "agreed_at": null + } + }, + "types": { + "terms": "이용약관", + "privacy": "개인정보 처리방침", + "marketing": "마케팅 정보 수신", + "push": "푸시 알림 수신", + "email": "이메일 수신", + "sms": "SMS 수신" + } + } +} +``` + +### 15.4 약관 동의 정보 수정 +``` +PUT /v1/account/agreements +``` + +**Request Body:** +```json +{ + "agreements": [ + { + "type": "marketing", + "agreed": true + }, + { + "type": "email", + "agreed": true + } + ] +} +``` + +| 필드 | 타입 | 필수 | 설명 | +|------|------|------|------| +| agreements | array | ✓ | 약관 동의 배열 | +| agreements[].type | string | ✓ | 약관 유형 | +| agreements[].agreed | boolean | ✓ | 동의 여부 | + +**type 옵션:** +- `terms` - 이용약관 (필수) +- `privacy` - 개인정보 처리방침 (필수) +- `marketing` - 마케팅 정보 수신 +- `push` - 푸시 알림 수신 +- `email` - 이메일 수신 +- `sms` - SMS 수신 + +**Response:** +```json +{ + "success": true, + "message": "message.updated", + "data": { + "agreements": { + "terms": {...}, + "privacy": {...}, + "marketing": { + "type": "marketing", + "label": "마케팅 정보 수신", + "required": false, + "agreed": true, + "agreed_at": "2025-12-19T16:30:00Z" + }, + "email": { + "type": "email", + "label": "이메일 수신", + "required": false, + "agreed": true, + "agreed_at": "2025-12-19T16:30:00Z" + }, + "sms": {...} + } + } +} +``` + +--- + +## 📝 에러 코드 + +| 코드 | 설명 | +|------|------| +| 400 | 잘못된 요청 (validation 실패) | +| 401 | 인증 실패 | +| 403 | 권한 없음 | +| 404 | 리소스 없음 | +| 409 | 충돌 (중복 등) | +| 422 | 처리 불가 (비즈니스 로직 오류) | +| 500 | 서버 오류 | + +--- + +## 🔗 관련 문서 + +- [ERP API 목록](./erp-api-list.md) +- [ERP API 개발 계획](../plans/erp-api-development-plan.md) +- Swagger UI: http://sam.kr/api-docs/index.html diff --git a/front/erp-api-list.md b/front/erp-api-list.md new file mode 100644 index 0000000..925dedd --- /dev/null +++ b/front/erp-api-list.md @@ -0,0 +1,396 @@ +# ERP API 목록 (List vs Detail 구분) + +> **작성일**: 2025-12-19 +> **기준 문서**: [erp-api-development-plan.md](../plans/erp-api-development-plan.md) +> **Swagger UI**: http://sam.kr/api-docs/index.html + +--- + +## 📊 API 유형 분류 + +| 유형 | 설명 | HTTP Method | +|------|------|-------------| +| **LIST** | 목록 조회 (페이지네이션) | GET | +| **DETAIL** | 단건 조회 | GET | +| **CREATE** | 생성 | POST | +| **UPDATE** | 수정 | PUT/PATCH | +| **DELETE** | 삭제 | DELETE | +| **ACTION** | 상태 변경/특수 액션 | POST/PATCH | + +--- + +## 🗂️ Phase 1: 확장 개발 API + +### 1. 휴가 관리 (Leaves) - 11개 API + +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/leaves` | 휴가 목록 조회 | +| DETAIL | GET | `/v1/leaves/{id}` | 휴가 상세 조회 | +| CREATE | POST | `/v1/leaves` | 휴가 신청 | +| UPDATE | PATCH | `/v1/leaves/{id}` | 휴가 수정 | +| DELETE | DELETE | `/v1/leaves/{id}` | 휴가 삭제 | +| ACTION | POST | `/v1/leaves/{id}/approve` | 휴가 승인 | +| ACTION | POST | `/v1/leaves/{id}/reject` | 휴가 반려 | +| ACTION | POST | `/v1/leaves/{id}/cancel` | 휴가 취소 | +| DETAIL | GET | `/v1/leaves/balance` | 내 잔여휴가 조회 | +| DETAIL | GET | `/v1/leaves/balance/{userId}` | 특정 사용자 잔여휴가 | +| UPDATE | PUT | `/v1/leaves/balance` | 잔여휴가 설정 | + +--- + +### 2. 근무/출퇴근 설정 (Work Settings) - 10개 API + +#### 2.1 근무 설정 (2개) +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| DETAIL | GET | `/v1/settings/work` | 근무 설정 조회 | +| UPDATE | PUT | `/v1/settings/work` | 근무 설정 수정 | + +#### 2.2 출퇴근 설정 (2개) +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| DETAIL | GET | `/v1/settings/attendance` | 출퇴근 설정 조회 | +| UPDATE | PUT | `/v1/settings/attendance` | 출퇴근 설정 수정 | + +#### 2.3 현장 관리 (Sites) - 6개 +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/sites` | 현장 목록 조회 | +| LIST | GET | `/v1/sites/active` | 활성 현장 목록 (셀렉트박스용) | +| DETAIL | GET | `/v1/sites/{id}` | 현장 상세 조회 | +| CREATE | POST | `/v1/sites` | 현장 등록 | +| UPDATE | PUT | `/v1/sites/{id}` | 현장 수정 | +| DELETE | DELETE | `/v1/sites/{id}` | 현장 삭제 | + +--- + +### 3. 카드/계좌 관리 - 15개 API + +#### 3.1 카드 관리 (Cards) - 7개 +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/cards` | 카드 목록 조회 | +| LIST | GET | `/v1/cards/active` | 활성 카드 목록 (셀렉트박스용) | +| DETAIL | GET | `/v1/cards/{id}` | 카드 상세 조회 | +| CREATE | POST | `/v1/cards` | 카드 등록 | +| UPDATE | PUT | `/v1/cards/{id}` | 카드 수정 | +| DELETE | DELETE | `/v1/cards/{id}` | 카드 삭제 | +| ACTION | PATCH | `/v1/cards/{id}/toggle` | 카드 사용/정지 토글 | + +#### 3.2 계좌 관리 (Bank Accounts) - 8개 +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/bank-accounts` | 계좌 목록 조회 | +| LIST | GET | `/v1/bank-accounts/active` | 활성 계좌 목록 (셀렉트박스용) | +| DETAIL | GET | `/v1/bank-accounts/{id}` | 계좌 상세 조회 | +| CREATE | POST | `/v1/bank-accounts` | 계좌 등록 | +| UPDATE | PUT | `/v1/bank-accounts/{id}` | 계좌 수정 | +| DELETE | DELETE | `/v1/bank-accounts/{id}` | 계좌 삭제 | +| ACTION | PATCH | `/v1/bank-accounts/{id}/toggle` | 계좌 사용/정지 토글 | +| ACTION | PATCH | `/v1/bank-accounts/{id}/set-primary` | 대표계좌 설정 | + +--- + +### 4. 입금/출금 관리 - 12개 API + +#### 4.1 입금 관리 (Deposits) - 6개 +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/deposits` | 입금 목록 조회 | +| LIST | GET | `/v1/deposits/summary` | 입금 요약 (집계) | +| DETAIL | GET | `/v1/deposits/{id}` | 입금 상세 조회 | +| CREATE | POST | `/v1/deposits` | 입금 등록 | +| UPDATE | PUT | `/v1/deposits/{id}` | 입금 수정 | +| DELETE | DELETE | `/v1/deposits/{id}` | 입금 삭제 | + +#### 4.2 출금 관리 (Withdrawals) - 6개 +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/withdrawals` | 출금 목록 조회 | +| LIST | GET | `/v1/withdrawals/summary` | 출금 요약 (집계) | +| DETAIL | GET | `/v1/withdrawals/{id}` | 출금 상세 조회 | +| CREATE | POST | `/v1/withdrawals` | 출금 등록 | +| UPDATE | PUT | `/v1/withdrawals/{id}` | 출금 수정 | +| DELETE | DELETE | `/v1/withdrawals/{id}` | 출금 삭제 | + +--- + +### 5. 매출/매입 관리 - 17개 API + +#### 5.1 매출 관리 (Sales) - 10개 +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/sales` | 매출 목록 조회 | +| LIST | GET | `/v1/sales/summary` | 매출 요약 (집계) | +| DETAIL | GET | `/v1/sales/{id}` | 매출 상세 조회 | +| CREATE | POST | `/v1/sales` | 매출 등록 | +| UPDATE | PUT | `/v1/sales/{id}` | 매출 수정 | +| DELETE | DELETE | `/v1/sales/{id}` | 매출 삭제 | +| ACTION | POST | `/v1/sales/{id}/confirm` | 매출 확정 | +| DETAIL | GET | `/v1/sales/{id}/statement` | 거래명세서 조회 | +| ACTION | POST | `/v1/sales/{id}/statement/issue` | 거래명세서 발행 | +| ACTION | POST | `/v1/sales/{id}/statement/send` | 거래명세서 이메일 발송 | + +#### 5.2 매입 관리 (Purchases) - 7개 +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/purchases` | 매입 목록 조회 | +| LIST | GET | `/v1/purchases/summary` | 매입 요약 (집계) | +| DETAIL | GET | `/v1/purchases/{id}` | 매입 상세 조회 | +| CREATE | POST | `/v1/purchases` | 매입 등록 | +| UPDATE | PUT | `/v1/purchases/{id}` | 매입 수정 | +| DELETE | DELETE | `/v1/purchases/{id}` | 매입 삭제 | +| ACTION | POST | `/v1/purchases/{id}/confirm` | 매입 확정 | + +--- + +### 6. 보고서 (Reports) - 4개 API + +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| DETAIL | GET | `/v1/reports/daily` | 일일 일보 조회 | +| ACTION | GET | `/v1/reports/daily/export` | 일일 일보 엑셀 다운로드 | +| DETAIL | GET | `/v1/reports/expense-estimate` | 지출 예상 내역서 조회 | +| ACTION | GET | `/v1/reports/expense-estimate/export` | 지출 예상 내역서 엑셀 다운로드 | + +--- + +## 👤 Phase 5: 사용자/계정 관리 API + +### 16. 사용자 초대 (User Invitations) - 5개 API + +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/users/invitations` | 초대 목록 조회 | +| CREATE | POST | `/v1/users/invite` | 사용자 초대 발송 | +| ACTION | POST | `/v1/users/invitations/{token}/accept` | 초대 수락 | +| DELETE | DELETE | `/v1/users/invitations/{id}` | 초대 취소 | +| ACTION | POST | `/v1/users/invitations/{id}/resend` | 초대 재발송 | + +--- + +### 17. 알림 설정 (Notification Settings) - 3개 API + +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/users/me/notification-settings` | 알림 설정 조회 | +| UPDATE | PUT | `/v1/users/me/notification-settings` | 알림 설정 수정 (단일) | +| ACTION | PUT | `/v1/users/me/notification-settings/bulk` | 알림 설정 일괄 저장 | + +--- + +### 18. 계정 관리 (Account) - 4개 API + +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| ACTION | POST | `/v1/account/withdraw` | 회원 탈퇴 (SAM 완전 탈퇴) | +| ACTION | POST | `/v1/account/suspend` | 사용 중지 (현재 테넌트에서만 탈퇴) | +| DETAIL | GET | `/v1/account/agreements` | 약관 동의 정보 조회 | +| UPDATE | PUT | `/v1/account/agreements` | 약관 동의 정보 수정 | + +--- + +## 🔨 Phase 2: 핵심 신규 개발 API + +### 7. 전자결재 모듈 - 26개 API + +#### 7.1 결재 양식 (Approval Forms) - 6개 +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/approval-forms` | 결재 양식 목록 | +| LIST | GET | `/v1/approval-forms/active` | 활성 결재 양식 (셀렉트박스용) | +| DETAIL | GET | `/v1/approval-forms/{id}` | 결재 양식 상세 | +| CREATE | POST | `/v1/approval-forms` | 결재 양식 등록 | +| UPDATE | PATCH | `/v1/approval-forms/{id}` | 결재 양식 수정 | +| DELETE | DELETE | `/v1/approval-forms/{id}` | 결재 양식 삭제 | + +#### 7.2 결재선 템플릿 (Approval Lines) - 5개 +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/approval-lines` | 결재선 목록 | +| DETAIL | GET | `/v1/approval-lines/{id}` | 결재선 상세 | +| CREATE | POST | `/v1/approval-lines` | 결재선 등록 | +| UPDATE | PATCH | `/v1/approval-lines/{id}` | 결재선 수정 | +| DELETE | DELETE | `/v1/approval-lines/{id}` | 결재선 삭제 | + +#### 7.3 결재 문서 (Approvals) - 15개 +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/approvals/drafts` | 기안함 (내가 작성한 문서) | +| LIST | GET | `/v1/approvals/drafts/summary` | 기안함 요약 | +| LIST | GET | `/v1/approvals/inbox` | 결재함 (결재 대기 문서) | +| LIST | GET | `/v1/approvals/inbox/summary` | 결재함 요약 | +| LIST | GET | `/v1/approvals/reference` | 참조함 (참조 문서) | +| DETAIL | GET | `/v1/approvals/{id}` | 결재 문서 상세 | +| CREATE | POST | `/v1/approvals` | 결재 문서 작성 (임시저장) | +| UPDATE | PATCH | `/v1/approvals/{id}` | 결재 문서 수정 | +| DELETE | DELETE | `/v1/approvals/{id}` | 결재 문서 삭제 | +| ACTION | POST | `/v1/approvals/{id}/submit` | 결재 상신 | +| ACTION | POST | `/v1/approvals/{id}/approve` | 결재 승인 | +| ACTION | POST | `/v1/approvals/{id}/reject` | 결재 반려 | +| ACTION | POST | `/v1/approvals/{id}/cancel` | 결재 취소/회수 | +| ACTION | POST | `/v1/approvals/{id}/read` | 참조 열람 처리 | +| ACTION | POST | `/v1/approvals/{id}/unread` | 참조 미열람 처리 | + +--- + +### 8. 급여 관리 (Payrolls) - 13개 API + +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/payrolls` | 급여 목록 조회 | +| LIST | GET | `/v1/payrolls/summary` | 급여 현황 요약 | +| DETAIL | GET | `/v1/payrolls/{id}` | 급여 상세 조회 | +| DETAIL | GET | `/v1/payrolls/{id}/payslip` | 급여명세서 조회 | +| CREATE | POST | `/v1/payrolls` | 급여 등록 | +| UPDATE | PUT | `/v1/payrolls/{id}` | 급여 수정 | +| DELETE | DELETE | `/v1/payrolls/{id}` | 급여 삭제 | +| ACTION | POST | `/v1/payrolls/calculate` | 급여 일괄 계산 | +| ACTION | POST | `/v1/payrolls/bulk-confirm` | 급여 일괄 확정 | +| ACTION | POST | `/v1/payrolls/{id}/confirm` | 급여 확정 | +| ACTION | POST | `/v1/payrolls/{id}/pay` | 급여 지급 처리 | +| DETAIL | GET | `/v1/settings/payroll` | 급여 설정 조회 | +| UPDATE | PUT | `/v1/settings/payroll` | 급여 설정 수정 | + +--- + +### 9. 대시보드 (Dashboard) - 3개 API + +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| DETAIL | GET | `/v1/dashboard/summary` | 대시보드 요약 데이터 | +| DETAIL | GET | `/v1/dashboard/charts` | 차트 데이터 | +| DETAIL | GET | `/v1/dashboard/approvals` | 결재 현황 | + +--- + +## 🔧 Phase 3: 추가 기능 API + +### 10. AI 리포트 (AI Reports) - 4개 API + +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/reports/ai` | AI 리포트 목록 | +| DETAIL | GET | `/v1/reports/ai/{id}` | AI 리포트 상세 | +| ACTION | POST | `/v1/reports/ai/generate` | AI 리포트 생성 | +| DELETE | DELETE | `/v1/reports/ai/{id}` | AI 리포트 삭제 | + +--- + +### 11. 가지급금 관리 (Loans) - 9개 API + +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/loans` | 가지급금 목록 조회 | +| LIST | GET | `/v1/loans/summary` | 가지급금 요약 | +| DETAIL | GET | `/v1/loans/{id}` | 가지급금 상세 조회 | +| DETAIL | GET | `/v1/loans/interest-report/{year}` | 연도별 인정이자 리포트 | +| CREATE | POST | `/v1/loans` | 가지급금 등록 | +| UPDATE | PUT | `/v1/loans/{id}` | 가지급금 수정 | +| DELETE | DELETE | `/v1/loans/{id}` | 가지급금 삭제 | +| ACTION | POST | `/v1/loans/calculate-interest` | 인정이자 계산 | +| ACTION | POST | `/v1/loans/{id}/settle` | 가지급금 정산 | + +--- + +### 12. 바로빌 연동 (세금계산서) - 12개 API + +#### 12.1 바로빌 설정 (3개) +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| DETAIL | GET | `/v1/barobill-settings` | 바로빌 설정 조회 | +| UPDATE | PUT | `/v1/barobill-settings` | 바로빌 설정 저장 | +| ACTION | POST | `/v1/barobill-settings/test-connection` | 연동 테스트 | + +#### 12.2 세금계산서 (9개) +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/tax-invoices` | 세금계산서 목록 | +| LIST | GET | `/v1/tax-invoices/summary` | 세금계산서 요약 통계 | +| DETAIL | GET | `/v1/tax-invoices/{id}` | 세금계산서 상세 | +| DETAIL | GET | `/v1/tax-invoices/{id}/check-status` | 국세청 전송 상태 조회 | +| CREATE | POST | `/v1/tax-invoices` | 세금계산서 생성 | +| UPDATE | PUT | `/v1/tax-invoices/{id}` | 세금계산서 수정 | +| DELETE | DELETE | `/v1/tax-invoices/{id}` | 세금계산서 삭제 | +| ACTION | POST | `/v1/tax-invoices/{id}/issue` | 세금계산서 발행 | +| ACTION | POST | `/v1/tax-invoices/{id}/cancel` | 세금계산서 취소 | + +--- + +## 💼 Phase 4: SaaS 기능 API + +### 13. 요금제 관리 (Plans) - 7개 API + +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/plans` | 요금제 목록 | +| LIST | GET | `/v1/plans/active` | 활성 요금제 목록 | +| DETAIL | GET | `/v1/plans/{id}` | 요금제 상세 | +| CREATE | POST | `/v1/plans` | 요금제 등록 | +| UPDATE | PUT | `/v1/plans/{id}` | 요금제 수정 | +| DELETE | DELETE | `/v1/plans/{id}` | 요금제 삭제 | +| ACTION | PATCH | `/v1/plans/{id}/toggle` | 요금제 활성/비활성 | + +--- + +### 14. 구독 관리 (Subscriptions) - 8개 API + +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/subscriptions` | 구독 목록 | +| DETAIL | GET | `/v1/subscriptions/current` | 현재 구독 조회 | +| DETAIL | GET | `/v1/subscriptions/{id}` | 구독 상세 | +| CREATE | POST | `/v1/subscriptions` | 구독 신청 | +| ACTION | POST | `/v1/subscriptions/{id}/cancel` | 구독 해지 | +| ACTION | POST | `/v1/subscriptions/{id}/renew` | 구독 갱신 | +| ACTION | POST | `/v1/subscriptions/{id}/suspend` | 구독 일시정지 | +| ACTION | POST | `/v1/subscriptions/{id}/resume` | 구독 재개 | + +--- + +### 15. 결제 관리 (Payments) - 7개 API + +| 유형 | Method | Endpoint | 설명 | +|------|--------|----------|------| +| LIST | GET | `/v1/payments` | 결제 내역 목록 | +| LIST | GET | `/v1/payments/summary` | 결제 요약 | +| DETAIL | GET | `/v1/payments/{id}` | 결제 상세 | +| CREATE | POST | `/v1/payments` | 결제 처리 | +| ACTION | POST | `/v1/payments/{id}/complete` | 결제 완료 처리 | +| ACTION | POST | `/v1/payments/{id}/cancel` | 결제 취소 | +| ACTION | POST | `/v1/payments/{id}/refund` | 결제 환불 | + +--- + +## 📊 API 통계 요약 + +| Phase | 카테고리 | API 개수 | +|-------|----------|----------| +| Phase 1 | 휴가 관리 | 11 | +| Phase 1 | 근무/출퇴근 설정 | 10 | +| Phase 1 | 카드/계좌 관리 | 15 | +| Phase 1 | 입금/출금 관리 | 12 | +| Phase 1 | 매출/매입 관리 | 17 | +| Phase 1 | 보고서 | 4 | +| Phase 2 | 전자결재 | 26 | +| Phase 2 | 급여 관리 | 13 | +| Phase 2 | 대시보드 | 3 | +| Phase 3 | AI 리포트 | 4 | +| Phase 3 | 가지급금 | 9 | +| Phase 3 | 바로빌 연동 | 12 | +| Phase 4 | 요금제 관리 | 7 | +| Phase 4 | 구독 관리 | 8 | +| Phase 4 | 결제 관리 | 7 | +| Phase 5 | 사용자 초대 | 5 | +| Phase 5 | 알림 설정 | 3 | +| Phase 5 | 계정 관리 | 4 | +| **Total** | - | **170개** | + +--- + +## 🔗 관련 문서 + +- [ERP API 개발 계획](../plans/erp-api-development-plan.md) +- [ERP API 상세 스펙](./erp-api-detail.md) +- Swagger UI: http://sam.kr/api-docs/index.html diff --git a/projects/quotation/MASTER_PLAN.md b/projects/quotation/MASTER_PLAN.md index ac9ae88..c5efd26 100644 --- a/projects/quotation/MASTER_PLAN.md +++ b/projects/quotation/MASTER_PLAN.md @@ -35,6 +35,7 @@ Phase 4: 견적 API 개발 docs/projects/quotation/ ├── MASTER_PLAN.md # 이 문서 ├── PROGRESS.md # 진행 현황 +├── screenshots/ # MES 프로토타입 화면 캡쳐 ├── phase-1-5130-analysis/ # 1단계: 5130 분석 │ ├── README.md │ ├── ui-analysis.md @@ -128,6 +129,25 @@ docs/projects/quotation/ - Phase 1 분석 결과 (js-formulas.md) - Phase 2 분석 결과 +**UI 참조 (MES 프로토타입 화면 캡쳐):** +> 📁 경로: `docs/projects/quotation/screenshots/` + +| 파일명 | 설명 | +|--------|------| +| `01-formula-list-main.png` | 견적수식 목록 메인 (기본정보 카테고리, 10개 수식) | +| `02-product-dropdown.png` | 제품 선택 드롭다운 (공통/스크린/철재/슬랫) | +| `03-category-management.png` | 분류 관리 (스크린/철재/전기부품/기타부자재) | +| `04-price-formula-management.png` | 단가 수식 관리 (그룹별 품목, 적용수식 설정) | +| `05-auto-quotation-input.png` | 자동 견적 산출 입력 (오픈사이즈, 옵션 선택) | +| `06-category-guiderail.png` | 가이드레일 카테고리 수식 (조회/계산식 타입) | +| `07-formula-add-modal.png` | 수식 추가 모달 (변수, 타입, 결과출력 설정) | + +**캡쳐 화면 핵심 기능:** +- 13개 카테고리 (기본정보~장수산출) +- 44개 수식 (공통) +- 수식 타입: 입력값, 계산식, 조회 +- 결과 타입: 변수저장, 품목/수량 출력 + **산출물:** | 파일 | 설명 | |------|------| @@ -189,5 +209,6 @@ MCP: Sequential Thinking 적용 | 날짜 | 변경 내용 | 작성자 | |------|----------|--------| +| 2025-12-19 | Phase 3에 MES 프로토타입 화면 캡쳐 참조 추가 | Claude | | 2025-12-19 | 공통 정책 분리, 견적 특화 내용만 유지 | Claude | | 2025-12-19 | 초기 마스터 플랜 작성 | Claude | \ No newline at end of file diff --git a/projects/quotation/PROGRESS.md b/projects/quotation/PROGRESS.md index e5104dc..d5c9c5b 100644 --- a/projects/quotation/PROGRESS.md +++ b/projects/quotation/PROGRESS.md @@ -6,11 +6,11 @@ --- -## 📊 전체 진행률 +## 전체 진행률 | Phase | 상태 | 진행률 | 시작일 | 완료일 | |-------|------|--------|--------|--------| -| Phase 1: 5130 분석 | ⏳ 대기 | 0% | - | - | +| Phase 1: 5130 분석 | ✅ 완료 | 100% | 2025-12-19 | 2025-12-19 | | Phase 2: mng 분석 | ⏳ 대기 | 0% | - | - | | Phase 3: 구현 | ⏳ 대기 | 0% | - | - | | Phase 4: API 개발 | ⏳ 대기 | 0% | - | - | @@ -19,36 +19,54 @@ ## 🔄 현재 작업 -**현재 Phase:** 시작 전 -**현재 작업:** 마스터 플랜 작성 완료 +**현재 Phase:** Phase 1 완료, Phase 2 대기 +**다음 작업:** mng 견적 수식 관리 현황 분석 --- -## 📋 Phase 1: 5130 견적 기능 분석 +## ✅ Phase 1: 5130 견적 기능 분석 (완료) ### 체크리스트 -- [ ] UI/화면 분석 (list.php, form.php 등) -- [ ] JS 수식 분석 (모든 JS 파일) -- [ ] DB 구조 분석 -- [ ] 비즈니스 로직 문서화 -- [ ] README.md 작성 +- [x] UI/화면 분석 (list.php, write_form.php 등) +- [x] JS 수식 분석 (calculation.js, fetch_unitprice.php) +- [x] PHP 계산 로직 분석 (get_screen_amount.php, get_slat_amount.php) +- [x] DB 구조 분석 (estimate, BDmodels, price_* 테이블) +- [x] 비즈니스 로직 문서화 +- [x] README.md 작성 ### 산출물 -- [ ] phase-1-5130-analysis/README.md -- [ ] phase-1-5130-analysis/ui-analysis.md -- [ ] phase-1-5130-analysis/js-formulas.md -- [ ] phase-1-5130-analysis/db-structure.md -- [ ] phase-1-5130-analysis/business-logic.md +- [x] [README.md](./phase-1-5130-analysis/README.md) - 분석 체크리스트 및 요약 +- [x] [js-formulas.md](./phase-1-5130-analysis/js-formulas.md) - **핵심** 수식 분석 (19개 항목) +- [x] [ui-analysis.md](./phase-1-5130-analysis/ui-analysis.md) - 화면별 기능 분석 +- [x] [db-structure.md](./phase-1-5130-analysis/db-structure.md) - 테이블/컬럼 구조 +- [x] [business-logic.md](./phase-1-5130-analysis/business-logic.md) - 비즈니스 로직 정리 -### 메모 -_Phase 1 진행 시 메모 기록_ +### 핵심 발견 사항 + +#### 견적 유형 (2가지) +| 유형 | 주요 특징 | +|------|----------| +| 스크린 | 면적 기반 (높이+550), 실리카/와이어 소재 | +| 슬랫(철재) | 면적 기반 (높이+50), 방화슬랫 소재 | + +#### 계산 항목 (18개) +검사비, 주자재, 조인트바, 모터, 제어기, 케이스, 케이스연기차단재, 마구리, 앵글, 가이드레일, 레일연기차단재, 하장바, L바, 보강평철, 샤프트, 무게평철, 환봉, 각파이프 + +#### 옵션 체크박스 (5개) +| 옵션 | 영향 항목 | +|------|----------| +| 절곡 | 케이스, 레일, 연기차단재, 하장바, L바, 보강평철 | +| 모터 | 모터 가격 | +| 보증 | 보증기간 | +| 슬랫 | 주자재(슬랫), 조인트바 | +| 부자재 | 샤프트, 각파이프, 앵글 | --- ## 📋 Phase 2: mng 견적 수식 관리 분석 ### 체크리스트 -- [ ] 현재 구현 상태 분석 +- [ ] 현재 구현 상태 분석 (quote-formulas) - [ ] 오류/문제점 목록화 - [ ] 개선 방향 도출 - [ ] README.md 작성 @@ -114,6 +132,8 @@ _Phase 4 진행 시 메모 기록_ |------|------|----------|------| | 2025-12-19 | DB 작업 위치 | api 프로젝트에서만 | mng 마이그레이션 방지 | | 2025-12-19 | 신규 테이블 정책 | options JSON 적용 | Hybrid EAV 전략 | +| 2025-12-19 | 견적 유형 | 스크린/슬랫 2가지 | 5130 기존 구조 유지 | +| 2025-12-19 | 체크박스 옵션 | JSON으로 통합 저장 | 확장성 고려 | --- @@ -126,4 +146,6 @@ _현재 이슈 없음_ ## 📚 참조 문서 - [MASTER_PLAN.md](./MASTER_PLAN.md) - 마스터 플랜 -- [docs/projects/mes/v1-analysis/quotation-analysis.md](../mes/v1-analysis/quotation-analysis.md) - MES 견적 분석 \ No newline at end of file +- [phase-1-5130-analysis/](./phase-1-5130-analysis/) - Phase 1 분석 결과 +- [docs/projects/mes/v1-analysis/quotation-analysis.md](../mes/v1-analysis/quotation-analysis.md) - MES 견적 분석 +- [docs/projects/legacy-5130/03_ESTIMATE.md](../legacy-5130/03_ESTIMATE.md) - 5130 레거시 분석 diff --git a/projects/quotation/phase-1-5130-analysis/README.md b/projects/quotation/phase-1-5130-analysis/README.md new file mode 100644 index 0000000..fa7cd04 --- /dev/null +++ b/projects/quotation/phase-1-5130-analysis/README.md @@ -0,0 +1,148 @@ +# Phase 1: 5130 견적 기능 분석 + +> **목표:** 5130 레거시 견적 시스템 완전 분석 및 문서화 +> **분석 일자:** 2025-12-19 +> **상태:** 🔄 진행 중 + +--- + +## 📋 분석 체크리스트 + +### 핵심 분석 (필수) +- [x] JS 수식 분석 (`common/calculation.js`) +- [x] PHP 단가 계산 로직 (`fetch_unitprice.php`) +- [x] 스크린 금액 계산 (`get_screen_amount.php`) +- [x] 슬랫 금액 계산 (`get_slat_amount.php`) +- [x] DB 스키마 분석 (`estimate` 테이블) + +### 문서 작성 +- [x] js-formulas.md - 수식 분석 (핵심!) +- [x] ui-analysis.md - 화면별 기능 분석 +- [x] db-structure.md - 테이블/컬럼 구조 +- [x] business-logic.md - 비즈니스 로직 정리 + +--- + +## 📁 분석 대상 파일 + +### 핵심 파일 +| 파일 | 크기 | 설명 | +|------|------|------| +| `common/calculation.js` | 182줄 | 프론트엔드 행 계산 로직 | +| `fetch_unitprice.php` | 875줄 | **핵심** - 단가 조회 및 수식 함수 | +| `get_screen_amount.php` | 583줄 | 스크린 견적 계산 | +| `get_slat_amount.php` | 541줄 | 슬랫(철재) 견적 계산 | +| `write_form.php` | 103KB | 견적서 작성 UI | + +### 디렉토리 구조 +``` +5130/estimate/ +├── common/ +│ ├── calculation.js # 행 계산 JS +│ ├── lastJS.php # 페이지 공통 JS +│ ├── common_screen.php # 스크린 공통 +│ └── common_slat.php # 슬랫 공통 +├── list.php # 견적 목록 +├── write_form.php # 견적서 작성 +├── estimate.php # 견적서 메인 +├── estimateSlat.php # 슬랫 견적 +├── estimateUnit.php # 단가 견적 +├── fetch_unitprice.php # 단가 조회 API +├── get_estimate_amount.php # 견적 금액 라우터 +├── get_screen_amount.php # 스크린 금액 계산 +├── get_slat_amount.php # 슬랫 금액 계산 +├── insert.php # 견적 저장 +└── generate_serial_pjnum.php # 번호 생성 +``` + +--- + +## 🔍 핵심 발견 사항 + +### 1. 견적 유형 (2가지) +| 유형 | 파일 | 주요 특징 | +|------|------|----------| +| **스크린** | `get_screen_amount.php` | 면적(m²) 기반, 실리카/와이어 소재 | +| **슬랫(철재)** | `get_slat_amount.php` | 면적 기반, 방화 슬랫 소재 | + +### 2. 계산 항목 (18개 항목) +1. 검사비 (인정검사비) +2. 주자재 (스크린/슬랫) +3. 조인트바 (슬랫 전용) +4. 모터 +5. 연동제어기 (매립형/노출형/뒷박스) +6. 케이스 +7. 케이스용 연기차단재 +8. 케이스 마구리 +9. 모터 받침용 앵글 +10. 가이드레일 +11. 레일용 연기차단재 +12. 하장바 +13. L바 (스크린 전용) +14. 보강평철 (스크린 전용) +15. 감기샤프트 +16. 무게평철 (스크린 전용) +17. 환봉 (스크린 전용) +18. 각파이프 +19. 앵글 + +### 3. 체크박스 옵션 (5개) +| 옵션 | 변수명 | 영향 항목 | +|------|--------|----------| +| 절곡 | `steel` | 케이스, 가이드레일, 연기차단재, 하장바, L바, 보강평철 | +| 모터 | `motor` | 모터 가격 포함 여부 | +| 보증 | `warranty` | 보증기간 | +| 슬랫 | `slatcheck` | 주자재(슬랫), 조인트바 | +| 부자재 | `partscheck` | 샤프트, 각파이프, 앵글 | + +### 4. 단가 테이블 (7개) +| 테이블 | 용도 | +|--------|------| +| `price_raw_materials` | 주자재 단가 (스크린, 슬랫) | +| `price_motor` | 모터/제어기 단가 | +| `price_shaft` | 샤프트 단가 | +| `price_pipe` | 각파이프 단가 | +| `price_angle` | 앵글 단가 | +| `BDmodels` | 케이스, 가이드레일, 부자재 단가 | +| `item_list` | 품목 마스터 | + +--- + +## 📊 상세 문서 + +| 문서 | 설명 | +|------|------| +| [js-formulas.md](./js-formulas.md) | **핵심** - 모든 수식 상세 분석 | +| [ui-analysis.md](./ui-analysis.md) | 화면별 기능 분석 | +| [db-structure.md](./db-structure.md) | DB 테이블 구조 | +| [business-logic.md](./business-logic.md) | 비즈니스 로직 정리 | + +--- + +## ⚠️ 주의 사항 + +### 코드 특성 +1. **레거시 PHP + jQuery** - ES6 문법 없음 +2. **동적 테이블명** - 일부 쿼리에서 테이블명 동적 설정 +3. **JSON 기반 데이터** - `itemList` 컬럼에 JSON으로 상세 데이터 저장 +4. **컬럼명 규칙** - `col1`, `col2`, ... 형태의 범용 컬럼 + +### SAM 이관 시 고려사항 +1. 단가 테이블 구조 재설계 필요 +2. `BDmodels` 테이블 → SAM 품목기준관리 연동 +3. 체크박스 옵션 → 견적 옵션 테이블 설계 +4. 수식 로직 → Service 클래스로 분리 + +--- + +## 📝 다음 단계 + +Phase 2로 이동하여 현재 mng 견적 수식 관리 상태 분석 예정 + +--- + +## 📚 참조 + +- [MASTER_PLAN.md](../MASTER_PLAN.md) +- [PROGRESS.md](../PROGRESS.md) +- [docs/projects/legacy-5130/03_ESTIMATE.md](../../legacy-5130/03_ESTIMATE.md) \ No newline at end of file diff --git a/projects/quotation/phase-1-5130-analysis/business-logic.md b/projects/quotation/phase-1-5130-analysis/business-logic.md new file mode 100644 index 0000000..86b1ad9 --- /dev/null +++ b/projects/quotation/phase-1-5130-analysis/business-logic.md @@ -0,0 +1,472 @@ +# 비즈니스 로직 분석 + +> **분석 대상:** 5130 레거시 견적 시스템 비즈니스 로직 +> **분석 일자:** 2025-12-19 + +--- + +## 비즈니스 프로세스 개요 + +### 견적 생성 플로우 +``` +┌─────────────────────────────────────────────────────────────┐ +│ 1. 견적 시작 │ +│ └─ 신규 / 복사 / 수주연계 │ +├─────────────────────────────────────────────────────────────┤ +│ 2. 기본 정보 입력 │ +│ └─ 현장명, 발주처, 담당자 │ +├─────────────────────────────────────────────────────────────┤ +│ 3. 제품 선택 │ +│ ├─ 대분류: 스크린 / 철재 │ +│ ├─ 모델: KSS01, KFS01 등 │ +│ └─ 규격: 폭, 높이, 마구리윙 │ +├─────────────────────────────────────────────────────────────┤ +│ 4. 옵션 선택 │ +│ └─ 절곡, 모터, 보증, 슬랫, 부자재 │ +├─────────────────────────────────────────────────────────────┤ +│ 5. 상세 항목 입력 │ +│ ├─ 각 행별 위치, 폭, 높이, 수량 입력 │ +│ └─ 자동 계산 트리거 │ +├─────────────────────────────────────────────────────────────┤ +│ 6. 금액 계산 (자동) │ +│ ├─ AJAX → get_screen_amount / get_slat_amount │ +│ ├─ 18개 항목별 단가 조회 및 계산 │ +│ └─ 합계 산출 │ +├─────────────────────────────────────────────────────────────┤ +│ 7. 할인 적용 │ +│ └─ 할인율 / 할인액 입력 → 최종금액 계산 │ +├─────────────────────────────────────────────────────────────┤ +│ 8. 저장 │ +│ ├─ 견적번호 생성 (KD-PR-YYMMDD-NN) │ +│ └─ DB 저장 (estimate 테이블) │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 1. 견적 유형별 처리 + +### 스크린 견적 (Screen) +| 특성 | 값 | +|------|-----| +| 대분류 | 스크린 | +| 주자재 소재 | 실리카, 와이어 | +| 면적 계산 | (높이 + 550) × 폭 / 1,000,000 m² | +| 기본 제작폭 | 160mm | +| 전용 항목 | L바, 보강평철, 무게평철, 환봉 | + +### 슬랫 견적 (Slat/철재) +| 특성 | 값 | +|------|-----| +| 대분류 | 철재 | +| 주자재 소재 | 방화슬랫 | +| 면적 계산 | (높이 + 50) × 폭 / 1,000,000 m² | +| 기본 제작폭 | 110mm | +| 전용 항목 | 조인트바 | + +--- + +## 2. 옵션 체크박스 로직 + +### 옵션별 영향 항목 + +| 옵션 | 변수 | 영향받는 항목 | +|------|------|---------------| +| 절곡 | `steel` | 케이스, 케이스용 연기차단재, 마구리, 가이드레일, 레일용 연기차단재, 하장바, L바(스크린), 보강평철(스크린) | +| 모터 | `motor` | 모터 가격 포함/미포함 | +| 보증 | `warranty` | 보증기간 표시 (인정) | +| 슬랫 | `slatcheck` | 주자재(슬랫), 조인트바 | +| 부자재 | `partscheck` | 감기샤프트, 각파이프, 앵글 | + +### 조건부 계산 로직 +```php +// 절곡 옵션 체크 시 +if ($steel == '1') { + // 케이스, 연기차단재, 레일, 하장바 등 계산 + $caseAmount = calculateCase($width, $caseType, $itemList); + $smokebanAmount = calculateSmokeban($width, $itemList); + // ... +} else { + // 해당 항목 0원 처리 + $caseAmount = 0; + $smokebanAmount = 0; +} + +// 모터 옵션 체크 시 +if ($motor == '1') { + $motorAmount = getMotorPrice($motorCapacity); +} else { + $motorAmount = 0; +} +``` + +--- + +## 3. 단가 조회 로직 + +### 조회 우선순위 +1. **BDmodels 테이블**: 모델별 품목 단가 +2. **price_* 테이블**: 품목별 세부 단가 +3. **기본값**: 조회 실패 시 기본 단가 적용 + +### 단가 조회 함수 +```php +// fetch_unitprice.php + +// 1. 모터 단가 조회 +function getMotorPrice($capacity) { + // price_motor 테이블에서 용량별 단가 조회 + $sql = "SELECT unit_price FROM price_motor WHERE capacity = ?"; + // ... +} + +// 2. 샤프트 단가 조회 +function getShaftPrice($length) { + // price_shaft 테이블에서 길이별 단가 조회 + $sql = "SELECT unit_price FROM price_shaft WHERE ? BETWEEN min_length AND max_length"; + // ... +} + +// 3. BDmodels에서 품목 단가 조회 +function getBDModelPrice($modelname, $itemname, $size) { + $sql = "SELECT itemList FROM BDmodels WHERE modelname = ? AND itemname = ?"; + // JSON 파싱 후 사이즈에 맞는 가격 반환 +} +``` + +--- + +## 4. 금액 계산 플로우 + +### 스크린 금액 계산 (get_screen_amount.php) + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 입력값 │ +│ - 폭(col2), 높이(col3), 수량(col4), 소재(col5) │ +│ - 케이스타입(col6), 레일타입(col7), 설치방식(col8) │ +│ - 체크박스옵션 (steel, motor, warranty, slatcheck, partscheck)│ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 1. 면적 계산 │ +│ area = (height + 550) × width / 1,000,000 │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 2. 중량 계산 (모터 용량 결정용) │ +│ weight = area × 소재별_단위중량 │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 3. 모터 용량 결정 │ +│ motorCapacity = searchBracketSize(weight, inch) │ +│ → 150K / 300K / 500K / 800K / 1000K │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 4. 18개 항목별 금액 계산 │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 1. 검사비: inspectionFee (고정) │ │ +│ │ 2. 주자재: area × 소재단가 │ │ +│ │ 3. 모터: getMotorPrice(capacity) × motor체크 │ │ +│ │ 4. 제어기: getControllerPrice(type) × motor체크 │ │ +│ │ 5. 케이스: (width+제작폭) × m당단가 × steel체크 │ │ +│ │ 6. 케이스연기차단재: width × m당단가 × steel체크 │ │ +│ │ 7. 마구리: 2개 × 개당단가 × steel체크 │ │ +│ │ 8. 앵글: 규격별단가 × steel체크 │ │ +│ │ 9. 가이드레일: (height+레일여유) × m당단가 × steel │ │ +│ │ 10. 레일연기차단재: height × m당단가 × steel │ │ +│ │ 11. 하장바: width × m당단가 × steel체크 │ │ +│ │ 12. L바: width × m당단가 × steel체크 │ │ +│ │ 13. 보강평철: width × m당단가 × steel체크 │ │ +│ │ 14. 샤프트: getShaftPrice(width) × partscheck │ │ +│ │ 15. 무게평철: weight계산 × 단가 │ │ +│ │ 16. 환봉: 길이계산 × m당단가 │ │ +│ │ 17. 각파이프: 길이계산 × m당단가 × partscheck │ │ +│ │ 18. 앵글: 길이계산 × m당단가 × partscheck │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 5. 행 합계 │ +│ rowTotal = Σ(항목별금액) × 수량 │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 6. 전체 합계 │ +│ estimateTotal = Σ(모든행 rowTotal) │ +│ EstimateFinalSum = estimateTotal - 할인액 │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 5. 모터 용량 결정 로직 + +### 중량 + 인치 기반 판단 + +```php +function searchBracketSize($motorWeight, $bracketInch = null) { + $weight = floatval($motorWeight); + $inch = intval($bracketInch); + + // 인치별 중량 기준 + if ($inch > 0) { + // 4인치 기준 + if ($inch == 4 && $weight <= 300) → 300K + if ($inch == 4 && $weight <= 400) → 400K + + // 5인치 기준 + if ($inch == 5 && $weight <= 246) → 300K + if ($inch == 5 && $weight <= 327) → 400K + if ($inch == 5 && $weight <= 500) → 500K + if ($inch == 5 && $weight <= 600) → 600K + + // 6인치 기준 + if ($inch == 6 && $weight <= 208) → 300K + if ($inch == 6 && $weight <= 277) → 400K + if ($inch == 6 && $weight <= 424) → 500K + if ($inch == 6 && $weight <= 508) → 600K + if ($inch == 6 && $weight <= 800) → 800K + if ($inch == 6 && $weight <= 1000) → 1000K + + // 8인치 기준 + if ($inch == 8 && $weight <= 324) → 500K + if ($inch == 8 && $weight <= 388) → 600K + if ($inch == 8 && $weight <= 611) → 800K + if ($inch == 8 && $weight <= 1000) → 1000K + } else { + // 인치 없이 중량만으로 판단 + if ($weight <= 300) → 300K + if ($weight <= 400) → 400K + if ($weight <= 500) → 500K + if ($weight <= 600) → 600K + if ($weight <= 800) → 800K + if ($weight <= 1000) → 1000K + } +} +``` + +### 브라켓 사이즈 매핑 + +| 모터 용량 | 브라켓 사이즈 | +|-----------|---------------| +| 300K, 400K | 530×320 | +| 500K, 600K | 600×350 | +| 800K, 1000K | 690×390 | + +--- + +## 6. 견적번호 생성 + +### 형식 +``` +KD-PR-YYMMDD-NN + +KD: 경동 (회사코드) +PR: 프로젝트 +YYMMDD: 날짜 (년월일 6자리) +NN: 일련번호 (01~99, 당일 기준) +``` + +### 생성 로직 +```php +// generate_serial_pjnum.php +function generatePjnum($pdo) { + $today = date('ymd'); + $prefix = "KD-PR-{$today}-"; + + // 오늘 날짜의 마지막 번호 조회 + $sql = "SELECT pjnum FROM estimate + WHERE pjnum LIKE ? + ORDER BY pjnum DESC LIMIT 1"; + $stmh = $pdo->prepare($sql); + $stmh->execute([$prefix . '%']); + $row = $stmh->fetch(); + + if ($row) { + // 마지막 번호 추출 후 +1 + $lastNum = intval(substr($row['pjnum'], -2)); + $nextNum = str_pad($lastNum + 1, 2, '0', STR_PAD_LEFT); + } else { + $nextNum = '01'; + } + + return $prefix . $nextNum; +} +``` + +--- + +## 7. 금액 처리 규칙 + +### 단위 변환 +| 항목 | 입력 단위 | 계산 단위 | 비고 | +|------|----------|----------|------| +| 폭/높이 | mm | m | /1000 변환 | +| 면적 | - | m² | 폭×높이/1,000,000 | +| 중량 | - | kg | 면적×단위중량 | +| 금액 | - | 원 | 천원 단위 반올림 | + +### 반올림 규칙 +```javascript +// calculation.js +// 금액은 천원 단위에서 반올림 +roundedAreaPrice = Math.round(areaPrice / 1000) * 1000; + +// 면적은 소수점 2자리 +area = Math.round(area * 100) / 100; +``` + +### 수동 편집 처리 +```javascript +// 수동 편집된 셀은 배경색 변경 +$('.manually-edited').css('background-color', '#f8d7da'); + +// 자동 계산 시 수동 편집 값 유지 옵션 +if (!isManuallyEdited) { + cell.val(calculatedValue); +} +``` + +--- + +## 8. 데이터 저장 규칙 + +### 저장 전 검증 +1. 필수값 확인: 현장명, 발주처, 담당자 +2. 수치 변환: 콤마 제거, 정수 변환 +3. 권한 확인: 레벨 5 이하 + +### 저장 데이터 +```php +// insert.php +$data = [ + 'pjnum' => generatePjnum(), + 'indate' => date('Y-m-d'), + 'orderman' => $_SESSION['name'], + 'outworkplace' => $outworkplace, + 'major_category' => $major_category, + 'model_name' => $model_name, + 'makeWidth' => intval(str_replace(',', '', $makeWidth)), + 'makeHeight' => intval(str_replace(',', '', $makeHeight)), + 'maguriWing' => $maguriWing, + 'inspectionFee' => intval(str_replace(',', '', $inspectionFee)), + 'estimateList' => json_encode($estimateList), + 'estimateList_auto' => json_encode($estimateList_auto), + 'estimateSlatList' => json_encode($estimateSlatList), + 'estimateSlatList_auto' => json_encode($estimateSlatList_auto), + 'estimateTotal' => intval(str_replace(',', '', $estimateTotal)), + 'steel' => $steel, + 'motor' => $motor, + 'warranty' => $warranty, + 'slatcheck' => $slatcheck, + 'partscheck' => $partscheck +]; +``` + +--- + +## 9. SAM 이관 시 로직 변경 + +### Service 클래스 분리 +```php +// app/Services/QuotationService.php +class QuotationService +{ + // 1. 견적 생성 + public function createQuote(array $data): Quote { } + + // 2. 금액 계산 + public function calculateAmount(Quote $quote): array { } + + // 3. 스크린 계산 + protected function calculateScreenAmount(array $details): array { } + + // 4. 슬랫 계산 + protected function calculateSlatAmount(array $details): array { } + + // 5. 모터 용량 결정 + protected function determineMotorCapacity(float $weight, ?int $inch): int { } + + // 6. 단가 조회 + protected function getUnitPrice(string $itemCode, array $params): float { } +} +``` + +### 계산 로직 캡슐화 +```php +// app/ValueObjects/QuoteDimension.php +class QuoteDimension +{ + public function __construct( + public readonly int $width, + public readonly int $height, + public readonly int $wing = 50 + ) {} + + public function getAreaForScreen(): float + { + return ($this->height + 550) * $this->width / 1000000; + } + + public function getAreaForSlat(): float + { + return ($this->height + 50) * $this->width / 1000000; + } +} +``` + +### 옵션 처리 +```php +// app/ValueObjects/QuoteOptions.php +class QuoteOptions +{ + public function __construct( + public readonly bool $steel = false, + public readonly bool $motor = false, + public readonly bool $warranty = false, + public readonly bool $slat = false, + public readonly bool $parts = false + ) {} + + public static function fromArray(array $data): self + { + return new self( + steel: $data['steel'] ?? false, + motor: $data['motor'] ?? false, + warranty: $data['warranty'] ?? false, + slat: $data['slat'] ?? false, + parts: $data['parts'] ?? false + ); + } + + public function toJson(): string + { + return json_encode([ + 'steel' => $this->steel, + 'motor' => $this->motor, + 'warranty' => $this->warranty, + 'slat' => $this->slat, + 'parts' => $this->parts + ]); + } +} +``` + +--- + +## 참조 파일 + +- `5130/estimate/get_screen_amount.php` - 스크린 계산 로직 +- `5130/estimate/get_slat_amount.php` - 슬랫 계산 로직 +- `5130/estimate/fetch_unitprice.php` - 단가 조회 함수 +- `5130/estimate/insert.php` - 저장 로직 +- `5130/estimate/generate_serial_pjnum.php` - 번호 생성 diff --git a/projects/quotation/phase-1-5130-analysis/db-structure.md b/projects/quotation/phase-1-5130-analysis/db-structure.md new file mode 100644 index 0000000..5864fbb --- /dev/null +++ b/projects/quotation/phase-1-5130-analysis/db-structure.md @@ -0,0 +1,448 @@ +# DB 구조 분석 + +> **분석 대상:** 5130 레거시 견적 시스템 데이터베이스 +> **데이터베이스:** chandj +> **분석 일자:** 2025-12-19 + +--- + +## 테이블 목록 + +### 핵심 테이블 +| 테이블명 | 용도 | 비고 | +|----------|------|------| +| `estimate` | 견적서 마스터 | 견적 헤더 + JSON 상세 | +| `BDmodels` | 모델별 단가 | 케이스, 레일, 부자재 단가 | +| `BDparts` | 부품 단가 | 부품별 가격 계수 | + +### 단가 테이블 +| 테이블명 | 용도 | 비고 | +|----------|------|------| +| `price_raw_materials` | 주자재 단가 | 스크린, 슬랫 소재 | +| `price_motor` | 모터 단가 | 용량별 모터 가격 | +| `price_shaft` | 샤프트 단가 | 길이별 샤프트 가격 | +| `price_pipe` | 각파이프 단가 | 규격별 파이프 가격 | +| `price_angle` | 앵글 단가 | 규격별 앵글 가격 | +| `price_bend` | 절곡비 단가 | 절곡 가공비 | +| `price_smokeban` | 연기차단재 단가 | 연기차단재 가격 | +| `price_etc` | 기타 단가 | 기타 부자재 | +| `price_pole` | 폴 단가 | 폴 관련 가격 | + +### 참조 테이블 +| 테이블명 | 용도 | +|----------|------| +| `item_list` | 품목 마스터 | +| `output` | 발주서 (수주→발주 연계) | + +--- + +## 1. estimate 테이블 (견적 마스터) + +### 스키마 +```sql +CREATE TABLE estimate ( + num INT AUTO_INCREMENT PRIMARY KEY, + + -- 기본 정보 + pjnum VARCHAR(50), -- 프로젝트 번호 (KD-PR-YYMMDD-NN) + indate DATE, -- 등록일 + orderman VARCHAR(50), -- 담당자 + outworkplace VARCHAR(200), -- 현장명/거래처 + + -- 분류 정보 + major_category VARCHAR(50), -- 대분류 (스크린/철재) + model_name VARCHAR(100), -- 모델명 (KSS01, KFS01 등) + position VARCHAR(50), -- 위치 + + -- 규격 정보 + makeWidth INT DEFAULT 160, -- 제작 폭 (스크린:160, 슬랫:110) + makeHeight INT DEFAULT 350, -- 제작 높이 + maguriWing VARCHAR(20) DEFAULT '50', -- 마구리 윙 + + -- 발주처 정보 + con_num VARCHAR(50), -- 계약번호 + secondord VARCHAR(100), -- 2차 발주처 + secondordman VARCHAR(50), -- 2차 담당자 + secondordmantel VARCHAR(20), -- 2차 담당자 연락처 + secondordnum VARCHAR(50), -- 2차 발주번호 + + -- 견적 상세 (JSON) + estimateList TEXT, -- 스크린 견적 리스트 (JSON) + estimateList_auto TEXT, -- 스크린 자동계산 리스트 (JSON) + estimateSlatList TEXT, -- 슬랫 견적 리스트 (JSON) + estimateSlatList_auto TEXT, -- 슬랫 자동계산 리스트 (JSON) + + -- 금액 정보 + estimateTotal INT DEFAULT 0, -- 견적 총액 + EstimateFirstSum INT DEFAULT 0, -- 최초 견적 합계 + EstimateUpdatetSum INT DEFAULT 0, -- 수정 견적 합계 + EstimateDiffer INT DEFAULT 0, -- 차액 + estimateSurang INT DEFAULT 0, -- 수량 + + -- 할인 정보 + EstimateDiscountRate INT DEFAULT 0,-- 할인율 (%) + EstimateDiscount INT DEFAULT 0, -- 할인금액 + EstimateFinalSum INT DEFAULT 0, -- 최종 금액 + + -- 검사비/옵션 + inspectionFee INT DEFAULT 50000, -- 인정검사비 + steel VARCHAR(50), -- 절곡 옵션 (1/0) + motor VARCHAR(100), -- 모터 옵션 (1/0) + warranty VARCHAR(100), -- 보증 (인정/빈값) + slatcheck VARCHAR(10), -- 슬랫 체크 (1/0) + partscheck VARCHAR(10), -- 부자재 체크 (1/0) + + -- 시스템 필드 + comment TEXT, -- 비고 + update_log TEXT, -- 수정이력 + is_deleted TINYINT DEFAULT 0, -- 삭제플래그 + + INDEX idx_pjnum (pjnum), + INDEX idx_outworkplace (outworkplace), + INDEX idx_indate (indate) +); +``` + +### 주요 컬럼 설명 + +#### 프로젝트 번호 (pjnum) +``` +형식: KD-PR-YYMMDD-NN +- KD: 경동 +- PR: 프로젝트 +- YYMMDD: 날짜 (6자리) +- NN: 일련번호 (01~99) + +예시: KD-PR-251219-01 +``` + +#### 대분류 (major_category) +| 값 | 설명 | +|----|------| +| 스크린 | 스크린 방화셔터 | +| 철재 | 철재 슬랫 방화셔터 | + +#### 체크박스 옵션 +| 컬럼 | 값 | 의미 | +|------|-----|------| +| steel | '1' / '0' | 절곡 포함/미포함 | +| motor | '1' / '0' | 모터 포함/미포함 | +| warranty | '인정' / '' | 보증 포함/미포함 | +| slatcheck | '1' / '0' | 슬랫 포함/미포함 | +| partscheck | '1' / '0' | 부자재 포함/미포함 | + +--- + +## 2. estimateList JSON 구조 + +### 수동 입력 항목 (estimateList, estimateSlatList) +```json +[ + { + "item_name": "가이드레일", + "specification": "A형 65×80", + "unit": "EA", + "quantity": 2, + "unit_price": 150000, + "amount": 300000, + "remark": "" + }, + { + "item_name": "스크린 판넬", + "specification": "1.0T × 1200W", + "unit": "m²", + "quantity": 24.5, + "unit_price": 45000, + "amount": 1102500, + "remark": "SUS304" + } +] +``` + +### 자동 계산 항목 (estimateList_auto, estimateSlatList_auto) +```json +[ + { + "item_code": "AUTO001", + "item_name": "벤딩 가공비", + "calc_type": "per_meter", + "base_value": 120.5, + "unit_price": 2500, + "amount": 301250 + } +] +``` + +--- + +## 3. BDmodels 테이블 (모델별 단가) + +### 스키마 +```sql +CREATE TABLE IF NOT EXISTS BDmodels ( + id INT AUTO_INCREMENT PRIMARY KEY, + modelname VARCHAR(50), -- 모델명 + itemname VARCHAR(100), -- 품목명 + itemsecond VARCHAR(100), -- 품목 세부 + itemList TEXT, -- 상세 가격 JSON + is_deleted TINYINT DEFAULT 0, + INDEX idx_modelname (modelname), + INDEX idx_itemname (itemname) +); +``` + +### itemList JSON 구조 +```json +{ + "prices": [ + {"size": "530*320", "price": 150000}, + {"size": "600*350", "price": 180000}, + {"size": "690*390", "price": 210000} + ], + "unit": "EA", + "description": "모터 브라켓" +} +``` + +### 주요 품목 +| modelname | itemname | 용도 | +|-----------|----------|------| +| 공통 | 케이스 | 케이스 단가 | +| 공통 | 가이드레일 | 레일 단가 | +| 공통 | 연기차단재 | 연기차단재 단가 | +| 공통 | 하장바 | 하장바 단가 | +| 공통 | 마구리 | 마구리 단가 | +| 스크린 | L바 | L바 단가 (스크린 전용) | +| 스크린 | 보강평철 | 보강평철 단가 (스크린 전용) | +| 슬랫 | 조인트바 | 조인트바 단가 (슬랫 전용) | + +--- + +## 4. 단가 테이블 + +### price_raw_materials (주자재) +```sql +CREATE TABLE price_raw_materials ( + id INT AUTO_INCREMENT PRIMARY KEY, + material_type VARCHAR(50), -- 소재 유형 (실리카, 와이어, 방화슬랫) + specification VARCHAR(100), -- 규격 + unit VARCHAR(20), -- 단위 (m², kg) + unit_price DECIMAL(10,0), -- 단가 + itemList TEXT, -- 상세 JSON + is_deleted TINYINT DEFAULT 0 +); +``` + +### price_motor (모터) +```sql +CREATE TABLE price_motor ( + id INT AUTO_INCREMENT PRIMARY KEY, + capacity VARCHAR(20), -- 용량 (150K, 300K, 500K, 800K, 1000K) + type VARCHAR(50), -- 유형 + unit_price DECIMAL(10,0), -- 단가 + is_deleted TINYINT DEFAULT 0 +); +``` + +### price_shaft (샤프트) +```sql +CREATE TABLE price_shaft ( + id INT AUTO_INCREMENT PRIMARY KEY, + length_range VARCHAR(50), -- 길이 범위 + unit_price DECIMAL(10,0), -- 단가 + weight_per_meter DECIMAL(5,2), -- m당 중량 + is_deleted TINYINT DEFAULT 0 +); +``` + +### price_pipe (각파이프) +```sql +CREATE TABLE price_pipe ( + id INT AUTO_INCREMENT PRIMARY KEY, + specification VARCHAR(50), -- 규격 (50×50, 75×75 등) + unit_price DECIMAL(10,0), -- m당 단가 + weight_per_meter DECIMAL(5,2), -- m당 중량 + is_deleted TINYINT DEFAULT 0 +); +``` + +### price_angle (앵글) +```sql +CREATE TABLE price_angle ( + id INT AUTO_INCREMENT PRIMARY KEY, + specification VARCHAR(50), -- 규격 (50×50×5 등) + unit_price DECIMAL(10,0), -- m당 단가 + weight_per_meter DECIMAL(5,2), -- m당 중량 + is_deleted TINYINT DEFAULT 0 +); +``` + +--- + +## 5. 컬럼 매핑 (스크린 vs 슬랫) + +### get_screen_amount.php 컬럼 +| 항목 | 컬럼 | 설명 | +|------|------|------| +| 위치 | col1 | 설치 위치 | +| 폭 | col2 | 오픈사이즈 폭 (mm) | +| 높이 | col3 | 오픈사이즈 높이 (mm) | +| 수량 | col4 | 수량 | +| 소재 | col5 | 실리카/와이어 | +| 케이스 타입 | col6 | 절곡/롤 | +| 레일 타입 | col7 | A형/B형 | +| 설치방식 | col8 | 매립/노출 | +| 면적 | col9 | 계산된 면적 (m²) | +| 케이스 길이 | col10 | mm | +| 레일 길이 | col11 | mm | +| 하장바 길이 | col12 | mm | +| 중량 | col13 | kg | +| 검사비 | col14 | 원 | +| 주자재 | col15 | 원 | +| 모터 | col16 | 원 | +| 제어기 | col17 | 원 | +| 케이스 | col18 | 원 | +| 레일 | col19 | 원 | +| 앵글 | col20 | 원 | +| 샤프트 | col21 | 원 | +| 인치 | col22 | 샤프트 인치 | + +### get_slat_amount.php 컬럼 +| 항목 | 컬럼 | 설명 | +|------|------|------| +| 위치 | col1 | 설치 위치 | +| 폭 | col2 | 오픈사이즈 폭 (mm) | +| 높이 | col3 | 오픈사이즈 높이 (mm) | +| 수량 | col4 | 수량 | +| 소재 | col5 | 방화슬랫 | +| 케이스 타입 | col6 | 절곡/롤 | +| 레일 타입 | col7 | A형/B형 | +| 설치방식 | col8 | 매립/노출 | +| 면적 | col9 | 계산된 면적 (m²) | +| 케이스 길이 | col10 | mm | +| 레일 길이 | col11 | mm | +| 하장바 길이 | col12 | mm | +| 중량 | col13 | kg | +| 검사비 | col14 | 원 | +| 주자재 (슬랫) | col15 | 원 | +| 조인트바 | col16 | 원 (슬랫 전용) | +| 모터 | col17 | 원 | +| 제어기 | col18 | 원 | +| 케이스 | col19 | 원 | +| 레일 | col20 | 원 | +| 앵글 | col21 | 원 | +| 샤프트 | col22 | 원 | +| 인치 | col23 | 샤프트 인치 | + +--- + +## 6. 데이터 관계도 + +``` +┌─────────────────────────────────────────────────────────┐ +│ estimate │ +│ (견적 마스터) │ +├─────────────────────────────────────────────────────────┤ +│ num (PK) │ +│ pjnum (견적번호) │ +│ major_category → '스크린' / '철재' │ +│ model_name → item_list.model_name │ +│ estimateList (JSON) ────┐ │ +│ estimateSlatList (JSON) │ │ +└──────────────────────────┼───────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────┐ +│ JSON 상세 항목 │ +├─────────────────────────────────────────────────────────┤ +│ item_name → BDmodels.itemname │ +│ unit_price ← price_* 테이블 조회 │ +└──────────────────────────────────────────────────────────┘ + │ + ┌─────────────────┼─────────────────┐ + ▼ ▼ ▼ +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│ BDmodels │ │ price_motor │ │ price_shaft │ +│ (모델 단가) │ │ (모터 단가) │ │ (샤프트 단가)│ +└──────────────┘ └──────────────┘ └──────────────┘ + ▼ ▼ ▼ +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│price_raw_mat │ │ price_pipe │ │ price_angle │ +│ (주자재) │ │ (각파이프) │ │ (앵글) │ +└──────────────┘ └──────────────┘ └──────────────┘ +``` + +--- + +## 7. SAM 이관 매핑 + +### 테이블 매핑 +| 5130 테이블 | SAM 테이블 | 비고 | +|-------------|------------|------| +| estimate | quotes + quote_items | 헤더/상세 분리 | +| BDmodels | products + prices | 품목기준관리 연동 | +| price_* | prices | 통합 단가 테이블 | + +### 주요 변환 포인트 +1. **JSON → 정규화**: estimateList JSON을 quote_items 테이블로 분리 +2. **동적 컬럼 → 고정 컬럼**: col1~col23을 명시적 컬럼명으로 변경 +3. **체크박스 → options JSON**: steel, motor 등을 options JSON으로 통합 +4. **단가 테이블 통합**: 7개 단가 테이블을 prices 테이블로 통합 + +### SAM 스키마 (제안) +```sql +-- 견적 헤더 +CREATE TABLE quotes ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + tenant_id BIGINT UNSIGNED NOT NULL, + quote_number VARCHAR(50) NOT NULL, + quote_date DATE NOT NULL, + customer_id BIGINT UNSIGNED, + project_name VARCHAR(200), + category ENUM('screen', 'slat') NOT NULL, + model_id BIGINT UNSIGNED, + options JSON, -- {steel: true, motor: true, warranty: true, ...} + dimensions JSON, -- {width: 160, height: 350, wing: 50} + inspection_fee DECIMAL(10,0) DEFAULT 50000, + subtotal DECIMAL(12,0) DEFAULT 0, + discount_rate DECIMAL(5,2) DEFAULT 0, + discount_amount DECIMAL(12,0) DEFAULT 0, + total_amount DECIMAL(12,0) DEFAULT 0, + status ENUM('draft', 'sent', 'accepted', 'rejected') DEFAULT 'draft', + created_by BIGINT UNSIGNED, + created_at TIMESTAMP, + updated_at TIMESTAMP, + deleted_at TIMESTAMP NULL, + + INDEX idx_tenant_quote (tenant_id, quote_number), + INDEX idx_tenant_date (tenant_id, quote_date) +); + +-- 견적 상세 +CREATE TABLE quote_items ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + quote_id BIGINT UNSIGNED NOT NULL, + item_type ENUM('manual', 'auto') DEFAULT 'manual', + item_code VARCHAR(50), + item_name VARCHAR(100) NOT NULL, + specification VARCHAR(200), + unit VARCHAR(20), + quantity DECIMAL(10,2) DEFAULT 1, + unit_price DECIMAL(12,0) DEFAULT 0, + amount DECIMAL(12,0) DEFAULT 0, + sort_order INT DEFAULT 0, + remark TEXT, + + FOREIGN KEY (quote_id) REFERENCES quotes(id) ON DELETE CASCADE, + INDEX idx_quote (quote_id) +); +``` + +--- + +## 참조 파일 + +- `5130/estimate/insert.php` - 견적 저장 로직 +- `5130/estimate/fetch_unitprice.php` - 단가 조회 +- `5130/bendingfee_backup/sql.php` - BDmodels 스키마 +- `docs/projects/legacy-5130/03_ESTIMATE.md` - 이전 분석 \ No newline at end of file diff --git a/projects/quotation/phase-1-5130-analysis/js-formulas.md b/projects/quotation/phase-1-5130-analysis/js-formulas.md new file mode 100644 index 0000000..cbb6905 --- /dev/null +++ b/projects/quotation/phase-1-5130-analysis/js-formulas.md @@ -0,0 +1,471 @@ +# 5130 견적 수식 분석 + +> **핵심 문서** - 모든 견적 계산 수식 상세 분석 +> **분석 일자:** 2025-12-19 + +--- + +## 📋 수식 개요 + +### 수식 파일 위치 +| 파일 | 용도 | 핵심 함수 | +|------|------|----------| +| `common/calculation.js` | 프론트엔드 행 계산 | `calculateRowTotal()` | +| `fetch_unitprice.php` | 단가 조회/계산 헬퍼 | 30+ 함수 | +| `get_screen_amount.php` | 스크린 견적 계산 | `calculateScreenAmount()` | +| `get_slat_amount.php` | 슬랫 견적 계산 | `calculateSlatAmount()` | + +--- + +## 🔢 기본 계산 수식 + +### 1. 행별 합계 계산 (calculation.js) +```javascript +// 기본 수식 +totalPrice = 수량(su) × 단가(unitPrice) + +// 면적 기반 수식 +if (면적단가 > 0) { + 단가 = 면적(areaLength) × 면적단가(areaPrice) + totalPrice = 수량 × 단가 +} +``` + +### 2. 면적 계산 +```php +// 스크린 면적 (m²) +// 기본 높이 350에 +550 추가 = 900 기준 +$calculateHeight = $height + 550; +$area = $width * $calculateHeight / 1000000; + +// 슬랫 면적 (m²) +// 기본 높이 350에 +50 추가 = 400 기준 +$calculateHeight = $height + 50; +$area = $width * $calculateHeight / 1000000; +``` + +--- + +## 💰 항목별 수식 상세 + +### 1. 검사비 (인정검사비) +```php +$inspectionFee = 기본값(50000); +$검사비 = $inspectionFee × $수량; +``` +| 입력 | 출력 | 단위 | +|------|------|------| +| 검사비 단가 | 검사비 총액 | 원 | + +--- + +### 2. 주자재 (스크린/슬랫) +```php +// 스크린 (실리카/와이어) +$screen_price = $price_raw_materials × round($area, 2); +$주자재_스크린 = $screen_price × $수량; + +// 슬랫 (방화) +$slat_price = $price_raw_materials × round($area, 2); +$주자재_슬랫 = $slat_price × $수량; +``` +| 입력 | 계산 | 출력 | +|------|------|------| +| 폭(W), 높이(H), 단가 | 면적 × 단가 × 수량 | 주자재 금액 | + +**조건:** `slatcheck == '1'` 일 때만 슬랫 주자재 계산 + +--- + +### 3. 조인트바 (슬랫 전용) +```php +$jointbar_price = $price_jointbar × $item['col76']; +``` +| 입력 | 출력 | +|------|------| +| 조인트바 개수(col76) × 단가 | 조인트바 금액 | + +**조건:** `slatcheck == '1'` 일 때만 계산 + +--- + +### 4. 모터 +```php +// 모터 용량 추출 (숫자만) +$motorSpec = preg_replace('/[a-zA-Z]/', '', $item['col19']); +$motorUnit_price = getPriceForMotor($motorSpec, $itemList); +$모터 = $motorUnit_price × $수량; +``` +| 입력 | 조건 | 출력 | +|------|------|------| +| 모터 용량, 수량 | 모터공급처='경동(견적가포함)' AND motor='1' | 모터 금액 | + +**모터 용량 판별 로직:** +```php +function calculateMotorSpec($item, $weight, $BracketInch) { + // 스크린/철재 구분 + $ItemSel = (substr($item['col4'], 0, 2) === 'KS') ? '스크린' : '철재'; + + // 중량 + 인치 조합으로 용량 결정 + // 스크린: 150K, 300K, 400K, 500K, 600K + // 철재: 300K, 400K, 500K, 600K, 800K, 1000K + + // 예시 조건 (스크린 150K) + if ($ItemSel === '스크린' && $BracketInch == 4 && $weight <= 150) { + return 150; + } + // ... 기타 조건들 +} +``` + +**모터 용량 매핑표:** +| 인치 | 중량 범위 | 스크린 용량 | 철재 용량 | +|------|----------|------------|----------| +| 4" | ≤150kg | 150K | - | +| 4" | ≤300kg | 300K | 300K | +| 4" | ≤400kg | 400K | 400K | +| 5" | ≤500kg | 500K | 500K | +| 5" | ≤600kg | 600K | 600K | +| 6" | ≤800kg | - | 800K | +| 8" | ≤1000kg | - | 1000K | + +--- + +### 5. 연동제어기 +```php +$price1 = calculateControllerSpec($item['col15'], $itemList, '매립형'); +$price2 = calculateControllerSpec($item['col16'], $itemList, '노출형'); +$price3 = calculateControllerSpec($item['col17'], $itemList, '뒷박스'); + +$controller_price = + $price1 × $매립형_수량 + + $price2 × $노출형_수량 + + $price3 × $뒷박스_수량; +``` +| 유형 | 입력 컬럼 | 설명 | +|------|----------|------| +| 매립형 | col15 (스크린) / col16 (슬랫) | 벽 매립 타입 | +| 노출형 | col16 (스크린) / col17 (슬랫) | 외부 노출 타입 | +| 뒷박스 | col17 (스크린) / col18 (슬랫) | 뒷면 박스 타입 | + +--- + +### 6. 케이스 +```php +// 규격별 단가 조회 (BDmodels 테이블) +if ($item['col36'] === 'custom') { + $dimension = $item['col36_custom']; // 커스텀 규격 +} else { + $dimension = $item['col36']; // 표준 규격 +} + +// 표준 규격이면 단가표에서 조회 +if (array_key_exists($dimension, $shutterBoxprices)) { + $shutter_price = $shutterBoxprices[$dimension] / 1000; +} else { + // 비표준 규격은 기본 단가 기준 면적비로 계산 + $basicbox_price = $shutterBoxprices['500*380']; // 스크린 기본 + // 또는 '650*550' (슬랫 기본) + $basicbox_pricePermeter = $basicbox_price / (500 * 380 / 1000); + $shutter_price = $basicbox_pricePermeter × $boxwidth × $boxheight / 1000; +} + +$케이스 = round($shutter_price × $total_length × 1000) × $수량; +``` +| 조건 | 계산 | +|------|------| +| steel='1' (절곡 체크) | 단가 × 길이(m) × 수량 | + +--- + +### 7. 케이스용 연기차단재 +```php +$boxSmokeBanPrices = BDmodels에서 '케이스용 연기차단재' 단가 조회; +$total_length = $item['col37'] / 1000; // mm → m 변환 +$케이스용_연기차단재 = round($boxSmokeBanPrices × $total_length) × $수량; +``` +| 조건 | 계산 | +|------|------| +| steel='1' AND 케이스규격 있음 | 단가 × 길이(m) × 수량 | + +--- + +### 8. 케이스 마구리 +```php +$maguriCol = $item['col45']; // 마구리 규격 +$maguriPrices = BDmodels에서 seconditem='마구리' AND spec=$maguriCol 조회; +$케이스_마구리 = round($maguriPrices × $수량); +``` +| 조건 | 계산 | +|------|------| +| steel='1' | 단가 × 수량 | + +--- + +### 9. 모터 받침용 앵글 +```php +// 스크린 +$price_angle = calculateAngle($item['col14'], $itemList, '스크린용'); + +// 슬랫 (브라켓 크기 기반) +if (empty($item['col21'])) { + $bracket_size = searchBracketSize($item['col13'], $item['col22']); +} else { + $bracket_size = $item['col21']; +} +$price_angle = calculateAngleBracket_slat($item['col15'], $itemList, $bracket_size); + +$모터받침용_앵글 = round($price_angle × $수량 × 4); // 4개 세트 +``` + +**브라켓 사이즈 결정 로직:** +```php +function searchBracketSize($motorWeight, $bracketInch) { + // 모터 용량 판별 + $motorCapacity = calculateMotorKG($weight, $inch); + + // 용량별 브라켓 사이즈 매핑 + if (in_array($motorCapacity, [300, 400])) return '530*320'; + if (in_array($motorCapacity, [500, 600])) return '600*350'; + if (in_array($motorCapacity, [800, 1000])) return '690*390'; + return '530*320'; // 기본값 +} +``` + +--- + +### 10. 가이드레일 +```php +// 레일 유형에 따른 가격 계산 +if (strpos($guideType, '혼합') !== false) { + // 혼합형: 벽면 + 측면 각각 다른 규격 + $wallPrice = $guidrailPrices[$wallKey]; + $sidePrice = $guidrailPrices[$sideKey]; + $guidrail_price = $wallPrice + $sidePrice; // 1개 세트 +} else { + // 단일형: 벽면 또는 측면 + $guidrail_price = $guidrailPrices[$guideKey] × 2; // 2개 세트 +} + +$total_length = $item['col23'] / 1000; // mm → m +$가이드레일 = round($guidrail_price × $total_length) × $수량; +``` + +**가이드레일 키 구성:** +``` +$key = $modelCode|$finishingType|$spec +예: KS-100|도장|65*80 +``` + +--- + +### 11. 레일용 연기차단재 +```php +$guiderailSmokeBanPrices = BDmodels에서 '가이드레일용 연기차단재' 조회; +$total_length = $item['col23'] / 1000; +$레일용_연기차단재 = round($guiderailSmokeBanPrices × $total_length) × 2 × $수량; +``` +| 조건 | 계산 | +|------|------| +| steel='1' AND 연기차단재 옵션 있음 | 단가 × 길이 × 2(양쪽) × 수량 | + +--- + +### 12. 하장바 +```php +$bottomBarPrices = BDmodels에서 + model_name=$modelCode AND + seconditem='하단마감재' AND + finishing_type=$finishingType 조회; + +$total_length = $item['col48'] / 1000 × $수량; // 스크린 +// 또는 $item['col49'] (슬랫) +$하장바 = round($bottomBarPrices × $total_length); +``` +| 조건 | 계산 | +|------|------| +| steel='1' AND 하장바 옵션 있음 | 단가 × 길이 × 수량 | + +--- + +### 13. L바 (스크린 전용) +```php +$LBarPrices = BDmodels에서 seconditem='L-BAR' 조회; +$total_length = $item['col51'] / 1000 × $수량; +$L바 = round($LBarPrices × $total_length); +``` +| 조건 | 계산 | +|------|------| +| steel='1' AND L바 옵션 있음 | 단가 × 길이 × 수량 | + +--- + +### 14. 보강평철 (스크린 전용) +```php +$bottomPlatePrices = BDmodels에서 seconditem='보강평철' 조회; +$total_length = $item['col54'] / 1000 × $수량; +$보강평철 = round($bottomPlatePrices × $total_length); +``` +| 조건 | 계산 | +|------|------| +| steel='1' AND 보강평철 옵션 있음 | 단가 × 길이 × 수량 | + +--- + +### 15. 감기샤프트 +```php +function calculateShaftPrice($item, $pdo) { + // 샤프트 규격별 가격 합산 + // 컬럼: col59~col65 (스크린), col61~col71 (슬랫) + + addShaftPrice($item['col59'], $itemList, '3', '300', $sum); // 3인치 300mm + addShaftPrice($item['col60'], $itemList, '4', '3000', $sum); // 4인치 3000mm + addShaftPrice($item['col61'], $itemList, '4', '4500', $sum); // 4인치 4500mm + // ... 기타 규격 + + return $sum_shaft_price; +} + +function addShaftPrice($column, $itemList, $size, $length, &$sum) { + $shaft_price = calculateShaft($column, $itemList, $size, $length); + if ($shaft_price > 0) { + $sum += $shaft_price; + } +} +``` + +**샤프트 규격표:** +| 인치 | 길이 | 스크린 컬럼 | 슬랫 컬럼 | +|------|------|------------|----------| +| 3" | 300mm | col59 | - | +| 4" | 3000mm | col60 | col61 | +| 4" | 4500mm | col61 | col62 | +| 4" | 6000mm | col62 | col63 | +| 5" | 6000mm | col63 | col64 | +| 5" | 7000mm | col64 | col65 | +| 5" | 8200mm | col65 | col66 | +| 6" | 3000mm | - | col67 | +| 6" | 6000mm | - | col68 | +| 6" | 7000mm | - | col69 | +| 6" | 8000mm | - | col70 | +| 8" | 8200mm | - | col71 | + +--- + +### 16. 무게평철 12T (스크린 전용) +```php +$baseWeightPlatePrice = 12000; // 고정 단가 +$무게평철 = $baseWeightPlatePrice × $item['col57']; +``` +| 조건 | 계산 | +|------|------| +| steel='1' | 12,000원 × 개수 | + +--- + +### 17. 환봉 (스크린 전용) +```php +$round_bar_price = 2000; // 고정 단가 +$round_bar_surang = $item['col70']; +$환봉 = round($round_bar_price × $round_bar_surang); +``` +| 조건 | 계산 | +|------|------| +| steel='1' | 2,000원 × 개수 | + +--- + +### 18. 각파이프 +```php +$pipe_price_3000 = calculatePipe($itemList, '1.4', '3000'); // 1.4T 3000mm +$pipe_price_6000 = calculatePipe($itemList, '1.4', '6000'); // 1.4T 6000mm + +$pipe_surang_3000 = $item['col68']; // 스크린 +$pipe_surang_6000 = $item['col69']; +// 또는 col74, col75 (슬랫) + +$각파이프_총액 = + ($pipe_price_3000 × $pipe_surang_3000) + + ($pipe_price_6000 × $pipe_surang_6000); +``` +| 조건 | 계산 | +|------|------| +| partscheck='1' | (3000mm 단가 × 수량) + (6000mm 단가 × 수량) | + +--- + +### 19. 앵글 +```php +$mainangle_price = calculateMainAngle(1, $itemList, '앵글3T', '2.5'); // 스크린 +// 또는 '앵글4T' (슬랫) +$mainangle_surang = $item['col71']; // 스크린 +// 또는 col77 (슬랫) +$앵글 = round($mainangle_price × $mainangle_surang); +``` +| 조건 | 계산 | +|------|------| +| partscheck='1' | 단가 × 수량 | + +--- + +## 📊 전체 금액 계산 + +```php +$totalRowAmount = 0; +foreach ($rowItemDetails as $key => $value) { + if (!in_array($key, ['TotalAmount', 'slatcheck', 'partscheck', 'steel', 'motor', 'warranty'])) { + $totalRowAmount += $value; + } +} +$rowItemDetails['TotalAmount'] = round($totalRowAmount); +``` + +**반환 데이터 구조:** +```php +return [ + 'total_amount' => $total_amount, // 전체 합계 + 'details' => $sums, // 행별 소계 배열 + 'itemDetails' => $itemDetails, // 항목별 상세 금액 + 'surangSum' => $surangSum // 총 수량 +]; +``` + +--- + +## 🏷️ 테스트 케이스 + +### 스크린 견적 예시 +| 입력 | 값 | +|------|-----| +| 폭(W) | 3,000mm | +| 높이(H) | 2,500mm | +| 수량 | 2 | +| 모터공급 | 경동(견적가포함) | +| 모터용량 | 300K | +| 케이스 | 500*380 | +| 검사비 | 50,000원 | + +| 항목 | 계산식 | 금액 | +|------|--------|------| +| 검사비 | 50,000 × 2 | 100,000 | +| 주자재 | 면적 × 단가 × 2 | (계산 필요) | +| 모터 | 300K 단가 × 2 | (단가표 참조) | +| ... | ... | ... | + +--- + +## ⚠️ 주의사항 + +1. **컬럼 번호 차이**: 스크린과 슬랫에서 같은 항목이 다른 컬럼 사용 +2. **단위 변환**: mm → m 변환 필수 (/ 1000) +3. **반올림 처리**: 대부분 `round()` 사용 +4. **조건부 계산**: 체크박스 옵션에 따라 계산 여부 결정 +5. **JSON 데이터**: 단가 테이블의 `itemList` 컬럼은 JSON 형식 + +--- + +## 📚 참조 + +- [fetch_unitprice.php](../../../../5130/estimate/fetch_unitprice.php) - 헬퍼 함수 +- [get_screen_amount.php](../../../../5130/estimate/get_screen_amount.php) - 스크린 계산 +- [get_slat_amount.php](../../../../5130/estimate/get_slat_amount.php) - 슬랫 계산 \ No newline at end of file diff --git a/projects/quotation/phase-1-5130-analysis/ui-analysis.md b/projects/quotation/phase-1-5130-analysis/ui-analysis.md new file mode 100644 index 0000000..3612ba8 --- /dev/null +++ b/projects/quotation/phase-1-5130-analysis/ui-analysis.md @@ -0,0 +1,382 @@ +# UI/화면 분석 + +> **분석 대상:** 5130 레거시 견적 시스템 화면 +> **분석 일자:** 2025-12-19 + +--- + +## 화면 목록 + +### 메인 화면 +| 파일 | 화면명 | 설명 | +|------|--------|------| +| `list.php` | 견적 목록 | 견적서 리스트, 검색, 필터링 | +| `write_form.php` | 견적 작성 | 견적서 작성/수정 폼 (103KB, 핵심 파일) | +| `viewEstimate.php` | 견적서 보기 | 견적서 조회/출력 | +| `statistics.php` | 견적 통계 | 통계 대시보드 | + +### 견적 유형별 화면 +| 파일 | 화면명 | 설명 | +|------|--------|------| +| `estimate.php` | 스크린 견적 | 스크린 견적서 메인 | +| `estimateSlat.php` | 슬랫 견적 | 슬랫(철재) 견적서 메인 | +| `estimateUnit.php` | 단가 견적 | 단가 기반 견적서 | +| `screen_view_original.php` | 스크린 상세 | 스크린 견적 상세 뷰 | +| `slat_view_original.php` | 슬랫 상세 | 슬랫 견적 상세 뷰 | + +### 상세/수정 화면 +| 파일 | 화면명 | 설명 | +|------|--------|------| +| `edit.php` | 견적 수정 | 스크린 견적 수정 | +| `edit_slat.php` | 슬랫 수정 | 슬랫 견적 수정 | +| `viewEstimateDetail.php` | 상세 보기 | 견적 상세 정보 | +| `EsDetail_screen.php` | 스크린 상세 | 스크린 항목 상세 | +| `EsDetail_slat.php` | 슬랫 상세 | 슬랫 항목 상세 | +| `compare.php` | 견적 비교 | 견적 버전 비교 | + +### 출력/다운로드 +| 파일 | 화면명 | 설명 | +|------|--------|------| +| `print_list.php` | 목록 인쇄 | 견적 목록 인쇄용 | +| `downloadExcel.php` | 엑셀 다운로드 | 견적서 엑셀 내보내기 | +| `saveExcel.php` | 엑셀 저장 | 엑셀 파일 저장 | + +--- + +## 1. 견적 목록 (list.php) + +### 화면 구조 +``` +┌─────────────────────────────────────────────────────────────┐ +│ 견적 List [새로고침] │ +├─────────────────────────────────────────────────────────────┤ +│ ▷ 123건 접수일 [2025-02-19] ~ [2025-12-19] │ +│ [전체] [스크린] [철재] 제품모델▼ 검색[______] [검색] │ +│ [신규] │ +├─────────────────────────────────────────────────────────────┤ +│ 번호│접수일 │견적번호 │구분 │제품│수량│금액 │발주처...│ +│─────│────────│────────────│──────│────│────│──────│─────────│ +│ 123 │25-12-19│KD-PR-251219│스크린│KSS01│ 5 │5,000K│(주)ABC │ +│ 122 │25-12-18│KD-PR-251218│철재 │KFS01│ 3 │3,200K│(주)DEF │ +│ ... │ │ │ │ │ │ │ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 필터/검색 조건 +| 항목 | 타입 | 설명 | +|------|------|------| +| `fromdate` | date | 시작일 (기본: -10개월) | +| `todate` | date | 종료일 (기본: +1개월) | +| `major_category` | radio | 전체/스크린/철재 | +| `model_name` | select | 제품 모델 선택 | +| `search` | text | 전체 컬럼 검색 | + +### 테이블 컬럼 +| 컬럼 | 폭 | 설명 | +|------|-----|------| +| 번호 | 30px | 일련번호 (역순) | +| 접수일 | 100px | indate | +| 견적번호 | 100px | pjnum | +| 구분 | 80px | major_category (스크린/철재) | +| 제품코드 | 80px | model_name | +| 수량 | 80px | estimateSurang | +| 금액 | 80px | estimateTotal | +| 발주처 | 150px | secondord | +| 담당자 | 80px | secondordman | +| 연락처 | 120px | secondordmantel | +| 현장명 | 200px | outworkplace | +| 작성자 | 80px | orderman | +| 비고 | 300px | comment | + +### 기능 버튼 +- **새로고침**: `location.reload()` +- **검색**: 필터 조건으로 목록 갱신 +- **신규**: `write_form.php` 이동 + +--- + +## 2. 견적 작성 폼 (write_form.php) + +### 화면 모드 +| 모드 | 설명 | +|------|------| +| 신규 (`mode=''`) | 새 견적 작성 | +| 수정 (`mode=modify`) | 기존 견적 수정 | +| 복사 (`mode=copy`) | 기존 견적 복사하여 신규 생성 | +| 발주 (`header=header`) | 수주에서 발주 산출 | +| 스크린 수정 (`itemoption=screen`) | 스크린 발주서 수정 | +| 슬랫 수정 (`itemoption=slat`) | 철재스라트 발주서 수정 | + +### 화면 구조 (추정) +``` +┌─────────────────────────────────────────────────────────────┐ +│ 견적 산출 │ +├─────────────────────────────────────────────────────────────┤ +│ ┌──────────── 기본 정보 ────────────┐ │ +│ │ 접수일: [2025-12-19] │ │ +│ │ 견적번호: KD-PR-251219-01 │ │ +│ │ 담당자: 홍길동 │ │ +│ │ 현장명: (주)ABC 빌딩 │ │ +│ └───────────────────────────────────┘ │ +│ │ +│ ┌──────────── 발주처 정보 ──────────┐ │ +│ │ 발주처: ____________ │ │ +│ │ 담당자: ____________ 연락처: ____│ │ +│ └───────────────────────────────────┘ │ +│ │ +│ ┌──────────── 제품 정보 ────────────┐ │ +│ │ 대분류: (●)스크린 (○)철재 │ │ +│ │ 모델명: [KSS01 ▼] │ │ +│ │ 제작폭: [160] 제작높이: [350] │ │ +│ │ 마구리윙: [50] │ │ +│ └───────────────────────────────────┘ │ +│ │ +│ ┌──────────── 옵션 체크 ────────────┐ │ +│ │ [✓] 절곡 [✓] 모터 [✓] 보증 │ │ +│ │ [✓] 슬랫 [✓] 부자재 │ │ +│ └───────────────────────────────────┘ │ +│ │ +│ ┌──────────── 견적 항목 ────────────┐ │ +│ │ 검사비: [50,000] │ │ +│ │ │ │ +│ │ [스크린 견적 테이블] │ │ +│ │ [슬랫 견적 테이블] │ │ +│ └───────────────────────────────────┘ │ +│ │ +│ ┌──────────── 금액 합계 ────────────┐ │ +│ │ 수량: 5 합계: 5,000,000 │ │ +│ │ 할인율: 10% 할인액: 500,000 │ │ +│ │ 최종금액: 4,500,000 │ │ +│ └───────────────────────────────────┘ │ +│ │ +│ [저장] [취소] [삭제] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 주요 입력 필드 + +#### 기본 정보 +| 필드 | 변수명 | 타입 | 기본값 | +|------|--------|------|--------| +| 접수일 | `indate` | date | 오늘 | +| 견적번호 | `pjnum` | text | 자동생성 | +| 담당자 | `orderman` | text | 로그인 사용자 | +| 현장명 | `outworkplace` | text | - | + +#### 발주처 정보 +| 필드 | 변수명 | 타입 | +|------|--------|------| +| 발주처 | `secondord` | text | +| 담당자 | `secondordman` | text | +| 연락처 | `secondordmantel` | text | + +#### 제품 정보 +| 필드 | 변수명 | 타입 | 기본값 | +|------|--------|------|--------| +| 대분류 | `major_category` | radio | 스크린 | +| 모델명 | `model_name` | select | - | +| 제작폭 | `makeWidth` | number | 160 (스크린), 110 (슬랫) | +| 제작높이 | `makeHeight` | number | 350 | +| 마구리윙 | `maguriWing` | number | 50 | + +#### 옵션 체크박스 +| 필드 | 변수명 | 영향 | +|------|--------|------| +| 절곡 | `steel` | 케이스, 레일, 연기차단재, 하장바, L바, 보강평철 | +| 모터 | `motor` | 모터 가격 포함 | +| 보증 | `warranty` | 보증기간 표시 | +| 슬랫 | `slatcheck` | 주자재(슬랫), 조인트바 | +| 부자재 | `partscheck` | 샤프트, 각파이프, 앵글 | + +#### 금액 정보 +| 필드 | 변수명 | 설명 | +|------|--------|------| +| 검사비 | `inspectionFee` | 인정검사비 (기본: 50,000) | +| 수량 | `estimateSurang` | 총 수량 | +| 합계 | `estimateTotal` | 견적 총액 | +| 최초합계 | `EstimateFirstSum` | 최초 견적 합계 | +| 수정합계 | `EstimateUpdatetSum` | 수정 견적 합계 | +| 차액 | `EstimateDiffer` | 최초-수정 차액 | +| 할인율 | `EstimateDiscountRate` | % | +| 할인액 | `EstimateDiscount` | 원 | +| 최종금액 | `EstimateFinalSum` | 최종 결정 금액 | + +### CSS 클래스 +```css +/* 수동 편집된 셀 강조 */ +.manually-edited { + background-color: #f8d7da !important; +} + +/* readonly 체크박스 */ +.readonly-checkbox, +.readonly-radio { + pointer-events: none; + opacity: 1; +} + +/* 커스텀 너비 */ +.w-40, .w-50, .w-60, .w-85 { ... } +``` + +--- + +## 3. 견적 항목 테이블 + +### 스크린 견적 테이블 (estimateList) +| 열 | 필드명 | 설명 | +|----|--------|------| +| 항목명 | `item_name` | 품목 이름 | +| 규격 | `specification` | 규격/사양 | +| 단위 | `unit` | EA, m², m, kg | +| 수량 | `quantity` | 수량 | +| 단가 | `unit_price` | 단가 | +| 금액 | `amount` | quantity × unit_price | +| 비고 | `remark` | 메모 | + +### 슬랫 견적 테이블 (estimateSlatList) +스크린과 동일 구조 + +### 자동계산 테이블 (estimateList_auto, estimateSlatList_auto) +| 열 | 필드명 | 설명 | +|----|--------|------| +| 항목코드 | `item_code` | 자동계산 항목 코드 | +| 항목명 | `item_name` | 품목 이름 | +| 계산타입 | `calc_type` | per_meter, per_area 등 | +| 기준값 | `base_value` | 계산 기준 수치 | +| 단가 | `unit_price` | 단가 | +| 금액 | `amount` | 계산 결과 | + +--- + +## 4. API 엔드포인트 (AJAX 호출) + +### 데이터 조회 +| 엔드포인트 | 용도 | +|------------|------| +| `fetch_unitprice.php` | 단가 조회 | +| `fetch_date.php` | 날짜 정보 | +| `fetch_receiver.php` | 수신자 정보 | +| `fetch_outworkplace.php` | 현장 목록 | +| `fetch_length_data.php` | 길이 데이터 | +| `fetch_price.php` | 가격 정보 | + +### 금액 계산 +| 엔드포인트 | 용도 | +|------------|------| +| `get_estimate_amount.php` | 견적 금액 라우터 | +| `get_screen_amount.php` | 스크린 금액 계산 | +| `get_slat_amount.php` | 슬랫 금액 계산 | +| `recalc_row.php` | 행 재계산 | + +### 데이터 저장 +| 엔드포인트 | 용도 | +|------------|------| +| `insert.php` | 견적 저장 (신규) | +| `update.php` | 견적 수정 | +| `delete.php` | 견적 삭제 | +| `insert_estimate.php` | 견적 등록 | +| `insert_detail.php` | 상세 저장 | + +### 기타 +| 엔드포인트 | 용도 | +|------------|------| +| `generate_serial_pjnum.php` | 견적번호 생성 | +| `get_initial_pjnum.php` | 초기 견적번호 | +| `insert_logmenu.php` | 로그 기록 | + +--- + +## 5. JavaScript 처리 + +### 공통 스크립트 (common/) +| 파일 | 용도 | +|------|------| +| `calculation.js` | 행 계산 로직 | +| `lastJS.php` | 페이지 공통 JS | +| `common_screen.php` | 스크린 공통 | +| `common_slat.php` | 슬랫 공통 | + +### 주요 이벤트 처리 +```javascript +// 수량/단가 변경 시 금액 재계산 +function calculateRowTotal(row) { ... } + +// 옵션 체크박스 변경 시 항목 재계산 +$('input[name="steel"]').change(function() { ... }); + +// 모델 변경 시 단가 조회 +$('#model_name').change(function() { + // AJAX: fetch_unitprice.php +}); + +// 크기 변경 시 전체 재계산 +$('#makeWidth, #makeHeight').change(function() { + // AJAX: get_estimate_amount.php +}); +``` + +--- + +## 6. 사용자 권한 + +### 접근 레벨 +```php +// 레벨 5 이하만 접근 가능 +if(!isset($_SESSION["level"]) || $_SESSION["level"]>5) { + header("Location:" . $WebSite . "login/login_form.php"); + exit; +} +``` + +### 작성 권한자 +```php +$authorities = [ + "개발자", "전진", "노완호", "이세희", + "함신옥", "손금주", "이은진", "이경호" +]; +``` + +--- + +## SAM 이관 시 UI 고려사항 + +### 1. Livewire + Blade 전환 +| 5130 | SAM | +|------|-----| +| jQuery AJAX | Livewire wire:click | +| PHP 직접 렌더링 | Blade 컴포넌트 | +| 전역 변수 | Livewire 프로퍼티 | +| form submit | wire:submit | + +### 2. 컴포넌트 분리 +``` +resources/views/livewire/quotation/ +├── quote-list.blade.php # 목록 +├── quote-form.blade.php # 작성/수정 +├── quote-detail.blade.php # 상세 +├── components/ +│ ├── quote-table.blade.php # 견적 테이블 +│ ├── option-checkboxes.blade.php # 옵션 체크박스 +│ └── amount-summary.blade.php # 금액 요약 +``` + +### 3. 반응형 개선 +- 현재: 고정 너비 테이블 +- 개선: Tailwind 반응형 그리드 + +### 4. UX 개선점 +- 실시간 금액 계산 (debounce 적용) +- 자동저장 (draft 기능) +- 견적 버전 비교 UI +- 모바일 최적화 + +--- + +## 참조 파일 + +- `list.php:111-192` - 목록 테이블 구조 +- `write_form.php:1-300` - 폼 초기화 로직 +- `common/calculation.js` - 행 계산 로직 +- `_row.php` - 행 렌더링 공통 +- `_request.php` - 요청 파라미터 처리 \ No newline at end of file diff --git a/projects/quotation/screenshots/01-formula-list-main.png b/projects/quotation/screenshots/01-formula-list-main.png new file mode 100644 index 0000000..adcbd48 Binary files /dev/null and b/projects/quotation/screenshots/01-formula-list-main.png differ diff --git a/projects/quotation/screenshots/02-product-dropdown.png b/projects/quotation/screenshots/02-product-dropdown.png new file mode 100644 index 0000000..0c3353b Binary files /dev/null and b/projects/quotation/screenshots/02-product-dropdown.png differ diff --git a/projects/quotation/screenshots/03-category-management.png b/projects/quotation/screenshots/03-category-management.png new file mode 100644 index 0000000..18b9850 Binary files /dev/null and b/projects/quotation/screenshots/03-category-management.png differ diff --git a/projects/quotation/screenshots/04-price-formula-management.png b/projects/quotation/screenshots/04-price-formula-management.png new file mode 100644 index 0000000..8545b63 Binary files /dev/null and b/projects/quotation/screenshots/04-price-formula-management.png differ diff --git a/projects/quotation/screenshots/05-auto-quotation-input.png b/projects/quotation/screenshots/05-auto-quotation-input.png new file mode 100644 index 0000000..c6114d1 Binary files /dev/null and b/projects/quotation/screenshots/05-auto-quotation-input.png differ diff --git a/projects/quotation/screenshots/06-category-guiderail.png b/projects/quotation/screenshots/06-category-guiderail.png new file mode 100644 index 0000000..d8233cb Binary files /dev/null and b/projects/quotation/screenshots/06-category-guiderail.png differ diff --git a/projects/quotation/screenshots/07-formula-add-modal.png b/projects/quotation/screenshots/07-formula-add-modal.png new file mode 100644 index 0000000..ec8e7a6 Binary files /dev/null and b/projects/quotation/screenshots/07-formula-add-modal.png differ