# 상품관리 ## 개요 상품관리는 영업에 사용되는 상품(프로그램)을 카테고리별로 등록하고 가격/수당률을 설정하는 기능입니다. 카테고리 관리, 상품 CRUD, 순서 조정, 가격/수당률 설정, 영업 시나리오 연동을 지원합니다. - **라우트**: `GET /sales/products` - **미들웨어**: `auth`, `hq.member` (본사 전용) - **UI 기술**: Blade + Alpine.js + HTMX + Tailwind CSS ## 파일 구조 ``` mng/ ├── app/Http/Controllers/Sales/ │ └── SalesProductController.php # 메인 컨트롤러 (12개 메서드) ├── app/Models/Sales/ │ ├── SalesProduct.php # 상품 모델 │ └── SalesProductCategory.php # 카테고리 모델 └── resources/views/sales/products/ ├── index.blade.php # 메인 페이지 └── partials/ └── product-list.blade.php # 상품 목록 파셜 (HTMX) api/ └── database/migrations/ ├── 2026_01_29_150000_create_sales_products_tables.php ├── 2026_01_29_161626_add_partner_manager_commission_to_sales_products_table.php └── 2026_01_29_162847_add_registration_fee_to_sales_products_table.php ``` ## 라우트 ```php // routes/web.php (sales/products prefix 그룹 내) // 상품 관리 GET /products → index() 메인 페이지 GET /products/list → productList() 카테고리별 상품 목록 (HTMX) POST /products → store() 상품 등록 PUT /products/{id} → update() 상품 수정 DELETE /products/{id} → destroy() 상품 삭제 POST /products/{id}/toggle → toggleActive() 활성화 토글 POST /products/reorder → reorder() 순서 변경 // 카테고리 관리 GET /products/categories → categories() 카테고리 목록 POST /products/categories → storeCategory() 카테고리 생성 PUT /products/categories/{id} → updateCategory() 카테고리 수정 DELETE /products/categories/{id} → deleteCategory() 카테고리 삭제 // API (영업 시나리오용) GET /products/api/list → getProductsApi() 활성 상품 목록 ``` ## 컨트롤러 ### SalesProductController | 메서드 | HTTP | 설명 | |--------|------|------| | `index()` | GET | 메인 페이지 (HX-Redirect 처리) | | `productList()` | GET | 카테고리별 상품 목록 (HTMX 파셜) | | `store()` | POST | 상품 등록 (코드 중복 체크, 자동 순서) | | `update()` | PUT | 상품 수정 (부분 업데이트 허용) | | `destroy()` | DELETE | 상품 삭제 (Soft Delete) | | `toggleActive()` | POST | 활성화/비활성화 토글 | | `reorder()` | POST | 다중 상품 순서 변경 (배치) | | `categories()` | GET | 활성 카테고리 목록 | | `storeCategory()` | POST | 카테고리 생성 (코드 unique) | | `updateCategory()` | PUT | 카테고리 수정 | | `deleteCategory()` | DELETE | 카테고리 삭제 (하위 상품 있으면 실패) | | `getProductsApi()` | GET | API: 활성 카테고리+상품 (영업 시나리오용) | ## 모델 ### SalesProductCategory **테이블**: `sales_product_categories` | 필드 | 타입 | 설명 | |------|------|------| | `code` | string(50) | 카테고리 코드 (unique) | | `name` | string(100) | 카테고리명 | | `description` | text | 설명 | | `base_storage` | string(20) | 기본 제공 용량 (기본: 100GB) | | `display_order` | int | 표시 순서 | | `is_active` | boolean | 활성화 | - SoftDeletes 적용 - 관계: `products()`, `activeProducts()` - Scope: `active()`, `ordered()` ### SalesProduct **테이블**: `sales_products` | 필드 | 타입 | 설명 | |------|------|------| | `category_id` | bigint (FK) | 카테고리 ID | | `code` | string(50) | 상품 코드 (카테고리별 unique) | | `name` | string(100) | 상품명 | | `description` | text | 설명 (프로그램 타입) | | `development_fee` | decimal(15,2) | 개발비 | | `registration_fee` | decimal(15,2) | 가입비 (할인가) | | `subscription_fee` | decimal(15,2) | 월 구독료 | | `partner_commission_rate` | decimal(5,2) | 영업파트너 수당율 (기본 20%) | | `manager_commission_rate` | decimal(5,2) | 매니저 수당율 (기본 5%) | | `allow_flexible_pricing` | boolean | 재량권 허용 여부 | | `is_required` | boolean | 필수 선택 여부 | | `display_order` | int | 표시 순서 | | `is_active` | boolean | 활성화 | - SoftDeletes 적용 - Unique Key: `(category_id, code)` - Scope: `active()`, `ordered()` - Accessor: `total_commission_rate`, `commission`, `formatted_*_fee` ### SalesContractProduct (계약별 선택 상품) **테이블**: `sales_contract_products` | 필드 | 타입 | 설명 | |------|------|------| | `tenant_id` | bigint (FK, nullable) | 테넌트 ID | | `management_id` | bigint (FK) | 영업관리 ID | | `category_id` | bigint (FK) | 선택 카테고리 | | `product_id` | bigint (FK) | 선택 상품 | | `development_fee` | decimal(15,2) | 적용 개발비 | | `registration_fee` | decimal(15,2) | 적용 가입비 | | `subscription_fee` | decimal(15,2) | 적용 구독료 | | `discount_rate` | decimal(5,2) | 할인율 (기본 0%) | | `notes` | text | 비고 | | `created_by` | bigint (FK) | 등록자 | ## 뷰 구성 ### index.blade.php ``` ┌─ 페이지 헤더 ────────────────────── │ 제목: "상품관리" │ [카테고리 관리] 버튼 │ ├─ 카테고리 탭 ────────────────────── │ [카테고리A] [카테고리B] [카테고리C] ... │ └─ 선택 시 HTMX로 상품 목록 갱신 │ ├─ 상품 영역 ──────────────────────── │ 헤더: 카테고리명 + 기본 용량 + [상품 추가] │ │ ┌─ 상품 카드 (그리드: 1/2/3열 반응형) ─┐ │ │ 상품명 + 필수/비활성 배지 + 코드 │ │ │ 프로그램 설명 │ │ │ 개발비(취소선) + 가입비(할인가) │ │ │ 월 구독료 │ │ │ 수당: 파트너 20%, 매니저 5% │ │ │ 재량권 허용/고정가 태그 + [삭제] │ │ └───────────────────────────────────────┘ │ ├─ 상품 등록/수정 모달 (Alpine.js) ── │ 코드, 상품명, 설명 │ 개발비, 가입비, 구독료 │ 파트너 수당율, 매니저 수당율 │ 재량권 허용, 필수 선택 │ └─ 카테고리 관리 모달 ────────────── 코드, 카테고리명, 설명, 기본 용량 ``` ## HTMX 호환성 - Alpine.js 스크립트가 `@push('scripts')`에 있어 **HX-Redirect 필요** - 카테고리 탭 전환은 HTMX `hx-get`으로 부분 로드