239 lines
6.7 KiB
Markdown
239 lines
6.7 KiB
Markdown
|
|
# 마이그레이션 063-064: 재고 관리 테이블 생성
|
||
|
|
|
||
|
|
## 목적
|
||
|
|
|
||
|
|
재고 현황 관리 및 입출고 이력 추적을 위한 테이블 생성
|
||
|
|
|
||
|
|
**테이블 타입관리 UI와 동일한 방식으로 생성됩니다.**
|
||
|
|
|
||
|
|
### 생성되는 테이블
|
||
|
|
|
||
|
|
| 테이블명 | 설명 | 용도 |
|
||
|
|
|----------|------|------|
|
||
|
|
| `inventory_stock` | 재고 현황 | 품목+로트별 현재 재고 상태 |
|
||
|
|
| `inventory_history` | 재고 이력 | 입출고 트랜잭션 기록 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 테이블 타입관리 UI 방식 특징
|
||
|
|
|
||
|
|
1. **기본 컬럼 자동 포함**: `id`, `created_date`, `updated_date`, `writer`, `company_code`
|
||
|
|
2. **데이터 타입 통일**: 날짜는 `TIMESTAMP`, 나머지는 `VARCHAR(500)`
|
||
|
|
3. **메타데이터 등록**:
|
||
|
|
- `table_labels`: 테이블 정보
|
||
|
|
- `column_labels`: 컬럼 정보 (라벨, input_type, detail_settings)
|
||
|
|
- `table_type_columns`: 회사별 컬럼 타입 정보
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 테이블 구조
|
||
|
|
|
||
|
|
### 1. inventory_stock (재고 현황)
|
||
|
|
|
||
|
|
| 컬럼명 | 타입 | input_type | 설명 |
|
||
|
|
|--------|------|------------|------|
|
||
|
|
| id | VARCHAR(500) | text | PK (자동생성) |
|
||
|
|
| created_date | TIMESTAMP | date | 생성일시 |
|
||
|
|
| updated_date | TIMESTAMP | date | 수정일시 |
|
||
|
|
| writer | VARCHAR(500) | text | 작성자 |
|
||
|
|
| company_code | VARCHAR(500) | text | 회사코드 |
|
||
|
|
| item_code | VARCHAR(500) | text | 품목코드 |
|
||
|
|
| lot_number | VARCHAR(500) | text | 로트번호 |
|
||
|
|
| warehouse_id | VARCHAR(500) | entity | 창고 (FK → warehouse_info) |
|
||
|
|
| location_code | VARCHAR(500) | text | 위치코드 |
|
||
|
|
| current_qty | VARCHAR(500) | number | 현재고량 |
|
||
|
|
| safety_qty | VARCHAR(500) | number | 안전재고 |
|
||
|
|
| last_in_date | TIMESTAMP | date | 최종입고일 |
|
||
|
|
| last_out_date | TIMESTAMP | date | 최종출고일 |
|
||
|
|
|
||
|
|
### 2. inventory_history (재고 이력)
|
||
|
|
|
||
|
|
| 컬럼명 | 타입 | input_type | 설명 |
|
||
|
|
|--------|------|------------|------|
|
||
|
|
| id | VARCHAR(500) | text | PK (자동생성) |
|
||
|
|
| created_date | TIMESTAMP | date | 생성일시 |
|
||
|
|
| updated_date | TIMESTAMP | date | 수정일시 |
|
||
|
|
| writer | VARCHAR(500) | text | 작성자 |
|
||
|
|
| company_code | VARCHAR(500) | text | 회사코드 |
|
||
|
|
| stock_id | VARCHAR(500) | text | 재고ID (FK) |
|
||
|
|
| item_code | VARCHAR(500) | text | 품목코드 |
|
||
|
|
| lot_number | VARCHAR(500) | text | 로트번호 |
|
||
|
|
| transaction_type | VARCHAR(500) | code | 구분 (IN/OUT) |
|
||
|
|
| transaction_date | TIMESTAMP | date | 일자 |
|
||
|
|
| quantity | VARCHAR(500) | number | 수량 |
|
||
|
|
| balance_qty | VARCHAR(500) | number | 재고량 |
|
||
|
|
| manager_id | VARCHAR(500) | text | 담당자ID |
|
||
|
|
| manager_name | VARCHAR(500) | text | 담당자명 |
|
||
|
|
| remark | VARCHAR(500) | text | 비고 |
|
||
|
|
| reference_type | VARCHAR(500) | text | 참조문서유형 |
|
||
|
|
| reference_id | VARCHAR(500) | text | 참조문서ID |
|
||
|
|
| reference_number | VARCHAR(500) | text | 참조문서번호 |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 실행 방법
|
||
|
|
|
||
|
|
### Docker 환경 (권장)
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 재고 현황 테이블
|
||
|
|
docker exec -i erp-node-db-1 psql -U postgres -d ilshin < db/migrations/063_create_inventory_stock.sql
|
||
|
|
|
||
|
|
# 재고 이력 테이블
|
||
|
|
docker exec -i erp-node-db-1 psql -U postgres -d ilshin < db/migrations/064_create_inventory_history.sql
|
||
|
|
```
|
||
|
|
|
||
|
|
### 로컬 PostgreSQL
|
||
|
|
|
||
|
|
```bash
|
||
|
|
psql -U postgres -d ilshin -f db/migrations/063_create_inventory_stock.sql
|
||
|
|
psql -U postgres -d ilshin -f db/migrations/064_create_inventory_history.sql
|
||
|
|
```
|
||
|
|
|
||
|
|
### pgAdmin / DBeaver
|
||
|
|
|
||
|
|
1. 각 SQL 파일 열기
|
||
|
|
2. 전체 내용 복사
|
||
|
|
3. SQL 쿼리 창에 붙여넣기
|
||
|
|
4. 실행 (F5 또는 Execute)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 검증 방법
|
||
|
|
|
||
|
|
### 1. 테이블 생성 확인
|
||
|
|
|
||
|
|
```sql
|
||
|
|
SELECT table_name
|
||
|
|
FROM information_schema.tables
|
||
|
|
WHERE table_name IN ('inventory_stock', 'inventory_history');
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. 메타데이터 등록 확인
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- table_labels
|
||
|
|
SELECT * FROM table_labels WHERE table_name IN ('inventory_stock', 'inventory_history');
|
||
|
|
|
||
|
|
-- column_labels
|
||
|
|
SELECT table_name, column_name, column_label, input_type, display_order
|
||
|
|
FROM column_labels
|
||
|
|
WHERE table_name IN ('inventory_stock', 'inventory_history')
|
||
|
|
ORDER BY table_name, display_order;
|
||
|
|
|
||
|
|
-- table_type_columns
|
||
|
|
SELECT table_name, column_name, company_code, input_type, display_order
|
||
|
|
FROM table_type_columns
|
||
|
|
WHERE table_name IN ('inventory_stock', 'inventory_history')
|
||
|
|
ORDER BY table_name, display_order;
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. 샘플 데이터 확인
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- 재고 현황
|
||
|
|
SELECT * FROM inventory_stock WHERE company_code = 'WACE';
|
||
|
|
|
||
|
|
-- 재고 이력
|
||
|
|
SELECT * FROM inventory_history WHERE company_code = 'WACE' ORDER BY transaction_date;
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 화면에서 사용할 조회 쿼리 예시
|
||
|
|
|
||
|
|
### 재고 현황 그리드 (좌측)
|
||
|
|
|
||
|
|
```sql
|
||
|
|
SELECT
|
||
|
|
s.item_code,
|
||
|
|
i.item_name,
|
||
|
|
i.size as specification,
|
||
|
|
i.unit,
|
||
|
|
s.lot_number,
|
||
|
|
w.warehouse_name,
|
||
|
|
s.location_code,
|
||
|
|
s.current_qty::numeric as current_qty,
|
||
|
|
s.safety_qty::numeric as safety_qty,
|
||
|
|
CASE
|
||
|
|
WHEN s.current_qty::numeric < s.safety_qty::numeric THEN '부족'
|
||
|
|
WHEN s.current_qty::numeric > s.safety_qty::numeric * 2 THEN '과다'
|
||
|
|
ELSE '정상'
|
||
|
|
END AS stock_status,
|
||
|
|
s.last_in_date,
|
||
|
|
s.last_out_date
|
||
|
|
FROM inventory_stock s
|
||
|
|
LEFT JOIN item_info i ON s.item_code = i.item_number AND s.company_code = i.company_code
|
||
|
|
LEFT JOIN warehouse_info w ON s.warehouse_id = w.id
|
||
|
|
WHERE s.company_code = 'WACE'
|
||
|
|
ORDER BY s.item_code, s.lot_number;
|
||
|
|
```
|
||
|
|
|
||
|
|
### 재고 이력 패널 (우측)
|
||
|
|
|
||
|
|
```sql
|
||
|
|
SELECT
|
||
|
|
h.transaction_type,
|
||
|
|
h.transaction_date,
|
||
|
|
h.quantity,
|
||
|
|
h.balance_qty,
|
||
|
|
h.manager_name,
|
||
|
|
h.remark
|
||
|
|
FROM inventory_history h
|
||
|
|
WHERE h.item_code = 'A001'
|
||
|
|
AND h.lot_number = 'LOT-2024-001'
|
||
|
|
AND h.company_code = 'WACE'
|
||
|
|
ORDER BY h.transaction_date DESC, h.created_date DESC;
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 데이터 흐름
|
||
|
|
|
||
|
|
```
|
||
|
|
[입고 발생]
|
||
|
|
│
|
||
|
|
├─→ inventory_history에 INSERT (+수량, 잔량)
|
||
|
|
│
|
||
|
|
└─→ inventory_stock에 UPDATE (current_qty 증가, last_in_date 갱신)
|
||
|
|
|
||
|
|
[출고 발생]
|
||
|
|
│
|
||
|
|
├─→ inventory_history에 INSERT (-수량, 잔량)
|
||
|
|
│
|
||
|
|
└─→ inventory_stock에 UPDATE (current_qty 감소, last_out_date 갱신)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 롤백 방법 (문제 발생 시)
|
||
|
|
|
||
|
|
```sql
|
||
|
|
-- 테이블 삭제
|
||
|
|
DROP TABLE IF EXISTS inventory_history;
|
||
|
|
DROP TABLE IF EXISTS inventory_stock;
|
||
|
|
|
||
|
|
-- 메타데이터 삭제
|
||
|
|
DELETE FROM column_labels WHERE table_name IN ('inventory_stock', 'inventory_history');
|
||
|
|
DELETE FROM table_labels WHERE table_name IN ('inventory_stock', 'inventory_history');
|
||
|
|
DELETE FROM table_type_columns WHERE table_name IN ('inventory_stock', 'inventory_history');
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 관련 테이블 (마스터 데이터)
|
||
|
|
|
||
|
|
| 테이블 | 역할 | 연결 컬럼 |
|
||
|
|
|--------|------|-----------|
|
||
|
|
| item_info | 품목 마스터 | item_number |
|
||
|
|
| warehouse_info | 창고 마스터 | id |
|
||
|
|
| warehouse_location | 위치 마스터 | location_code |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**작성일**: 2025-12-09
|
||
|
|
**영향 범위**: 재고 관리 시스템
|
||
|
|
**생성 방식**: 테이블 타입관리 UI와 동일
|
||
|
|
|
||
|
|
|