Files
hskwon 1066ea25b2 docs: Phase 5 API 문서 추가 (사용자 초대, 알림설정, 계정관리)
- erp-api-list.md: Phase 5 섹션 추가 (12개 API)
- erp-api-detail.md: Phase 5 상세 스펙 추가
  - 13. 사용자 초대 (5개): 목록, 발송, 수락, 취소, 재발송
  - 14. 알림 설정 (3개): 조회, 수정, 일괄수정
  - 15. 계정 관리 (4개): 탈퇴, 사용중지, 약관조회, 약관수정
2025-12-19 15:35:41 +09:00

19 KiB
Raw Permalink Blame History

비즈니스 로직 분석

분석 대상: 5130 레거시 견적 시스템 비즈니스 로직 분석 일자: 2025-12-19


비즈니스 프로세스 개요

견적 생성 플로우

┌─────────────────────────────────────────────────────────────┐
│  1. 견적 시작                                                │
│     └─ 신규 / 복사 / 수주연계                                │
├─────────────────────────────────────────────────────────────┤
│  2. 기본 정보 입력                                           │
│     └─ 현장명, 발주처, 담당자                                │
├─────────────────────────────────────────────────────────────┤
│  3. 제품 선택                                                │
│     ├─ 대분류: 스크린 / 철재                                 │
│     ├─ 모델: KSS01, KFS01 등                                 │
│     └─ 규격: 폭, 높이, 마구리윙                              │
├─────────────────────────────────────────────────────────────┤
│  4. 옵션 선택                                                │
│     └─ 절곡, 모터, 보증, 슬랫, 부자재                        │
├─────────────────────────────────────────────────────────────┤
│  5. 상세 항목 입력                                           │
│     ├─ 각 행별 위치, 폭, 높이, 수량 입력                     │
│     └─ 자동 계산 트리거                                      │
├─────────────────────────────────────────────────────────────┤
│  6. 금액 계산 (자동)                                         │
│     ├─ AJAX → get_screen_amount / get_slat_amount            │
│     ├─ 18개 항목별 단가 조회 및 계산                          │
│     └─ 합계 산출                                             │
├─────────────────────────────────────────────────────────────┤
│  7. 할인 적용                                                │
│     └─ 할인율 / 할인액 입력 → 최종금액 계산                  │
├─────────────────────────────────────────────────────────────┤
│  8. 저장                                                     │
│     ├─ 견적번호 생성 (KD-PR-YYMMDD-NN)                       │
│     └─ DB 저장 (estimate 테이블)                             │
└─────────────────────────────────────────────────────────────┘

1. 견적 유형별 처리

스크린 견적 (Screen)

특성
대분류 스크린
주자재 소재 실리카, 와이어
면적 계산 (높이 + 550) × 폭 / 1,000,000 m²
기본 제작폭 160mm
전용 항목 L바, 보강평철, 무게평철, 환봉

슬랫 견적 (Slat/철재)

특성
대분류 철재
주자재 소재 방화슬랫
면적 계산 (높이 + 50) × 폭 / 1,000,000 m²
기본 제작폭 110mm
전용 항목 조인트바

2. 옵션 체크박스 로직

옵션별 영향 항목

옵션 변수 영향받는 항목
절곡 steel 케이스, 케이스용 연기차단재, 마구리, 가이드레일, 레일용 연기차단재, 하장바, L바(스크린), 보강평철(스크린)
모터 motor 모터 가격 포함/미포함
보증 warranty 보증기간 표시 (인정)
슬랫 slatcheck 주자재(슬랫), 조인트바
부자재 partscheck 감기샤프트, 각파이프, 앵글

조건부 계산 로직

// 절곡 옵션 체크 시
if ($steel == '1') {
    // 케이스, 연기차단재, 레일, 하장바 등 계산
    $caseAmount = calculateCase($width, $caseType, $itemList);
    $smokebanAmount = calculateSmokeban($width, $itemList);
    // ...
} else {
    // 해당 항목 0원 처리
    $caseAmount = 0;
    $smokebanAmount = 0;
}

// 모터 옵션 체크 시
if ($motor == '1') {
    $motorAmount = getMotorPrice($motorCapacity);
} else {
    $motorAmount = 0;
}

3. 단가 조회 로직

조회 우선순위

  1. BDmodels 테이블: 모델별 품목 단가
  2. price_ 테이블*: 품목별 세부 단가
  3. 기본값: 조회 실패 시 기본 단가 적용

단가 조회 함수

// fetch_unitprice.php

// 1. 모터 단가 조회
function getMotorPrice($capacity) {
    // price_motor 테이블에서 용량별 단가 조회
    $sql = "SELECT unit_price FROM price_motor WHERE capacity = ?";
    // ...
}

// 2. 샤프트 단가 조회
function getShaftPrice($length) {
    // price_shaft 테이블에서 길이별 단가 조회
    $sql = "SELECT unit_price FROM price_shaft WHERE ? BETWEEN min_length AND max_length";
    // ...
}

// 3. BDmodels에서 품목 단가 조회
function getBDModelPrice($modelname, $itemname, $size) {
    $sql = "SELECT itemList FROM BDmodels WHERE modelname = ? AND itemname = ?";
    // JSON 파싱 후 사이즈에 맞는 가격 반환
}

4. 금액 계산 플로우

스크린 금액 계산 (get_screen_amount.php)

┌─────────────────────────────────────────────────────────────┐
│  입력값                                                      │
│  - 폭(col2), 높이(col3), 수량(col4), 소재(col5)              │
│  - 케이스타입(col6), 레일타입(col7), 설치방식(col8)          │
│  - 체크박스옵션 (steel, motor, warranty, slatcheck, partscheck)│
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  1. 면적 계산                                                │
│     area = (height + 550) × width / 1,000,000                │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  2. 중량 계산 (모터 용량 결정용)                             │
│     weight = area × 소재별_단위중량                          │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  3. 모터 용량 결정                                           │
│     motorCapacity = searchBracketSize(weight, inch)          │
│     → 150K / 300K / 500K / 800K / 1000K                      │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  4. 18개 항목별 금액 계산                                    │
│     ┌─────────────────────────────────────────────────────┐ │
│     │ 1. 검사비: inspectionFee (고정)                     │ │
│     │ 2. 주자재: area × 소재단가                          │ │
│     │ 3. 모터: getMotorPrice(capacity) × motor체크        │ │
│     │ 4. 제어기: getControllerPrice(type) × motor체크     │ │
│     │ 5. 케이스: (width+제작폭) × m당단가 × steel체크     │ │
│     │ 6. 케이스연기차단재: width × m당단가 × steel체크    │ │
│     │ 7. 마구리: 2개 × 개당단가 × steel체크               │ │
│     │ 8. 앵글: 규격별단가 × steel체크                     │ │
│     │ 9. 가이드레일: (height+레일여유) × m당단가 × steel  │ │
│     │ 10. 레일연기차단재: height × m당단가 × steel        │ │
│     │ 11. 하장바: width × m당단가 × steel체크             │ │
│     │ 12. L바: width × m당단가 × steel체크                │ │
│     │ 13. 보강평철: width × m당단가 × steel체크           │ │
│     │ 14. 샤프트: getShaftPrice(width) × partscheck       │ │
│     │ 15. 무게평철: weight계산 × 단가                     │ │
│     │ 16. 환봉: 길이계산 × m당단가                        │ │
│     │ 17. 각파이프: 길이계산 × m당단가 × partscheck       │ │
│     │ 18. 앵글: 길이계산 × m당단가 × partscheck           │ │
│     └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  5. 행 합계                                                  │
│     rowTotal = Σ(항목별금액) × 수량                          │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  6. 전체 합계                                                │
│     estimateTotal = Σ(모든행 rowTotal)                       │
│     EstimateFinalSum = estimateTotal - 할인액                │
└─────────────────────────────────────────────────────────────┘

5. 모터 용량 결정 로직

중량 + 인치 기반 판단

function searchBracketSize($motorWeight, $bracketInch = null) {
    $weight = floatval($motorWeight);
    $inch = intval($bracketInch);

    // 인치별 중량 기준
    if ($inch > 0) {
        // 4인치 기준
        if ($inch == 4 && $weight <= 300)  300K
        if ($inch == 4 && $weight <= 400)  400K

        // 5인치 기준
        if ($inch == 5 && $weight <= 246)  300K
        if ($inch == 5 && $weight <= 327)  400K
        if ($inch == 5 && $weight <= 500)  500K
        if ($inch == 5 && $weight <= 600)  600K

        // 6인치 기준
        if ($inch == 6 && $weight <= 208)  300K
        if ($inch == 6 && $weight <= 277)  400K
        if ($inch == 6 && $weight <= 424)  500K
        if ($inch == 6 && $weight <= 508)  600K
        if ($inch == 6 && $weight <= 800)  800K
        if ($inch == 6 && $weight <= 1000)  1000K

        // 8인치 기준
        if ($inch == 8 && $weight <= 324)  500K
        if ($inch == 8 && $weight <= 388)  600K
        if ($inch == 8 && $weight <= 611)  800K
        if ($inch == 8 && $weight <= 1000)  1000K
    } else {
        // 인치 없이 중량만으로 판단
        if ($weight <= 300)  300K
        if ($weight <= 400)  400K
        if ($weight <= 500)  500K
        if ($weight <= 600)  600K
        if ($weight <= 800)  800K
        if ($weight <= 1000)  1000K
    }
}

브라켓 사이즈 매핑

모터 용량 브라켓 사이즈
300K, 400K 530×320
500K, 600K 600×350
800K, 1000K 690×390

6. 견적번호 생성

형식

KD-PR-YYMMDD-NN

KD: 경동 (회사코드)
PR: 프로젝트
YYMMDD: 날짜 (년월일 6자리)
NN: 일련번호 (01~99, 당일 기준)

생성 로직

// generate_serial_pjnum.php
function generatePjnum($pdo) {
    $today = date('ymd');
    $prefix = "KD-PR-{$today}-";

    // 오늘 날짜의 마지막 번호 조회
    $sql = "SELECT pjnum FROM estimate
            WHERE pjnum LIKE ?
            ORDER BY pjnum DESC LIMIT 1";
    $stmh = $pdo->prepare($sql);
    $stmh->execute([$prefix . '%']);
    $row = $stmh->fetch();

    if ($row) {
        // 마지막 번호 추출 후 +1
        $lastNum = intval(substr($row['pjnum'], -2));
        $nextNum = str_pad($lastNum + 1, 2, '0', STR_PAD_LEFT);
    } else {
        $nextNum = '01';
    }

    return $prefix . $nextNum;
}

7. 금액 처리 규칙

단위 변환

항목 입력 단위 계산 단위 비고
폭/높이 mm m /1000 변환
면적 - 폭×높이/1,000,000
중량 - kg 면적×단위중량
금액 - 천원 단위 반올림

반올림 규칙

// calculation.js
// 금액은 천원 단위에서 반올림
roundedAreaPrice = Math.round(areaPrice / 1000) * 1000;

// 면적은 소수점 2자리
area = Math.round(area * 100) / 100;

수동 편집 처리

// 수동 편집된 셀은 배경색 변경
$('.manually-edited').css('background-color', '#f8d7da');

// 자동 계산 시 수동 편집 값 유지 옵션
if (!isManuallyEdited) {
    cell.val(calculatedValue);
}

8. 데이터 저장 규칙

저장 전 검증

  1. 필수값 확인: 현장명, 발주처, 담당자
  2. 수치 변환: 콤마 제거, 정수 변환
  3. 권한 확인: 레벨 5 이하

저장 데이터

// insert.php
$data = [
    'pjnum' => generatePjnum(),
    'indate' => date('Y-m-d'),
    'orderman' => $_SESSION['name'],
    'outworkplace' => $outworkplace,
    'major_category' => $major_category,
    'model_name' => $model_name,
    'makeWidth' => intval(str_replace(',', '', $makeWidth)),
    'makeHeight' => intval(str_replace(',', '', $makeHeight)),
    'maguriWing' => $maguriWing,
    'inspectionFee' => intval(str_replace(',', '', $inspectionFee)),
    'estimateList' => json_encode($estimateList),
    'estimateList_auto' => json_encode($estimateList_auto),
    'estimateSlatList' => json_encode($estimateSlatList),
    'estimateSlatList_auto' => json_encode($estimateSlatList_auto),
    'estimateTotal' => intval(str_replace(',', '', $estimateTotal)),
    'steel' => $steel,
    'motor' => $motor,
    'warranty' => $warranty,
    'slatcheck' => $slatcheck,
    'partscheck' => $partscheck
];

9. SAM 이관 시 로직 변경

Service 클래스 분리

// app/Services/QuotationService.php
class QuotationService
{
    // 1. 견적 생성
    public function createQuote(array $data): Quote { }

    // 2. 금액 계산
    public function calculateAmount(Quote $quote): array { }

    // 3. 스크린 계산
    protected function calculateScreenAmount(array $details): array { }

    // 4. 슬랫 계산
    protected function calculateSlatAmount(array $details): array { }

    // 5. 모터 용량 결정
    protected function determineMotorCapacity(float $weight, ?int $inch): int { }

    // 6. 단가 조회
    protected function getUnitPrice(string $itemCode, array $params): float { }
}

계산 로직 캡슐화

// app/ValueObjects/QuoteDimension.php
class QuoteDimension
{
    public function __construct(
        public readonly int $width,
        public readonly int $height,
        public readonly int $wing = 50
    ) {}

    public function getAreaForScreen(): float
    {
        return ($this->height + 550) * $this->width / 1000000;
    }

    public function getAreaForSlat(): float
    {
        return ($this->height + 50) * $this->width / 1000000;
    }
}

옵션 처리

// app/ValueObjects/QuoteOptions.php
class QuoteOptions
{
    public function __construct(
        public readonly bool $steel = false,
        public readonly bool $motor = false,
        public readonly bool $warranty = false,
        public readonly bool $slat = false,
        public readonly bool $parts = false
    ) {}

    public static function fromArray(array $data): self
    {
        return new self(
            steel: $data['steel'] ?? false,
            motor: $data['motor'] ?? false,
            warranty: $data['warranty'] ?? false,
            slat: $data['slat'] ?? false,
            parts: $data['parts'] ?? false
        );
    }

    public function toJson(): string
    {
        return json_encode([
            'steel' => $this->steel,
            'motor' => $this->motor,
            'warranty' => $this->warranty,
            'slat' => $this->slat,
            'parts' => $this->parts
        ]);
    }
}

참조 파일

  • 5130/estimate/get_screen_amount.php - 스크린 계산 로직
  • 5130/estimate/get_slat_amount.php - 슬랫 계산 로직
  • 5130/estimate/fetch_unitprice.php - 단가 조회 함수
  • 5130/estimate/insert.php - 저장 로직
  • 5130/estimate/generate_serial_pjnum.php - 번호 생성