feat(expense+admin): 결재 워크플로 + 첨부 + 집계 + 엑셀 + 사용자 직접 등록
[admin]
- POST /api/users 사용자 직접 등록 (이메일만으로). UserStore.create_user 추가
- admin.html: 등록 폼 + 삭제 버튼 + 한글 라벨 + 신규 권한 표시
- 신규 권한 키: expense_approver, vacation_approver (APPROVER_KEYS)
- is_approver(user_rec, kind) 헬퍼
[expense 워크플로]
- 상태 전이: 작성중 → 제출 → 승인/반려 → 정산완료
- API: /api/items/{id}/{submit,revert,approve,reject,settle}
- 권한: submit/revert=owner, approve/reject/settle=expense_approver 또는 admin
- expense_items 컬럼: approver_email, decided_at, reject_reason
- 작성중/반려 상태에서만 수정/삭제/첨부 가능
[expense 첨부]
- 영수증/기타 (kind: receipt|other). 최대 20MB, 확장자 화이트리스트
- 저장: DATA_DIR/uploads/expense/{item_id}/{att_id}_{filename}
- expense_attachments 테이블 (CASCADE on item delete)
- API: 업로드/목록/다운로드/삭제
[expense 집계]
- /expense/reports: 연도별 월×카테고리 피벗 표
[expense 엑셀]
- GET /expense/api/export.xlsx?from&to&scope=mine|all
- scope=all 은 승인자/관리자만
[DDL]
- expense_db_init.sql: 신규 컬럼/테이블 포함 (멱등)
- expense_db_002_workflow_attachments.sql: 운영 DB 마이그레이션용
[deps]
- openpyxl>=3.1, python-multipart>=0.0.20
[docs]
- DATABASES.md: 워크플로 다이어그램, attachments 테이블, 마이그레이션
- PROJECT_OVERVIEW.md: 권한 키 표
- DEPLOYMENT.md: DATA_DIR/uploads 안내
This commit is contained in:
@@ -105,10 +105,54 @@ DDL: `scripts/sql/expense_db_init.sql` (멱등). DB·역할·테이블·인덱
|
||||
| `amount` | BIGINT | 원 단위, ≥ 0 |
|
||||
| `memo` | TEXT | 비고 |
|
||||
| `status` | TEXT | 작성중/제출/승인/반려/정산완료 |
|
||||
| `approver_email` | TEXT | 결재자 email (승인/반려/정산 시 기록) |
|
||||
| `decided_at` | TIMESTAMPTZ | 결재 시점 |
|
||||
| `reject_reason` | TEXT | 반려 사유 |
|
||||
| `created_at` / `updated_at` | TIMESTAMPTZ | 트리거로 자동 갱신 |
|
||||
|
||||
인덱스: `(owner, spent_at DESC)`, `(status)`, `(created_at DESC)`.
|
||||
|
||||
### 테이블 `expense_attachments`
|
||||
|
||||
영수증/기타파일 메타데이터. 실제 파일은 `DATA_DIR/uploads/expense/{item_id}/` 에 저장.
|
||||
|
||||
| 컬럼 | 타입 | 비고 |
|
||||
| --- | --- | --- |
|
||||
| `id` | TEXT PK | 12자 hex |
|
||||
| `item_id` | TEXT FK | `expense_items(id)` ON DELETE CASCADE |
|
||||
| `owner` | TEXT | 업로드한 사용자 email |
|
||||
| `kind` | TEXT | `receipt` 또는 `other` (CHECK) |
|
||||
| `filename` | TEXT | 원본 파일명 |
|
||||
| `stored_path` | TEXT | 디스크 경로 (절대) |
|
||||
| `content_type` | TEXT | MIME |
|
||||
| `size_bytes` | BIGINT | 바이트 |
|
||||
| `uploaded_at` | TIMESTAMPTZ | 업로드 시각 |
|
||||
|
||||
인덱스: `(item_id)`.
|
||||
|
||||
### 결재 워크플로
|
||||
|
||||
```
|
||||
작성중 ─submit──▶ 제출 ─approve──▶ 승인 ─settle──▶ 정산완료
|
||||
▲ │
|
||||
└─revert/reject──┴─reject──▶ 반려 ─revert──▶ 작성중
|
||||
```
|
||||
|
||||
- `submit`/`revert`: owner 본인
|
||||
- `approve`/`reject`/`settle`: `expense_approver` 또는 `admin`
|
||||
- 첨부 추가/항목 수정/삭제: `작성중` 또는 `반려` 상태에서만
|
||||
|
||||
### 마이그레이션
|
||||
|
||||
기존 운영 DB 에 신규 컬럼/테이블 적용:
|
||||
|
||||
```bash
|
||||
docker exec -i postgres-db psql -U postgres -d expense_db \
|
||||
< scripts/sql/expense_db_002_workflow_attachments.sql
|
||||
```
|
||||
|
||||
신규 설치는 `expense_db_init.sql` 하나로 충분 (둘 다 멱등).
|
||||
|
||||
### 운영 서버 초기화 (1회)
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user