docs: 5130 레거시 분석 문서 및 기존 문서 초기 커밋

- 5130 레거시 시스템 분석 (00_OVERVIEW ~ 08_SAM_COMPARISON)
- MES 프로젝트 문서
- API/프론트엔드 스펙 문서
- 가이드 및 레퍼런스 문서
This commit is contained in:
2025-12-04 18:47:19 +09:00
commit 08a8259313
135 changed files with 32721 additions and 0 deletions

View File

@@ -0,0 +1,110 @@
# 5130 프로젝트 분석 개요
## 문서 정보
- **분석일**: 2025-12-04
- **분석자**: Claude Code
- **목적**: SAM 시스템으로 마이그레이션을 위한 5130 레거시 시스템 분석
## 프로젝트 개요
### 시스템 정보
- **프로젝트명**: 5130 (주일/경동 통합정보시스템)
- **기술 스택**: PHP (레거시), MySQL, Bootstrap 3.x
- **URL**: https://5130.co.kr
- **DB명**: `chandj` (기본값, 세션으로 동적 변경 가능)
### 멀티테넌시 구조
```php
// session.php
$DB = isset($_SESSION["DB"]) ? $_SESSION["DB"] : 'chandj';
$mycompany = $_SESSION["mycompany"] ?? ''; // 회사: 주일, 경동
$mypart = $_SESSION["mypart"] ?? ''; // 부서
$authority = $_SESSION["authority"] ?? ''; // 권한: ACCOUNT 등
```
## 도메인 구조
### 1. 자재/재고 (Material/Instock)
- **디렉토리**: `/instock/`
- **주요 테이블**: `i_*` (자재 유형별 테이블)
- **기능**: LOT 관리, 입고 검사, 자재 추적
### 2. 품목/모델 (Product/Model)
- **디렉토리**: `/models/`
- **주요 테이블**: `models`, `parts`, `parts_sub`
- **기능**: 제품 모델 정의, BOM 관리, 단가 관리
### 3. 견적 (Estimate)
- **디렉토리**: `/estimate/`
- **주요 테이블**: `estimate` (동적 테이블명)
- **기능**: 견적서 작성, 단가 계산, 견적 이력
### 4. 생산/공사 (Production/Work)
- **디렉토리**: `/make/`, `/work/`
- **주요 테이블**: `work`, `make_*`
- **기능**: 공사 관리, 생산 지시, 작업 진행 추적
### 5. 출하 (Shipping/Output)
- **디렉토리**: `/output/`
- **주요 테이블**: `output`, `output_extra`
- **기능**: 출하 관리, 배송 추적, 인정검사(ACI)
### 6. 품질/AS (Quality/After Service)
- **디렉토리**: `/as/`
- **주요 테이블**: `work` (AS 관련 컬럼)
- **기능**: AS 접수, 처리 현황, 보증 관리
### 7. 회계 (Accounting)
- **디렉토리**: `/account/`, `/account_plan/`
- **주요 테이블**: `account`
- **기능**: 입출금 관리, 미수금, 전자세금계산서
## 공통 패턴
### 파일 구조
```
/도메인/
├── _request.php # 요청 파라미터 정의
├── _row.php # 행 데이터 렌더링
├── insert.php # 데이터 INSERT/UPDATE/DELETE
├── list.php # 목록 조회
├── write_form.php # 등록/수정 폼
├── view.php # 상세 조회
└── common/ # 도메인별 공통 모듈
```
### DB 공통 컬럼
- `num`: PK (AUTO_INCREMENT)
- `is_deleted`: 소프트 삭제 플래그 (0/1)
- `update_log`: 수정 이력 (날짜 + 사용자명)
- `searchtag`: 검색용 통합 텍스트
### JSON 데이터 저장 패턴
- 복잡한 리스트 데이터는 JSON 문자열로 저장
- 예: `estimateList`, `screenlist`, `slatlist`, `accountList`
## SAM 마이그레이션 고려사항
### 멀티테넌시
- 5130: DB명 분리 방식 (`chandj.테이블`)
- SAM: `tenant_id` 컬럼 기반 분리
### 데이터 구조 개선
1. JSON 필드 → 정규화된 관계 테이블
2. 동적 테이블명 → 고정 테이블 + 타입 컬럼
3. 검색태그 → Full-Text Search 또는 Elasticsearch
### 코드 현대화
1. 레거시 PHP → Laravel 12
2. 직접 SQL → Eloquent ORM
3. jQuery → React/Next.js
## 관련 문서
- [01_MATERIAL.md](./01_MATERIAL.md) - 자재/재고 분석
- [02_PRODUCT.md](./02_PRODUCT.md) - 품목/모델 분석
- [03_ESTIMATE.md](./03_ESTIMATE.md) - 견적 분석
- [04_PRODUCTION.md](./04_PRODUCTION.md) - 생산 분석
- [05_SHIPPING.md](./05_SHIPPING.md) - 출하 분석
- [06_QUALITY.md](./06_QUALITY.md) - 품질/AS 분석
- [07_ACCOUNTING.md](./07_ACCOUNTING.md) - 회계 분석
- [08_SAM_COMPARISON.md](./08_SAM_COMPARISON.md) - SAM 비교 검증

View File

@@ -0,0 +1,202 @@
# 자재/재고 (Material/Instock) 분석
## 개요
- **디렉토리**: `/instock/`
- **DB 테이블**: 자재 유형별 개별 테이블 (i_* 패턴)
- **주요 기능**: LOT 관리, 입고 검사, 자재 추적, 재고 관리
## 디렉토리 구조
```
/instock/
├── _request.php # 요청 파라미터 정의
├── _row.php # 행 렌더링
├── insert.php # 데이터 저장
├── list.php # 자재 목록
├── list_sheet.php # 시트 형태 목록
├── statistics.php # 통계
├── i_*.php # 자재 유형별 입력 폼 (23개)
│ ├── i_EGI155.php # EGI155 자재
│ ├── i_GIplate.php # GI 판재
│ ├── i_SUSplate.php # SUS 판재
│ ├── i_SUScoil.php # SUS 코일
│ ├── i_angle.php # 앵글
│ ├── i_anglebottom.php # 앵글 바텀
│ ├── i_antifireglass.php # 방화 유리
│ ├── i_bendingcoil.php # 벤딩 코일
│ ├── i_bracket.php # 브라켓
│ ├── i_cerakwool.php # 세락울
│ ├── i_controller.php # 컨트롤러
│ ├── i_fiber.php # 파이버
│ ├── i_fireproofWire.php # 방화 와이어
│ ├── i_motor.php # 모터
│ ├── i_platesteel.php # 판강
│ ├── i_pole.php # 폴
│ ├── i_recpipe.php # 각관
│ ├── i_shaft.php # 샤프트
│ ├── i_sillica.php # 실리카
│ ├── i_slatcoil.php # 슬랫 코일
│ ├── i_wire.php # 와이어
│ ├── i_wireDaehan.php # 대한 와이어
│ └── i_Fireproof_sealings.php # 방화 씰링
├── fetch_*.php # AJAX 데이터 조회
└── common/ # 공통 모듈
```
## DB 스키마
### 자재 공통 테이블 구조
```sql
CREATE TABLE i_[자재유형] (
num INT AUTO_INCREMENT PRIMARY KEY,
-- LOT 정보
lot_no VARCHAR(50), -- LOT 번호
inspection_date DATE, -- 검사일
lotDone DATE, -- LOT 완료일
-- 공급업체 정보
supplier VARCHAR(100), -- 공급업체
manufacturer VARCHAR(100), -- 제조사
-- 자재 정보
item_name VARCHAR(200), -- 품명
specification VARCHAR(200), -- 규격
unit VARCHAR(20), -- 단위
prodcode VARCHAR(50), -- 제품코드
-- 수량/가격 정보
received_qty DECIMAL(15,2), -- 입고수량
weight_kg DECIMAL(15,2), -- 중량(kg)
purchase_price_excl_vat DECIMAL(15,0), -- 구매단가(부가세 제외)
-- 참조/이력
material_no VARCHAR(50), -- 자재번호
remarks TEXT, -- 비고
-- 시스템 필드
searchtag TEXT, -- 검색태그 (자동생성)
update_log TEXT, -- 수정이력
is_deleted TINYINT DEFAULT 0, -- 삭제플래그
INDEX idx_lot_no (lot_no),
INDEX idx_supplier (supplier),
INDEX idx_item_name (item_name)
);
```
### 검색태그 생성 로직
```php
// insert.php에서 searchtag 자동 생성
$searchtag = $lot_no . ' ' .
$inspection_date . ' ' .
$supplier . ' ' .
$item_name . ' ' .
$specification . ' ' .
$unit . ' ' .
$received_qty . ' ' .
$material_no . ' ' .
$manufacturer . ' ' .
$purchase_price_excl_vat . ' ' .
$weight_kg . ' ' .
$lotDone . ' ' .
$prodcode . ' ' .
$remarks;
```
## 자재 유형별 특성
### 1. 코일 자재
- **테이블**: `i_SUScoil`, `i_bendingcoil`, `i_slatcoil`
- **특징**: 중량(kg) 기반 재고 관리
- **추가 컬럼**: 두께, 폭, 소재
### 2. 판재 자재
- **테이블**: `i_GIplate`, `i_SUSplate`, `i_platesteel`
- **특징**: 면적(m²) 또는 장 단위 관리
- **추가 컬럼**: 가로, 세로, 두께
### 3. 부품 자재
- **테이블**: `i_motor`, `i_controller`, `i_bracket`
- **특징**: 개수 단위 관리, 모델별 구분
- **추가 컬럼**: 모델명, 제조번호
### 4. 구조재
- **테이블**: `i_angle`, `i_pole`, `i_shaft`, `i_recpipe`
- **특징**: 길이(m) 단위 관리
- **추가 컬럼**: 길이, 단면규격
## 비즈니스 로직
### LOT 관리
1. **LOT 번호 생성**: `lotnum_generator.php`
2. **LOT 완료 처리**: `lotDone` 날짜 설정
3. **LOT 추적**: 출하 시 LOT 번호 연결
### 입고 검사
1. 자재 입고 시 검사일(`inspection_date`) 기록
2. 합격 시 재고에 반영
3. 불합격 시 반품 처리
### 가격 관리
- 구매단가는 부가세 제외 금액 저장
- 환율/원자재 가격 변동 반영 필요
## SAM 마이그레이션 포인트
### 1. 테이블 통합
```sql
-- 5130: 자재 유형별 개별 테이블
i_SUSplate, i_GIplate, i_motor, ...
-- SAM: 단일 테이블 + 카테고리
materials (
id,
tenant_id,
category_id, -- categories 테이블 참조
lot_no,
...
)
```
### 2. 카테고리 계층화
```sql
-- SAM: 4단계 카테고리 구조
material_categories (
id,
parent_id,
name,
level, -- 1: 대분류, 2: 중분류, 3: 소분류, 4: 세분류
attributes JSON -- 카테고리별 추가 속성 정의
)
```
### 3. 속성 동적 관리
```sql
-- SAM: 카테고리별 동적 속성
material_attributes (
material_id,
attribute_key,
attribute_value,
attribute_type -- string, number, date, etc.
)
```
### 4. LOT 추적 강화
```sql
-- SAM: LOT 이력 테이블
lot_histories (
id,
lot_id,
action, -- created, inspected, used, returned
quantity,
reference_type, -- output, production
reference_id,
created_at
)
```
## 관련 파일 참조
- `/instock/insert.php` - INSERT/UPDATE 로직
- `/instock/_request.php` - 요청 파라미터 정의
- `/instock/list.php` - 목록 조회 쿼리
- `/instock/fetch_inspection.php` - 검사 데이터 조회

View File

@@ -0,0 +1,250 @@
# 품목/모델 (Product/Model) 분석
## 개요
- **디렉토리**: `/models/`
- **DB 테이블**: `models`, `parts`, `parts_sub`
- **주요 기능**: 제품 모델 정의, BOM(Bill of Materials) 관리, 단가 계산
## 디렉토리 구조
```
/models/
├── _request.php # 요청 파라미터
├── _row.php # 행 렌더링
├── _part_row.php # 부품 행 렌더링
├── _part_sub_row.php # 하위 부품 행 렌더링
├── insert.php # 모델/부품 저장
├── list.php # 모델 목록
├── write_form.php # 모델 등록/수정 폼
├── modelslist.php # 모델 리스트 조회
├── itemlist.php # 아이템 리스트
├── sub_table.php # 하위 부품 테이블
├── basic_model.php # 기본 모델 설정
├── models.json # 모델 JSON 데이터
└── items.json # 아이템 JSON 데이터
```
## DB 스키마
### 1. models 테이블 (모델 마스터)
```sql
CREATE TABLE models (
model_id INT AUTO_INCREMENT PRIMARY KEY,
model_name VARCHAR(100), -- 모델명
major_category VARCHAR(50), -- 대분류 (슬랫, 스크린 등)
finishing_type VARCHAR(50), -- 마감 유형
description TEXT, -- 설명
guiderail_type VARCHAR(50), -- 가이드레일 유형
update_log TEXT, -- 수정이력
is_deleted TINYINT DEFAULT 0, -- 삭제플래그
INDEX idx_model_name (model_name),
INDEX idx_major_category (major_category)
);
```
### 2. parts 테이블 (부품 - 2단계)
```sql
CREATE TABLE parts (
part_id INT AUTO_INCREMENT PRIMARY KEY,
model_id INT, -- 부모 모델 ID
part_name VARCHAR(100), -- 부품명
spec VARCHAR(100), -- 규격
unit VARCHAR(20), -- 단위
quantity DECIMAL(10,2), -- 수량
unitprice DECIMAL(15,0), -- 단가
memo TEXT, -- 메모
is_deleted TINYINT DEFAULT 0, -- 삭제플래그
FOREIGN KEY (model_id) REFERENCES models(model_id),
INDEX idx_model_id (model_id)
);
```
### 3. parts_sub 테이블 (하위 부품 - 3단계)
```sql
CREATE TABLE parts_sub (
subpart_id INT AUTO_INCREMENT PRIMARY KEY,
part_id INT, -- 부모 부품 ID
subpart_name VARCHAR(100), -- 하위 부품명
material VARCHAR(100), -- 소재
-- 가격 계산 필드
bendSum DECIMAL(15,0), -- 벤딩 합계
plateSum DECIMAL(15,0), -- 판재 합계
finalSum DECIMAL(15,0), -- 최종 합계
unitPrice DECIMAL(15,0), -- 단가
computedPrice DECIMAL(15,0), -- 계산 가격
quantity DECIMAL(10,2), -- 수량
lineTotal DECIMAL(15,0), -- 행 합계
image_url VARCHAR(500), -- 이미지 URL
is_deleted TINYINT DEFAULT 0, -- 삭제플래그
FOREIGN KEY (part_id) REFERENCES parts(part_id),
INDEX idx_part_id (part_id)
);
```
## 계층 구조
```
models (모델)
└── parts (부품)
└── parts_sub (하위 부품)
예시:
├── KD-SLAT-001 (슬랫 모델)
│ ├── 가이드레일 (부품)
│ │ ├── 상부 브라켓 (하위 부품)
│ │ ├── 하부 브라켓 (하위 부품)
│ │ └── 레일 바디 (하위 부품)
│ ├── 슬랫 본체 (부품)
│ │ ├── 슬랫 코일 (하위 부품)
│ │ └── 인터락 (하위 부품)
│ └── 구동부 (부품)
│ ├── 모터 (하위 부품)
│ └── 샤프트 (하위 부품)
```
## 비즈니스 로직
### 모델 관리 (insert.php)
```php
// 1단계: 모델 저장
if ($mode == "insert") {
$sql = "INSERT INTO models (model_name, major_category, finishing_type,
description, update_log, guiderail_type) VALUES (?, ?, ?, ?, ?, ?)";
}
// 2단계: 부품 저장 (배열 처리)
if(isset($_POST['part_name']) && is_array($_POST['part_name'])) {
// 기존 부품과 비교하여 삭제된 부품 처리
// 신규 부품 INSERT, 기존 부품 UPDATE
}
// 3단계: 하위 부품 저장
if(isset($_POST['subpart_name']) && is_array($_POST['subpart_name'])) {
// parent_part_id로 부모 부품과 연결
}
```
### 복사 기능
```php
// 모델 복사 시 하위 구조 전체 복사
if ($mode == "copy") {
// 1. 모델 복사 → 새 model_id
// 2. 부품 복사 → partMapping (old_id → new_id)
// 3. 하위 부품 복사 → partMapping 기반 parent_id 변환
}
```
### 가격 계산
- `bendSum`: 벤딩 공정 비용 합계
- `plateSum`: 판재 비용 합계
- `finalSum`: bendSum + plateSum
- `computedPrice`: 추가 가공비 포함
- `lineTotal`: unitPrice × quantity
## 관련 JSON 데이터
### models.json
```json
[
{
"model_name": "KD-SLAT-001",
"slatitem": "슬랫",
"pair": "single"
},
{
"model_name": "KD-SCREEN-001",
"slatitem": "스크린",
"pair": "double"
}
]
```
### items.json
```json
[
{
"item_code": "ITM001",
"item_name": "가이드레일 A형",
"category": "가이드레일"
}
]
```
## 관련 디렉토리
### 단가 관리 디렉토리
```
/price_angle/ # 앵글 단가
/price_bend/ # 벤딩 단가
/price_motor/ # 모터 단가
/price_pipe/ # 파이프 단가
/price_pole/ # 폴 단가
/price_raw_materials/ # 원자재 단가
/price_screenplate/ # 스크린판 단가
/price_shaft/ # 샤프트 단가
/price_smokeban/ # 연기차단 단가
```
### 벤딩/가공 관련
```
/bending/ # 벤딩 작업
/bendingfee/ # 벤딩 비용
/bendingmap/ # 벤딩 맵
/etcbending/ # 기타 벤딩
```
## SAM 마이그레이션 포인트
### 1. BOM 구조 개선
```sql
-- SAM: 재귀적 BOM 구조
bom_items (
id,
tenant_id,
parent_id, -- NULL이면 최상위 모델
item_type, -- model, assembly, part, material
item_code,
item_name,
quantity,
unit_price,
level, -- BOM 레벨 (1, 2, 3, ...)
sort_order
)
```
### 2. 가격 계산 분리
```sql
-- SAM: 가격 계산 이력 관리
price_calculations (
id,
bom_item_id,
calculation_type, -- bending, material, labor
base_price,
markup_rate,
final_price,
effective_from,
effective_to
)
```
### 3. 버전 관리
```sql
-- SAM: 모델 버전 관리
model_versions (
id,
model_id,
version,
snapshot JSON, -- BOM 스냅샷
created_at,
created_by
)
```
## 참고 파일
- `/models/insert.php` - 3단계 계층 저장 로직
- `/models/write_form.php` - BOM 편집 UI
- `/common.php:selectModel()` - 모델 선택 헬퍼 함수

View File

@@ -0,0 +1,289 @@
# 견적 (Estimate) 분석
## 개요
- **디렉토리**: `/estimate/`
- **DB 테이블**: `estimate` (동적 테이블명 가능)
- **주요 기능**: 견적서 작성, 단가 자동계산, 견적 이력 관리
## 디렉토리 구조
```
/estimate/
├── _request.php # 요청 파라미터
├── _row.php # 행 렌더링
├── insert.php # 견적 저장
├── list.php # 견적 목록
├── list_unit.php # 단가 목록
├── write_form.php # 견적서 작성 폼 (103KB)
├── edit.php # 견적 수정
├── edit_slat.php # 슬랫 견적 수정
├── estimate.php # 견적서 메인
├── estimateSlat.php # 슬랫 견적
├── estimateUnit.php # 단가 견적
├── view.php # 견적 상세
├── viewEstimate.php # 견적서 보기
├── viewEstimateDetail.php # 견적 상세 보기
├── statistics.php # 견적 통계
├── fetch_unitprice.php # 단가 조회 (32KB)
├── get_estimate_amount.php # 견적 금액 계산
├── get_screen_amount.php # 스크린 금액 계산
├── get_slat_amount.php # 슬랫 금액 계산
├── generate_serial_pjnum.php # 프로젝트 번호 생성
├── screen_view_*.php # 스크린 견적 뷰
├── slat_view_*.php # 슬랫 견적 뷰
└── common/ # 공통 모듈
```
## DB 스키마
### estimate 테이블
```sql
CREATE TABLE estimate (
num INT AUTO_INCREMENT PRIMARY KEY,
-- 기본 정보
pjnum VARCHAR(50), -- 프로젝트 번호 (KD-PR-YYMMDD-NN)
indate DATE, -- 등록일
orderman VARCHAR(50), -- 담당자
outworkplace VARCHAR(200), -- 현장명/거래처
-- 분류 정보
major_category VARCHAR(50), -- 대분류 (슬랫, 스크린)
model_name VARCHAR(100), -- 모델명
position VARCHAR(50), -- 위치
-- 규격 정보
makeWidth INT, -- 제작 폭 (기본 160)
makeHeight INT, -- 제작 높이 (기본 350)
maguriWing VARCHAR(20), -- 마구리 윙 (기본 50)
-- 발주처 정보
con_num VARCHAR(50), -- 계약번호
secondord VARCHAR(100), -- 2차 발주처
secondordman VARCHAR(50), -- 2차 담당자
secondordmantel VARCHAR(20), -- 2차 담당자 연락처
secondordnum VARCHAR(50), -- 2차 발주번호
-- 견적 상세 (JSON)
estimateList TEXT, -- 스크린 견적 리스트 (JSON)
estimateList_auto TEXT, -- 스크린 자동계산 리스트 (JSON)
estimateSlatList TEXT, -- 슬랫 견적 리스트 (JSON)
estimateSlatList_auto TEXT, -- 슬랫 자동계산 리스트 (JSON)
-- 금액 정보
estimateTotal INT DEFAULT 0, -- 견적 총액
EstimateFirstSum INT DEFAULT 0, -- 최초 견적 합계
EstimateUpdatetSum INT DEFAULT 0, -- 수정 견적 합계
EstimateDiffer INT DEFAULT 0, -- 차액
estimateSurang INT DEFAULT 0, -- 수량
-- 할인 정보
EstimateDiscountRate INT DEFAULT 0,-- 할인율
EstimateDiscount INT DEFAULT 0, -- 할인금액
EstimateFinalSum INT DEFAULT 0, -- 최종 금액
-- 검사비/부가정보
inspectionFee INT DEFAULT 50000, -- 인정검사비
steel VARCHAR(50), -- 강종
motor VARCHAR(100), -- 모터
warranty VARCHAR(100), -- 보증기간
-- 체크 플래그
slatcheck VARCHAR(10), -- 슬랫 체크
partscheck VARCHAR(10), -- 부품 체크
-- 시스템 필드
comment TEXT, -- 비고
update_log TEXT, -- 수정이력
is_deleted TINYINT DEFAULT 0, -- 삭제플래그
INDEX idx_pjnum (pjnum),
INDEX idx_outworkplace (outworkplace),
INDEX idx_indate (indate)
);
```
## 견적 데이터 구조
### estimateList JSON 구조
```json
[
{
"item_name": "가이드레일",
"specification": "A형 65×80",
"unit": "EA",
"quantity": 2,
"unit_price": 150000,
"amount": 300000,
"remark": ""
},
{
"item_name": "스크린 판넬",
"specification": "1.0T × 1200W",
"unit": "m²",
"quantity": 24.5,
"unit_price": 45000,
"amount": 1102500,
"remark": "SUS304"
}
]
```
### estimateList_auto JSON 구조
```json
[
{
"item_code": "AUTO001",
"item_name": "벤딩 가공비",
"calc_type": "per_meter",
"base_value": 120.5,
"unit_price": 2500,
"amount": 301250
}
]
```
## 비즈니스 로직
### 프로젝트 번호 생성
```php
// generate_serial_pjnum.php
function generatePjnum() {
// 형식: KD-PR-YYMMDD-NN
// KD: 경동
// PR: 프로젝트
// YYMMDD: 날짜
// NN: 일련번호 (01~99)
$today = date('ymd');
$prefix = "KD-PR-{$today}-";
// 오늘 날짜의 마지막 번호 조회
$sql = "SELECT pjnum FROM estimate
WHERE pjnum LIKE '{$prefix}%'
ORDER BY pjnum DESC LIMIT 1";
// 일련번호 증가
$nextNum = str_pad($lastNum + 1, 2, '0', STR_PAD_LEFT);
return $prefix . $nextNum;
}
```
### 금액 계산
```php
// insert.php
$estimateTotal = intval(str_replace(',', '', $estimateTotal));
$EstimateFirstSum = intval(str_replace(',', '', $EstimateFirstSum));
$EstimateUpdatetSum = intval(str_replace(',', '', $EstimateUpdatetSum));
$EstimateDiffer = intval(str_replace(',', '', $EstimateDiffer));
$EstimateFinalSum = intval(str_replace(',', '', $EstimateFinalSum));
```
### 단가 조회 (fetch_unitprice.php)
- 모델별 기본 단가
- 규격별 가격 가감
- 강종(steel)별 가격 계수
- 마진율 적용
## 견적 유형
### 1. 스크린 견적 (Screen)
- **파일**: `estimate.php`, `screen_view_*.php`
- **특징**: 면적(m²) 기반 계산
- **주요 항목**: 판넬, 가이드레일, 브라켓
### 2. 슬랫 견적 (Slat)
- **파일**: `estimateSlat.php`, `slat_view_*.php`
- **특징**: 폭/높이 기반 계산
- **주요 항목**: 슬랫 코일, 인터락, 샤프트
### 3. 단가 견적 (Unit)
- **파일**: `estimateUnit.php`, `list_unit.php`
- **특징**: 품목별 단가 설정
## 견적서 출력
- `/estimate/print_list.php` - 견적 목록 인쇄
- `/estimate/viewEstimate.php` - 견적서 보기
- `/estimate/saveExcel.php` - 엑셀 저장
## SAM 마이그레이션 포인트
### 1. 견적 구조 개선
```sql
-- SAM: 견적 헤더/상세 분리
estimates (
id,
tenant_id,
estimate_number, -- 견적번호
customer_id, -- 거래처
project_name,
estimate_date,
valid_until, -- 유효기간
status, -- draft, sent, accepted, rejected
total_amount,
discount_rate,
discount_amount,
final_amount,
created_by
)
estimate_items (
id,
estimate_id,
item_type, -- manual, auto_calculated
item_code,
item_name,
specification,
unit,
quantity,
unit_price,
amount,
sort_order
)
```
### 2. 단가표 관리
```sql
-- SAM: 단가 마스터
price_masters (
id,
tenant_id,
category_id,
item_code,
item_name,
base_price,
effective_from,
effective_to,
is_active
)
-- SAM: 단가 조건
price_conditions (
id,
price_master_id,
condition_type, -- size, material, quantity
condition_value,
adjustment_type, -- fixed, percentage
adjustment_value
)
```
### 3. 견적 이력
```sql
-- SAM: 견적 버전 관리
estimate_versions (
id,
estimate_id,
version,
snapshot JSON, -- 견적 전체 스냅샷
changed_by,
changed_at,
change_reason
)
```
## 참고 파일
- `/estimate/insert.php` - 저장 로직
- `/estimate/write_form.php` - 견적서 UI (대용량 파일)
- `/estimate/fetch_unitprice.php` - 단가 계산 로직
- `/estimate/generate_serial_pjnum.php` - 번호 생성

View File

@@ -0,0 +1,305 @@
# 생산/공사 (Production/Work) 분석
## 개요
- **디렉토리**: `/make/`, `/work/`
- **DB 테이블**: `work`, 동적 테이블 (`make_*`)
- **주요 기능**: 공사 관리, 생산 지시, 작업 진행 추적
## 디렉토리 구조
### /make/ (생산 지시)
```
/make/
├── _request.php # 요청 파라미터
├── _row.php # 행 렌더링
├── insert.php # 데이터 저장
├── insert_new.php # 신규 저장
├── list.php # 생산 목록
├── write.php # 생산 지시 등록
├── write_form.php # 생산 지시 폼 (37KB)
├── print.php # 인쇄
├── print_list.php # 목록 인쇄
├── make.js # 자바스크립트 (22KB)
├── get_check_done.php # 완료 체크
├── get_screenlist.php # 스크린 목록
├── update_checkbox.php # 체크박스 업데이트
└── load*.php # 데이터 로드
```
### /work/ (공사 관리)
```
/work/
├── _request.php # 요청 파라미터 (91개 변수)
├── _row.php # 행 렌더링
├── insert.php # 데이터 저장
├── list.php # 공사 목록
├── write_form.php # 공사 등록 폼
├── view.php # 공사 상세
├── searchkd.php # 검색
├── workerlist.php # 작업자 목록
├── savefile.php # 파일 저장
└── ...
```
## DB 스키마
### work 테이블 (공사 마스터)
```sql
CREATE TABLE work (
num INT AUTO_INCREMENT PRIMARY KEY,
-- 공사 기본정보
work_state VARCHAR(50), -- 공사상태
workplacename VARCHAR(200), -- 현장명
address TEXT, -- 현장주소
chargedperson VARCHAR(50), -- 공사담당자
-- 발주처 정보 (1차)
firstord VARCHAR(100), -- 1차 발주처 (건설사)
firstordman VARCHAR(50), -- 1차 담당자
firstordmantel VARCHAR(20), -- 1차 연락처
-- 발주처 정보 (2차)
secondord VARCHAR(100), -- 2차 발주처 (시공사)
secondordman VARCHAR(50), -- 2차 담당자
secondordmantel VARCHAR(20), -- 2차 연락처
-- 작업 정보
worklist TEXT, -- 작업 목록
motormaker VARCHAR(100), -- 모터 제조사
power VARCHAR(50), -- 전원
-- 일정 정보
workday DATE, -- 시공 시작일
endworkday DATE, -- 시공 완료일
cableday DATE, -- 결선 시작일
endcableday DATE, -- 결선 완료일
-- 작업자 정보
worker VARCHAR(100), -- 시공팀
cablestaff VARCHAR(100), -- 결선팀
-- AS 정보
asday DATE, -- AS 접수일
asman VARCHAR(50), -- AS 담당자
asendday DATE, -- AS 완료일
asproday DATE, -- AS 처리예정일
setdate DATE, -- 세팅예정일
asorderman VARCHAR(50), -- AS 의뢰자
asordermantel VARCHAR(20), -- AS 의뢰자 연락처
aslist TEXT, -- AS 내역
asresult TEXT, -- AS 결과
ashistory TEXT, -- AS 이력
as_state VARCHAR(50), -- AS 상태
as_step VARCHAR(50), -- AS 단계
as_check TINYINT, -- AS 체크
aswriter VARCHAR(50), -- AS 등록자
asfee TINYINT, -- AS 유/무상
asfee_estimate INT, -- AS 유상금액
as_checkboxvalue1 CHAR(1), -- AS 체크1
as_checkboxvalue2 CHAR(1), -- AS 체크2
as_checkboxvalue3 CHAR(1), -- AS 체크3
as_checkboxvalue4 CHAR(1), -- AS 체크4
-- 클레임 정보
claimperson VARCHAR(50), -- 클레임 담당자
claimtel VARCHAR(20), -- 클레임 연락처
claimList TEXT, -- 클레임 리스트 (JSON)
-- 금액 정보
sum_estimate DECIMAL(15,0), -- 견적합계
sum_bill DECIMAL(15,0), -- 청구합계
sum_receivable DECIMAL(15,0), -- 미수금
sum_deposit DECIMAL(15,0), -- 입금합계
sum_claimamount DECIMAL(15,0), -- 클레임금액
receivable DECIMAL(15,0), -- 미수잔액
totalbill DECIMAL(15,0), -- 총청구액
decided_estimate DECIMAL(15,0), -- 확정견적
total_receivable DECIMAL(15,0), -- 총미수금
total_deposit DECIMAL(15,0), -- 총입금액
issued_receivable DECIMAL(15,0), -- 발행미수금
issued_amount DECIMAL(15,0), -- 발행금액
-- 보증 정보
warrantyFromDate DATE, -- 보증 시작일
warrantyToDate DATE, -- 보증 종료일
warrantyPeriod VARCHAR(50), -- 보증 기간
warrantyMemo TEXT, -- 보증 메모
-- 인정검사 정보
certifiedInspector VARCHAR(50), -- 인정검사원
certifiedLabelAttachedDate DATE, -- 인정라벨 부착일
-- 상태 정보
checkbox TINYINT, -- 계약체결여부
checkstep VARCHAR(50), -- 진행단계
workStatus VARCHAR(50), -- 시공상태
cableworkStatus VARCHAR(50), -- 결선상태
motorwirestatus VARCHAR(50), -- 모터결선상태
-- 체크 플래그
checkreceivable TINYINT, -- 미수금 체크
checkbond TINYINT, -- 채권 체크
-- JSON 데이터
accountList TEXT, -- 회계 리스트 (JSON)
estimateList TEXT, -- 견적 리스트 (JSON)
equipmentList TEXT, -- 장비 리스트 (JSON)
-- 기타
comment TEXT, -- 비고
outputmemo TEXT, -- 출하 메모
accountnote TEXT, -- 회계 메모
change_worklist TEXT, -- 변경 작업목록
as_refer TEXT, -- AS 참조
promiseday DATE, -- 약속일
-- 시스템 필드
regist_day DATETIME, -- 등록일
update_day DATETIME, -- 수정일
update_log TEXT, -- 수정이력
searchtag TEXT, -- 검색태그
is_deleted TINYINT DEFAULT 0, -- 삭제플래그
hit INT DEFAULT 0, -- 조회수
INDEX idx_workplacename (workplacename),
INDEX idx_secondord (secondord),
INDEX idx_work_state (work_state)
);
```
## 공사 진행 상태
### work_state 상태값
| 상태 | 설명 | 조건 |
|------|------|------|
| 계약전 | 초기 상태 | checkbox = 0 |
| 착공전 | 계약 완료 | checkbox = 1 |
| 시공중 | 시공 시작 | workday 입력 |
| 결선대기 | 시공 완료 | endworkday 입력 |
| 결선중 | 결선 시작 | cableday 입력 |
| 결선완료 | 결선 완료 | endcableday 입력 |
### AS 상태 (as_state)
| 상태 | 설명 | 조건 |
|------|------|------|
| 미접수 | 초기 상태 | - |
| 접수완료 | AS 접수 | asday 입력 |
| 처리예약 | 처리 예정 | asproday 입력 |
| 세팅예약 | 세팅 예정 | setdate 입력 |
| 처리완료 | AS 완료 | asendday 입력 |
## 비즈니스 로직
### 공사 상태 자동 계산
```php
// as/list.php
$state_work = 0;
if ($row["checkbox"] == 0) $state_work = 1; // 착공전
if (substr($row["workday"], 0, 2) == "20") $state_work = 2; // 시공중
if (substr($row["endworkday"], 0, 2) == "20") $state_work = 3; // 결선대기
if (substr($row["cableday"], 0, 2) == "20") $state_work = 4; // 결선중
if (substr($row["endcableday"], 0, 2) == "20") $state_work = 5; // 결선완료
```
### 생산 지시 처리
```php
// make/insert.php
// screen_state 업데이트
$sql = "UPDATE $DB.$tablename SET screen_state=?, update_log=? WHERE num=?";
```
## 관련 디렉토리
### 벤딩 관련
```
/bending/ # 벤딩 작업 관리
/bendingfee/ # 벤딩 비용
/bendingmap/ # 벤딩 맵
/etcbending/ # 기타 벤딩
```
### 출력물 관련
```
/output/ # 출하/출력
/egimake/ # EGI 제작
/guiderail/ # 가이드레일
/shutterbox/ # 셔터박스
```
## SAM 마이그레이션 포인트
### 1. 공사/프로젝트 분리
```sql
-- SAM: 프로젝트 마스터
projects (
id,
tenant_id,
project_number,
project_name,
customer_id,
status, -- draft, contracted, in_progress, completed
contracted_date,
start_date,
end_date
)
-- SAM: 공사 단계
project_phases (
id,
project_id,
phase_type, -- construction, wiring, testing
planned_start,
planned_end,
actual_start,
actual_end,
assigned_team,
status
)
```
### 2. 작업 지시서
```sql
-- SAM: 작업 지시
work_orders (
id,
project_id,
work_order_number,
work_type, -- production, assembly, installation
status,
priority,
due_date,
assigned_to
)
-- SAM: 작업 상세
work_order_items (
id,
work_order_id,
item_id,
quantity,
completed_quantity,
status
)
```
### 3. 진행 상태 추적
```sql
-- SAM: 상태 이력
status_histories (
id,
entity_type, -- project, work_order, phase
entity_id,
from_status,
to_status,
changed_by,
changed_at,
reason
)
```
## 참고 파일
- `/work/_request.php` - 91개 요청 변수 정의
- `/work/insert.php` - 저장 로직
- `/make/write_form.php` - 생산 지시 UI
- `/as/list.php` - 공사 상태 계산 로직

View File

@@ -0,0 +1,352 @@
# 출하 (Shipping/Output) 분석
## 개요
- **디렉토리**: `/output/`
- **DB 테이블**: `output` (메인), `output_extra` (서브)
- **주요 기능**: 출하 관리, 배송 추적, 인정검사(ACI), 품질 서류
## 디렉토리 구조
```
/output/
├── _request.php # 요청 파라미터 (108개)
├── _row.php # 행 렌더링
├── _row_extra.php # 추가 행 렌더링
├── insert.php # 출하 저장 (342줄)
├── list.php # 출하 목록
├── list_*.php # 용도별 목록 (20개+)
│ ├── list_ACI.php # 인정검사 목록
│ ├── list_POD.php # 출고증 목록
│ ├── list_QCdoc.php # 품질서류 목록
│ ├── list_QCsales.php # 품질매출 목록
│ ├── list_account.php # 회계 목록
│ ├── list_bending.php # 벤딩 목록
│ ├── list_bending_mid.php # 벤딩 중간검사
│ ├── list_deliveryfee.php # 배송비 목록
│ ├── list_document.php # 서류 목록
│ ├── list_jointbar.php # 조인트바 목록
│ ├── list_order.php # 발주 목록
│ ├── list_output.php # 출력 목록
│ ├── list_requestACI.php # ACI 요청 목록
│ ├── list_screen.php # 스크린 목록
│ ├── list_screen_mid.php # 스크린 중간검사
│ ├── list_slat.php # 슬랫 목록
│ └── list_slat_mid.php # 슬랫 중간검사
├── write_form.php # 출하 등록 폼 (107KB)
├── write_form_script.php # 스크립트 (267KB)
├── view*.php # 각종 뷰 (30개+)
├── insert_*.php # 부분 저장
├── fetch_*.php # AJAX 데이터 조회
└── json/ # JSON 데이터 저장
```
## DB 스키마
### output 테이블 (메인)
```sql
CREATE TABLE output (
num INT AUTO_INCREMENT PRIMARY KEY,
-- 기본 정보
outdate DATE, -- 출하일
indate DATE, -- 등록일
orderman VARCHAR(50), -- 담당자
outworkplace VARCHAR(200), -- 현장명/거래처
outputplace VARCHAR(200), -- 출하장소
receiver VARCHAR(50), -- 수령자
phone VARCHAR(20), -- 연락처
delivery VARCHAR(100), -- 배송방법/업체
-- 분류 정보
root VARCHAR(50), -- 경로
steel VARCHAR(50), -- 강종
motor VARCHAR(100), -- 모터
-- 상태 정보
regist_state VARCHAR(20), -- 등록상태 (등록, 완료)
bend_state VARCHAR(20), -- 벤딩상태
motor_state VARCHAR(20), -- 모터상태 (준비완료 등)
-- 발주처 정보
con_num VARCHAR(50), -- 계약번호
secondord VARCHAR(100), -- 2차 발주처
secondordman VARCHAR(50), -- 2차 담당자
secondordmantel VARCHAR(20), -- 2차 연락처
secondordnum VARCHAR(50), -- 2차 발주번호
-- 스크린 정보
screen VARCHAR(100), -- 스크린 유형
screen_su INT, -- 스크린 수량
screen_m2 DECIMAL(10,2), -- 스크린 면적(m²)
screenlist TEXT, -- 스크린 리스트 (JSON)
-- 슬랫 정보
slat VARCHAR(100), -- 슬랫 유형
slat_su INT, -- 슬랫 수량
slat_m2 DECIMAL(10,2), -- 슬랫 면적(m²)
slatlist TEXT, -- 슬랫 리스트 (JSON)
-- 제품/LOT 정보
prodCode VARCHAR(50), -- 제품코드
warrantyNum VARCHAR(50), -- 보증번호
lotNum VARCHAR(50), -- LOT 번호
warranty VARCHAR(100), -- 보증정보
-- 인정검사(ACI) 정보
ACIregDate DATE, -- ACI 등록일
ACIaskDate DATE, -- ACI 신청일
ACIdoneDate DATE, -- ACI 완료일
ACImemo TEXT, -- ACI 메모
ACIcheck TINYINT, -- ACI 체크
ACIgroupCode VARCHAR(50), -- ACI 그룹코드
ACIgroupName VARCHAR(100), -- ACI 그룹명
-- 배송비 정보
deliveryfeeList TEXT, -- 배송비 리스트 (JSON)
-- 견적 연결
estimate_num INT, -- 연결된 견적번호
displayText TEXT, -- 표시 텍스트
-- 체크 플래그
slatcheck VARCHAR(10), -- 슬랫 체크
partscheck VARCHAR(10), -- 부품 체크
devMode VARCHAR(10), -- 개발자모드
-- 기타
comment TEXT, -- 비고
updatecomment TEXT, -- 수정 비고
orderdate DATE, -- 발주일
-- 시스템 필드
searchtag TEXT, -- 검색태그
update_log TEXT, -- 수정이력
is_deleted TINYINT DEFAULT 0, -- 삭제플래그
INDEX idx_outdate (outdate),
INDEX idx_outworkplace (outworkplace),
INDEX idx_prodCode (prodCode)
);
```
### output_extra 테이블 (상세/견적)
```sql
CREATE TABLE output_extra (
id INT AUTO_INCREMENT PRIMARY KEY,
parent_num INT, -- output.num 참조
-- 견적 상세 JSON
detailJson TEXT, -- 상세 정보 JSON
estimateList TEXT, -- 견적 리스트 JSON
estimateSlatList TEXT, -- 슬랫 견적 리스트 JSON
estimateList_auto TEXT, -- 자동계산 리스트 JSON
estimateSlatList_auto TEXT, -- 슬랫 자동계산 JSON
-- 금액 정보
estimateTotal INT DEFAULT 0, -- 견적 총액
EstimateFirstSum INT DEFAULT 0, -- 최초 견적
EstimateUpdatetSum INT DEFAULT 0, -- 수정 견적
EstimateDiffer INT DEFAULT 0, -- 차액
estimateSurang INT DEFAULT 0, -- 수량
inspectionFee INT DEFAULT 50000, -- 인정검사비
EstimateDiscountRate DECIMAL(5,2), -- 할인율
EstimateDiscount INT DEFAULT 0, -- 할인금액
EstimateFinalSum INT DEFAULT 0, -- 최종금액
ET_unapproved INT DEFAULT 0, -- 비인정 금액
ET_total INT DEFAULT 0, -- 총 금액
-- 규격 정보
pjnum VARCHAR(50), -- 프로젝트 번호
major_category VARCHAR(50), -- 대분류
position VARCHAR(50), -- 위치
makeWidth INT, -- 제작 폭
makeHeight INT, -- 제작 높이
maguriWing VARCHAR(20), -- 마구리 윙
-- 작업/검사 리스트 (JSON)
screen_unapprovedList TEXT, -- 스크린 비인정 리스트
slat_unapprovedList TEXT, -- 슬랫 비인정 리스트
motorList TEXT, -- 모터 리스트
bendList TEXT, -- 벤딩 리스트
etcList TEXT, -- 기타 리스트
controllerList TEXT, -- 컨트롤러 리스트
-- 회계 정보
accountDate DATE, -- 회계일
accountList TEXT, -- 회계 리스트 (JSON)
FOREIGN KEY (parent_num) REFERENCES output(num),
INDEX idx_parent_num (parent_num)
);
```
## JSON 데이터 구조
### screenlist / slatlist
```json
[
{
"seq": 1,
"item_code": "SCR001",
"width": 1200,
"height": 2400,
"quantity": 2,
"area": 5.76,
"note": "비고"
}
]
```
### deliveryfeeList
```json
[
{
"delivery_date": "2025-01-15",
"carrier": "한진택배",
"fee": 50000,
"paid_by": "발송인"
}
]
```
### motorList / bendList
```json
[
{
"model": "KD-M001",
"quantity": 1,
"status": "완료",
"note": ""
}
]
```
## 비즈니스 로직
### 출하 등록 프로세스
```php
// insert.php
if ($mode == "insert") {
// 1. output 테이블 저장
$sql = "INSERT INTO $DB.output (outdate, indate, orderman, ...) VALUES (?, ?, ?, ...)";
$output_num = $pdo->lastInsertId();
// 2. output_extra 테이블 저장
$sql2 = "INSERT INTO $DB.output_extra (parent_num, detailJson, ...) VALUES (?, ?, ...)";
// 3. 첨부파일 연결 (timekey → num)
if ($timekey != '') {
$sql = "UPDATE $DB.picuploads SET parentnum=? WHERE parentnum=?";
}
}
```
### 상태 변경
```php
// 완료 처리 시 모터 상태 자동 변경
if ($regist_state == '완료') {
$motor_state = "준비완료";
}
```
## 관련 뷰 파일
### 작업 뷰 (생산 작업지시서)
- `viewBendingWork.php` - 벤딩 작업
- `viewScreenWork.php` - 스크린 작업
- `viewSlatWork.php` - 슬랫 작업
### 검사 뷰 (중간검사)
- `viewMidInspectBending.php` - 벤딩 중간검사
- `viewMidInspectScreen.php` - 스크린 중간검사
- `viewMidInspectSlat.php` - 슬랫 중간검사
### 발주/출고 뷰
- `viewOrder.php` - 발주서
- `viewOutput.php` - 출하서
- `viewConfirm.php` - 확인서
### ACI 뷰
- `view_requestACI.php` - ACI 요청
- `view_requestACIgroup.php` - ACI 그룹 요청
- `view_QCcertificate.php` - 품질인증서
## SAM 마이그레이션 포인트
### 1. 출하 구조 개선
```sql
-- SAM: 출하 헤더
shipments (
id,
tenant_id,
shipment_number,
project_id,
customer_id,
ship_date,
status, -- draft, confirmed, shipped, delivered
delivery_address,
receiver_name,
receiver_phone,
carrier,
tracking_number
)
-- SAM: 출하 상세
shipment_items (
id,
shipment_id,
item_type, -- screen, slat, motor, etc.
item_code,
quantity,
lot_number,
note
)
```
### 2. 인정검사(ACI) 분리
```sql
-- SAM: 인정검사 관리
aci_inspections (
id,
shipment_id,
inspection_number,
request_date,
inspection_date,
result, -- pending, passed, failed
inspector,
certificate_number,
documents JSON -- 첨부문서
)
```
### 3. 배송비 관리
```sql
-- SAM: 배송비 이력
delivery_fees (
id,
shipment_id,
carrier,
fee_amount,
payment_type, -- prepaid, collect
paid_date,
receipt_number
)
```
### 4. 문서 관리
```sql
-- SAM: 출하 문서
shipment_documents (
id,
shipment_id,
document_type, -- work_order, inspection, certificate
document_number,
generated_at,
file_path
)
```
## 참고 파일
- `/output/insert.php` - 저장 로직 (342줄)
- `/output/_request.php` - 108개 요청 변수
- `/output/write_form.php` - 대형 UI (107KB)
- `/output/write_form_script.php` - 스크립트 (267KB)

View File

@@ -0,0 +1,325 @@
# 품질/AS (Quality/After-Service) 분석
## 개요
- **디렉토리**: `/as/`
- **DB 테이블**: `work` (공사 테이블 내 AS 컬럼 사용)
- **주요 기능**: AS 접수, 처리 추적, 클레임 관리, 보증 관리
## 디렉토리 구조
```
/as/
├── list.php # AS 목록 (54KB)
├── view.php # AS 상세 (56KB)
├── write_form.php # AS 등록/수정 폼 (54KB)
├── func.php # 공통 함수
├── delete.php # AS 삭제
├── delete_ripple.php # 연관 삭제
├── print_area.php # 인쇄 영역
├── print_aslist.php # AS 목록 인쇄
├── outputlist.php # 출력 목록
└── outputview.php # 출력 뷰
```
## DB 스키마
### work 테이블 - AS 관련 컬럼
```sql
-- AS는 별도 테이블이 아닌 work 테이블의 컬럼으로 관리
-- work 테이블에 AS 관련 필드 포함
-- AS 기본 정보
as_check TINYINT, -- AS 체크 (1: AS 대상)
as_state VARCHAR(50), -- AS 상태 (미접수, 접수완료, 처리예약, 세팅예약, 처리완료)
as_step VARCHAR(50), -- AS 단계
-- AS 일정 정보
asday DATE, -- AS 접수일
asproday DATE, -- AS 처리예정일
setdate DATE, -- 세팅예정일
asendday DATE, -- AS 완료일
-- AS 담당자 정보
asman VARCHAR(50), -- AS 담당자
aswriter VARCHAR(50), -- AS 등록자
asorderman VARCHAR(50), -- AS 의뢰자
asordermantel VARCHAR(20), -- AS 의뢰자 연락처
-- AS 내용
aslist TEXT, -- AS 내역
asresult TEXT, -- AS 결과
ashistory TEXT, -- AS 이력
as_refer TEXT, -- AS 참조
-- AS 비용
asfee TINYINT, -- AS 유/무상 (0: 무상, 1: 유상)
asfee_estimate INT, -- AS 유상금액
-- AS 체크박스
as_checkboxvalue1 CHAR(1), -- AS 체크1
as_checkboxvalue2 CHAR(1), -- AS 체크2
as_checkboxvalue3 CHAR(1), -- AS 체크3
as_checkboxvalue4 CHAR(1), -- AS 체크4
```
### 클레임 관련 컬럼
```sql
-- work 테이블 내 클레임 필드
claimperson VARCHAR(50), -- 클레임 담당자
claimtel VARCHAR(20), -- 클레임 연락처
claimList TEXT, -- 클레임 리스트 (JSON)
sum_claimamount DECIMAL(15,0), -- 클레임금액
```
### 보증 관련 컬럼
```sql
-- work 테이블 내 보증 필드
warrantyFromDate DATE, -- 보증 시작일
warrantyToDate DATE, -- 보증 종료일
warrantyPeriod VARCHAR(50), -- 보증 기간
warrantyMemo TEXT, -- 보증 메모
```
### 인정검사 관련 컬럼
```sql
-- work 테이블 내 인정검사 필드
certifiedInspector VARCHAR(50), -- 인정검사원
certifiedLabelAttachedDate DATE, -- 인정라벨 부착일
```
## AS 상태 흐름
### as_state 상태값
| 상태 | 설명 | 조건 |
|------|------|------|
| 미접수 | 초기 상태 | as_check = 1, asday 미입력 |
| 접수완료 | AS 접수됨 | asday 입력 |
| 처리예약 | 처리 예정 | asproday 입력 |
| 세팅예약 | 세팅 예정 | setdate 입력 |
| 처리완료 | AS 완료 | asendday 입력 |
### 상태 전이 다이어그램
```
[미접수] → [접수완료] → [처리예약] → [처리완료]
↘ [세팅예약] ↗
```
## 비즈니스 로직
### AS 목록 조회 (as/list.php)
```php
// AS 대상 조회 조건
$a = "(as_check = '1') or (asday <> '0000-00-00') order by num desc";
// 상태별 필터링
if($asprocess == "전체") {
$sql = "SELECT * FROM chandj.work WHERE " . $a;
} elseif($asprocess == "미접수") {
$sql = "SELECT * FROM chandj.work WHERE (as_state LIKE '%$asprocess%')
AND (as_check = '1') ORDER BY num DESC";
} else {
$sql = "SELECT * FROM chandj.work WHERE (as_state LIKE '%$asprocess%')
ORDER BY num DESC";
}
```
### AS 등록 조건
- `as_check = 1`: 해당 공사를 AS 대상으로 지정
- `asday` 입력 시: 자동으로 "접수완료" 상태로 변경
- work 테이블에 직접 UPDATE
### 유/무상 판단
```php
// asfee 값에 따른 유/무상 구분
if ($asfee == 0) {
// 무상 AS
} else {
// 유상 AS - asfee_estimate 금액 적용
}
```
## JSON 데이터 구조
### claimList (클레임 리스트)
```json
[
{
"seq": 1,
"claim_date": "2025-01-15",
"claim_type": "품질불량",
"description": "표면 스크래치",
"status": "처리완료",
"result": "교체 완료"
}
]
```
### ashistory (AS 이력)
```json
[
{
"date": "2025-01-15",
"action": "접수",
"handler": "홍길동",
"note": "고객 불만 접수"
},
{
"date": "2025-01-20",
"action": "처리완료",
"handler": "김철수",
"note": "부품 교체 완료"
}
]
```
## 관련 뷰 파일
### 목록 뷰
- `list.php` - AS 전체 목록 (상태별 필터링)
- `print_aslist.php` - AS 목록 인쇄
### 상세 뷰
- `view.php` - AS 상세 정보
- `outputview.php` - 출력용 뷰
### 등록/수정 뷰
- `write_form.php` - AS 등록/수정 폼
## SAM 마이그레이션 포인트
### 1. AS 테이블 분리
```sql
-- SAM: AS 독립 테이블
after_services (
id,
tenant_id,
project_id, -- work → projects 참조
as_number, -- AS 번호 (자동생성)
status, -- pending, received, scheduled, completed
-- 접수 정보
received_date,
received_by,
requester_name,
requester_phone,
-- 처리 정보
scheduled_date,
setting_date,
completed_date,
handler_id,
-- 비용
fee_type, -- free, paid
fee_amount,
-- 내용
issue_description,
result_description,
created_at,
updated_at
)
```
### 2. AS 이력 테이블
```sql
-- SAM: AS 상태 이력
after_service_histories (
id,
after_service_id,
from_status,
to_status,
action, -- received, scheduled, completed, reopened
handler_id,
note,
created_at
)
```
### 3. 클레임 테이블 분리
```sql
-- SAM: 클레임 관리
claims (
id,
tenant_id,
project_id,
after_service_id, -- AS와 연결 (선택)
claim_number,
claim_type, -- quality, delivery, etc.
claim_date,
description,
status, -- open, investigating, resolved, closed
resolution,
claimed_amount,
approved_amount,
created_at,
resolved_at
)
```
### 4. 보증 테이블 분리
```sql
-- SAM: 보증 관리
warranties (
id,
tenant_id,
project_id,
warranty_number,
start_date,
end_date,
period_months,
coverage_type, -- full, limited, parts_only
terms TEXT,
is_active,
created_at
)
```
### 5. 인정검사 이력
```sql
-- SAM: 인정검사 이력 (output 테이블 ACI와 연계)
certifications (
id,
tenant_id,
project_id,
shipment_id, -- output → shipments 참조
inspector_name,
inspection_date,
label_attached_date,
certificate_number,
result, -- passed, failed, conditional
documents JSON,
created_at
)
```
## 특이사항
### work 테이블 통합 구조
5130에서 AS는 별도 테이블이 아닌 `work` (공사) 테이블에 통합:
- 장점: 공사-AS 데이터 일체화로 조회 용이
- 단점: 하나의 공사에 다수 AS 발생 시 관리 어려움
- SAM에서는 1:N 관계로 분리 권장
### 상태 관리
- 상태값이 한글 문자열로 저장 (`미접수`, `접수완료` 등)
- SAM에서는 enum 또는 상수 테이블로 정규화 권장
### 클레임-AS 관계
- 현재 구조에서 클레임과 AS가 명확히 구분되지 않음
- SAM에서는 claims와 after_services 테이블로 분리하되 연결 가능하도록 설계
## 참고 파일
- `/as/list.php` - AS 목록 쿼리 및 상태 필터링 (54KB)
- `/as/view.php` - AS 상세 표시 (56KB)
- `/work/_request.php` - AS 관련 91개 변수 정의
- `/work/insert.php` - work 테이블 저장 (AS 컬럼 포함)

View File

@@ -0,0 +1,474 @@
# 회계 (Accounting) 분석
## 개요
- **디렉토리**: `/account/`
- **DB 테이블**: `account` (메인), `recordlist` (미수금 약속이력)
- **주요 기능**: 금전출납, 미수금 관리, 매출채권 관리, 거래처별 잔액
## 디렉토리 구조
```
/account/
├── _request.php # 요청 파라미터 (17개)
├── _row.php # 행 렌더링
├── insert.php # 회계 저장
├── insert_bulk.php # 대량 저장
├── list.php # 회계 목록 (92KB)
├── list_daily.php # 일별 목록 (23KB)
├── detail.php # 상세 보기
├── modal.php # 모달 팝업
├── fetch_modal.php # 모달 데이터 조회 (21KB)
├── fetch_balance.php # 잔액 조회
├── fetch_options.php # 옵션 조회
├── fetch_todoMonthly.php # 월별 할일
├── receivable.php # 미수금 현황 (35KB)
├── receivablelist.php # 미수금 목록 (28KB)
├── baddebt.php # 대손 관리 (29KB)
├── S_transaction.php # 거래 내역 (30KB)
├── s_transaction_sheet.php # 거래 시트 (26KB)
├── month_sales.php # 월별 매출 (31KB)
├── monthly_balance_popup.php # 월별 잔액 팝업
├── customer_last.php # 거래처 최근 거래 (23KB)
├── schedule.php # 일정 관리 (14KB)
├── settings.php # 설정 (15KB)
├── saveExcel.php # 엑셀 저장
├── downloadExcel.php # 엑셀 다운로드
├── order_saveExcel.php # 주문 엑셀 저장
├── customer_saveExcel.php # 거래처 엑셀 저장
├── accoutlist.php # 계정 목록
├── cardlist.php # 카드 목록
├── get_electronic_bills.php # 전자세금계산서 조회
├── accountContents.json # 계정과목 설정 (6KB)
├── accoutlist.json # 계정 목록 JSON
├── cardlist.json # 카드 목록 JSON
├── bankbook.txt # 통장 목록
├── css/ # 스타일시트
├── excel/ # 엑셀 출력
└── json/ # JSON 데이터
```
## DB 스키마
### account 테이블
```sql
CREATE TABLE account (
num INT AUTO_INCREMENT PRIMARY KEY,
-- 기본 정보
registDate DATE, -- 거래일
dueDate DATE, -- 만기일/결제예정일
endorsementDate DATE, -- 배서일 (전자어음)
-- 입출 구분
inoutsep VARCHAR(20), -- 입출구분 (수입/지출)
-- 내용 정보
content VARCHAR(200), -- 내용 (대분류: 거래처 수금, 자재비 등)
contentSub VARCHAR(200), -- 내용 상세 (중분류)
content_detail TEXT, -- 상세 내용 (적요)
-- 금액
amount DECIMAL(15,0), -- 금액 (콤마 제거 후 저장)
-- 은행/결제 정보
bankbook VARCHAR(100), -- 통장/결제수단
-- 거래처 정보
secondordnum VARCHAR(50), -- 거래처 번호 (발주번호)
parentEBNum VARCHAR(50), -- 부모 전자어음 번호
-- 시스템 필드
first_writer VARCHAR(100), -- 최초 작성자 (날짜 + 이름)
update_log TEXT, -- 수정이력
searchtag TEXT, -- 검색태그
is_deleted TINYINT DEFAULT 0, -- 삭제플래그
INDEX idx_registDate (registDate),
INDEX idx_inoutsep (inoutsep),
INDEX idx_content (content),
INDEX idx_secondordnum (secondordnum)
);
```
### recordlist 테이블 (미수금 약속 이력)
```sql
CREATE TABLE recordlist (
num INT AUTO_INCREMENT PRIMARY KEY,
secondordnum VARCHAR(50), -- 거래처 번호
primisedate DATE, -- 약속일
comment TEXT, -- 코멘트
is_deleted TINYINT DEFAULT 0, -- 삭제플래그
INDEX idx_secondordnum (secondordnum)
);
```
## 계정과목 구조 (accountContents.json)
### 수입 계정
```json
{
"수입": {
"거래처 수금": {
"description": "거래처에서 입금한 금액",
"level": 1,
"하위계정": ["외상매출금"]
},
"차입금": {
"description": "대출금(개인,은행 등)",
"level": 1
},
"이자": {
"description": "결산이자",
"level": 1,
"하위계정": ["이자수익"]
},
"최초전월이월": {
"description": "금전출납부 시작",
"level": 1
},
"잡이익": {
"description": "잡이익",
"level": 1
}
}
}
```
### 지출 계정
```json
{
"지출": {
"자재비": {"하위계정": ["외상매입금"]},
"급여": {"하위계정": ["임직원급여", "상여금"]},
"복리후생비": {"description": "직원 식대외 직원 작업복등"},
"소모품비": {"description": "각종 소모품 비용"},
"차량유지비": {"description": "유류대, 통행료"},
"운반비": {"description": "택배운반비외 각종운반비"},
"통신비": {"description": "전화요금, 인터넷요금"},
"수도요금": {"하위계정": ["수도광열비"]},
"전기요금": {"하위계정": ["수도광열비"]},
"보험료": {"description": "차량보험료, 화재보험료등"},
"세금과공과금": {"description": "등록면허세, 취득세, 재산세등"},
"접대비": {"description": "경조사비용"},
"교통비": {"하위계정": ["여비교통비"]},
"카드대금": {"하위계정": ["미지급비용"]},
"이자비용": {"하위계정": ["국민은행"]},
"차입금상환": {"description": "대출에 의한 차입금"},
"임차료": {"description": "지급임차료"},
"렌트비용": {"하위계정": ["지급임차료"]},
"수선비": {"description": "수선비"},
"개발비": {"description": "프로그램 개발비용"},
"기계장치": {"description": "기계구입"},
"선급금": {"description": "미리 지급하는 금액"},
"예치금": {"description": "예치금"},
"보증금": {"하위계정": ["기타보증금"]},
"수수료": {"하위계정": ["지급수수료"]},
"외상매출채권(전자어음)": {"description": "외상매출채권(전자어음)"}
}
}
```
## 비즈니스 로직
### 회계 등록 (insert.php)
```php
// 검색태그 생성
$searchtag = $registDate . ' ' . $inoutsep . ' ' . $content . ' ' .
$contentSub . ' ' . $amount . $content_detail;
// INSERT
$sql = "INSERT INTO account (
registDate, inoutsep, content, content_detail, amount,
dueDate, searchtag, update_log, first_writer, bankbook,
secondordnum, contentSub, endorsementDate, parentEBNum
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
// 금액에서 콤마 제거
$amount = str_replace(',', '', $amount);
```
### 미수금 계산 (receivable.php)
```php
// 이월 잔액 계산
$previousMonthFromDate = date("Y-m-01", strtotime($previousMonth . '01'));
$previousMonthToDate = date("Y-m-t", strtotime($previousMonth . '01'));
// recordlist에서 약속일/코멘트 조회
$recordSql = "
SELECT secondordnum, primisedate, comment
FROM recordlist
WHERE (is_deleted IS NULL or is_deleted = 0)
ORDER BY num DESC
";
// 거래처별 미수금 = 청구금액 - 입금금액
```
### 잔액 조회 (fetch_balance.php)
- 월별 이월 잔액 계산
- 거래처별 채권/채무 현황
- 전자어음 만기 관리
## 회계 유형
### 1. 금전출납 (Daily Cash)
- **파일**: `list.php`, `list_daily.php`
- **특징**: 일자별 입출금 기록
- **주요 기능**: 일계표, 월계표
### 2. 미수금 관리 (Receivable)
- **파일**: `receivable.php`, `receivablelist.php`
- **특징**: 거래처별 미수금 추적
- **주요 기능**: 이월잔액, 약속일 관리, 대손 처리
### 3. 거래 내역 (Transaction)
- **파일**: `S_transaction.php`, `s_transaction_sheet.php`
- **특징**: 거래처별 거래 이력
- **주요 기능**: 거래처 원장, 거래 명세
### 4. 월별 매출 (Monthly Sales)
- **파일**: `month_sales.php`
- **특징**: 월별 매출 집계
- **주요 기능**: 매출 통계, 추이 분석
## work 테이블 회계 연동
### work 테이블 내 금액 필드
```sql
-- 견적/청구 금액
sum_estimate DECIMAL(15,0), -- 견적합계
decided_estimate DECIMAL(15,0), -- 확정견적
-- 청구/발행 금액
sum_bill DECIMAL(15,0), -- 청구합계
totalbill DECIMAL(15,0), -- 총청구액
issued_amount DECIMAL(15,0), -- 발행금액
-- 입금/미수금
sum_deposit DECIMAL(15,0), -- 입금합계
total_deposit DECIMAL(15,0), -- 총입금액
sum_receivable DECIMAL(15,0), -- 미수금
total_receivable DECIMAL(15,0), -- 총미수금
receivable DECIMAL(15,0), -- 미수잔액
issued_receivable DECIMAL(15,0), -- 발행미수금
-- 클레임
sum_claimamount DECIMAL(15,0), -- 클레임금액
-- JSON 리스트
accountList TEXT, -- 회계 리스트 (JSON)
estimateList TEXT, -- 견적 리스트 (JSON)
```
### accountList JSON 구조
```json
[
{
"date": "2025-01-15",
"type": "입금",
"amount": 5000000,
"memo": "1차 입금"
},
{
"date": "2025-02-15",
"type": "청구",
"amount": 10000000,
"memo": "세금계산서 발행"
}
]
```
## SAM 마이그레이션 포인트
### 1. 회계 테이블 정규화
```sql
-- SAM: 회계 전표
journal_entries (
id,
tenant_id,
entry_number, -- 전표번호
entry_date, -- 전표일자
entry_type, -- income, expense, transfer
-- 계정 정보
account_code, -- 계정코드 (계정과목 테이블 참조)
sub_account_code, -- 하위계정코드
-- 금액
debit_amount, -- 차변
credit_amount, -- 대변
-- 상대방
counterpart_id, -- 거래처 ID
counterpart_name,
-- 결제 정보
payment_method, -- cash, bank, card, note
bank_account,
due_date,
-- 참조
reference_type, -- project, shipment, purchase
reference_id,
description,
created_by,
created_at
)
```
### 2. 계정과목 테이블
```sql
-- SAM: 계정과목 마스터
account_codes (
id,
tenant_id,
code, -- 계정코드
name, -- 계정명
parent_id, -- 상위 계정
level, -- 계층 레벨
account_type, -- asset, liability, equity, income, expense
is_cash_account, -- 현금성 계정 여부
is_receivable, -- 매출채권 여부
is_payable, -- 매입채무 여부
description,
is_active,
sort_order
)
```
### 3. 미수금 관리 분리
```sql
-- SAM: 채권 관리
receivables (
id,
tenant_id,
receivable_number,
customer_id,
-- 채권 정보
original_amount, -- 원금
paid_amount, -- 입금액
balance, -- 잔액 (computed)
-- 일자
invoice_date, -- 청구일
due_date, -- 만기일
-- 상태
status, -- open, partial, paid, overdue, bad_debt
-- 약속 관리
promised_date,
promised_memo,
-- 참조
project_id,
invoice_id,
created_at,
closed_at
)
-- SAM: 채권 이력
receivable_histories (
id,
receivable_id,
action, -- created, payment, promise, overdue, bad_debt
amount,
note,
created_by,
created_at
)
```
### 4. 전자어음 관리
```sql
-- SAM: 전자어음
electronic_notes (
id,
tenant_id,
note_number, -- 어음번호
-- 어음 정보
drawer, -- 발행인
payee, -- 수취인
amount,
issue_date, -- 발행일
due_date, -- 만기일
-- 배서 정보
endorsement_date, -- 배서일
endorsee, -- 피배서인
-- 상태
status, -- issued, endorsed, collected, dishonored
-- 참조
parent_note_id, -- 분할 시 부모
receivable_id,
created_at
)
```
### 5. 통합 재무 현황
```sql
-- SAM: 월별 재무 요약 (캐시 테이블)
monthly_financial_summaries (
id,
tenant_id,
year_month, -- YYYYMM
-- 수입
total_income,
income_by_category JSON,
-- 지출
total_expense,
expense_by_category JSON,
-- 채권/채무
receivables_balance,
payables_balance,
-- 현금 흐름
cash_beginning,
cash_ending,
calculated_at
)
```
## 특이사항
### 계정과목 JSON 관리
- 계정과목이 `accountContents.json` 파일로 관리됨
- 2단계 계층구조 (대분류 → 하위계정)
- SAM에서는 DB 테이블로 정규화 권장
### 미수금-약속일 연동
- `recordlist` 테이블로 거래처별 약속일/코멘트 관리
- 최신 약속만 표시 (ORDER BY num DESC)
- SAM에서는 이력 관리로 확장 필요
### work 테이블 금액 동기화
- 공사별 회계 정보가 work 테이블에 집계됨
- `accountList` JSON으로 상세 내역 저장
- SAM에서는 정규화된 테이블로 관계 설정
## 참고 파일
- `/account/insert.php` - 회계 저장 로직
- `/account/_request.php` - 17개 요청 변수
- `/account/accountContents.json` - 계정과목 설정
- `/account/receivable.php` - 미수금 계산 로직
- `/account/fetch_balance.php` - 잔액 조회

View File

@@ -0,0 +1,695 @@
# 5130 vs SAM 비교 검증 리포트
**분석일**: 2025-12-04
**분석 범위**: 5130 레거시 시스템 ↔ SAM 멀티테넌트 시스템
**목적**: DB Upsert 전략 및 마이그레이션 가이드 도출
---
## Executive Summary
### 핵심 발견사항
**✅ SAM에서 잘 구현된 부분**:
- 멀티테넌트 격리 (tenant_id + BelongsToTenant 스코프)
- 가격 시스템 (price_histories: 시계열, 고객별, 다형성)
- 설계 워크플로우 (models → model_versions → bom_templates)
- 감사 로그 및 soft delete 일관성
**⚠️ 5130 → SAM 마이그레이션 시 고려사항**:
1. **품목 타입 불일치**: 5130의 23개 자재 테이블 → SAM의 materials 단일 테이블
2. **JSON 데이터 정규화**: 5130의 TEXT 컬럼 JSON → SAM의 정규화된 테이블
3. **공사-AS 분리 필요**: 5130의 work 통합 테이블 → SAM의 projects + after_services 분리
4. **계정과목 정규화**: 5130의 JSON 파일 → SAM의 account_codes 테이블
**마이그레이션 우선순위**:
1. 🔴 자재/품목 (materials/products) - 핵심 마스터
2. 🔴 BOM 구조 (product_components/bom_templates)
3. 🟡 견적/주문 (estimates → orders 변환)
4. 🟡 출하 (output → shipments 변환)
5. 🟢 회계 (account → journal_entries 정규화)
6. 🟢 품질/AS (work.AS필드 → after_services 분리)
---
## 1. 도메인별 상세 비교
### 1.1 자재 (Material) 비교
#### 5130 구조
```sql
-- 자재 유형별 개별 테이블 (23개)
i_SUSplate, i_GIplate, i_SUScoil, i_slatcoil, i_bendingcoil,
i_angle, i_pole, i_shaft, i_recpipe, i_motor, i_controller, ...
-- 공통 컬럼
num, lot_no, inspection_date, supplier, item_name, specification,
unit, received_qty, weight_kg, purchase_price_excl_vat,
searchtag, update_log, is_deleted
```
#### SAM 구조
```sql
-- 단일 materials 테이블
materials (
id, tenant_id, category_id, name, item_name, material_code,
specification, unit, attributes JSON, options JSON,
is_inspection, search_tag,
created_by, updated_by, deleted_by, created_at, updated_at, deleted_at
)
-- 카테고리로 유형 구분
categories (id, parent_id, name, level, attributes JSON)
```
#### 매핑 전략
| 5130 | SAM | 변환 로직 |
|------|-----|----------|
| `i_*` 테이블명 | `category_id` | 테이블명 → 카테고리 생성 |
| `num` | `id` | 시퀀스 리매핑 |
| `lot_no` | - | `lots` 테이블로 분리 |
| `supplier` | - | `clients` 테이블 참조 |
| `item_name` | `item_name` | 직접 매핑 |
| `specification` | `specification` | 직접 매핑 |
| `purchase_price` | - | `price_histories` 참조 |
| `searchtag` | `search_tag` | 직접 매핑 |
| `is_deleted` | `deleted_at` | 0→NULL, 1→timestamp |
#### DB Upsert 전략
```php
// 5130 → SAM 마이그레이션
public function upsertMaterial(array $data): Material
{
// 1. 카테고리 매핑 (테이블명 → category_id)
$category = $this->getCategoryByTableName($data['table_name']);
// 2. Upsert 조건: tenant_id + material_code (자연키)
return Material::updateOrCreate(
[
'tenant_id' => $this->tenantId(),
'material_code' => $data['lot_no'] ?? $this->generateCode($data),
],
[
'category_id' => $category->id,
'name' => $data['item_name'] ?? $data['name'],
'item_name' => $data['item_name'],
'specification' => $data['specification'],
'unit' => $data['unit'],
'search_tag' => $data['searchtag'],
// 기타 필드...
]
);
}
```
---
### 1.2 품목/BOM (Product/Model) 비교
#### 5130 구조
```sql
-- 3단계 계층 구조
models (model_id, model_name, major_category, finishing_type, guiderail_type)
parts (part_id, model_id, part_name, spec, unit, quantity, unitprice)
parts_sub (subpart_id, part_id, subpart_name, material, bendSum, plateSum, finalSum)
```
#### SAM 구조
```sql
-- 설계 모델 (Design)
models (id, tenant_id, code, name, category_id, lifecycle, is_active)
model_versions (id, model_id, version_no, status, effective_from, effective_to)
bom_templates (id, model_version_id, name, is_primary, calculation_schema)
bom_template_items (id, bom_template_id, ref_type, ref_id, qty, waste_rate, calculation_formula)
-- 제품 BOM (Production)
products (id, tenant_id, code, name, product_type, category_id, unit)
product_components (id, parent_product_id, ref_type, ref_id, quantity, sort_order)
```
#### 매핑 전략
| 5130 | SAM | 변환 로직 |
|------|-----|----------|
| `models` | `models` + `model_versions` | 버전 관리 추가 |
| `parts` | `bom_template_items` | 설계 BOM |
| `parts_sub` | `bom_template_items` (중첩) | 3단계 → 2단계 평탄화 |
| `unitprice` | `price_histories` | 가격 이력으로 분리 |
| `bendSum/plateSum` | `calculation_formula` | 계산식으로 변환 |
#### 계층 구조 평탄화 로직
```php
// 5130 3단계 → SAM 2단계 변환
public function flattenBomHierarchy(array $model): array
{
$bomItems = [];
foreach ($model['parts'] as $part) {
// 2단계 부품
$bomItems[] = [
'ref_type' => 'PRODUCT',
'ref_id' => $this->findOrCreateProduct($part)->id,
'qty' => $part['quantity'],
'sort_order' => count($bomItems),
];
// 3단계 하위 부품 (평탄화)
foreach ($part['parts_sub'] ?? [] as $subpart) {
$bomItems[] = [
'ref_type' => 'MATERIAL',
'ref_id' => $this->findOrCreateMaterial($subpart)->id,
'qty' => $subpart['quantity'],
'sort_order' => count($bomItems),
'calculation_formula' => $this->convertFormula($subpart),
];
}
}
return $bomItems;
}
```
---
### 1.3 견적 (Estimate) 비교
#### 5130 구조
```sql
estimate (
num, pjnum, indate, orderman, outworkplace,
major_category, model_name, position, makeWidth, makeHeight,
estimateList TEXT, -- JSON: 스크린 견적 항목
estimateSlatList TEXT, -- JSON: 슬랫 견적 항목
estimateList_auto TEXT, -- JSON: 자동계산 항목
estimateSlatList_auto TEXT, -- JSON: 슬랫 자동계산
estimateTotal, EstimateFirstSum, EstimateFinalSum,
EstimateDiscountRate, EstimateDiscount,
inspectionFee, steel, motor, warranty
)
```
#### SAM 구조
```sql
-- 견적 헤더/상세 분리
estimates (
id, tenant_id, estimate_number, customer_id, project_name,
estimate_date, valid_until, status, total_amount,
discount_rate, discount_amount, final_amount, created_by
)
estimate_items (
id, estimate_id, item_type, item_code, item_name,
specification, unit, quantity, unit_price, amount, sort_order
)
```
#### JSON 데이터 정규화 전략
```php
// 5130 JSON → SAM 정규화 테이블
public function normalizeEstimateItems(array $legacyEstimate): void
{
// 1. 견적 헤더 생성
$estimate = Estimate::create([
'tenant_id' => $this->tenantId(),
'estimate_number' => $legacyEstimate['pjnum'],
'customer_id' => $this->findCustomer($legacyEstimate['secondord']),
'project_name' => $legacyEstimate['outworkplace'],
'estimate_date' => $legacyEstimate['indate'],
'total_amount' => $legacyEstimate['estimateTotal'],
'discount_rate' => $legacyEstimate['EstimateDiscountRate'],
'final_amount' => $legacyEstimate['EstimateFinalSum'],
]);
// 2. JSON 파싱 및 상세 생성
$estimateList = json_decode($legacyEstimate['estimateList'], true) ?? [];
foreach ($estimateList as $index => $item) {
EstimateItem::create([
'estimate_id' => $estimate->id,
'item_type' => 'manual',
'item_code' => $item['item_code'] ?? null,
'item_name' => $item['item_name'],
'specification' => $item['specification'] ?? null,
'unit' => $item['unit'],
'quantity' => $item['quantity'],
'unit_price' => $item['unit_price'],
'amount' => $item['amount'],
'sort_order' => $index,
]);
}
// 3. 자동계산 항목 (estimateList_auto)
$autoList = json_decode($legacyEstimate['estimateList_auto'], true) ?? [];
foreach ($autoList as $index => $item) {
EstimateItem::create([
'estimate_id' => $estimate->id,
'item_type' => 'auto_calculated',
// ... 나머지 필드
]);
}
}
```
---
### 1.4 생산/공사 (Production/Work) 비교
#### 5130 구조
```sql
work (
num, work_state, workplacename, address, chargedperson,
-- 발주처 (1차/2차)
firstord, firstordman, firstordmantel,
secondord, secondordman, secondordmantel,
-- 일정
workday, endworkday, cableday, endcableday,
-- 작업자
worker, cablestaff,
-- AS (통합)
asday, asman, asendday, asproday, setdate,
aslist TEXT, asresult TEXT, ashistory TEXT, as_state,
-- 클레임
claimperson, claimtel, claimList TEXT,
-- 금액
sum_estimate, sum_bill, sum_receivable, sum_deposit,
-- 회계 JSON
accountList TEXT, estimateList TEXT
)
```
#### SAM 구조 제안
```sql
-- 프로젝트 분리
projects (
id, tenant_id, project_number, project_name, customer_id,
status, contracted_date, start_date, end_date
)
project_phases (
id, project_id, phase_type, planned_start, planned_end,
actual_start, actual_end, assigned_team, status
)
-- AS 분리
after_services (
id, tenant_id, project_id, as_number, status,
received_date, received_by, requester_name, requester_phone,
scheduled_date, completed_date, handler_id,
fee_type, fee_amount, issue_description, result_description
)
-- 클레임 분리
claims (
id, tenant_id, project_id, after_service_id,
claim_number, claim_type, claim_date, description,
status, resolution, claimed_amount, approved_amount
)
```
#### AS 분리 마이그레이션 전략
```php
// work 테이블의 AS 필드 → after_services 테이블
public function extractAfterServices(array $workData): void
{
// AS 대상 여부 확인
if ($workData['as_check'] != 1 && empty($workData['asday'])) {
return;
}
// 프로젝트 먼저 생성/조회
$project = $this->findOrCreateProject($workData);
// AS 레코드 생성
AfterService::create([
'tenant_id' => $this->tenantId(),
'project_id' => $project->id,
'as_number' => $this->generateAsNumber(),
'status' => $this->mapAsStatus($workData['as_state']),
'received_date' => $workData['asday'],
'scheduled_date' => $workData['asproday'],
'setting_date' => $workData['setdate'],
'completed_date' => $workData['asendday'],
'requester_name' => $workData['asorderman'],
'requester_phone' => $workData['asordermantel'],
'handler_id' => $this->findUser($workData['asman']),
'fee_type' => $workData['asfee'] == 0 ? 'free' : 'paid',
'fee_amount' => $workData['asfee_estimate'] ?? 0,
'issue_description' => $workData['aslist'],
'result_description' => $workData['asresult'],
]);
}
// AS 상태 매핑
private function mapAsStatus(string $legacyStatus): string
{
return match ($legacyStatus) {
'미접수' => 'pending',
'접수완료' => 'received',
'처리예약', '세팅예약' => 'scheduled',
'처리완료' => 'completed',
default => 'pending',
};
}
```
---
### 1.5 출하 (Shipping/Output) 비교
#### 5130 구조
```sql
output (
num, outdate, indate, orderman, outworkplace, outputplace,
receiver, phone, delivery,
screen VARCHAR, screen_su, screen_m2, screenlist TEXT,
slat VARCHAR, slat_su, slat_m2, slatlist TEXT,
ACIregDate, ACIaskDate, ACIdoneDate, ACImemo, ACIgroupCode,
deliveryfeeList TEXT,
estimate_num, prodCode, lotNum, warrantyNum
)
output_extra (
parent_num, detailJson TEXT, estimateList TEXT, estimateSlatList TEXT,
estimateTotal, ET_unapproved, ET_total,
motorList TEXT, bendList TEXT, controllerList TEXT, accountList TEXT
)
```
#### SAM 구조 제안
```sql
-- 출하 헤더
shipments (
id, tenant_id, shipment_number, project_id, customer_id,
ship_date, status, delivery_address, receiver_name, receiver_phone,
carrier, tracking_number
)
-- 출하 상세
shipment_items (
id, shipment_id, item_type, item_code, quantity, lot_number, note
)
-- ACI 분리
aci_inspections (
id, shipment_id, inspection_number, request_date, inspection_date,
result, inspector, certificate_number, documents JSON
)
-- 배송비 분리
delivery_fees (
id, shipment_id, carrier, fee_amount, payment_type, paid_date
)
```
---
### 1.6 회계 (Accounting) 비교
#### 5130 구조
```sql
account (
num, registDate, dueDate, inoutsep, content, contentSub,
content_detail, amount, bankbook, secondordnum,
endorsementDate, parentEBNum,
first_writer, update_log, searchtag, is_deleted
)
-- accountContents.json (계정과목)
{
"수입": {"거래처 수금": {...}, "차입금": {...}, ...},
"지출": {"자재비": {...}, "급여": {...}, ...}
}
```
#### SAM 구조 제안
```sql
-- 계정과목 정규화
account_codes (
id, tenant_id, code, name, parent_id, level,
account_type, is_cash_account, is_receivable, is_payable,
description, is_active, sort_order
)
-- 회계 전표
journal_entries (
id, tenant_id, entry_number, entry_date, entry_type,
account_code, sub_account_code,
debit_amount, credit_amount,
counterpart_id, counterpart_name, payment_method, bank_account,
due_date, reference_type, reference_id, description, created_by
)
-- 채권 관리
receivables (
id, tenant_id, receivable_number, customer_id,
original_amount, paid_amount, balance,
invoice_date, due_date, status, promised_date, promised_memo,
project_id, invoice_id
)
```
#### 계정과목 JSON → 테이블 변환
```php
// accountContents.json → account_codes 테이블
public function migrateAccountCodes(array $jsonContent): void
{
// 1. 대분류 (수입/지출)
foreach ($jsonContent as $mainType => $accounts) {
$mainAccount = AccountCode::create([
'tenant_id' => $this->tenantId(),
'code' => $mainType === '수입' ? 'INCOME' : 'EXPENSE',
'name' => $mainType,
'parent_id' => null,
'level' => 1,
'account_type' => $mainType === '수입' ? 'income' : 'expense',
]);
// 2. 중분류
foreach ($accounts as $accountName => $accountData) {
$subAccount = AccountCode::create([
'tenant_id' => $this->tenantId(),
'code' => $this->generateAccountCode($accountName),
'name' => $accountName,
'parent_id' => $mainAccount->id,
'level' => 2,
'description' => $accountData['description'] ?? null,
]);
// 3. 하위계정
foreach ($accountData['하위계정'] ?? [] as $subAccountName => $subData) {
AccountCode::create([
'tenant_id' => $this->tenantId(),
'code' => $this->generateAccountCode($subAccountName),
'name' => is_string($subAccountName) ? $subAccountName : $subData,
'parent_id' => $subAccount->id,
'level' => 3,
]);
}
}
}
}
```
---
## 2. 공통 변환 패턴
### 2.1 Soft Delete 변환
```php
// 5130: is_deleted TINYINT → SAM: deleted_at TIMESTAMP
$deletedAt = $legacyData['is_deleted'] == 1
? Carbon::now()->toDateTimeString()
: null;
```
### 2.2 searchtag 변환
```php
// 5130: searchtag (공백 구분) → SAM: search_tag (동일)
// SAM은 Laravel Scout + Meilisearch 권장
$searchTag = $legacyData['searchtag'];
// 또는 동적 생성
$searchTag = implode(' ', array_filter([
$data['name'],
$data['code'],
$data['specification'],
// ...
]));
```
### 2.3 update_log 변환
```php
// 5130: update_log TEXT (누적) → SAM: audit_logs 테이블
public function migrateUpdateLog(string $entityType, int $entityId, ?string $updateLog): void
{
if (empty($updateLog)) return;
// 로그 파싱 (형식: "2025-01-15 10:30:00 - 홍길동 내용")
$lines = explode("&#10", $updateLog);
foreach ($lines as $line) {
if (preg_match('/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) - (\S+) (.*)$/', $line, $matches)) {
AuditLog::create([
'tenant_id' => $this->tenantId(),
'target_type' => $entityType,
'target_id' => $entityId,
'action' => 'updated',
'after' => ['note' => $matches[3]],
'actor_id' => $this->findUserByName($matches[2]),
'created_at' => $matches[1],
]);
}
}
}
```
### 2.4 JSON TEXT → 정규화 테이블
```php
// 공통 JSON 파싱 유틸
public function parseJsonField(?string $jsonText): array
{
if (empty($jsonText)) return [];
$decoded = json_decode($jsonText, true);
if (json_last_error() !== JSON_ERROR_NONE) {
Log::warning("JSON parse error: " . json_last_error_msg());
return [];
}
return $decoded;
}
```
---
## 3. 마이그레이션 실행 전략
### 3.1 단계별 마이그레이션 계획
#### Phase 1: 마스터 데이터 (1-2주)
```
1. categories (5130 테이블명 → SAM 카테고리)
2. clients (5130 발주처 → SAM 거래처)
3. materials (5130 i_* 테이블들 → SAM materials)
4. products (5130 models → SAM products)
5. account_codes (5130 JSON → SAM account_codes)
```
#### Phase 2: 구조 데이터 (2-3주)
```
6. bom_templates (5130 parts → SAM bom_template_items)
7. product_components (5130 parts_sub → SAM product_components)
8. price_histories (5130 unitprice 필드들 → SAM price_histories)
```
#### Phase 3: 트랜잭션 데이터 (3-4주)
```
9. projects (5130 work → SAM projects)
10. project_phases (5130 work 일정필드 → SAM project_phases)
11. estimates (5130 estimate → SAM estimates + estimate_items)
12. shipments (5130 output → SAM shipments + shipment_items)
13. after_services (5130 work.AS필드 → SAM after_services)
14. journal_entries (5130 account → SAM journal_entries)
```
#### Phase 4: 검증 및 정리 (1-2주)
```
15. 데이터 정합성 검증
16. 누락 데이터 보정
17. 레거시 참조 정리
18. 성능 최적화
```
### 3.2 Upsert 전략 요약
| 도메인 | Upsert Key | 충돌 처리 |
|-------|-----------|----------|
| materials | tenant_id + material_code | UPDATE |
| products | tenant_id + code | UPDATE |
| categories | tenant_id + name | UPDATE |
| estimates | tenant_id + estimate_number | UPDATE |
| shipments | tenant_id + shipment_number | UPDATE |
| journal_entries | tenant_id + entry_number | UPDATE |
| after_services | tenant_id + as_number | UPDATE |
### 3.3 롤백 전략
```php
// 트랜잭션 기반 마이그레이션
DB::transaction(function () use ($legacyData) {
// 마이그레이션 로직
}, 5); // 5회 재시도
// 실패 시 백업 테이블에서 복원
// CREATE TABLE _backup_materials AS SELECT * FROM materials;
```
---
## 4. 검증 체크리스트
### 4.1 데이터 정합성 검증
```sql
-- 1. 레코드 수 비교
SELECT '5130' as source, COUNT(*) FROM legacy.i_SUSplate
UNION ALL
SELECT 'SAM' as source, COUNT(*) FROM sam.materials WHERE category_id = ?;
-- 2. 금액 합계 비교
SELECT SUM(amount) FROM legacy.account WHERE is_deleted = 0
UNION ALL
SELECT SUM(debit_amount) FROM sam.journal_entries WHERE entry_type = 'expense';
-- 3. 참조 무결성 검증
SELECT pc.id FROM product_components pc
LEFT JOIN products p ON pc.ref_type = 'PRODUCT' AND pc.ref_id = p.id
LEFT JOIN materials m ON pc.ref_type = 'MATERIAL' AND pc.ref_id = m.id
WHERE p.id IS NULL AND m.id IS NULL;
```
### 4.2 비즈니스 로직 검증
- [ ] 견적 금액 계산 결과 일치
- [ ] BOM 전개 결과 일치
- [ ] AS 상태 흐름 정상 작동
- [ ] 미수금 계산 결과 일치
- [ ] 재고 수량 계산 결과 일치
### 4.3 성능 검증
- [ ] 목록 조회 응답 시간 < 500ms
- [ ] BOM 전개 응답 시간 < 1s
- [ ] 견적 산출 응답 시간 < 2s
---
## 5. 결론
### 5.1 핵심 권장사항
1. **단계별 마이그레이션 필수**: 번에 전체 마이그레이션은 리스크가 높음
2. **JSON 데이터 정규화 우선**: 5130의 TEXT JSON 필드들을 SAM의 정규화된 테이블로 변환
3. **AS/클레임 분리**: work 테이블의 AS 필드를 별도 테이블로 분리하여 이력 관리 강화
4. **가격 시스템 활용**: SAM의 price_histories 테이블로 가격 이력 통합 관리
5. **감사 로그 마이그레이션**: 5130의 update_log를 SAM의 audit_logs로 변환
### 5.2 예상 공수
| 단계 | 예상 공수 | 주요 작업 |
|-----|---------|----------|
| Phase 1 | 1-2주 | 마스터 데이터 마이그레이션 |
| Phase 2 | 2-3주 | 구조 데이터 (BOM, 가격) |
| Phase 3 | 3-4주 | 트랜잭션 데이터 |
| Phase 4 | 1-2주 | 검증 정리 |
| **합계** | **7-11주** | - |
### 5.3 다음 액션
1. 마이그레이션 스크립트 프로토타입 작성
2. 테스트 데이터로 검증
3. 운영 데이터 샘플 마이그레이션
4. 전체 마이그레이션 실행
---
**문서 버전**: v1.0
**작성일**: 2025-12-04
**작성자**: Claude Code
**참조 문서**:
- 5130 분석 문서 (01~07)
- SAM_Item_DB_API_Analysis_v3_FINAL.md
- SAM_Item_Management_DB_Modeling_Analysis.md

View File

@@ -0,0 +1,397 @@
# Drawing Module API 문서
## 📋 개요
Drawing Module은 웹 브라우저에서 사용할 수 있는 독립적인 Canvas 기반 그리기 도구입니다. 모달 형태의 UI를 제공하며, 다양한 그리기 기능과 이미지 편집 기능을 포함합니다.
## 🚀 빠른 시작
### 1. 파일 포함
```html
<!DOCTYPE html>
<html>
<head>
<!-- Bootstrap CSS (선택사항) -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons (선택사항) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
<!-- Drawing Module CSS (필수) -->
<link href="css/drawingModule.css" rel="stylesheet">
</head>
<body>
<!-- jQuery (필수) -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- Drawing Module JS (필수) -->
<script src="js/drawingModule.js"></script>
</body>
</html>
```
### 2. 기본 사용법
```javascript
// 기본 인스턴스 생성
const drawer = new DrawingModule({
container: 'body',
onSave: (data) => {
console.log('저장된 이미지:', data.imageData);
console.log('저장 시간:', data.timestamp);
}
});
// 그리기 도구 표시
drawer.show();
```
## 📊 API 참조
### 생성자 (Constructor)
```javascript
new DrawingModule(options)
```
#### 옵션 매개변수
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| `container` | string | 'body' | 모달을 생성할 컨테이너 선택자 |
| `width` | number | 800 | 모달의 전체 너비 (px) |
| `height` | number | 600 | 모달의 전체 높이 (px) |
| `canvasWidth` | number | 320 | 캔버스의 실제 너비 (px) |
| `canvasHeight` | number | 240 | 캔버스의 실제 높이 (px) |
| `title` | string | '그리기 도구' | 모달 헤더 제목 |
| `initialImage` | string | null | 초기에 로드할 이미지 URL 또는 데이터 URL |
| `onSave` | function | null | 저장 시 호출되는 콜백 함수 |
| `onCancel` | function | null | 취소 시 호출되는 콜백 함수 |
#### 콜백 함수 시그니처
**onSave 콜백:**
```javascript
onSave: (data) => {
// data.imageData - base64 인코딩된 이미지 데이터
// data.timestamp - 저장 시각 (Date 객체)
// data.width - 캔버스 너비
// data.height - 캔버스 높이
}
```
**onCancel 콜백:**
```javascript
onCancel: () => {
// 사용자가 취소 버튼을 클릭했을 때 호출
}
```
### 메서드 (Methods)
#### show()
그리기 도구 모달을 표시합니다.
```javascript
drawer.show();
```
#### hide()
그리기 도구 모달을 숨깁니다.
```javascript
drawer.hide();
```
#### loadImage(imageUrl)
캔버스에 이미지를 로드합니다.
```javascript
drawer.loadImage('path/to/image.png');
// 또는 데이터 URL
drawer.loadImage('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==');
```
#### clear()
캔버스의 모든 내용을 지웁니다.
```javascript
drawer.clear();
```
#### getImageData()
현재 캔버스의 이미지 데이터를 반환합니다.
```javascript
const imageData = drawer.getImageData();
console.log(imageData); // base64 문자열
```
## 🎨 주요 기능
### 그리기 모드
- **점연결 (Polyline)**: 클릭한 점들을 연결하여 선을 그립니다
- **직선 (Line)**: 시작점과 끝점을 클릭하여 직선을 그립니다
- **자유선 (Free)**: 마우스를 드래그하여 자유롭게 선을 그립니다
### 추가 기능
- **직각 모드**: 수평/수직선만 그리기 (체크박스로 활성화)
- **색상 선택**: 16진수 컬러 피커로 선 색상 변경
- **선 굵기 조절**: 1-10px 범위의 슬라이더로 선 굵기 조정
- **지우개**: 원형 지우개로 특정 영역 삭제 (크기 조절 가능)
- **텍스트 추가**: 클릭한 위치에 텍스트 입력
- **실행취소 (Undo)**: 마지막 작업 되돌리기
- **초기화**: 캔버스의 모든 내용 삭제
- **터치 지원**: 모바일/태블릿 디바이스 터치 이벤트 지원
## 📱 사용 예제
### 1. 기본 그리기 도구
```javascript
const basicDrawer = new DrawingModule({
container: 'body',
onSave: (data) => {
// 이미지를 로컬 저장소에 저장
localStorage.setItem('drawing', data.imageData);
alert('그리기가 저장되었습니다!');
}
});
basicDrawer.show();
```
### 2. 커스텀 크기 캔버스
```javascript
const largeDrawer = new DrawingModule({
container: '#myContainer',
canvasWidth: 640,
canvasHeight: 480,
title: '큰 캔버스 그리기',
onSave: (data) => {
console.log(`캔버스 크기: ${data.width}x${data.height}`);
downloadImage(data.imageData, 'large-drawing.png');
}
});
largeDrawer.show();
```
### 3. 이미지 편집
```javascript
const imageEditor = new DrawingModule({
container: 'body',
initialImage: '/path/to/existing-image.jpg',
title: '이미지 편집',
onSave: (data) => {
// 편집된 이미지를 서버에 업로드
uploadImageToServer(data.imageData);
},
onCancel: () => {
console.log('이미지 편집이 취소되었습니다.');
}
});
imageEditor.show();
```
### 4. 서버로 이미지 저장
```javascript
const drawer = new DrawingModule({
container: 'body',
onSave: (data) => {
// AJAX로 서버에 이미지 전송
$.ajax({
url: '/api/save-drawing',
method: 'POST',
data: {
image: data.imageData,
timestamp: data.timestamp.toISOString(),
width: data.width,
height: data.height
},
success: function(response) {
alert('서버에 저장되었습니다!');
console.log('저장된 이미지 ID:', response.imageId);
},
error: function(error) {
alert('저장에 실패했습니다: ' + error.responseText);
}
});
}
});
drawer.show();
```
### 5. 다중 인스턴스
```javascript
// 동시에 여러 개의 그리기 도구 사용
const drawer1 = new DrawingModule({
container: 'body',
title: '그리기 1',
canvasWidth: 200,
canvasHeight: 200,
onSave: (data) => console.log('Drawing 1 saved')
});
const drawer2 = new DrawingModule({
container: 'body',
title: '그리기 2',
canvasWidth: 300,
canvasHeight: 200,
onSave: (data) => console.log('Drawing 2 saved')
});
drawer1.show();
setTimeout(() => drawer2.show(), 100); // 약간의 지연
```
## 🛠️ 유틸리티 함수
### 이미지 다운로드 함수
```javascript
function downloadImage(dataUrl, filename) {
const link = document.createElement('a');
link.download = filename;
link.href = dataUrl;
link.click();
}
// 사용법
const drawer = new DrawingModule({
onSave: (data) => {
downloadImage(data.imageData, 'my-drawing.png');
}
});
```
### Base64를 Blob으로 변환
```javascript
function dataURLtoBlob(dataURL) {
const arr = dataURL.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type: mime});
}
// 사용법
const drawer = new DrawingModule({
onSave: (data) => {
const blob = dataURLtoBlob(data.imageData);
const formData = new FormData();
formData.append('image', blob, 'drawing.png');
// FormData를 서버에 전송
fetch('/upload', {
method: 'POST',
body: formData
});
}
});
```
## 🎯 파일 구조
```
project/
├── test_drawing.php # 테스트 페이지 (PHP)
├── css/
│ └── drawingModule.css # 스타일시트
├── js/
│ └── drawingModule.js # 메인 JavaScript 모듈
└── drawModule.md # 이 문서
```
## 🔧 커스터마이징
### CSS 커스터마이징
주요 CSS 클래스들을 오버라이드하여 스타일을 변경할 수 있습니다:
```css
/* 모달 배경색 변경 */
.dm-modal {
background-color: rgba(0, 0, 0, 0.8);
}
/* 버튼 색상 변경 */
.dm-btn-primary {
background-color: #custom-color;
}
/* 캔버스 테두리 스타일 변경 */
#dmCanvas {
border: 3px solid #custom-border-color;
}
```
### JavaScript 이벤트 훅
모듈의 동작을 커스터마이징하려면 콜백을 사용하세요:
```javascript
const drawer = new DrawingModule({
onSave: (data) => {
// 저장 전 검증
if (data.width < 100 || data.height < 100) {
alert('그림이 너무 작습니다!');
return;
}
// 커스텀 저장 로직
customSaveFunction(data);
},
onCancel: () => {
// 취소 시 정리 작업
cleanupResources();
}
});
```
## 🚨 주의사항
1. **jQuery 의존성**: 이 모듈은 jQuery에 의존하므로 jQuery를 먼저 로드해야 합니다.
2. **CSS 파일 필수**: `drawingModule.css` 파일이 없으면 UI가 제대로 표시되지 않습니다.
3. **브라우저 호환성**: HTML5 Canvas를 지원하는 브라우저에서만 작동합니다.
4. **이미지 크기 제한**: 매우 큰 캔버스는 브라우저 성능에 영향을 줄 수 있습니다.
5. **메모리 관리**: 여러 인스턴스를 사용할 때는 사용하지 않는 인스턴스를 적절히 정리하세요.
## 🐛 문제 해결
### 일반적인 문제들
**Q: 모달이 표시되지 않아요.**
A: CSS 파일이 제대로 로드되었는지, 그리고 `show()` 메서드를 호출했는지 확인하세요.
**Q: 그리기가 작동하지 않아요.**
A: jQuery가 로드되었는지, 그리고 JavaScript 콘솔에 오류가 없는지 확인하세요.
**Q: 이미지 저장이 안 돼요.**
A: `onSave` 콜백이 제대로 설정되었는지 확인하세요.
**Q: 모바일에서 터치가 작동하지 않아요.**
A: 최신 버전의 모듈을 사용하고 있는지 확인하세요. 터치 이벤트는 자동으로 처리됩니다.
## 📄 라이선스
이 모듈은 MIT 라이선스 하에 배포됩니다.
## 🔄 업데이트 내역
- **v1.0.0**: 초기 릴리스
- 기본 그리기 기능
- 모달 UI
- 이미지 로드/저장
- 터치 지원