From 9c1ec1a6ebfef98afda86cb8a6e2f5983878deb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Fri, 20 Feb 2026 20:06:32 +0900 Subject: [PATCH] =?UTF-8?q?docs:=EB=B2=95=EC=9D=B8=EC=B9=B4=EB=93=9C=20?= =?UTF-8?q?=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=20=EA=B8=B0=EC=88=A0?= =?UTF-8?q?=EB=AC=B8=EC=84=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- CORPORATE_CARD_DASHBOARD.md | 465 ++++++++++++++++++++++++++++++++++++ 1 file changed, 465 insertions(+) create mode 100644 CORPORATE_CARD_DASHBOARD.md diff --git a/CORPORATE_CARD_DASHBOARD.md b/CORPORATE_CARD_DASHBOARD.md new file mode 100644 index 0000000..5afe65a --- /dev/null +++ b/CORPORATE_CARD_DASHBOARD.md @@ -0,0 +1,465 @@ +# 법인카드 대시보드 기술 문서 + +> **작성일**: 2026-02-20 +> **프로젝트**: SAM MNG (관리자 웹) +> **경로**: `/finance/corporate-cards` + +--- + +## 1. 개요 + +법인카드 대시보드는 회사의 법인카드를 등록/관리하고, 바로빌(Barobill) SOAP API를 통해 수집된 카드 거래 데이터를 기반으로 사용 현황을 실시간 파악할 수 있는 재무 관리 도구입니다. + +### 핵심 기능 +- **카드 등록/수정/비활성화/삭제**: 법인카드 CRUD +- **대시보드 요약 카드 6종**: 등록카드, 총한도, 매월결제일, 사용금액, 결제, 잔여한도 +- **결제일 동적 계산**: 현재일이 결제일을 지나면 자동으로 다음 월로 전환 +- **휴일/주말 결제일 조정**: 공휴일·주말이면 다음 영업일로 자동 이동 +- **바로빌 카드거래 연동**: 카드번호 기반 사용금액 자동 집계 +- **결제(선불결제) 내역 관리**: 복수 건의 결제 내역 입력/수정 + +--- + +## 2. 시스템 아키텍처 + +``` +┌─────────────────────────────────────────────────────┐ +│ Frontend (React 18 + Babel 브라우저 트랜스파일링) │ +│ corporate-cards.blade.php │ +│ └─ CorporateCardsManagement 컴포넌트 │ +│ ├─ 요약 카드 6종 (summary API) │ +│ ├─ 카드 목록 테이블 (list API) │ +│ ├─ 카드 등록/수정 모달 │ +│ └─ 결제 내역 수정 모달 │ +└──────────────┬──────────────────────────────────────┘ + │ fetch() API 호출 + ▼ +┌─────────────────────────────────────────────────────┐ +│ Backend (Laravel 11) │ +│ ├─ CorporateCardController (카드 CRUD + 요약) │ +│ ├─ CardTransactionController (거래 CRUD) │ +│ └─ EcardController (바로빌 SOAP API 연동) │ +└──────────────┬──────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────┐ +│ Database (MySQL 8.0) │ +│ ├─ corporate_cards (카드 정보) │ +│ ├─ corporate_card_prepayments (결제 내역) │ +│ ├─ barobill_card_transactions (바로빌 거래) │ +│ ├─ barobill_card_transaction_hides (숨긴 거래) │ +│ └─ holidays (공휴일 마스터) │ +└─────────────────────────────────────────────────────┘ +``` + +--- + +## 3. API 엔드포인트 + +모든 엔드포인트는 `/finance/corporate-cards` 접두사 하위에 정의됩니다. + +| Method | URI | Controller Method | 설명 | +|--------|-----|-------------------|------| +| GET | `/finance/corporate-cards` | (클로저) | 페이지 렌더링 (React SPA) | +| GET | `/finance/corporate-cards/list` | `index()` | 카드 목록 JSON | +| GET | `/finance/corporate-cards/summary` | `summary()` | 대시보드 요약 데이터 | +| POST | `/finance/corporate-cards/store` | `store()` | 카드 신규 등록 | +| PUT | `/finance/corporate-cards/{id}` | `update()` | 카드 정보 수정 | +| POST | `/finance/corporate-cards/{id}/deactivate` | `deactivate()` | 카드 비활성화 | +| DELETE | `/finance/corporate-cards/{id}` | `destroy()` | 카드 영구삭제 | +| POST | `/finance/corporate-cards/prepayment` | `updatePrepayment()` | 결제 내역 저장 | + +### 카드거래 내역 API (별도 컨트롤러) + +| Method | URI | Controller | 설명 | +|--------|-----|------------|------| +| GET | `/finance/card-transactions` | `EcardController::index()` | 카드사용내역 페이지 | +| GET | `/finance/card-transactions/list` | `CardTransactionController::index()` | 거래내역 JSON | +| POST | `/finance/card-transactions/store` | `CardTransactionController::store()` | 거래 수동 등록 | +| PUT | `/finance/card-transactions/{id}` | `CardTransactionController::update()` | 거래 수정 | +| DELETE | `/finance/card-transactions/{id}` | `CardTransactionController::destroy()` | 거래 삭제 | + +--- + +## 4. 데이터베이스 테이블 + +### 4.1 corporate_cards (법인카드) + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| id | bigint (PK) | | +| tenant_id | int | 테넌트 ID (Multi-tenant 격리) | +| card_name | varchar(100) | 카드 이름 (예: "업무용 법인카드") | +| card_company | varchar(50) | 카드사 (삼성카드, 현대카드 등) | +| card_number | varchar(30) | 카드번호 (하이픈 포함) | +| card_type | enum | `credit` (신용) / `debit` (체크) | +| payment_day | int | 결제일 (1~31, 기본값 15) | +| credit_limit | decimal(10,2) | 카드 한도 | +| current_usage | decimal(10,2) | 현재 사용량 (수동 관리) | +| card_holder_name | varchar(100) | 카드 명의자 | +| actual_user | varchar(100) | 실사용자 | +| expiry_date | varchar(10) | 유효기간 (YY/MM) | +| cvc | varchar(10) | CVC 코드 | +| status | enum | `active` / `inactive` | +| memo | text | 메모 | +| deleted_at | timestamp | SoftDeletes | + +**스코프**: `forTenant($tenantId)`, `active()` + +### 4.2 corporate_card_prepayments (결제 내역) + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| id | bigint (PK) | | +| tenant_id | int | 테넌트 ID | +| year_month | varchar(7) | 결제 기준 월 (예: "2026-03") | +| amount | int | 총 결제 금액 | +| items | json | 개별 결제 내역 배열 | +| memo | text | 메모 | + +**items JSON 구조**: +```json +[ + { + "date": "2026-02-10", + "amount": 500000, + "description": "카드대금 납부" + }, + { + "date": "2026-02-15", + "amount": 300000, + "description": "추가 납부" + } +] +``` + +**주요 메서드**: `getOrCreate($tenantId, $yearMonth)` — 해당 월 레코드가 없으면 amount=0으로 자동 생성 + +### 4.3 barobill_card_transactions (바로빌 카드거래) + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| id | bigint (PK) | | +| tenant_id | int | 테넌트 ID | +| card_num | varchar | 카드번호 (하이픈 없음, 정규화) | +| card_company | varchar | 카드사 코드 | +| card_company_name | varchar | 카드사명 | +| use_dt | varchar | 사용일시 (YYYYMMDDHHmmss) | +| use_date | varchar(8) | 사용일 (YYYYMMDD) | +| use_time | varchar(6) | 사용시간 (HHmmss) | +| approval_num | varchar | 승인번호 | +| approval_type | char(1) | `1`: 승인, `2`: 취소 | +| approval_amount | decimal(10,2) | 승인금액 | +| tax | decimal(10,2) | 세금 | +| service_charge | decimal(10,2) | 봉사료 | +| merchant_name | varchar | 가맹점명 | +| merchant_biz_num | varchar | 가맹점 사업자번호 | +| is_manual | boolean | 수동 입력 여부 | + +**unique_key (가상 속성)**: `card_num|use_dt|approval_num|approval_amount` — 거래 식별용 + +### 4.4 barobill_card_transaction_hides (숨긴 거래) + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| id | bigint (PK) | | +| tenant_id | int | 테넌트 ID | +| original_unique_key | varchar | 원래 거래의 unique_key | +| card_num | varchar | 카드번호 | +| use_date | varchar(8) | 사용일 | +| original_amount | decimal | 원래 금액 | +| merchant_name | varchar | 가맹점명 | +| hidden_by | int | 숨김 처리한 사용자 ID | + +### 4.5 holidays (공휴일) + +| 컬럼 | 타입 | 설명 | +|------|------|------| +| id | bigint (PK) | | +| tenant_id | int | 테넌트 ID | +| start_date | date | 시작일 | +| end_date | date | 종료일 | +| name | varchar | 공휴일 이름 | +| type | varchar | 유형 | +| is_recurring | boolean | 매년 반복 여부 | + +--- + +## 5. 핵심 비즈니스 로직 + +### 5.1 결제일 동적 계산 (★ 핵심 전략) + +법인카드의 매월 결제일은 **현재 날짜를 기준으로 동적 계산**됩니다. + +**파일**: `CorporateCardController::summary()` + +#### 계산 흐름 + +``` +1. 활성 신용카드의 대표 결제일(payment_day) 조회 + └─ 첫 번째 활성 신용카드 기준 (예: 15일) + +2. 현재 월의 결제일 생성 + └─ createPaymentDate(2026, 2, 15) → 2026-02-15 + +3. 휴일/주말 조정 + └─ getAdjustedPaymentDate() → 토/일/공휴일이면 다음 영업일로 이동 + └─ 예: 2/15(일) → 2/16(월) + +4. 현재일이 결제일을 지났는지 확인 + └─ if (now > adjustedDate) → 다음 월로 이동 + └─ 예: 현재 2/20, 결제일 2/16 → 다음: 3/15 (→ 3/16 조정) + +5. 결과 반환 + ├─ paymentDate: 조정된 결제일 (표시용) + ├─ paymentDay: 원래 결제일 (설정값) + ├─ originalDate: 조정 전 결제일 + └─ isAdjusted: 조정 여부 (true이면 "15일→휴일조정" 표시) +``` + +#### 월 말일 처리 + +```php +// 2월 30일 같은 불가능한 날짜 방지 +private function createPaymentDate(int $year, int $month, int $day): Carbon +{ + $maxDay = Carbon::create($year, $month)->daysInMonth; + return Carbon::create($year, $month, min($day, $maxDay)); +} +// 예: payment_day=31, 2월 → 2/28(또는 2/29) +``` + +#### 휴일 조정 로직 + +```php +private function getAdjustedPaymentDate($tenantId, $year, $month, $paymentDay): Carbon +{ + $date = createPaymentDate($year, $month, $paymentDay); + + // holidays 테이블에서 해당 기간의 휴일 조회 + $holidays = Holiday::forTenant($tenantId) + ->where('start_date', '<=', $date->addDays(10)) + ->where('end_date', '>=', $date) + ->get(); + + // 토/일/공휴일이면 다음 영업일로 이동 (앞으로만) + while ($date->isWeekend() || in_array($date, $holidayDates)) { + $date->addDay(); + } + + return $date; +} +``` + +### 5.2 청구기간 계산 + +결제일을 기준으로 청구기간을 산출합니다. + +``` +청구기간 = 결제일 기준 전월 1일 ~ 결제일 당일 + +예시 (결제일: 3/15): + - billingStart: 2026-02-01 + - billingEnd: 2026-03-15 +``` + +### 5.3 사용금액 집계 (바로빌 카드거래) + +**파일**: `CorporateCardController::calculateBillingUsage()` + +``` +1. 활성 카드의 카드번호 수집 + └─ 하이픈 제거하여 정규화 (1234-5678-9012-3456 → 1234567890123456) + +2. 바로빌 거래 조회 + └─ barobill_card_transactions 테이블 + └─ card_num IN (정규화된 카드번호들) + └─ use_date BETWEEN 청구시작(YYYYMMDD) AND 청구끝(YYYYMMDD) + +3. 숨긴 거래 제외 + └─ barobill_card_transaction_hides에서 hidden unique_key 조회 + └─ 해당 거래는 집계에서 제외 + +4. 승인/취소 구분 합산 + └─ approval_type = '1' (승인): +금액 + └─ approval_type = '2' (취소): -금액 + +5. 결과: 전체 합계 + 카드별 합계 + └─ { total: 1500000, perCard: { "1234...": 800000, "5678...": 700000 } } +``` + +### 5.4 잔여 한도 계산 + +``` +잔여한도 = 총한도 - 사용금액 + 결제금액 + +예시: + 총한도: 10,000,000원 (모든 활성 신용카드 credit_limit 합계) + 사용금액: 3,000,000원 (바로빌 청구기간 거래 합산) + 결제금액: 1,000,000원 (corporate_card_prepayments 해당월) + 잔여한도: 8,000,000원 +``` + +--- + +## 6. 대시보드 요약 카드 (6종) + +React 컴포넌트 `CorporateCardsManagement` 내 그리드 레이아웃 (`grid-cols-2 lg:grid-cols-6`) + +| # | 카드명 | 데이터 소스 | 계산 방식 | +|---|--------|------------|-----------| +| 1 | **등록 카드** | `cards.length` | 전체/활성 카드 수 | +| 2 | **총 한도** | `totalLimit` | 활성 신용카드의 `credit_limit` 합계 | +| 3 | **매월결제일** | `summaryData.paymentDate` | 동적 계산 (현재일 > 결제일이면 익월) | +| 4 | **사용금액** | `summaryData.billingUsage` | 바로빌 거래 합산 (청구기간 기준) | +| 5 | **결제** | `summaryData.prepaidAmount` | `corporate_card_prepayments` 합계 | +| 6 | **잔여 한도** | 계산값 | `총한도 - 사용금액 + 결제금액` | + +### 매월결제일 카드 상세 + +- **파란색 강조** (border-blue-200, bg-blue-50/30) +- 메인 숫자: `M/D(요일)` 형식 (예: "3/15(월)") +- 보조 텍스트: + - 휴일 미조정: "매월 15일" + - 휴일 조정됨: "15일→휴일조정" + +### 결제 카드 상세 + +- **주황색 강조** (border-amber-200, bg-amber-50/30) +- **수정 버튼**: 결제 내역 모달 열기 +- 보조 텍스트: "N건" (입력된 결제 건수) 또는 "미입력" + +--- + +## 7. 프론트엔드 구조 + +### 기술 스택 +- **React 18** (Babel 브라우저 트랜스파일링, CDN) +- **Lucide Icons** (아이콘 라이브러리) +- **Tailwind CSS** (스타일링) + +### 컴포넌트 구조 + +``` +CorporateCardsManagement (메인 컴포넌트) +├─ Header (타이틀 + 카드 추가/테스트 버튼) +├─ Summary Cards (6개 요약 카드 그리드) +├─ Search & Filter (검색바 + 상태 필터) +├─ Card Table (카드 목록 테이블) +│ └─ 각 행: 카드정보 + 바로빌 사용금액 + 수정/삭제 버튼 +├─ Card Modal (카드 등록/수정 모달) +│ └─ 카드명, 카드사, 카드번호, 유형, 결제일, 한도 등 +└─ Prepayment Modal (결제 내역 수정 모달) + └─ 복수 결제 건 (날짜, 금액, 설명) + 합계 +``` + +### 데이터 흐름 + +``` +useEffect (초기 로드) + → Promise.all([fetchCards(), fetchSummary()]) + → setCards(cardsResult.data) + → setSummaryData(summaryResult.data) + +카드 목록 테이블의 각 카드 행: + → getBarobillUsage(card.cardNumber) + → summaryData.cardUsages에서 해당 카드번호의 사용금액 조회 +``` + +--- + +## 8. Summary API 응답 구조 + +**GET** `/finance/corporate-cards/summary` + +```json +{ + "success": true, + "data": { + "paymentDate": "2026-03-16", + "paymentDay": 15, + "originalDate": "2026-03-15", + "isAdjusted": true, + "billingPeriod": { + "start": "2026-02-01", + "end": "2026-03-16" + }, + "billingUsage": 2850000, + "cardUsages": { + "1234567890123456": 1500000, + "9876543210987654": 1350000 + }, + "prepaidAmount": 500000, + "prepaidMemo": "", + "prepaidItems": [ + { + "date": "2026-02-10", + "amount": 500000, + "description": "카드대금 납부" + } + ] + } +} +``` + +--- + +## 9. 파일 경로 정리 + +### Backend (Laravel) + +| 파일 | 설명 | +|------|------| +| `app/Http/Controllers/Finance/CorporateCardController.php` | 카드 CRUD + 요약 + 결제 | +| `app/Http/Controllers/Finance/CardTransactionController.php` | 카드거래 CRUD | +| `app/Http/Controllers/Barobill/EcardController.php` | 바로빌 SOAP API 연동 | +| `app/Models/Finance/CorporateCard.php` | 법인카드 모델 | +| `app/Models/Finance/CorporateCardPrepayment.php` | 결제 내역 모델 | +| `app/Models/Barobill/CardTransaction.php` | 바로빌 카드거래 모델 | +| `app/Models/Barobill/CardTransactionHide.php` | 숨긴 거래 모델 | +| `app/Models/System/Holiday.php` | 공휴일 모델 | +| `routes/web.php` (909~939행) | 라우트 정의 | + +### Frontend (React + Blade) + +| 파일 | 설명 | +|------|------| +| `resources/views/finance/corporate-cards.blade.php` | React SPA 전체 (약 1,000행) | + +--- + +## 10. 전략적 고려사항 + +### 10.1 바로빌 데이터 신뢰성 +- 바로빌 SOAP API를 통해 카드거래 데이터를 주기적으로 수집 +- `unique_key`(카드번호+사용일시+승인번호+금액)로 중복 방지 +- 사용자가 특정 거래를 숨길 수 있음 (비용 집계에서 제외) + +### 10.2 Multi-tenant 데이터 격리 +- 모든 쿼리에 `tenant_id` 조건 적용 +- `session('selected_tenant_id', 1)`로 현재 테넌트 식별 + +### 10.3 카드번호 매칭 +- DB(corporate_cards)에는 하이픈 포함 카드번호 저장 +- 바로빌(barobill_card_transactions)에는 하이픈 없이 저장 +- 매칭 시 `str_replace('-', '', $num)`으로 정규화 후 비교 + +### 10.4 결제일 자동 조정 +- 토요일, 일요일, 공휴일(holidays 테이블)이면 **다음 영업일**로 이동 +- 현재일이 해당 월 결제일을 이미 지났으면 **다음 월 결제일**로 자동 전환 +- 월 말일 초과 방지 (31일 결제 → 2월은 28일/29일) + +### 10.5 결제(Prepayment) 관리 +- 월별로 복수 건의 결제 내역 관리 가능 +- `items` JSON 필드로 개별 건(날짜, 금액, 설명) 저장 +- `amount` 필드에는 `items`의 합계 자동 계산 +- 잔여한도 = 총한도 - 사용금액 + 결제금액 + +--- + +## 11. 변경 이력 + +| 날짜 | 변경 내용 | +|------|----------| +| 2026-02-20 | 매월결제일 동적 계산 로직 추가 (현재일 > 결제일 → 익월 전환) | +| 2026-02-20 | 기술문서 최초 작성 |