docs:영업/매출관리 개발문서 추가 (7개 메뉴)

- 영업관리 대시보드: 수당 현황, 테넌트 진행률, 파트너 활동
- 파트너관리: 영업파트너 CRUD, 역할 관리, 서류 관리
- 영업파트너승인: 신규 파트너 신청 승인/반려 워크플로우
- 상품관리: 카테고리별 상품, 가격/수당률 설정
- 고객관리(관리자): 전사 고객 현황, 본사 진행상태 8단계
- 영업파트너 고객관리: 명함등록 기반 영업권, 테넌트 전환
- 인터뷰 시나리오: 질문 템플릿, 세션 기반 인터뷰 기록

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-02-11 17:04:06 +09:00
parent 157e8a95d5
commit c5b1eb050e
8 changed files with 1318 additions and 0 deletions

128
features/sales/README.md Normal file
View File

@@ -0,0 +1,128 @@
# 영업/매출관리
## 개요
영업/매출관리 모듈은 영업파트너 관리, 고객 영업권, 상품 관리, 인터뷰 시나리오 등
영업 전체 프로세스를 관리하는 기능 그룹입니다.
- **라우트 Prefix**: `/sales`
- **미들웨어**: `auth`, `hq.member` (본사 회원 전용)
- **UI 기술**: Blade + HTMX + Alpine.js (일부 React)
## 메뉴 구성
| # | 메뉴명 | 경로 | 문서 | 상태 |
|---|--------|------|------|------|
| 1 | 영업관리 대시보드 | `/sales/salesmanagement/dashboard` | [sales-dashboard.md](sales-dashboard.md) | 구현완료 |
| 2 | 파트너관리 | `/sales/managers` | [partners.md](partners.md) | 구현완료 |
| 3 | 영업파트너승인 | `/sales/managers/approvals` | [partner-approvals.md](partner-approvals.md) | 구현완료 |
| 4 | 상품관리 | `/sales/products` | [products.md](products.md) | 구현완료 |
| 5 | 세일즈사이트 | (외부 링크) | - | 미구현 |
| 6 | 랜딩페이지 | (외부 링크) | - | 미구현 |
| 7 | 고객 관리 | `/sales/admin-prospects` | [admin-prospects.md](admin-prospects.md) | 구현완료 |
| 8 | 영업파트너 고객관리 | `/sales/prospects` | [prospects.md](prospects.md) | 구현완료 |
| 9 | 인터뷰 시나리오 | `/sales/interviews` | [interviews.md](interviews.md) | 구현완료 |
## 아키텍처
```
영업/매출관리
├── 대시보드 ──── 수당 현황, 테넌트 진행률, 파트너 활동
├── 파트너관리
│ ├── 파트너 CRUD ──── 영업파트너 등록/수정/역할 관리
│ └── 파트너 승인 ──── 신규 파트너 신청 승인/반려
├── 상품관리 ──── 카테고리별 상품, 가격/수당률 설정
├── 고객관리
│ ├── 고객 관리(관리자) ──── 전사 고객 현황, 본사 진행상태
│ └── 파트너 고객관리 ──── 명함등록 기반 영업권 관리
└── 인터뷰 시나리오 ──── 질문 템플릿, 인터뷰 세션 기록
```
## 파일 구조
```
mng/
├── app/Http/Controllers/Sales/
│ ├── SalesDashboardController.php # 영업관리 대시보드
│ ├── SalesManagerController.php # 파트너관리 + 승인
│ ├── SalesProductController.php # 상품관리
│ ├── TenantProspectController.php # 영업권(명함등록) 관리
│ ├── AdminProspectController.php # 관리자 전체 고객 관리
│ └── InterviewScenarioController.php # 인터뷰 시나리오
├── app/Models/Sales/
│ ├── SalesPartner.php # 영업파트너
│ ├── SalesTenantManagement.php # 테넌트 영업 관리
│ ├── TenantProspect.php # 가망고객 (영업권)
│ ├── SalesCommission.php # 영업 수수료
│ ├── SalesProduct.php # 영업 상품
│ ├── SalesProductCategory.php # 상품 카테고리
│ └── SalesManagerDocument.php # 파트너 서류
├── app/Models/Interview/
│ ├── InterviewCategory.php # 인터뷰 카테고리
│ ├── InterviewTemplate.php # 인터뷰 템플릿
│ ├── InterviewQuestion.php # 인터뷰 질문
│ ├── InterviewSession.php # 인터뷰 세션
│ └── InterviewAnswer.php # 인터뷰 답변
├── app/Services/Sales/
│ ├── SalesManagerService.php # 파트너 관리 서비스
│ ├── TenantProspectService.php # 영업권 서비스
│ └── InterviewScenarioService.php # 인터뷰 서비스
└── resources/views/sales/
├── dashboard/ # 대시보드 뷰
├── managers/ # 파트너관리 뷰
├── products/ # 상품관리 뷰
├── prospects/ # 영업권 관리 뷰
├── admin-prospects/ # 관리자 고객관리 뷰
└── interviews/ # 인터뷰 시나리오 뷰
api/
└── database/migrations/
├── 2026_01_27_221000_create_tenant_prospects_table.php
├── 2026_01_28_090000_add_attachments_to_tenant_prospects_table.php
├── 2026_01_29_100000_create_sales_partners_table.php
├── 2026_01_29_100100_create_sales_tenant_managements_table.php
├── 2026_01_29_150000_create_sales_products_tables.php
└── 2026_02_06_10000x_create_interview_*_tables.php (5개)
```
## 핵심 데이터 모델 관계
```
User (영업파트너)
├── SalesPartner (파트너 정보, 수수료율, 계좌)
├── TenantProspect (영업권 = 명함등록)
│ ├── status: active → expired / converted / completed
│ ├── 유효기간: 2개월
│ └── 쿨다운: 만료 후 1개월
│ │
│ ↓ convert()
│ Tenant (계약 고객사)
│ │
│ ↓
│ SalesTenantManagement (영업 관리)
│ ├── status: prospect → approach → negotiation → contracted → onboarding → active
│ ├── hq_status: pending → review → planning → coding → dev_test → dev_done → int_test → handover
│ └── SalesCommission (수당 정보)
└── SalesManagerDocument (등록 서류)
SalesProductCategory
└── SalesProduct (개발비, 가입비, 구독료, 수당율)
└── SalesContractProduct (계약별 선택 상품)
InterviewCategory
└── InterviewTemplate
└── InterviewQuestion
└── InterviewSession → InterviewAnswer
```
## 권한 체계
| 역할 | 대시보드 | 파트너관리 | 승인 | 상품 | 고객관리 | 파트너고객 | 인터뷰 |
|------|---------|-----------|------|------|---------|-----------|--------|
| 영업파트너 | O (본인) | - | - | - | - | O (본인) | O |
| 상담매니저 | O (본인) | - | - | - | - | - | O |
| 관리자 | O (전체) | O | O | O | O | O | O |
| 슈퍼관리자 | O (전체) | O | O | O | O (삭제) | O | O |

View File

@@ -0,0 +1,155 @@
# 고객 관리 (관리자)
## 개요
고객 관리(관리자)는 본사 관리자가 모든 영업파트너의 고객을 통합 관리하는 기능입니다.
전사 고객 현황 파악, 본사 진행상태 관리, 수당 지급 기록, 상태 변경을 지원합니다.
- **라우트**: `GET /sales/admin-prospects`
- **미들웨어**: `auth`, `hq.member` + 관리자 권한 체크
- **UI 기술**: Blade + HTMX + Tailwind CSS
## 파일 구조
```
mng/
├── app/Http/Controllers/Sales/
│ └── AdminProspectController.php # 메인 컨트롤러 (8개 메서드)
└── resources/views/sales/admin-prospects/
├── index.blade.php # 전체 고객 목록 (460줄)
└── partials/
├── content.blade.php # 콘텐츠 새로고침
└── show-modal.blade.php # 고객 상세 모달
```
## 라우트
```php
// routes/web.php (sales prefix 그룹 내)
GET /admin-prospects index() 전체 고객 목록
GET /admin-prospects/refresh refresh() HTMX 새로고침
GET /admin-prospects/{id}/modal-show modalShow() 상세 모달
POST /admin-prospects/{id}/hq-status updateHqStatus() 본사 진행상태 변경
POST /admin-prospects/{id}/commission-date updateCommissionDate() 수당 날짜 기록
DELETE /admin-prospects/{id}/commission-date clearCommissionDate() 수당 날짜 초기화
DELETE /admin-prospects/{id} destroy() 삭제 (슈퍼관리자)
POST /admin-prospects/{id}/toggle-status toggleStatus() 상태 토글
```
## 컨트롤러
### AdminProspectController
| 메서드 | HTTP | 설명 | 권한 |
|--------|------|------|------|
| `index()` | GET | 전체 고객 목록 (필터+통계) | 관리자/슈퍼관리자 |
| `refresh()` | GET | 콘텐츠 새로고침 (HTMX) | 관리자/슈퍼관리자 |
| `modalShow()` | GET | 고객 상세 모달 (진행률 포함) | 관리자/슈퍼관리자 |
| `updateHqStatus()` | POST | 본사 진행상태 변경 | 관리자/슈퍼관리자 |
| `updateCommissionDate()` | POST | 수당 지급 날짜 기록/수정 | 관리자/슈퍼관리자 |
| `clearCommissionDate()` | DELETE | 수당 날짜 초기화 | 관리자/슈퍼관리자 |
| `toggleStatus()` | POST | 상태 토글 (영업중 ↔ 완료) | 관리자/슈퍼관리자 |
| `destroy()` | DELETE | 삭제 | 슈퍼관리자 전용 |
### 본사 진행상태 (HQ Status) 8단계
```
pending (대기)
→ review (검토)
→ planning (기획안작성)
→ coding (개발코드작성)
→ dev_test (개발테스트)
→ dev_done (개발완료)
→ int_test (통합테스트)
→ handover (인계)
```
### 수당 날짜 관리
```
updateCommissionDate():
- 납입일(membership_paid_at) 입력 시
- 자동 지급일 계산: 익월 10일
- commission_paid_at 기록
```
## 모델
### SalesTenantManagement (핵심 모델)
**테이블**: `sales_tenant_managements`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint (FK, unique) | 테넌트 ID (1:1) |
| `tenant_prospect_id` | bigint (FK) | 가망고객 ID |
| `sales_partner_id` | bigint (FK) | 영업 담당자 ID |
| `manager_user_id` | bigint (FK) | 상담매니저 사용자 ID |
| `sales_scenario_step` | int | 영업 시나리오 단계 (1-6) |
| `manager_scenario_step` | int | 매니저 시나리오 단계 (1-6) |
| `status` | enum | 영업 상태 |
| `hq_status` | enum | 본사 진행상태 |
| `first_contact_at` | timestamp | 최초 접촉일 |
| `contracted_at` | timestamp | 계약 체결일 |
| `onboarding_completed_at` | timestamp | 온보딩 완료일 |
| `membership_fee` | decimal | 가입비 |
| `membership_paid_at` | timestamp | 가입비 입금일 |
| `membership_status` | enum | pending / partial / paid / refunded |
| `sales_commission` | decimal | 영업 수당 |
| `manager_commission` | decimal | 관리 수당 |
| `commission_paid_at` | timestamp | 수당 지급일 |
| `sales_progress` | tinyint | 영업 진행률 (%) |
| `manager_progress` | tinyint | 매니저 진행률 (%) |
| `incentive_status` | enum | pending / eligible / paid |
| `notes` | text | 메모 |
#### 영업 상태 흐름
```
prospect (잠재) → approach (접근) → negotiation (협상)
→ contracted (계약) → onboarding (온보딩) → active (활성) / churned (이탈)
```
#### 수당 상태
```
pending (대기) → eligible (지급대상) → paid (지급완료)
```
## 뷰 구성
### index.blade.php
```
┌─ 페이지 헤더 ──────────────────────
│ 제목: "고객 관리"
│ [새로고침] 버튼
├─ 통계 카드 ────────────────────────
│ 전체 | 활성 | 완료 | 인계완료
├─ 필터 영역 ────────────────────────
│ 검색 (회사명, 사업자번호, 대표자, 연락처)
│ 상태: active / expired / converted / progress_complete
│ 인계: hq_status = 'handover'
│ 영업파트너: registered_by 필터
├─ 고객 목록 테이블 ────────────────
│ 회사명 | 사업자번호 | 대표자 | 영업파트너
│ 영업진행률 | 매니저진행률 | 본사상태 | 작업
│ └─ 본사상태: 8단계 프로그레스바
│ └─ 작업: 상세, 상태변경, 삭제
└─ 상세 모달 ───────────────────────
기본 정보 (회사명, 사업자번호, 대표자, 연락처)
영업 진행률 (영업 시나리오, 매니저 시나리오)
본사 진행상태 (8단계 프로그레스바 + 변경 드롭다운)
수당 정보 (납입일, 지급일, 자동 계산)
담당 매니저 정보
```
## HTMX 호환성
- Blade + HTMX 기반으로 **HX-Redirect 불필요**
- `hx-get`으로 부분 새로고침
- 모달로 상세 조회 및 상태 변경

View File

@@ -0,0 +1,281 @@
# 인터뷰 시나리오
## 개요
인터뷰 시나리오는 영업 상담 시 사용할 질문 템플릿을 관리하고,
인터뷰 세션을 진행/기록하는 기능입니다.
카테고리별 질문 구조, 체크리스트/텍스트 답변, MD 일괄 가져오기를 지원합니다.
- **라우트**: `GET /sales/interviews`
- **미들웨어**: `auth`, `hq.member`
- **UI 기술**: Blade + React/JavaScript (API 기반, 1,076줄)
## 파일 구조
```
mng/
├── app/Http/Controllers/Sales/
│ └── InterviewScenarioController.php # 메인 컨트롤러 (16개 메서드)
├── app/Services/Sales/
│ └── InterviewScenarioService.php # 비즈니스 로직 서비스
├── app/Models/Interview/
│ ├── InterviewCategory.php # 카테고리
│ ├── InterviewTemplate.php # 템플릿 (항목)
│ ├── InterviewQuestion.php # 질문
│ ├── InterviewSession.php # 인터뷰 세션
│ └── InterviewAnswer.php # 답변
└── resources/views/sales/interviews/
└── index.blade.php # 메인 페이지 (1,076줄)
api/
└── database/migrations/
├── 2026_02_06_100000_create_interview_categories_table.php
├── 2026_02_06_100001_create_interview_templates_table.php
├── 2026_02_06_100002_create_interview_questions_table.php
├── 2026_02_06_100003_create_interview_sessions_table.php
└── 2026_02_06_100004_create_interview_answers_table.php
```
## 라우트
```php
// routes/web.php (sales/interviews prefix 그룹 내)
// 페이지
GET /interviews index() 메인 페이지
// 카테고리 API
GET /interviews/api/categories categories() 카테고리 목록
POST /interviews/api/categories storeCategory() 카테고리 생성
PUT /interviews/api/categories/{id} updateCategory() 카테고리 수정
DELETE /interviews/api/categories/{id} destroyCategory() 카테고리 삭제
// 트리 API
GET /interviews/api/tree tree() 전체 계층 구조
// 템플릿(항목) API
POST /interviews/api/templates storeTemplate() 템플릿 생성
PUT /interviews/api/templates/{id} updateTemplate() 템플릿 수정
DELETE /interviews/api/templates/{id} destroyTemplate() 템플릿 삭제
// 질문 API
POST /interviews/api/questions storeQuestion() 질문 생성
PUT /interviews/api/questions/{id} updateQuestion() 질문 수정
DELETE /interviews/api/questions/{id} destroyQuestion() 질문 삭제
// 일괄 가져오기
POST /interviews/api/bulk-import bulkImport() MD 파일에서 가져오기
// 세션(인터뷰) API
GET /interviews/api/sessions sessions() 세션 목록 (필터)
POST /interviews/api/sessions storeSession() 인터뷰 시작
GET /interviews/api/sessions/{id} showSession() 세션 상세
POST /interviews/api/sessions/toggle-answer toggleAnswer() 답변 토글
POST /interviews/api/sessions/{id}/complete completeSession() 인터뷰 완료
```
## 컨트롤러
### InterviewScenarioController
| 메서드 | HTTP | 설명 |
|--------|------|------|
| `index()` | GET | 메인 페이지 |
| `categories()` | GET | 카테고리 목록 |
| `storeCategory()` | POST | 카테고리 생성 |
| `updateCategory()` | PUT | 카테고리 수정 |
| `destroyCategory()` | DELETE | 카테고리 삭제 |
| `tree()` | GET | 전체 계층 구조 (카테고리→템플릿→질문) |
| `storeTemplate()` | POST | 템플릿 생성 |
| `updateTemplate()` | PUT | 템플릿 수정 |
| `destroyTemplate()` | DELETE | 템플릿 삭제 |
| `storeQuestion()` | POST | 질문 생성 |
| `updateQuestion()` | PUT | 질문 수정 |
| `destroyQuestion()` | DELETE | 질문 삭제 |
| `bulkImport()` | POST | MD 파일에서 일괄 가져오기 |
| `sessions()` | GET | 인터뷰 세션 목록 |
| `storeSession()` | POST | 인터뷰 시작 |
| `showSession()` | GET | 세션 상세 |
| `toggleAnswer()` | POST | 답변 토글/기록 |
| `completeSession()` | POST | 인터뷰 완료 |
### InterviewScenarioService
| 메서드 | 설명 |
|--------|------|
| `getCategories()` | 카테고리 목록 |
| `createCategory()` | 카테고리 생성 |
| `updateCategory()` | 카테고리 수정 |
| `deleteCategory()` | 카테고리 삭제 |
| `getTree()` | 전체 계층 구조 조회 |
| `createTemplate()` | 템플릿 생성 |
| `updateTemplate()` | 템플릿 수정 |
| `deleteTemplate()` | 템플릿 삭제 |
| `createQuestion()` | 질문 생성 |
| `updateQuestion()` | 질문 수정 |
| `deleteQuestion()` | 질문 삭제 |
| `bulkImport()` | MD에서 일괄 가져오기 |
| `getSessions()` | 세션 목록 (필터) |
| `startSession()` | 인터뷰 시작 |
| `getSessionDetail()` | 세션 상세 |
| `toggleAnswer()` | 답변 토글 |
| `completeSession()` | 인터뷰 완료 |
## 모델
### 데이터 계층 구조
```
InterviewCategory (카테고리)
└── InterviewTemplate (템플릿/항목)
└── InterviewQuestion (질문)
InterviewSession (인터뷰 세션)
└── InterviewAnswer (답변)
```
### InterviewCategory
**테이블**: `interview_categories`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `name` | string | 카테고리명 (예: 제조-방화셔터) |
| `description` | text | 설명 |
| `sort_order` | int | 정렬 순서 |
| `is_active` | boolean | 활성 여부 |
| `created_by` | bigint | 등록자 |
- SoftDeletes 적용
- 관계: `templates()`, `sessions()`
### InterviewTemplate
**테이블**: `interview_templates`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `interview_category_id` | bigint (FK) | 카테고리 ID |
| `name` | string | 항목명 (예: 견적서 제작) |
| `description` | text | 설명 |
| `sort_order` | int | 정렬 순서 |
| `is_active` | boolean | 활성 여부 |
- SoftDeletes 적용
- 관계: `category()`, `questions()`
### InterviewQuestion
**테이블**: `interview_questions`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `interview_template_id` | bigint (FK) | 템플릿 ID |
| `question_text` | string(500) | 질문 텍스트 |
| `question_type` | string | **checkbox** / **text** |
| `options` | json | 선택지 (배열) |
| `is_required` | boolean | 필수 여부 |
| `sort_order` | int | 정렬 순서 |
| `is_active` | boolean | 활성 여부 |
- SoftDeletes 적용
- 관계: `template()`
#### 질문 타입
| 타입 | 설명 | 답변 방식 |
|------|------|----------|
| `checkbox` | 체크 질문 | is_checked 토글 |
| `text` | 서술형 질문 | answer_text 입력 |
### InterviewSession
**테이블**: `interview_sessions`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `interview_category_id` | bigint (FK) | 사용한 카테고리 ID |
| `interviewer_id` | bigint (FK) | 면담자(매니저) ID |
| `interviewee_name` | string(100) | 면담 상대 이름 |
| `interviewee_company` | string(200) | 면담 상대 회사 |
| `interview_date` | date | 면담 일자 |
| `status` | string | **in_progress** / **completed** |
| `total_questions` | int | 총 질문 수 |
| `answered_questions` | int | 답변 완료 수 |
| `memo` | text | 메모 |
| `completed_at` | timestamp | 완료 일시 |
- SoftDeletes 적용
- 관계: `category()`, `interviewer()`, `answers()`
### InterviewAnswer
**테이블**: `interview_answers`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `interview_session_id` | bigint (FK) | 세션 ID |
| `interview_question_id` | bigint (FK) | 질문 ID |
| `interview_template_id` | bigint (FK) | 템플릿 ID |
| `is_checked` | boolean | 체크 여부 (checkbox 유형) |
| `answer_text` | text | 답변 텍스트 (text 유형) |
| `memo` | text | 메모 |
### 인터뷰 진행 흐름
```
1. 카테고리 선택 + 면담 상대 정보 입력
→ storeSession() → 세션 생성 (status: in_progress)
→ 해당 카테고리의 모든 질문에 대해 빈 Answer 생성
2. 질문별 답변 기록
→ toggleAnswer() → is_checked 토글 / answer_text 저장
→ answered_questions 카운트 갱신
3. 인터뷰 완료
→ completeSession() → status: completed, completed_at 기록
```
## 뷰 구성
### index.blade.php
```
┌─ 페이지 헤더 ──────────────────────
│ 제목: "인터뷰 시나리오"
│ [카테고리 추가] [일괄 가져오기] 버튼
├─ 좌측 사이드바 ───────────────────
│ 카테고리 트리
│ ├── 카테고리 A
│ │ ├── 템플릿 1 (질문 3개)
│ │ └── 템플릿 2 (질문 5개)
│ └── 카테고리 B
│ └── 템플릿 3 (질문 2개)
├─ 우측 메인 영역 ──────────────────
│ ├─ 템플릿 편집 모드
│ │ 질문 목록 + 추가/수정/삭제
│ │ 질문 타입(checkbox/text) + 옵션
│ │
│ └─ 인터뷰 세션 모드
│ 면담 상대 정보 + 날짜
│ 질문별 체크/답변 기록
│ 진행률 표시
│ [완료] 버튼
└─ 세션 목록 ───────────────────────
과거 인터뷰 기록 (필터: 카테고리, 날짜)
면담자 | 상대방 | 회사 | 날짜 | 완료율 | 상태
```
## HTMX 호환성
- JavaScript/React 기반 페이지이므로 **HX-Redirect 필요**
- API 호출로 동적 CRUD 관리
- `@push('scripts')` 블록에 스크립트 포함

View File

@@ -0,0 +1,102 @@
# 영업파트너승인
## 개요
영업파트너승인은 신규 영업파트너 등록 신청을 검토하고 승인/반려를 처리하는 기능입니다.
본사 관리자 전용 페이지로, 신청자의 정보와 서류를 확인하고 일괄 처리할 수 있습니다.
- **라우트**: `GET /sales/managers/approvals`
- **미들웨어**: `auth`, `hq.member` + 관리자 권한 체크
- **UI 기술**: Blade + HTMX + Tailwind CSS
## 파일 구조
```
mng/
├── app/Http/Controllers/Sales/
│ └── SalesManagerController.php # 파트너관리와 동일 컨트롤러 (승인 메서드 포함)
├── app/Services/Sales/
│ └── SalesManagerService.php # approve(), reject() 로직
└── resources/views/sales/managers/
└── approvals.blade.php # 승인 관리 페이지
```
## 라우트
```php
// routes/web.php (sales prefix 그룹 내, resource 전에 정의)
GET /managers/approvals approvals() 승인 목록 페이지
POST /managers/approvals/{id}/approve approveFromList() 목록에서 승인
POST /managers/approvals/{id}/reject rejectFromList() 목록에서 반려
```
## 컨트롤러
### SalesManagerController (승인 관련 메서드)
| 메서드 | HTTP | 설명 | 권한 |
|--------|------|------|------|
| `approvals()` | GET | 승인 대기 목록 페이지 | 관리자 전용 |
| `approveFromList()` | POST | 목록에서 승인 처리 | 관리자 전용 |
| `rejectFromList()` | POST | 목록에서 반려 처리 | 관리자 전용 |
| `approve()` | POST | 상세에서 승인 | - |
| `reject()` | POST | 상세에서 반려 | - |
### 승인 처리 흐름
```
영업파트너 등록 신청 (store)
approval_status = 'pending' (Users 테이블)
관리자 승인 목록에서 확인
├── approveFromList() → status = 'approved'
│ ├── approved_at 기록
│ └── approved_by 기록
└── rejectFromList() → status = 'rejected'
└── rejection_reason 저장 (필수)
```
### 응답 방식
```
HTMX 요청 → JsonResponse (success, message)
일반 요청 → Redirect (redirect back)
```
## 뷰 구성
### approvals.blade.php
```
┌─ 페이지 헤더 ──────────────────────
│ 제목: "영업파트너 승인관리"
├─ 통계 카드 ────────────────────────
│ 승인 대기 | 오늘 승인 | 오늘 반려
├─ 검색/필터 영역 ──────────────────
│ 검색 (이름, 아이디, 이메일, 전화번호)
├─ 2분할 레이아웃 ──────────────────
│ ┌─ 좌: 승인 대기 ────────────┐ ┌─ 우: 승인 완료 ─────────────┐
│ │ 신청자명 | 역할 | 유치자 │ │ 파트너명 | 승인일 | 승인자 │
│ │ └─ [승인] [반려] 버튼 │ │ │
│ └────────────────────────────┘ └─────────────────────────────┘
└─ 상세 모달 ───────────────────────
파트너 정보 (이름, 아이디, 이메일, 전화)
계층 정보 (레벨, 추천인)
등록 서류 목록 (다운로드/삭제)
[승인] [반려] 버튼
```
## 반려 처리
```
반려 시:
- rejection_reason (반려 사유) 필수 입력
- 상태: pending → rejected
- 반려 후 재신청 가능
```

166
features/sales/partners.md Normal file
View File

@@ -0,0 +1,166 @@
# 파트너관리
## 개요
파트너관리는 영업파트너(판매자/매니저)의 등록, 수정, 역할 관리, 서류 관리를 수행하는 기능입니다.
파트너 등록 신청, 서류 업로드, 역할 부여/위임, 계층 구조(추천인) 관리를 지원합니다.
- **라우트**: `GET /sales/managers`
- **미들웨어**: `auth`, `hq.member`
- **UI 기술**: Blade + HTMX + Alpine.js + Tailwind CSS
## 파일 구조
```
mng/
├── app/Http/Controllers/Sales/
│ └── SalesManagerController.php # 메인 컨트롤러 (425줄)
├── app/Services/Sales/
│ └── SalesManagerService.php # 비즈니스 로직 서비스
├── app/Models/Sales/
│ ├── SalesPartner.php # 영업파트너 모델
│ └── SalesManagerDocument.php # 파트너 서류 모델
└── resources/views/sales/managers/
├── index.blade.php # 파트너 목록
├── create.blade.php # 등록 폼
├── show.blade.php # 상세 페이지
├── edit.blade.php # 수정 폼
└── partials/
├── show-modal.blade.php # 상세 모달
└── edit-modal.blade.php # 수정 모달
api/
└── database/migrations/
└── 2026_01_29_100000_create_sales_partners_table.php
```
## 라우트
```php
// routes/web.php (sales prefix 그룹 내)
// Resource 라우트
GET /managers index() 파트너 목록
GET /managers/create create() 등록
POST /managers store() 등록 처리
GET /managers/{id} show() 상세 페이지
GET /managers/{id}/edit edit() 수정
PUT /managers/{id} update() 수정 처리
DELETE /managers/{id} destroy() 비활성화 (관리자)
// 추가 라우트
GET /managers/{id}/modal-show modalShow() 상세 모달
GET /managers/{id}/modal-edit modalEdit() 수정 모달
POST /managers/{id}/approve approve() 승인
POST /managers/{id}/reject reject() 반려
POST /managers/{id}/delegate-role delegateRole() 역할 위임
POST /managers/{id}/assign-role assignRole() 역할 부여
POST /managers/{id}/remove-role removeRole() 역할 제거
GET /managers/{id}/documents/{docId}/download downloadDocument() 서류 다운로드
DELETE /managers/{id}/documents/{docId} deleteDocument() 서류 삭제
```
## 컨트롤러
### SalesManagerController
| 메서드 | HTTP | 설명 |
|--------|------|------|
| `index()` | GET | 파트너 목록 (현재 사용자 유치분) |
| `create()` | GET | 등록 폼 |
| `store()` | POST | 파트너 등록 (서류 업로드 포함) |
| `show()` | GET | 상세 페이지 |
| `edit()` | GET | 수정 폼 |
| `update()` | PUT | 정보 수정 |
| `destroy()` | DELETE | 비활성화 (관리자 전용) |
| `modalShow()` | GET | 상세 모달 (HTMX) |
| `modalEdit()` | GET | 수정 모달 (HTMX) |
| `delegateRole()` | POST | 상담매니저 역할 위임 |
| `assignRole()` | POST | 역할 부여 (sales/manager) |
| `removeRole()` | POST | 역할 제거 |
| `downloadDocument()` | GET | 서류 파일 다운로드 |
| `deleteDocument()` | DELETE | 서류 파일 삭제 |
### SalesManagerService
| 메서드 | 설명 |
|--------|------|
| `getSalesPartners()` | 필터 적용 파트너 목록 조회 |
| `getStats()` | 파트너 통계 계산 |
| `getApprovalStats()` | 승인 관련 통계 |
| `createSalesPartner()` | 파트너 생성 |
| `updateSalesPartner()` | 파트너 수정 |
| `approve()` | 승인 처리 |
| `reject()` | 반려 처리 |
## 모델
### SalesPartner
**테이블**: `sales_partners`
| 필드 | 타입 | 설명 |
|------|------|------|
| `user_id` | bigint (FK) | 연결된 사용자 ID |
| `partner_code` | string | 파트너 고유 코드 (SP + 연도 + 순번) |
| `partner_type` | string | individual(개인) / corporate(법인) |
| `commission_rate` | decimal(5,2) | 기본 수수료율 |
| `manager_commission_rate` | decimal(5,2) | 관리자 수수료율 |
| `bank_name` | string | 은행명 |
| `account_number` | string | 계좌번호 |
| `account_holder` | string | 예금주 |
| `status` | string | pending / active / inactive / suspended |
| `approved_at` | timestamp | 승인 일시 |
| `approved_by` | bigint (FK) | 승인자 ID |
| `total_contracts` | int | 총 계약 건수 (캐시) |
| `total_commission` | decimal | 총 수당 (캐시) |
- SoftDeletes 적용
### SalesManagerDocument
**테이블**: `sales_manager_documents`
- 파트너 등록 시 필수 서류 관리 (신분증, 통장사본 등)
- `DOCUMENT_TYPES` 상수로 서류 타입 정의
### 파트너 계층 구조
```
User 모델의 parent_id를 통한 다단계 구조:
추천인 (parent)
└── 영업파트너 (children)
└── 하위 파트너 (children)
```
## 뷰 구성
```
┌─ 페이지 헤더 ──────────────────────
│ 제목: "파트너관리"
│ [등록] 버튼
├─ 통계 카드 ────────────────────────
│ 전체 | 활성 | 대기중 | 비활성
├─ 필터 영역 ────────────────────────
│ 검색 (이름, 아이디) | 상태 필터 | 타입 필터
├─ 파트너 목록 테이블 ───────────────
│ 이름 | 파트너코드 | 타입 | 수수료율 | 계약수 | 상태 | 작업
│ └─ 작업: 상세, 수정, 역할관리, 승인/반려
├─ 등록 폼 ─────────────────────────
│ 사용자 정보, 파트너 타입, 수수료율
│ 계좌 정보, 서류 업로드
└─ 상세/수정 모달 ──────────────────
파트너 정보, 계층, 서류 목록, 역할 관리
```
## HTMX 호환성
- Blade + HTMX 기반으로 **HX-Redirect 불필요**
- 모달로 상세/수정 처리
- HTMX 또는 JavaScript AJAX 호출

175
features/sales/products.md Normal file
View File

@@ -0,0 +1,175 @@
# 상품관리
## 개요
상품관리는 영업에 사용되는 상품(프로그램)을 카테고리별로 등록하고 가격/수당률을 설정하는 기능입니다.
카테고리 관리, 상품 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`으로 부분 로드

189
features/sales/prospects.md Normal file
View File

@@ -0,0 +1,189 @@
# 영업파트너 고객관리
## 개요
영업파트너 고객관리는 영업파트너가 명함을 등록하여 영업권을 확보하고,
고객과의 계약 진행을 관리하는 기능입니다.
명함 등록, 영업권 유효기간(2개월), 사업자번호 중복 체크, 테넌트 전환을 지원합니다.
- **라우트**: `GET /sales/prospects`
- **미들웨어**: `auth`, `hq.member`
- **UI 기술**: Blade + HTMX + Tailwind CSS
## 파일 구조
```
mng/
├── app/Http/Controllers/Sales/
│ └── TenantProspectController.php # 메인 컨트롤러 (12개 메서드)
├── app/Services/Sales/
│ └── TenantProspectService.php # 비즈니스 로직 서비스
├── app/Models/Sales/
│ └── TenantProspect.php # 가망고객 모델
└── resources/views/sales/prospects/
├── index.blade.php # 영업권 목록
├── create.blade.php # 명함 등록 폼
├── show.blade.php # 상세 페이지
├── edit.blade.php # 수정 폼
└── partials/
├── show-modal.blade.php # 상세 모달
└── edit-modal.blade.php # 수정 모달
api/
└── database/migrations/
├── 2026_01_27_221000_create_tenant_prospects_table.php
└── 2026_01_28_090000_add_attachments_to_tenant_prospects_table.php
```
## 라우트
```php
// routes/web.php (sales prefix 그룹 내)
// Resource 라우트
GET /prospects index() 영업권 목록 (본인 기준)
GET /prospects/create create() 명함 등록
POST /prospects store() 명함 등록 처리
GET /prospects/{id} show() 상세 페이지
GET /prospects/{id}/edit edit() 수정
PUT /prospects/{id} update() 수정 처리
DELETE /prospects/{id} destroy() 삭제 (관리자)
// 추가 라우트
POST /prospects/{id}/convert convert() 테넌트 전환
POST /prospects/check-business-number checkBusinessNumber() 사업자번호 중복 체크
DELETE /prospects/{id}/attachment deleteAttachment() 첨부파일 삭제
GET /prospects/{id}/modal-show modalShow() 상세 모달
GET /prospects/{id}/modal-edit modalEdit() 수정 모달
```
## 컨트롤러
### TenantProspectController
| 메서드 | HTTP | 설명 |
|--------|------|------|
| `index()` | GET | 영업권 목록 (현재 사용자 기준) |
| `create()` | GET | 명함 등록 폼 |
| `store()` | POST | 명함 등록 (등록자 = 현재 사용자) |
| `show()` | GET | 상세 페이지 |
| `edit()` | GET | 수정 폼 |
| `update()` | PUT | 정보 수정 |
| `destroy()` | DELETE | 삭제 (관리자 전용) |
| `convert()` | POST | 영업권 → 테넌트 전환 |
| `checkBusinessNumber()` | POST | 사업자번호 중복 체크 (AJAX) |
| `modalShow()` | GET | 상세 모달 |
| `modalEdit()` | GET | 수정 모달 |
| `deleteAttachment()` | DELETE | 첨부 이미지 삭제 |
### TenantProspectService
| 메서드 | 설명 |
|--------|------|
| `register()` | 명함 등록 (영업권 확보), expires_at = 등록일 + 2개월 |
| `update()` | 정보 수정 + 파일 업로드 (명함/신분증/통장) |
| `convertToTenant()` | 영업권 → 테넌트 전환 (Tenant + user_tenants 생성) |
| `expireOldProspects()` | 만료 영업권 자동 처리 (배치) |
| `canRegister()` | 사업자번호 등록 가능 여부 확인 |
| `getProspects()` | 목록 조회 (검색, 상태, 파트너 필터) |
| `getStats()` | 통계 (total, active, expired, converted) |
| `uploadAttachment()` | 파일 업로드 (tenant disk) |
| `deleteAttachment()` | 파일 삭제 |
## 모델
### TenantProspect
**테이블**: `tenant_prospects`
| 필드 | 타입 | 설명 |
|------|------|------|
| `business_number` | string(20) | 사업자번호 (중복 체크 키) |
| `company_name` | string(100) | 회사명 |
| `ceo_name` | string(50) | 대표자명 |
| `contact_phone` | string(20) | 연락처 |
| `contact_email` | string(100) | 이메일 |
| `address` | string(500) | 주소 |
| `registered_by` | bigint (FK) | 등록한 영업파트너 ID |
| `business_card_path` | string(500) | 명함 이미지 경로 |
| `id_card_path` | string(500) | 신분증 이미지 경로 |
| `bankbook_path` | string(500) | 통장 이미지 경로 |
| `status` | string(20) | active / expired / converted / completed |
| `registered_at` | timestamp | 등록일 |
| `expires_at` | timestamp | 만료일 (등록일 + 2개월) |
| `cooldown_ends_at` | timestamp | 재등록 가능일 (만료일 + 1개월) |
| `tenant_id` | bigint (FK, nullable) | 전환된 테넌트 ID |
| `converted_at` | timestamp | 전환일 |
| `converted_by` | bigint (FK) | 전환 처리자 ID |
| `memo` | text | 메모 |
- SoftDeletes 적용
#### 상태 흐름
```
명함 등록 → active (영업권 유효, 2개월)
├── convert() → converted (테넌트 전환 완료)
│ └→ completed (영업 완료)
└── (2개월 경과) → expired (만료)
└→ (1개월 쿨다운 후 재등록 가능)
```
#### 영업권 규칙
| 규칙 | 설명 |
|------|------|
| 유효기간 | 등록일로부터 2개월 |
| 쿨다운 | 만료 후 1개월간 재등록 불가 |
| 중복 체크 | 동일 사업자번호 중복 등록 방지 |
| 전환 | 영업권 → Tenant + user_tenants 자동 생성 |
#### 주요 속성/메서드
| 메서드 | 설명 |
|--------|------|
| `isActive()` | 영업권 유효 여부 |
| `isExpired()` | 만료 여부 |
| `isConverted()` | 테넌트 전환 완료 여부 |
| `canReRegister()` | 재등록 가능 여부 |
| `getStatusLabelAttribute()` | 상태 라벨 (영업중/완료/계약완료/대기중/만료) |
| `getStatusColorAttribute()` | Tailwind CSS 색상 |
| `getRemainingDaysAttribute()` | 남은 일수 |
## 뷰 구성
```
┌─ 페이지 헤더 ──────────────────────
│ 제목: "고객관리 (영업권)"
│ [명함 등록] 버튼
├─ 통계 카드 ────────────────────────
│ 전체 | 영업중 | 만료 | 전환완료
├─ 필터 영역 ────────────────────────
│ 검색 (회사명, 사업자번호) | 상태 필터
├─ 영업권 목록 테이블 ──────────────
│ 회사명 | 사업자번호 | 대표자 | 연락처 | 상태 | 남은일수 | 작업
│ └─ 상태: 영업중(초록), 만료(빨강), 전환(파랑), 완료(회색) 배지
│ └─ 작업: 상세, 수정, 전환, 삭제
├─ 등록 폼 ─────────────────────────
│ 사업자번호 (중복 체크), 회사명, 대표자명
│ 연락처, 이메일, 주소
│ 명함 이미지 업로드
│ 신분증, 통장사본 (선택)
└─ 상세 모달 ───────────────────────
회사 정보 + 첨부파일 미리보기
영업권 상태 + 남은 기간
[테넌트 전환] 버튼
```
## HTMX 호환성
- Blade + HTMX 기반으로 **HX-Redirect 불필요**
- 사업자번호 중복 체크: AJAX 실시간 검증
- 모달로 상세/수정 처리

View File

@@ -0,0 +1,122 @@
# 영업관리 대시보드
## 개요
영업관리 대시보드는 영업파트너/상담매니저의 수당 현황, 테넌트 진행률, 유치 파트너 활동을 종합적으로 보여주는 페이지입니다.
로그인한 사용자 기준의 실적 및 수당 정보를 실시간으로 제공합니다.
- **라우트**: `GET /sales/salesmanagement/dashboard`
- **미들웨어**: `auth`, `hq.member`
- **UI 기술**: Blade + HTMX + Alpine.js + Tailwind CSS
## 파일 구조
```
mng/
├── app/Http/Controllers/Sales/
│ └── SalesDashboardController.php # 메인 컨트롤러 (18개 메서드, 1,073줄)
└── resources/views/sales/dashboard/
├── index.blade.php # 메인 페이지
└── partials/
├── data-container.blade.php # HTMX 새로고침 컨테이너
├── stats.blade.php # 통계 카드
├── commission-by-role.blade.php # 역할별 수당
├── tenant-list.blade.php # 테넌트 목록
├── tenant-stats.blade.php # 테넌트 통계
├── partner-activity.blade.php # 유치 파트너 활동
├── my-commission.blade.php # 내 수당 정보
├── help-modal.blade.php # 도움말
└── prospect-row.blade.php # 가망고객 행
```
## 라우트
```php
// routes/web.php (sales prefix 그룹 내)
GET /salesmanagement/dashboard index() 메인 페이지
GET /salesmanagement/dashboard/refresh refresh() HTMX 부분 새로고침
GET /salesmanagement/dashboard/tenants refreshTenantList() 테넌트 목록 갱신
GET /salesmanagement/dashboard/partner-activity partnerActivity() 유치 파트너 활동
GET /salesmanagement/dashboard/help helpGuide() 도움말 모달
GET /salesmanagement/dashboard/prospect/{id}/row getProspectRow() 가망고객
GET /managers/list getManagers() 매니저 목록
GET /managers/search searchManagers() 매니저 검색
POST /tenants/{tenant}/assign-manager assignManager() 매니저 지정
POST /prospects/{prospect}/assign-manager assignProspectManager() 가망고객 매니저 지정
```
## 컨트롤러
### SalesDashboardController
| 메서드 | HTTP | 설명 |
|--------|------|------|
| `index()` | GET | 대시보드 메인 화면 |
| `refresh()` | GET | HTMX 부분 새로고침 |
| `getDashboardData()` | - | 대시보드 핵심 데이터 조회 (private) |
| `refreshTenantList()` | GET | 테넌트 목록 새로고침 |
| `getManagers()` | GET | 매니저 드롭다운 목록 |
| `searchManagers()` | GET | 매니저 검색 API |
| `partnerActivity()` | GET | 유치 파트너 활동 현황 |
| `getPartnerActivityData()` | - | 파트너 활동 데이터 (private) |
| `calculatePartnerSummaryStats()` | - | 파트너 요약 통계 (private) |
| `getPartnerActivitiesDetail()` | - | 파트너별 상세 활동 (private) |
| `getManagerOnlyProspects()` | - | 매니저 전용 가망고객 (private) |
| `getCommissionData()` | - | 수당 정보 조회 (private) |
| `getAllManagerUsers()` | - | 상담매니저 사용자 목록 (private) |
| `helpGuide()` | GET | 도움말 모달 |
| `getProspectRow()` | GET | 가망고객 개별 행 |
| `assignManager()` | POST | 테넌트 매니저 변경 |
| `assignProspectManager()` | POST | 가망고객 매니저 변경 |
| `calculateExpectedCommissionSummary()` | - | 예상 수당 계산 (private) |
### 수당 계산 로직
```
영업파트너 수당:
- 판매자 수당: 가입비 × 20%
- 협업지원금: 개발비 × 10% (인계 완료 시)
- 1차/2차 분할: 각 50%
매니저 수당:
- 1개월 구독료 (매니저 기본 수당)
- 1차/2차 분할: 각 50%
```
## 뷰 구성
```
┌─ 페이지 헤더 ──────────────────────
│ 제목: "영업관리"
│ [새로고침] 버튼
├─ 탭 전환 (Alpine.js) ─────────────
│ [내 활동] | [유치 파트너 현황]
├─ 탭1: 내 활동 ─────────────────────
│ ├─ 통계 카드 (수당 요약)
│ │ 총 수당 | 지급완료 | 미지급 | 예상 수당
│ │
│ ├─ 역할별 수당 상세
│ │ 영업파트너 수당 | 매니저 수당
│ │
│ └─ 테넌트/가망고객 목록
│ 회사명 | 상태 | 진행률 | 수당 | 매니저 | 작업
│ └─ 매니저 지정 변경 (드롭다운)
├─ 탭2: 유치 파트너 현황 ────────────
│ ├─ 파트너 요약 통계
│ │ 총 파트너 | 활성 | 계약건수 | 총 수당
│ │
│ └─ 파트너별 활동 상세
│ 파트너명 | 등록 고객 | 계약 | 진행중 | 수당
└─ 도움말 모달 ──────────────────────
수당 계산 방식, 진행 단계 설명
```
## HTMX 호환성
- Blade + HTMX 기반으로 **HX-Redirect 불필요**
- `hx-get`으로 부분 새로고침 처리
- Alpine.js 탭 전환