docs:카드/차량관리 개발문서 추가 (5개 메뉴)

- 법인카드관리: 카드 CRUD, 결제일 휴일조정, 사용금액 집계, 선불결제
- 카드사용내역: 바로빌 SOAP 연동, 분개, 거래숨김, 금액수정
- 차량목록: 법인/렌트/리스 차량 등록, 주행거리 자동계산
- 차량일지: 운행기록, 용도별 통계, 출발↔도착 교환
- 정비이력: 카테고리별 비용 관리, 주행거리 자동갱신

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-02-11 16:31:25 +09:00
parent 2950038b72
commit a23e2560ac
6 changed files with 1195 additions and 0 deletions

View File

@@ -0,0 +1,117 @@
# 카드/차량관리 기능
## 개요
SAM 프로젝트의 카드/차량관리 모듈은 법인카드 관리, 카드 사용내역 조회, 차량 관리, 운행일지, 정비이력을 종합적으로 관리하는 시스템입니다.
바로빌 API 연동을 통한 카드거래 자동 수집, 카드별 사용금액 집계, 차량 운행/정비 기록 관리 기능을 제공합니다.
## 메뉴 구성
| 메뉴 | 경로 | 설명 | UI 기술 |
|------|------|------|---------|
| [법인카드관리](./corporate-cards.md) | `/finance/corporate-cards` | 법인카드 등록/조회, 요약 대시보드 | React 18 |
| [카드사용내역](./card-transactions.md) | `/finance/card-transactions` | 바로빌 연동 카드거래 조회 및 회계 분류 | React 18 |
| [차량목록](./corporate-vehicles.md) | `/finance/corporate-vehicles` | 법인/렌트/리스 차량 등록 관리 | React 18 |
| [차량일지](./vehicle-logs.md) | `/finance/vehicle-logs` | 차량 운행기록 관리 | React 18 |
| [정비이력](./vehicle-maintenance.md) | `/finance/vehicle-maintenance` | 차량 정비/주유/보험 등 비용 관리 | React 18 |
## 아키텍처
```
┌───────────────────────────────────────────────────────────────┐
│ 카드/차량관리 모듈 │
├──────────────────────┬────────────────────────────────────────┤
│ 카드 관리 영역 │ 차량 관리 영역 │
│ │ │
│ ┌────────────────┐ │ ┌──────────┐ ┌────────┐ ┌────────┐ │
│ │ 법인카드관리 │ │ │ 차량목록 │ │ 차량일지 │ │ 정비이력│ │
│ │ (카드 CRUD) │ │ │(차량CRUD)│ │(운행기록)│ │(비용기록)│ │
│ └───────┬────────┘ │ └────┬─────┘ └────┬───┘ └───┬────┘ │
│ │ │ │ │ │ │
│ ┌───────▼────────┐ │ └──────────────┼──────────┘ │
│ │ 카드사용내역 │ │ │ │
│ │(바로빌 연동) │ │ │ │
│ └───────┬────────┘ │ │ │
├──────────┼───────────┴──────────────────────┼─────────────────┤
│ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 데이터베이스 │ │
│ │ corporate_cards, card_transactions, │ │
│ │ barobill_card_transactions, corporate_card_prepayments │ │
│ │ corporate_vehicles, vehicle_logs, vehicle_maintenances │ │
│ └──────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────┘
```
## 주요 기술 스택
| 기술 | 용도 |
|------|------|
| Laravel 11 (PHP 8.3) | 백엔드 프레임워크 |
| React 18 + Babel | 클라이언트 렌더링 UI (전 페이지) |
| Tailwind CSS + Lucide | 스타일링 및 아이콘 |
| Barobill SOAP API | 카드거래 실시간 연동 |
| MySQL 8.0 | 데이터 저장 |
## 데이터 흐름
```
바로빌 SOAP API ──────────────────────────────┐
(CARD.asmx: 카드거래 자동 수집) │
카드 관리: corporate_cards ← 매칭 → barobill_card_transactions
(카드번호 하이픈 제거 후 매칭)
├── barobill_card_transaction_splits (분개)
├── barobill_card_transaction_hides (숨김)
├── barobill_card_transaction_amount_logs (수정이력)
└── corporate_card_prepayments (선불결제)
차량 관리: corporate_vehicles
├── vehicle_logs (운행기록 → distance_km 합산)
└── vehicle_maintenances (정비기록 → mileage 갱신)
```
## 공통 모델/패턴
### 멀티 테넌트
모든 테이블은 `tenant_id` 기반으로 데이터를 격리합니다.
### React 18 + Babel
모든 페이지는 브라우저 트랜스파일링 방식의 React 18을 사용합니다:
- `@push('scripts')` 블록에 React/Babel/Lucide 스크립트 포함
- `@verbatim` + `<script type="text/babel">` 패턴
- HTMX 네비게이션 시 HX-Redirect 필수 (전체 페이지 리로드)
### 카드번호 매칭 로직
```
corporate_cards.card_number ←매칭→ barobill_card_transactions.card_num
(예: "9438-8309-3638-4247") (예: "9438830936384247")
→ 하이픈 제거 후 매칭: str_replace('-', '', $cardNumber)
```
## 데이터베이스 테이블 요약
### 카드 관련
| 테이블 | 역할 |
|--------|------|
| `corporate_cards` | 법인카드 정보 (카드명, 카드사, 번호, 한도 등) |
| `corporate_card_prepayments` | 월별 선불결제 금액 |
| `card_transactions` | 수동 입력 카드거래 |
| `barobill_card_transactions` | 바로빌 자동 수집 카드거래 |
| `barobill_card_transaction_splits` | 카드거래 분개 (1거래→N계정과목) |
| `barobill_card_transaction_hides` | 거래 숨김 처리 |
| `barobill_card_transaction_amount_logs` | 거래 금액 수정 이력 |
### 차량 관련
| 테이블 | 역할 |
|--------|------|
| `corporate_vehicles` | 법인/렌트/리스 차량 정보 |
| `vehicle_logs` | 차량 운행기록 (출발/도착, 거리, 용도) |
| `vehicle_maintenances` | 정비/주유/보험 등 비용 기록 |

View File

@@ -0,0 +1,311 @@
# 카드사용내역
## 개요
카드사용내역은 바로빌 SOAP API를 통해 카드사 거래내역을 실시간 조회하고,
회계 분류(계정과목 지정), 분개(1거래→N계정과목), 거래 숨김, 금액 수정, 수동 거래 등록 등을 지원하는 기능입니다.
- **라우트**: `GET /finance/card-transactions` → 바로빌 EcardController로 리다이렉트
- **실제 페이지**: `GET /barobill/ecard`
- **UI 기술**: React 18 + Babel (브라우저 트랜스파일링)
## 파일 구조
```
mng/
├── app/Http/Controllers/
│ ├── Barobill/
│ │ └── EcardController.php # 메인 컨트롤러 (바로빌 연동)
│ └── Finance/
│ └── CardTransactionController.php # 수동 거래내역 컨트롤러
├── app/Models/Barobill/
│ ├── CardTransaction.php # 바로빌 카드거래 모델
│ ├── CardTransactionSplit.php # 거래 분개 모델
│ ├── CardTransactionHide.php # 거래 숨김 모델
│ └── CardTransactionAmountLog.php # 금액 수정 이력 모델
├── app/Models/Finance/
│ └── CardTransaction.php # 수동 입력 거래 모델
└── resources/views/
├── barobill/ecard/
│ └── index.blade.php # 바로빌 카드거래 React 페이지
└── finance/
└── card-transactions.blade.php # 수동 거래내역 React 페이지
api/
└── database/migrations/
├── 2026_01_23_150000_create_barobill_card_transactions_table.php
├── 2026_01_23_160000_create_barobill_card_transaction_splits_table.php
├── 2026_02_05_100003_create_card_transactions_table.php
├── 2026_02_05_200100_create_barobill_card_transaction_amount_logs_table.php
└── 2026_02_05_500000_create_barobill_card_transaction_hides_table.php
```
## 라우트
### 바로빌 카드거래 (메인)
```php
// routes/web.php (barobill/ecard prefix)
GET / index() React 페이지 렌더링
GET /cards cards() 등록된 카드 목록
GET /transactions transactions() 거래내역 조회 (API+DB 병합)
GET /account-codes accountCodes() 계정과목 목록
POST /save save() 거래 저장 (회계 분류)
POST /export exportExcel() Excel 내보내기
GET /splits splits() 분개 내역 조회
POST /splits saveSplits() 분개 저장
DELETE /splits deleteSplits() 분개 삭제
POST /manual storeManual() 수동 거래 등록
PUT /manual/{id} updateManual() 수동 거래 수정
DELETE /manual/{id} destroyManual() 수동 거래 삭제
POST /hide hideTransaction() 거래 숨김
POST /restore restoreTransaction() 거래 숨김 해제
GET /hidden hiddenTransactions() 숨긴 거래 목록
```
### 수동 거래내역 (보조)
```php
// routes/web.php (card-transactions prefix)
GET /list index() 수동 거래 목록
POST /store store() 수동 거래 등록
PUT /{id} update() 수동 거래 수정
DELETE /{id} destroy() 수동 거래 삭제
```
## 컨트롤러
### EcardController (바로빌 카드거래)
| 메서드 | HTTP | 설명 |
|--------|------|------|
| `index()` | GET | React 페이지 렌더링 (HX-Redirect 적용) |
| `cards()` | GET | 바로빌 등록 카드 목록 (SOAP API) |
| `transactions()` | GET | **거래내역 조회** (API + DB 병합) |
| `accountCodes()` | GET | 계정과목 목록 |
| `save()` | POST | 거래 저장 (계정과목 지정) |
| `exportExcel()` | POST | Excel 내보내기 |
| `splits()` | GET | 분개 내역 조회 |
| `saveSplits()` | POST | 분개 저장 |
| `deleteSplits()` | DELETE | 분개 삭제 |
| `storeManual()` | POST | 수동 거래 등록 |
| `updateManual()` | PUT | 수동 거래 수정 |
| `destroyManual()` | DELETE | 수동 거래 삭제 |
| `hideTransaction()` | POST | 거래 숨김 처리 |
| `restoreTransaction()` | POST | 거래 숨김 해제 |
| `hiddenTransactions()` | GET | 숨긴 거래 목록 |
### CardTransactionController (수동 거래)
| 메서드 | HTTP | 설명 |
|--------|------|------|
| `index()` | GET | 수동 거래 목록 (필터, 통계 포함) |
| `store()` | POST | 거래 등록 |
| `update()` | PUT | 거래 수정 |
| `destroy()` | DELETE | 거래 삭제 (Soft Delete) |
## 바로빌 SOAP API 연동
### 사용 SOAP 메서드
| WSDL | 메서드 | 기능 |
|------|--------|------|
| CARD.asmx | `GetRegisteredCard` | 등록된 카드 목록 조회 |
| CARD.asmx | `GetPeriodCardLog` | 기간별 카드거래 내역 조회 |
### 테넌트별 서버 모드
```
BarobillMember.server_mode → 'test' 또는 'production'
→ BarobillConfig 재로드 → SOAP 클라이언트 재초기화
```
## 핵심 로직
### 거래내역 조회 흐름 (transactions)
```
React 컴포넌트 (카드/기간 선택)
GET /barobill/ecard/transactions
1. BarobillMember 조회 (바로빌 인증 정보)
2. 테넌트별 서버 모드 적용 (test/production)
3. SOAP 호출: GetPeriodCardLog (바로빌 API)
4. DB 저장된 거래와 병합 (barobill_card_transactions)
5. 숨긴 거래 표시 (barobill_card_transaction_hides)
6. 분개 데이터 병합 (barobill_card_transaction_splits)
7. 수동 거래 병합
8. 정렬 + JSON 응답
```
### 거래 고유 키 (unique_key)
```php
// CardTransaction (Barobill) 모델
return implode('|', [
$this->card_num,
$this->use_dt, // 사용일시
$this->approval_num,
$this->approval_amount,
]);
```
### 거래 금액 처리
```
approval_type = '1' → 승인 (+ 금액)
approval_type = '2' → 취소 (- 금액)
modified_supply_amount, modified_tax → 금액 수정 시 원본 대신 사용
```
### 분개 저장 흐름
```
React (분개 모달)
POST /barobill/ecard/splits
{
originalUniqueKey: "카드번호|사용일시|승인번호|금액",
originalData: { cardNum, useDt, ... },
splits: [
{ amount: 50000, accountCode: "1000", accountName: "당좌예금" },
{ amount: 50000, accountCode: "2000", accountName: "미수금" }
]
}
기존 분개 삭제 → 새 분개 생성 × N
```
## 모델
### CardTransaction (Barobill)
**테이블**: `barobill_card_transactions`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `card_num` | string | 카드번호 (하이픈 없음) |
| `card_company` / `card_company_name` | string | 카드사 코드/명 |
| `use_dt` | string | 사용일시 (YYYYMMDDHHMMSS) |
| `use_date` / `use_time` | string | 사용일/시간 분리 |
| `approval_num` | string | 승인번호 |
| `approval_type` | string | 1=승인, 2=취소 |
| `approval_amount` | decimal | 승인금액 |
| `tax` / `service_charge` | decimal | 세금/봉사료 |
| `merchant_name` / `merchant_biz_num` | string | 가맹점명/사업자번호 |
| `account_code` / `account_name` | string | 계정과목 코드/명 |
| `modified_supply_amount` / `modified_tax` | decimal | 수정 공급가/세액 |
| `is_manual` | boolean | 수동 입력 여부 |
- **Unique Index**: `[tenant_id, card_num, use_dt, approval_num, approval_amount]`
### CardTransactionSplit
**테이블**: `barobill_card_transaction_splits`
| 필드 | 타입 | 설명 |
|------|------|------|
| `original_unique_key` | string(200) | 원본 거래 고유 키 |
| `split_amount` | decimal | 분개 금액 |
| `split_supply_amount` / `split_tax` | decimal | 공급가/세액 |
| `account_code` / `account_name` | string | 계정과목 |
| `deduction_type` / `evidence_name` | string | 공제구분/증빙명 |
| `sort_order` | int | 정렬 순서 |
### CardTransactionHide
**테이블**: `barobill_card_transaction_hides`
| 필드 | 타입 | 설명 |
|------|------|------|
| `original_unique_key` | string | 거래 고유 키 |
| `card_num` / `use_date` / `approval_num` | string | 원본 거래 정보 |
| `original_amount` / `merchant_name` | string | 원본 금액/가맹점 |
| `hidden_by` | bigint | 숨김 처리 사용자 |
### CardTransactionAmountLog
**테이블**: `barobill_card_transaction_amount_logs`
| 필드 | 타입 | 설명 |
|------|------|------|
| `card_transaction_id` | bigint | 카드거래 FK |
| `before_supply_amount` / `before_tax` | decimal | 수정 전 금액 |
| `after_supply_amount` / `after_tax` | decimal | 수정 후 금액 |
| `modified_by` / `modified_by_name` | string | 수정자 |
| `ip_address` | string | 수정 IP |
### CardTransaction (Finance - 수동)
**테이블**: `card_transactions`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `card_id` | bigint | 법인카드 FK |
| `transaction_date` | date | 거래일 |
| `time` | string | 거래시간 (HH:MM) |
| `merchant` | string(200) | 가맹점명 |
| `category` | string | 카테고리 |
| `amount` | bigint | 금액 |
| `approval_no` | string | 승인번호 |
| `status` | enum | approved / cancelled |
## 뷰 구성 (React)
### card-transactions.blade.php (수동 거래)
```
┌─ 페이지 헤더 ──────────────────────
│ Excel 내보내기 | 거래 추가 버튼
├─ 통계 카드 (4열) ──────────────────
│ 조회 건수 | 총 사용금액 | 승인 금액 | 취소 금액
├─ 카테고리 사용현황 (수평 바) ──────
│ 식비 | 교통비 | 접대비 | ... (금액 순 정렬)
├─ 필터 영역 ────────────────────────
│ 검색 | 카드 선택 | 카테고리 | 기간 | 상태(전체/승인/취소)
├─ 거래 목록 (날짜별 그룹) ──────────
│ 날짜 헤더 (건수, 소계)
│ ├─ 가맹점명 + 카테고리 아이콘
│ ├─ 시간, 카드명, 카테고리라벨
│ ├─ 금액 (승인=빨강, 취소=파랑)
│ └─ 수정/삭제 버튼
└─ 등록/수정 모달
카드, 날짜, 시간, 가맹점명, 카테고리
금액, 승인번호, 상태, 메모
```
### 카테고리 목록
| 카테고리 | 아이콘 | 색상 |
|---------|--------|------|
| 식비 | Utensils | amber |
| 교통비 | Car | blue |
| 운영비 | Settings | gray |
| 마케팅비 | Megaphone | purple |
| 사무용품 | Paperclip | teal |
| 도서/교육 | BookOpen | indigo |
| 접대비 | Wine | rose |
| 장비구매 | Monitor | cyan |
| 기타 | MoreHorizontal | gray |
## HTMX 호환성
- React 기반 페이지이므로 **HX-Redirect 필요**
- `@push('scripts')` 블록에 React/Babel 스크립트 포함
- HTMX 네비게이션 시 전체 페이지 리로드 필수

View File

@@ -0,0 +1,222 @@
# 법인카드관리
## 개요
법인카드관리는 회사의 법인카드(신용/체크)를 등록하고 관리하는 기능입니다.
카드 CRUD, 결제일 휴일 조정, 바로빌 기반 사용금액 집계, 선불결제 관리 등을 지원합니다.
- **라우트**: `GET /finance/corporate-cards`
- **라우트 이름**: `finance.corporate-cards`
- **UI 기술**: React 18 + Babel (브라우저 트랜스파일링)
## 파일 구조
```
mng/
├── app/Http/Controllers/Finance/
│ └── CorporateCardController.php # 메인 컨트롤러 (7개 메서드)
├── app/Models/Finance/
│ ├── CorporateCard.php # 법인카드 모델
│ └── CorporateCardPrepayment.php # 선불결제 모델
├── app/Models/Barobill/
│ ├── CardTransaction.php # 바로빌 카드거래 모델
│ └── CardTransactionHide.php # 거래 숨김 모델
├── app/Models/System/
│ └── Holiday.php # 휴일 모델
└── resources/views/finance/
└── corporate-cards.blade.php # React 기반 단일 페이지
api/
└── database/migrations/
├── 2026_01_30_180000_create_corporate_cards_table.php
└── 2026_02_11_100000_create_corporate_card_prepayments_table.php
```
## 라우트
```php
// routes/web.php (finance prefix 그룹 내)
Route::get('/corporate-cards', fn() => ...)->name('corporate-cards');
// API 라우트 (corporate-cards prefix)
GET /list index() 카드 목록 조회
GET /summary summary() 요약 데이터 (결제일, 사용금액 )
POST /prepayment updatePrepayment() 선불결제 수정
POST /store store() 카드 등록
PUT /{id} update() 카드 수정
POST /{id}/deactivate deactivate() 카드 비활성화
DELETE /{id} destroy() 카드 영구삭제
```
## 컨트롤러
### CorporateCardController
| 메서드 | HTTP | 설명 |
|--------|------|------|
| `index()` | GET | 카드 목록 JSON 반환 |
| `store()` | POST | 카드 등록 |
| `update()` | PUT | 카드 수정 |
| `deactivate()` | POST | 비활성화 (status → inactive) |
| `destroy()` | DELETE | 영구삭제 (forceDelete) |
| `summary()` | GET | **요약 데이터** (결제일, 사용금액, 선불결제) |
| `updatePrepayment()` | POST | 선불결제 금액 수정 (upsert) |
### summary() 응답 구조
```json
{
"success": true,
"data": {
"paymentDate": "2026-02-19",
"paymentDay": 15,
"originalDate": "2026-02-15",
"isAdjusted": true,
"billingPeriod": { "start": "2026-01-01", "end": "2026-02-19" },
"billingUsage": 3500000,
"cardUsages": { "9438830936384247": 1200000, "1234567890123456": 2300000 },
"prepaidAmount": 500000,
"prepaidMemo": "2월 선결제"
}
}
```
## 핵심 로직
### 휴일 조정 결제일 계산 (getAdjustedPaymentDate)
```
당월 payment_day (예: 15일)
Carbon 날짜 생성
holidays 테이블에서 해당 기간 휴일 조회
토/일/공휴일이면 다음 날로 반복 이동
영업일 결제일 반환 (예: 15일→토→17일(월))
```
### 사용금액 계산 (calculateBillingUsage)
```
청구기간: 전월 1일 ~ 당월 결제일(휴일조정)
corporate_cards에서 활성 카드번호 수집
카드번호 정규화 (하이픈 제거)
barobill_card_transactions 조회 (use_date 범위)
숨긴 거래 제외 (barobill_card_transaction_hides)
approval_type별 합산:
- '1' (승인): + approval_amount
- '2' (취소): - approval_amount
총 사용금액 반환
```
### 잔여 한도 계산
```
잔여 한도 = 총 한도 - 사용금액 + 선불결제
```
## 모델
### CorporateCard
**테이블**: `corporate_cards`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `card_name` | string(100) | 카드명 (예: "업무용 법인카드") |
| `card_company` | string(50) | 카드사 (삼성, 현대 등) |
| `card_number` | string(30) | 카드번호 (하이픈 포함) |
| `card_type` | enum | credit / debit |
| `payment_day` | tinyint | 결제일 (기본: 15) |
| `credit_limit` | decimal(15,2) | 사용한도 |
| `current_usage` | decimal(15,2) | 현재 사용액 |
| `card_holder_name` | string(100) | 이용자명 (법인 명의) |
| `actual_user` | string(100) | 실사용자 |
| `expiry_date` | string(10) | 유효기간 (YY/MM) |
| `cvc` | string(10) | CVC |
| `status` | enum | active / inactive |
| `memo` | text | 메모 |
#### 주요 Scope
```php
->active() // status = 'active'
->forTenant($id) // tenant_id 필터
```
### CorporateCardPrepayment
**테이블**: `corporate_card_prepayments`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `year_month` | string(7) | "2026-02" 형식 |
| `amount` | decimal(15,0) | 선불결제 금액 |
| `memo` | string(200) | 메모 |
- Unique: `[tenant_id, year_month]`
- `getOrCreate(tenantId, yearMonth)`: 조회 또는 기본값(0) 생성
## 뷰 구성 (React)
### corporate-cards.blade.php
```
┌─ 페이지 헤더 ──────────────────────
│ 제목: "법인카드 등록/조회"
│ 버튼: "카드 등록" (보라색)
├─ 요약 카드 (2열 / lg:6열) ─────────
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│ │등록카드│ │총 한도│ │매월 │ │사용 │ │선불 │ │잔여 │
│ │ │ │ │ │결제일 │ │금액 │ │결제 │ │한도 │
│ │3장 │ │50백만│ │2/19 │ │35백만│ │5백만 │ │20백만│
│ │활성2 │ │신용 │ │(수) │ │1/1~ │ │[수정]│ │한도- │
│ │ │ │기준 │ │15→조정│ │2/19 │ │ │ │사용+ │
│ │ │ │ │ │ │ │기준 │ │ │ │선결제│
│ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘
├─ 검색 및 필터 ─────────────────────
│ 검색창 | 전체 | 활성 | 비활성
├─ 카드 목록 테이블 ─────────────────
│ 카드명(카드사) | 카드번호 | 실사용자 | 사용현황(진행바) | 상태
│ └─ 신용: 사용률 % + 프로그레스 바
│ └─ 체크: 사용금액 표시
├─ 등록/수정 모달 ───────────────────
│ 카드명, 카드사, 카드종류, 카드번호, 이용자명
│ 유효기간, CVC, 상태, 실사용자
│ 결제일(신용만), 사용한도(신용만), 메모
│ [비활성화] [영구삭제] [취소] [등록/저장]
└─ 선불결제 수정 모달 ───────────────
금액 입력 (3자리 콤마), 메모 (선택)
[취소] [저장]
```
### React 주요 기능
| 기능 | 설명 |
|------|------|
| 요약 카드 6개 | 등록카드, 총한도, 매월결제일, 사용금액, 선불결제, 잔여한도 |
| 카드별 사용률 | billingUsage 기준 프로그레스 바 표시 |
| 선불결제 인라인 수정 | 수정 버튼 → 모달 → 금액/메모 입력 → 즉시 반영 |
| 상태 필터 | 전체 / 활성 / 비활성 |
| 카드 검색 | 카드명, 카드사, 이용자명, 실사용자 검색 |
## HTMX 호환성
- React 기반 페이지이므로 **HX-Redirect 필요**
- `@push('scripts')` 블록에 React/Babel 스크립트 포함
- HTMX 네비게이션 시 전체 페이지 리로드 필수

View File

@@ -0,0 +1,163 @@
# 차량목록
## 개요
차량목록은 회사의 법인차량(법인/렌트/리스)을 등록하고 관리하는 기능입니다.
소유형태별 차량 등록, 총 주행거리 자동 계산, 차량 상태 관리 등을 지원합니다.
- **라우트**: `GET /finance/corporate-vehicles`
- **UI 기술**: React 18 + Babel (브라우저 트랜스파일링)
## 파일 구조
```
mng/
├── app/Http/Controllers/Finance/
│ └── CorporateVehicleController.php # 메인 컨트롤러 (5개 메서드)
├── app/Models/
│ └── CorporateVehicle.php # 차량 모델
└── resources/views/finance/
└── corporate-vehicles.blade.php # React 기반 단일 페이지
api/
└── database/migrations/
└── 2026_02_02_220000_create_corporate_vehicles_table.php
```
## 라우트
```php
// routes/web.php (finance prefix 그룹 내)
GET /corporate-vehicles index() 페이지 렌더링
GET /corporate-vehicles/list list() 차량 목록 (JSON)
POST /corporate-vehicles store() 차량 등록
PUT /corporate-vehicles/{id} update() 차량 수정
DELETE /corporate-vehicles/{id} destroy() 차량 삭제
```
## 컨트롤러
### CorporateVehicleController
| 메서드 | HTTP | 설명 |
|--------|------|------|
| `index()` | GET | React 페이지 렌더링 (HX-Redirect 적용) |
| `list()` | GET | 차량 목록 (필터 + 총 주행거리 계산) |
| `store()` | POST | 차량 등록 |
| `update()` | PUT | 차량 수정 |
| `destroy()` | DELETE | 차량 삭제 |
### list() 핵심 로직
```
필터 파라미터: ownership_type, vehicle_type, status, search
CorporateVehicle::where(tenant_id, ...)
각 차량별 총 주행거리 계산:
total_mileage = initial_mileage + SUM(vehicle_logs.distance_km)
JSON 응답
```
## 모델
### CorporateVehicle
**테이블**: `corporate_vehicles`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `plate_number` | string(20) | 차량번호 |
| `model` | string(100) | 차량 모델명 |
| `vehicle_type` | string(20) | 차종 |
| `ownership_type` | enum | **corporate** / rent / lease |
| `year` | year | 연식 |
| `driver` | string(50) | 주 운전자 |
| `status` | enum | **active** / maintenance / disposed |
| `mileage` | int | 현재 주행거리 (정비 기준) |
| `memo` | text | 메모 |
#### 법인 전용 필드
| 필드 | 타입 | 설명 |
|------|------|------|
| `purchase_date` | date | 취득일자 |
| `purchase_price` | int | 취득가액 |
#### 렌트/리스 전용 필드
| 필드 | 타입 | 설명 |
|------|------|------|
| `contract_date` | date | 계약일자 |
| `rent_company` | string | 렌트/리스사 |
| `rent_company_tel` | string | 연락처 |
| `rent_period` | string | 계약기간 |
| `agreed_mileage` | int | 약정주행거리 |
| `vehicle_price` | int | 차량가액 |
| `residual_value` | int | 잔존가치 |
| `deposit` | int | 보증금 |
| `monthly_rent` | int | 월 렌트료 |
| `monthly_rent_tax` | int | 부가세 |
| `insurance_company` | string | 보험사 |
| `insurance_company_tel` | string | 보험사 연락처 |
#### Traits / Scope
- `SoftDeletes` 적용
- Casts: year, mileage, purchase_price, vehicle_price, residual_value, deposit, monthly_rent, monthly_rent_tax → integer
## 뷰 구성 (React)
### corporate-vehicles.blade.php
```
┌─ 페이지 헤더 ──────────────────────
│ 제목: "차량목록"
│ 검색 | Excel 다운로드 | 차량 등록 버튼
├─ 요약 카드 (4열) ──────────────────
│ 총 차량 | 법인 취득가 | 월 렌트/리스비 | 총 주행거리
├─ 필터 바 ──────────────────────────
│ 소유형태: 전체 | 법인 | 렌트 | 리스
│ 상태: 전체 | 운행중 | 정비중 | 처분
├─ 차량 목록 테이블 (12열 그리드) ──
│ 차량번호 | 차종/모델 | 소유형태 | 운전자 | 주행거리 | 상태
│ └─ 소유형태: 법인(보라), 렌트(파랑), 리스(초록) 배지
│ └─ 상태: 운행중(초록), 정비중(노랑), 처분(빨강) 배지
├─ 등록/수정 모달 ───────────────────
│ 차량번호, 모델명, 차종, 소유형태
│ 연식, 주 운전자, 상태, 메모
│ ─────────────────────────
│ [법인일 때] 취득일자, 취득가액
│ [렌트/리스일 때] 계약일, 렌트사, 계약기간,
│ 약정주행, 차량가액, 잔존가치, 보증금,
│ 월렌트료, 부가세, 보험사
│ ─────────────────────────
│ [삭제] [취소] [등록/저장]
└─ 비어있을 때: "등록된 차량이 없습니다" 안내
```
## 데이터 흐름
```
CorporateVehicle (1) ← (N) VehicleLog (총 주행거리 합산)
← (N) VehicleMaintenance (주행거리 갱신)
```
### 주행거리 계산 방식
```
총 주행거리 = CorporateVehicle.mileage (정비 시 갱신 기준값)
+ SUM(vehicle_logs.distance_km) (운행일지 거리 합계)
```
## HTMX 호환성
- React 기반 페이지이므로 **HX-Redirect 필요**
- `@push('scripts')` 블록에 React/Babel 스크립트 포함

View File

@@ -0,0 +1,179 @@
# 차량일지
## 개요
차량일지는 법인차량의 운행기록을 관리하는 기능입니다.
출발/도착지, 운행 거리, 용도(출퇴근/업무/비업무), 월별 통계 등을 지원합니다.
- **라우트**: `GET /finance/vehicle-logs`
- **UI 기술**: React 18 + Babel (브라우저 트랜스파일링)
## 파일 구조
```
mng/
├── app/Http/Controllers/Finance/
│ └── VehicleLogController.php # 메인 컨트롤러 (7개 메서드)
├── app/Models/
│ └── VehicleLog.php # 운행기록 모델
└── resources/views/finance/
└── vehicle-logs.blade.php # React 기반 단일 페이지
api/
└── database/migrations/
├── 2026_02_03_100000_create_vehicle_logs_table.php
└── 2026_02_03_133000_modify_vehicle_logs_trip_type_enum.php
```
## 라우트
```php
// routes/web.php (finance prefix 그룹 내)
GET /vehicle-logs index() 페이지 렌더링
GET /vehicle-logs/vehicles vehicles() 차량 목록 (드롭다운용)
GET /vehicle-logs/list list() 운행기록 목록 (JSON)
GET /vehicle-logs/summary summary() 월별 통계
POST /vehicle-logs store() 기록 등록
PUT /vehicle-logs/{id} update() 기록 수정
DELETE /vehicle-logs/{id} destroy() 기록 삭제
```
## 컨트롤러
### VehicleLogController
| 메서드 | HTTP | 설명 |
|--------|------|------|
| `index()` | GET | React 페이지 렌더링 (HX-Redirect 적용) |
| `vehicles()` | GET | 전체 차량 목록 (드롭다운 선택용) |
| `list()` | GET | 운행기록 목록 (필터, 검색) |
| `summary()` | GET | 월별 용도별 통계 (건수, 거리) |
| `store()` | POST | 운행기록 등록 |
| `update()` | PUT | 운행기록 수정 |
| `destroy()` | DELETE | 운행기록 삭제 |
### list() 필터 파라미터
| 파라미터 | 설명 |
|---------|------|
| `vehicle_id` | 차량 ID |
| `year` / `month` | 연/월 |
| `trip_type` | 운행 용도 |
| `search` | 검색 (운전자, 부서, 출발지, 도착지, 비고) |
### summary() 응답 구조
```json
{
"success": true,
"data": [
{ "trip_type": "business", "count": 15, "total_distance": 450 },
{ "trip_type": "commute_to", "count": 22, "total_distance": 330 },
{ "trip_type": "commute_from", "count": 22, "total_distance": 330 },
{ "trip_type": "personal", "count": 3, "total_distance": 45 }
]
}
```
## 모델
### VehicleLog
**테이블**: `vehicle_logs`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `vehicle_id` | bigint | 차량 FK (corporate_vehicles) |
| `log_date` | date | 운행일 |
| `department` | string(50) | 부서 |
| `driver_name` | string(50) | 운전자명 |
| `trip_type` | enum | 운행 용도 (아래 참조) |
| `departure_type` | string | 출발지 유형 |
| `departure_name` | string | 출발지명 |
| `departure_address` | string | 출발지 주소 |
| `arrival_type` | string | 도착지 유형 |
| `arrival_name` | string | 도착지명 |
| `arrival_address` | string | 도착지 주소 |
| `distance_km` | int | 운행거리 (km) |
| `note` | string(200) | 비고 |
#### 운행 용도 (trip_type)
| 값 | 설명 |
|----|------|
| `commute_to` | 출근용 |
| `commute_from` | 퇴근용 |
| `business` | 업무용 |
| `personal` | 비업무 |
| `commute_round` | 출퇴근 왕복 |
| `business_round` | 업무용 왕복 |
| `personal_round` | 비업무 왕복 |
#### 위치 유형 (departure_type / arrival_type)
| 값 | 설명 |
|----|------|
| `home` | 자택 |
| `office` | 회사 |
| `client` | 거래처 |
| `other` | 기타 |
#### Relationships
```php
$log->vehicle // BelongsTo CorporateVehicle
$log->tenant // BelongsTo Tenant
```
#### Static 메서드
```php
VehicleLog::getTripTypes() // 용도 라벨 배열
VehicleLog::getLocationTypes() // 위치 유형 라벨 배열
VehicleLog::getNoteOptions() // 미리 정의된 비고 옵션
```
## 뷰 구성 (React)
### vehicle-logs.blade.php
```
┌─ 페이지 헤더 ──────────────────────
│ 차량 선택 드롭다운 (현재 주행거리 표시)
│ 연/월 선택 | Excel 다운로드 | 기록 추가 버튼
├─ 용도별 통계 카드 ─────────────────
│ 출근 | 퇴근 | 업무 | 비업무
│ 건수 + 총 거리(km)
├─ 운행기록 테이블 ──────────────────
│ 날짜 | 차량 | 부서/이름 | 용도 | 출발지 | 도착지 | 거리 | 비고
│ └─ 용도: 출근(초록), 퇴근(파랑), 업무(보라), 비업무(회색) 배지
│ └─ 복사 버튼 (이전 기록 복제)
│ └─ 출발↔도착 교환 버튼 (trip_type도 자동 전환)
├─ 등록/수정 모달 ───────────────────
│ 차량 선택, 날짜, 부서, 운전자, 용도
│ 출발지: 유형 + 이름 + 주소
│ 도착지: 유형 + 이름 + 주소
│ 운행거리(km)
│ 비고: 미리 정의된 옵션 버튼 + 자유 입력
│ [삭제] [취소] [등록/저장]
└─ 비어있을 때: 안내 메시지
```
### 특수 기능
| 기능 | 설명 |
|------|------|
| 기록 복사 | 이전 운행기록을 새 날짜로 복제 |
| 출발↔도착 교환 | 출발지와 도착지를 스왑, trip_type도 자동 전환 (commute_to ↔ commute_from) |
| 미리 정의 비고 | 버튼 클릭으로 빠른 비고 입력 + 자유 텍스트 |
| 용도별 통계 | 월별 용도별 건수 및 총 거리 집계 |
## HTMX 호환성
- React 기반 페이지이므로 **HX-Redirect 필요**
- `@push('scripts')` 블록에 React/Babel 스크립트 포함

View File

@@ -0,0 +1,203 @@
# 정비이력
## 개요
정비이력은 법인차량의 정비, 주유, 보험, 세차, 주차, 통행료 등 비용을 기록하고 관리하는 기능입니다.
카테고리별 비용 집계, 주행거리 자동 갱신, 기간별 필터링 등을 지원합니다.
- **라우트**: `GET /finance/vehicle-maintenance`
- **UI 기술**: React 18 + Babel (브라우저 트랜스파일링)
## 파일 구조
```
mng/
├── app/Http/Controllers/Finance/
│ └── VehicleMaintenanceController.php # 메인 컨트롤러 (6개 메서드)
├── app/Models/
│ └── VehicleMaintenance.php # 정비이력 모델
└── resources/views/finance/
└── vehicle-maintenance.blade.php # React 기반 단일 페이지
api/
└── database/migrations/
└── 2026_02_03_195000_create_vehicle_maintenances_table.php
```
## 라우트
```php
// routes/web.php (finance prefix 그룹 내)
GET /vehicle-maintenance index() 페이지 렌더링
GET /vehicle-maintenance/vehicles vehicles() 차량 목록 (드롭다운용)
GET /vehicle-maintenance/list list() 정비이력 목록 (JSON)
POST /vehicle-maintenance store() 정비 등록
PUT /vehicle-maintenance/{id} update() 정비 수정
DELETE /vehicle-maintenance/{id} destroy() 정비 삭제
```
## 컨트롤러
### VehicleMaintenanceController
| 메서드 | HTTP | 설명 |
|--------|------|------|
| `index()` | GET | React 페이지 렌더링 (HX-Redirect 적용) |
| `vehicles()` | GET | 전체 차량 목록 (드롭다운 선택용) |
| `list()` | GET | 정비이력 목록 (필터, 검색, 차량 관계 포함) |
| `store()` | POST | 정비 등록 + **차량 주행거리 갱신** |
| `update()` | PUT | 정비 수정 + **차량 주행거리 갱신** |
| `destroy()` | DELETE | 정비 삭제 |
### list() 필터 파라미터
| 파라미터 | 설명 |
|---------|------|
| `vehicle_id` | 차량 ID |
| `category` | 카테고리 (주유, 정비, 보험 등) |
| `start_date` / `end_date` | 기간 (기본: 최근 3개월) |
| `search` | 검색 (설명, 업체명, 메모) |
### 주행거리 자동 갱신
```php
// store() / update() 에서
if ($request->mileage) {
$vehicle = CorporateVehicle::find($request->vehicle_id);
$vehicle->update(['mileage' => $request->mileage]);
}
```
정비 등록/수정 시 입력한 주행거리가 해당 차량의 기준 주행거리(mileage)를 갱신합니다.
## 모델
### VehicleMaintenance
**테이블**: `vehicle_maintenances`
| 필드 | 타입 | 설명 |
|------|------|------|
| `tenant_id` | bigint | 테넌트 ID |
| `vehicle_id` | bigint | 차량 FK (corporate_vehicles) |
| `date` | date | 정비일 |
| `category` | string(20) | 카테고리 |
| `description` | string(200) | 설명 |
| `amount` | bigint | 금액 |
| `mileage` | int | 정비 시 주행거리 |
| `vendor` | string(100) | 업체명 |
| `memo` | text | 메모 |
#### 카테고리 목록
| 카테고리 | 설명 | 아이콘/색상 |
|---------|------|------------|
| `주유` | 연료비 | Fuel / amber |
| `정비` | 차량 정비/수리 | Wrench / blue |
| `보험` | 자동차 보험료 | Shield / emerald |
| `세차` | 세차 비용 | Droplets / cyan |
| `주차` | 주차 비용 | ParkingCircle / purple |
| `통행료` | 고속도로 등 통행료 | Route / orange |
| `검사` | 정기검사/점검 | ClipboardCheck / indigo |
| `기타` | 기타 비용 | MoreHorizontal / gray |
#### Relationships
```php
$maintenance->vehicle // BelongsTo CorporateVehicle
$maintenance->tenant // BelongsTo Tenant
```
#### Static 메서드
```php
VehicleMaintenance::getCategories()
// ['주유', '정비', '보험', '세차', '주차', '통행료', '검사', '기타']
```
## 뷰 구성 (React)
### vehicle-maintenance.blade.php
```
┌─ 페이지 헤더 ──────────────────────
│ 제목: "정비이력"
│ 새로고침 | Excel 다운로드 | 정비 등록 버튼
├─ 요약 카드 (4열) ──────────────────
│ 총 정비비용 | 주유비 | 정비비 | 기타비용
│ (기간 내 합산)
├─ 필터 영역 ────────────────────────
│ 기간: 시작일 ~ 종료일 (기본: 최근 3개월)
│ 카테고리: 전체 | 주유 | 정비 | 보험 | 세차 | 주차 | 통행료 | 검사 | 기타
│ 차량: 드롭다운 선택
│ 검색: 설명, 업체명 검색
├─ 정비이력 테이블 ──────────────────
│ 날짜 | 차량 | 카테고리 | 설명 | 금액 | 주행거리 | 작업
│ └─ 카테고리: 컬러 배지 (카테고리별 고유 색상)
│ └─ 금액: 원 단위 포맷
│ └─ 작업: 수정/삭제 버튼
├─ 등록/수정 모달 ───────────────────
│ 차량 선택, 날짜, 카테고리
│ 설명, 금액, 주행거리, 업체명, 메모
│ [삭제] [취소] [등록/저장]
└─ 비어있을 때: 안내 메시지
```
## 핵심 로직
### 비용 집계
```
요약 카드 계산:
총 정비비용 = 조회 기간 내 모든 정비 금액 합계
주유비 = category === '주유' 인 금액 합계
정비비 = category === '정비' 인 금액 합계
기타비용 = 나머지 카테고리 금액 합계
```
### 차량 주행거리 연동
```
정비 등록/수정 시 mileage 입력
해당 차량(corporate_vehicles)의 mileage 필드 갱신
차량목록에서 총 주행거리 재계산:
total_mileage = mileage + SUM(vehicle_logs.distance_km)
```
## 데이터베이스 스키마
### vehicle_maintenances
```sql
CREATE TABLE vehicle_maintenances (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT NOT NULL,
vehicle_id BIGINT NOT NULL,
date DATE NOT NULL,
category VARCHAR(20) NOT NULL,
description VARCHAR(200),
amount BIGINT UNSIGNED DEFAULT 0,
mileage INT UNSIGNED,
vendor VARCHAR(100),
memo TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
deleted_at TIMESTAMP,
INDEX (tenant_id, vehicle_id, date),
INDEX (tenant_id, category),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
FOREIGN KEY (vehicle_id) REFERENCES corporate_vehicles(id) ON DELETE CASCADE
);
```
## HTMX 호환성
- React 기반 페이지이므로 **HX-Redirect 필요**
- `@push('scripts')` 블록에 React/Babel 스크립트 포함