# 구독료정산

## 개요

구독료정산은 고객사별 구독 플랜과 과금을 관리하는 기능입니다.
플랜별 월정액 관리, 과금주기(월/연), MRR/ARR 집계, 사용자 수 추적을 지원합니다.

- **라우트**: `GET /finance/subscription`
- **UI 기술**: React 18 + Babel (브라우저 트랜스파일링)

## 파일 구조

```
mng/
├── app/Http/Controllers/Finance/
│   └── SubscriptionController.php         # MNG 컨트롤러 (4개 메서드)
├── app/Models/Finance/
│   └── Subscription.php                   # MNG 구독 모델
└── resources/views/finance/
    └── subscription.blade.php             # React 기반 단일 페이지

api/
├── app/Http/Controllers/Api/V1/
│   └── SubscriptionController.php         # API 컨트롤러 (REST)
├── app/Services/
│   └── SubscriptionService.php            # 비즈니스 로직 서비스
├── app/Models/Tenants/
│   └── Subscription.php                   # API 구독 모델 (상세 구현)
└── database/migrations/
    ├── 2026_02_04_230008_create_subscriptions_table.php
    └── 2025_12_22_222722_add_cancel_columns_to_subscriptions_table.php
```

## 라우트

### MNG 라우트

```php
// routes/web.php (finance prefix 그룹 내)
GET    /subscription              → Blade 페이지 렌더링 (HX-Redirect)

// API 라우트 (subscriptions prefix)
GET    /subscriptions/list        → index()     목록 + 통계 (JSON)
POST   /subscriptions/store       → store()     등록
PUT    /subscriptions/{id}        → update()    수정
DELETE /subscriptions/{id}        → destroy()   삭제
```

### API 라우트

```php
// routes/api.php (v1/subscriptions prefix)
GET    /                          → index()     목록 (페이지네이션)
GET    /{id}                      → show()      상세 조회
POST   /                          → store()     생성
PUT    /{id}                      → update()    수정
DELETE /{id}                      → destroy()   삭제
POST   /{id}/cancel               → cancel()    구독 취소
POST   /{id}/suspend              → suspend()   일시정지
POST   /{id}/resume               → resume()    재개
```

## 컨트롤러

### SubscriptionController (MNG)

| 메서드 | HTTP | 설명 |
|--------|------|------|
| `index()` | GET | 목록 + MRR/ARR 통계 (검색, 상태 필터) |
| `store()` | POST | 구독 등록 |
| `update()` | PUT | 구독 수정 |
| `destroy()` | DELETE | 삭제 (Soft Delete) |

### index() 응답 구조

```json
{
  "success": true,
  "data": [
    {
      "id": 1,
      "customer": "A사",
      "plan": "Business",
      "monthlyFee": 500000,
      "billingCycle": "monthly",
      "startDate": "2026-01-01",
      "nextBilling": "2026-03-01",
      "status": "active",
      "users": 10,
      "memo": ""
    }
  ],
  "stats": {
    "activeCount": 15,
    "monthlyRecurring": 7500000,
    "yearlyRecurring": 90000000,
    "totalUsers": 150
  }
}
```

## 핵심 로직

### MRR/ARR 계산

```
MRR (Monthly Recurring Revenue)
  = SUM(active 구독의 monthly_fee) (billingCycle=monthly)
  + SUM(active 구독의 monthly_fee / 12) (billingCycle=yearly를 월 환산)

ARR (Annual Recurring Revenue)
  = MRR × 12
```

### 구독 상태 관리 (API 모델)

```
pending (대기)
    ↓ activate()
active (활성)
    ├── cancel(reason) → cancelled (취소)
    ├── suspend() → suspended (일시정지)
    │       ↓ resume() → active (재활성)
    └── (만료) → expired
```

### 구독 플랜

| 플랜 | 설명 |
|------|------|
| Starter | 소규모 기본 플랜 |
| Business | 중규모 비즈니스 플랜 |
| Enterprise | 대규모 기업 플랜 |

### 과금 주기

| 주기 | 설명 |
|------|------|
| monthly | 월간 과금 |
| yearly | 연간 과금 |

## 모델

### Subscription (MNG)

**테이블**: `subscriptions`

| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `customer` | string | 고객사명 |
| `plan` | string | 플랜명 (Starter/Business/Enterprise) |
| `monthly_fee` | int | 월 정액 |
| `billing_cycle` | string | monthly / yearly |
| `start_date` | date | 시작일 |
| `next_billing` | date | 다음 과금일 |
| `status` | string | active / trial / cancelled |
| `users` | int | 사용자 수 |
| `memo` | text | 메모 |
| `cancelled_at` | timestamp | 취소 일시 |
| `cancel_reason` | string(500) | 취소 사유 |

- SoftDeletes 적용
- Casts: monthly_fee, users → integer, start_date, next_billing → date
- Scope: `forTenant($tenantId)`

### Subscription (API - 상세 구현)

API 프로젝트의 모델은 더 상세한 구독 라이프사이클을 지원합니다:

#### 상태 상수

```php
const STATUS_ACTIVE = 'active';
const STATUS_CANCELLED = 'cancelled';
const STATUS_EXPIRED = 'expired';
const STATUS_PENDING = 'pending';
const STATUS_SUSPENDED = 'suspended';
```

#### Relationships

```php
$subscription->tenant     // BelongsTo Tenant
$subscription->plan       // BelongsTo Plan
$subscription->payments   // HasMany Payment
```

#### 주요 Accessor

```php
$subscription->statusLabel      // 상태 한글 라벨
$subscription->isExpired        // 만료 여부
$subscription->remainingDays    // 잔여 일수
$subscription->isValid          // 유효 여부 (active + 미만료)
$subscription->totalPaid        // 총 결제 금액
```

#### 주요 메서드

```php
$subscription->activate()           // 활성화
$subscription->renew($newEndDate)   // 갱신
$subscription->cancel($reason)      // 취소
$subscription->suspend()            // 일시정지
$subscription->resume()             // 재개
$subscription->isCancellable()      // 취소 가능 여부
```

## 서비스 클래스 (API)

### SubscriptionService

| 메서드 | 설명 |
|--------|------|
| `index(params)` | 페이지네이션 목록 (필터, 정렬) |
| `current()` | 현재 활성 구독 조회 |
| `show(id)` | 상세 (플랜, 최근 결제 포함) |
| `store(data)` | 구독 생성 (결제 처리) |
| `update(id, data)` | 구독 수정 |
| `cancel(id, reason)` | 구독 취소 |
| `suspend(id)` | 일시정지 |
| `resume(id)` | 재개 |
| `renew(id, newEndDate)` | 갱신 |

## 뷰 구성 (React)

### subscription.blade.php

```
┌─ 페이지 헤더 ──────────────────────
│  제목: "구독료정산"
│  [CSV 내보내기] [등록] 버튼
│
├─ 통계 카드 (4열) ──────────────────
│  활성 구독 | MRR (월간 반복 수익) | ARR (연간 반복 수익) | 총 사용자
│
├─ 필터 영역 ────────────────────────
│  검색 (고객사명) | 플랜 (Starter/Business/Enterprise) | 상태 (active/trial/cancelled)
│
├─ 구독 목록 테이블 ─────────────────
│  고객사 | 플랜 | 월정액 | 과금주기 | 다음과금일 | 사용자 | 상태 | 작업
│  └─ 플랜: Starter(회색), Business(파랑), Enterprise(보라) 배지
│  └─ 상태: active(초록), trial(파랑), cancelled(빨강) 배지
│  └─ 작업: 수정/삭제 버튼
│
├─ 등록/수정 모달 ───────────────────
│  고객사명, 플랜 선택
│  월정액, 과금주기(월/연)
│  시작일, 다음 과금일
│  상태, 사용자 수, 메모
│  [삭제] [취소] [등록/저장]
│
└─ 비어있을 때: 안내 메시지
```

## HTMX 호환성

- React 기반 페이지이므로 **HX-Redirect 필요**
- `@push('scripts')` 블록에 React/Babel 스크립트 포함
