## 계좌별 잔액 계산 로직 (개발자 문서)

### 목적
- 본 문서는 `account/list.php`에서 구현된 계좌별 잔액 계산 원리를 정리합니다.
- 다른 회사(다른 DB/스키마)에도 동일한 원리로 구현할 수 있도록 단계별 절차와 AI 지시 템플릿을 제공합니다.

### 전제 및 데이터 모델 요약
- 테이블: `account` (변수 `$_POST['tablename']` 또는 `$tablename`으로 주입)
- 핵심 컬럼
  - `registDate`: 거래 등록일자 (yyyy-mm-dd)
  - `inoutsep`: 구분 (수입 | 지출 | 최초전월이월)
  - `amount`: 금액 (문자열 저장, 천단위 콤마 포함 가능)
  - `bankbook`: 계좌 표시명 (예: 기업은행 123-456-7890 (법인), 전자어음 등)
  - `dueDate`: 만기일자 (어음성 항목에 사용, 없는 경우 '0000-00-00' 또는 NULL 또는 빈문자)
  - `endorsementDate`: 배서일자 (전자어음 지출 시 사용)
  - `is_deleted`: 논리 삭제 여부 (0만 유효 데이터)
- 금액 계산 시 DB 내 `amount`가 문자열+콤마 형태이므로 SQL에서 `REPLACE(amount, ',', '')`로 숫자 변환 후 합산합니다.
- 어음 처리: 기본 합산에서 어음(만기설정)의 잔액 반영을 제한하고, 전자어음은 별도 규칙을 적용합니다(아래 참조).

### 전체(기간 기반) 집계 로직
1) 기간 시작 이전의 초기 잔액
```sql
SELECT 
  SUM(CASE WHEN inoutsep = '수입' THEN REPLACE(amount, ',', '') ELSE 0 END) +
  SUM(CASE WHEN inoutsep = '최초전월이월' THEN REPLACE(amount, ',', '') ELSE 0 END) -
  SUM(CASE WHEN inoutsep = '지출' THEN REPLACE(amount, ',', '') ELSE 0 END) AS balance
FROM account
WHERE is_deleted = '0'
  AND registDate < :fromdate
  AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')
```

2) 기간 내 수입 합계(최초전월이월 포함)
```sql
SELECT SUM(REPLACE(amount, ',', '')) AS totalIncome 
FROM account
WHERE is_deleted = '0'
  AND (inoutsep = '수입' OR inoutsep = '최초전월이월')
  AND registDate BETWEEN :fromdate AND :todate
  AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')
```

3) 기간 내 지출 합계
```sql
SELECT SUM(REPLACE(amount, ',', '')) AS totalExpense 
FROM account
WHERE is_deleted = '0'
  AND inoutsep = '지출'
  AND registDate BETWEEN :fromdate AND :todate
  AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')
```

4) 최종 잔액 계산
```php
$finalBalance = $initialBalance + $totalIncome - $totalExpense;
```

요점
- 초기 잔액은 기간 시작 이전의 누적(수입+전월이월-지출)입니다.
- 기간 내 수입/지출은 어음 미반영(만기 미설정 또는 공백만 포함) 기준으로 합산합니다.
- 최종 잔액은 초기 + (기간 내 수입) - (기간 내 지출) 입니다.

### 계좌별 집계(전체 기간 최종 잔액)
- 모든 기간을 대상으로 계좌별 순증감(수입+전월이월-지출)을 합산합니다.
```sql
SELECT 
  (SUM(CASE WHEN inoutsep IN ('수입','최초전월이월') THEN REPLACE(amount, ',', '') ELSE 0 END) -
   SUM(CASE WHEN inoutsep = '지출' THEN REPLACE(amount, ',', '') ELSE 0 END)) AS balance
FROM account
WHERE is_deleted = '0'
  AND bankbook = :bankbook
  AND (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')
```

표시 규칙
- 계좌명에 'USD'가 포함되면 금액 앞에 `$`를 붙여 표시합니다(회사 설정에 따라 변경 가능).

### 계좌별 기간 요약(초기/기간 내 수입/지출/최종)
각 계좌별로 아래 4단계를 수행합니다.
1) 기간 시작 이전 잔액(초기)
2) 기간 내 수입 (수입, 최초전월이월)
3) 기간 내 지출
4) 최종 = 초기 + 수입 - 지출

각 단계는 위의 전체 집계와 동일한 필터를 사용하며 `bankbook = :bankbook` 조건만 추가하면 됩니다.

### 목록 테이블의 행별 러닝 잔액(계좌별)
1) 페이지 로딩 시, 각 계좌별로 "기간 시작 이전" 기준의 초기 잔액을 구해 `runningBalances[계좌명]`에 저장합니다.
2) 목록을 순차로 렌더링하며, (어음이 아닌 경우에 한해) 해당 계좌의 수입/지출에 따라 러닝 잔액을 가감합니다.
3) 전자어음은 계좌명이 '전자어음'인 경우에 한해 별도 규칙으로 러닝 잔액 반영을 허용합니다.

어음 반영 조건
```php
// dueDate가 비어있거나 '0000-00-00'이면 현금성으로 간주하여 러닝 잔액에 반영
// 또는 계좌가 '전자어음'이면 러닝 잔액에 반영
if ($dueDate === '0000-00-00' || !$dueDate || $dueDate === '' || $bankbook === '전자어음') {
    if ($row['inoutsep'] === '수입' || $row['inoutsep'] === '최초전월이월') {
        $runningBalances[$bankbook] += $amount;
    } else {
        $runningBalances[$bankbook] -= $amount;
    }
}
```

### 계좌 목록 소스
- 파일: `/account/accoutlist.json` (오타 주의: accoutlist.json)
- 구조 예시
```json
[
  { "company": "기업은행", "number": "123-456-7890", "memo": "법인" },
  { "company": "신한은행", "number": "111-222-3333", "memo": "일반" }
]
```
- 화면 및 계산 시 표기는 `company + ' ' + number (+ ' (' + memo + ')')` 조합 문자열을 사용합니다.

### 표시 포맷 규칙
- 금액은 `number_format()`으로 천단위 콤마 표시
- 계좌명이 'USD'를 포함하면 `$' + number_format()`으로 표기

### 구현 체크리스트 (PHP 7.3 호환)
- [ ] 모든 합산 SQL에서 `REPLACE(amount, ',', '')`로 문자열 금액을 정수로 변환
- [ ] `is_deleted = 0` 필터 일관 유지
- [ ] 어음성 거래 제외 조건: `(dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '')`
- [ ] 러닝 잔액 반영 시 전자어음(`bankbook === '전자어음'`) 예외 허용
- [ ] `accoutlist.json` 파싱 및 표기 문자열 일치
- [ ] USD 계좌 표기 시 `$` 접두사 적용
- [ ] PDO 바인딩 사용 및 SQL 인젝션 방지
- [ ] PHP 7.3 문법/함수만 사용 (타입힌트 과도 사용 지양, 최신 문법 미사용)

### 다른 회사 코드에 이식하기 (필드명/테이블명 상이 시)
1) 테이블/컬럼 매핑 정의
   - `TABLE_ACCOUNT`, `COL_DATE`, `COL_INOUT`, `COL_AMOUNT`, `COL_BANKBOOK`, `COL_DUE`, `COL_DELETE`
2) 모든 SQL에서 위 매핑을 사용하여 동등한 쿼리 작성
3) 금액은 문자열+콤마 저장 전제 → `REPLACE()` 사용 또는 마이그레이션
4) 어음 제외 조건/전자어음 예외를 비즈니스 규칙에 맞게 조정
5) 계좌 표기 포맷(회사명/계좌번호/메모) 규칙 수립
6) 통화 표기(USD 등) 접두사/접미사 정책 정의 후 적용

예시 의사코드
```sql
-- 초기 잔액 (기간 시작 이전)
SELECT
  SUM(CASE WHEN COL_INOUT IN ('수입','최초전월이월') THEN REPLACE(COL_AMOUNT, ',', '') ELSE 0 END) -
  SUM(CASE WHEN COL_INOUT = '지출' THEN REPLACE(COL_AMOUNT, ',', '') ELSE 0 END) AS balance
FROM TABLE_ACCOUNT
WHERE COL_DELETE = 0 AND COL_DATE < :fromdate
  AND (COL_DUE = '0000-00-00' OR COL_DUE IS NULL OR COL_DUE = '')

-- 기간 내 수입/지출 및 최종 잔액, 계좌별 요약은 동일한 패턴
```

### 인공지능(AI) 지시 템플릿
아래 지시문을 프로젝트 상황에 맞게 값만 치환하여 사용하세요.

```
당신은 PHP 7.3 호환 코드를 작성하는 백엔드 개발자입니다.
목표: [TABLE_ACCOUNT]에서 기간별 전체 잔액과 계좌별 잔액 요약, 목록용 러닝 잔액을 계산하는 기능을 구현하세요.

요구사항:
- 공통 필터: is_deleted = 0. 어음 제외 조건: (dueDate = '0000-00-00' OR dueDate IS NULL OR dueDate = '').
- 초기 잔액: 기간 시작 이전의 (수입 + 최초전월이월 - 지출).
- 기간 내 합계: 수입(수입 + 최초전월이월), 지출.
- 최종 잔액: 초기 + 수입 - 지출.
- 계좌별 요약: 각 계좌에 대해 초기/수입/지출/최종을 별도 계산.
- 목록용 러닝 잔액: 각 계좌의 초기 잔액으로 시작하여 행을 순회하며 가감. 단, 전자어음은 계좌명이 '전자어음'이면 반영 허용.
- 금액은 문자열+콤마 저장 전제이므로 SQL에서 REPLACE로 정수 변환.
- 표기: 천단위 콤마, 계좌명이 'USD' 포함 시 금액 앞에 '$' 접두사.

입력 변수:
- fromdate, todate (yyyy-mm-dd), 선택 필터: inoutsep_select, content_select

출력:
- finalBalance, accountFinalBalances[], accountSummaries[], 목록 행별 runningBalances 적용 결과

검증:
- 샘플 데이터로 수작업 계산값과 SQL 집계 결과 일치 검증
- dueDate가 설정된 어음 거래가 합계에서 적절히 제외되는지 확인
```

### 성능/운영 팁
- `registDate`, `inoutsep`, `bankbook`, `is_deleted`, `dueDate`에 조합 인덱스 고려
- 금액은 가능하면 정수 컬럼으로 정규화(문자열 저장 유지 시 `REPLACE` 비용 감안)
- 대용량 환경에서 계좌 요약은 캐시 또는 미리 집계 테이블 고려

### 관련 파일
- `account/list.php`: 화면 및 집계 로직 구현
- `account/accoutlist.json`: 계좌 목록 소스
- `account/fetch_modal.php`: 항목/세부항목 옵션 로딩
- `account/insert.php`, `account/insert_bulk.php`: 등록/대량등록 처리


