- customer-pricing.md: 최저가 정책, 개발비-구독료 연동 비율 섹션 추가 - partner-commission.md: 최저가와 수당 관계, 시뮬레이터 안내 섹션 추가 - products.md: min_development_fee/min_subscription_fee 필드, 시뮬레이터 연동 섹션 추가
253 lines
10 KiB
Markdown
253 lines
10 KiB
Markdown
# 상품관리
|
||
|
||
## 개요
|
||
|
||
상품관리는 영업에 사용되는 상품(프로그램)을 카테고리별로 등록하고 가격/수당률을 설정하는 기능입니다.
|
||
카테고리 관리, 상품 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
|
||
├── 2026_03_14_100000_add_min_fees_to_sales_product_categories_table.php
|
||
└── 2026_03_14_100001_add_min_fees_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) |
|
||
| `min_development_fee` | decimal(15,2) | 최저 개발비 (카테고리 레벨, 기본 0) |
|
||
| `min_subscription_fee` | decimal(15,2) | 최저 구독료 (카테고리 레벨, 기본 0) |
|
||
| `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) | 월 구독료 |
|
||
| `min_development_fee` | decimal(15,2) | 최저 개발비 (이 금액 이하 설정 불가, 기본 0) |
|
||
| `min_subscription_fee` | decimal(15,2) | 최저 구독료 (이 금액 이하 설정 불가, 기본 0) |
|
||
| `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) ──
|
||
│ 코드, 상품명, 설명
|
||
│ 개발비(원가), 개발비(적용가), 구독료
|
||
│ ┌─ 최저가 설정 (빨간 박스) ────────┐
|
||
│ │ 최저 개발비, 최저 구독료 │
|
||
│ │ "절대 이 금액 이하로 내릴 수 없음"│
|
||
│ └──────────────────────────────────┘
|
||
│ 파트너 수당율, 매니저 수당율
|
||
│ 재량권 허용, 필수 선택
|
||
│
|
||
└─ 카테고리 관리 모달 ──────────────
|
||
코드, 카테고리명, 설명, 기본 용량
|
||
```
|
||
|
||
## 최저가 정책
|
||
|
||
상품별로 **최저 개발비**와 **최저 구독료**를 설정할 수 있다. 설정된 최저가 이하로는 절대 가격을 내릴 수 없다.
|
||
|
||
### 적용 범위
|
||
|
||
| 영역 | 최저 개발비 | 최저 구독료 |
|
||
|------|:----------:|:----------:|
|
||
| 상품 등록/수정 (MNG) | ✅ 서버 검증 | ✅ 서버 검증 |
|
||
| 가격 시뮬레이터 슬라이더 | ✅ 슬라이더 min 제한 | ✅ 연동 시 min 제한 |
|
||
| 프로모션 개발비 할인 | ✅ 할인 max 제한 | — |
|
||
| 프로모션 구독료 할인 | — | ✅ 할인 max 제한 |
|
||
| 개발비 전액 면제 | ✅ 최저가 설정 시 비활성화 | — |
|
||
|
||
### 검증 로직
|
||
|
||
- **컨트롤러**: `store()`, `update()` 시 `registration_fee >= min_development_fee`, `subscription_fee >= min_subscription_fee` 검증
|
||
- **시뮬레이터**: `setAdjustedFee()`에서 `Math.max(minDevFee, value)` 적용
|
||
- **프로모션**: `promoDevDiscountMax()`, `promoSubDiscountMaxPercent()`로 슬라이더 max 제한
|
||
|
||
---
|
||
|
||
## 가격 시뮬레이터 연동
|
||
|
||
**라우트**: `GET /sales/price-simulator`
|
||
|
||
가격 시뮬레이터는 상품관리의 데이터를 기반으로 실시간 비용/수당 시뮬레이션을 제공한다.
|
||
|
||
### 개발비-구독료 연동 조절
|
||
|
||
토글 스위치로 활성화하는 기능이다. 개발비 슬라이더를 조정하면 구독료가 원래 비율을 유지하며 자동 연동된다.
|
||
|
||
**비율 계산**:
|
||
|
||
```
|
||
ratio = 원래 구독료 / 원래 개발비(할인가)
|
||
연동 구독료 = 조정된 개발비 × ratio (만원 단위 반올림)
|
||
```
|
||
|
||
**예시** (개발비 2,000만원, 구독료 50만원인 상품):
|
||
|
||
| 조정된 개발비 | 비율 | 연동 구독료 |
|
||
|-------------:|:----:|----------:|
|
||
| 2,000만원 | 2.5% | 50만원 |
|
||
| 1,500만원 | 2.5% | 38만원 |
|
||
| 1,000만원 | 2.5% | 25만원 |
|
||
| 500만원 | 2.5% | 13만원 |
|
||
|
||
**제한사항**:
|
||
|
||
- 최저 구독료 이하로는 내려가지 않음
|
||
- 연동 OFF 시 구독료가 원래값으로 즉시 복원
|
||
- 프로모션 할인과 독립적으로 동작 (연동 → 프로모션 순서로 적용)
|
||
|
||
### 프로모션 할인과 최저가
|
||
|
||
프로모션 영역의 모든 할인 슬라이더는 최저가를 초과하지 않도록 max가 자동 제한된다.
|
||
|
||
| 프로모션 항목 | max 산정 기준 |
|
||
|-------------|-------------|
|
||
| 개발비 할인 (%) | `(적용가 합계 - 최저 개발비 합계) / 적용가 합계 × 100` |
|
||
| 개발비 할인 (원) | `적용가 합계 - 최저 개발비 합계` |
|
||
| 개발비 전액 면제 | 최저 개발비 > 0이면 체크박스 비활성화 |
|
||
| 구독료 할인 (%) | `(1 - 최저 구독료 합계 / 구독료 합계) × 100` |
|
||
|
||
---
|
||
|
||
## HTMX 호환성
|
||
|
||
- Alpine.js 스크립트가 `@push('scripts')`에 있어 **HX-Redirect 필요**
|
||
- 카테고리 탭 전환은 HTMX `hx-get`으로 부분 로드
|