Files
sam-docs/plans/simulator-planning-page-sync-plan.md
hskwon 5e1e5b4fcf docs: 견적 시뮬레이터 계산 로직 분석 및 동기화 계획 문서 추가
- simulator-calculation-logic-mapping.md: design.sam.kr 계산 로직 상세 분석
  - 변수 계산 규칙 (W0,H0 → W1,H1,M)
  - 수식 평가 함수 및 지원 함수 목록
  - BOM 10단계 계산 과정
  - 단가 계산 방식 (pricing → itemMaster → 면적단가)
- simulator-planning-page-sync-plan.md: mng 시뮬레이터 동기화 계획
- index_plans.md: 새 문서 인덱스 추가
2025-12-23 23:41:47 +09:00

14 KiB
Raw Blame History

견적 시뮬레이터 - 기획 페이지 동기화 작업 계획

작성일: 2025-12-23 수정일: 2025-12-23 (계산 로직 분석 완료) 목표: mng 시뮬레이터를 기획 페이지(design.sam.kr)와 동일한 결과 출력하도록 구현 참조 문서: 계산 로직 매핑


1. 현황 분석

1.1 기획 페이지 (localhost:3002) 분석

입력 항목:

변수 라벨 샘플값 설명
층수 층수 1층 건물 층수
부호 부호 A 식별 코드
PC 제품카테고리 스크린 product_category
PRODUCT_ID 제품명 스크린 셔터 (표준형) 제품 선택
W0 가로 2000 오픈사이즈 가로 (mm)
H0 세로 2500 오픈사이즈 세로 (mm)
GT 설치유형 벽면 guide_type (벽면/천장/바닥)
MP 모터전원 220V motor_power
CT 제어기 단독제어 controller_type
QTY 수량 1 수량

산출 결과 (총 12개 품목, 1,393,683원):

공정 품목수 소계 품목 상세
스크린 공정 2 558,441원 스크린 원단(518,415), 연기차단재(40,026)
절곡 공정 8 385,242원 가이드레일, 하부베이스, 케이스, 측면덮개, 상부덮개, 하단마감재, 하단보강빔바, 샤프트
전기 공정 2 400,000원 모터(250,000), 제어기(150,000)

1.2 현재 mng 시뮬레이터 분석

현재 구현된 기능:

  • 변수 입력 (W0, H0 등)
  • 수식 계산 (FormulaEvaluatorService)
  • BOM 트리 구조 표시
  • 품목 목록 표시

미구현 기능:

  • 공정별 분류 (스크린/절곡/전기)
  • 단가 연동 (Price 모델)
  • 금액 계산 (수량 × 단가)
  • 공정별 소계
  • 총합계 금액

1.3 데이터 구조 차이점

구분 기획 페이지 mng 시뮬레이터
데이터 소스 DataContext.tsx (하드코딩) DB (quote_formulas, items)
공정 분류 process 필드로 그룹화 미구현
단가 하드코딩된 가격 prices 테이블
BOM JSON 내 quantityFormula bom JSON 필드

2. 작업 계획

Phase 1: 데이터 구조 정비 (1-2일)

1.1 items 테이블 공정 필드 추가

ALTER TABLE items ADD COLUMN process_type VARCHAR(20) DEFAULT NULL
  COMMENT '공정유형: screen(스크린), bending(절곡), electric(전기)';

1.2 기존 품목에 공정 타입 매핑

품목명 process_type
스크린 원단 screen
연기차단재 screen
가이드레일, 하부베이스, 케이스, 측면덮개, 상부덮개, 하단마감재, 하단보강빔바, 샤프트 bending
모터, 제어기 electric

1.3 prices 테이블 더미 데이터 추가

  • 기획 페이지의 가격 데이터를 prices 테이블에 시딩
  • item_code와 연동 확인

파일 변경:

  • mng/database/migrations/xxxx_add_process_type_to_items.php
  • mng/database/seeders/ItemProcessTypeSeeder.php
  • mng/database/seeders/PriceSeeder.php

Phase 2: 백엔드 수정 (2-3일)

2.1 FormulaEvaluatorService 확장

현재 반환 구조:

[
    'variables' => [...],
    'items' => [
        ['item_code' => 'SCREEN-001', 'quantity' => 5.18, ...]
    ],
    'bom_tree' => [...]
]

목표 반환 구조:

[
    'variables' => [...],
    'items' => [
        [
            'item_code' => 'SCREEN-001',
            'item_name' => '스크린 원단',
            'specification' => '...',
            'unit' => 'm²',
            'quantity' => 5.18,
            'unit_price' => 100000,      // 신규
            'total_price' => 518000,     // 신규
            'process_type' => 'screen',  // 신규
        ]
    ],
    'items_by_process' => [              // 신규
        'screen' => ['items' => [...], 'subtotal' => 558441],
        'bending' => ['items' => [...], 'subtotal' => 385242],
        'electric' => ['items' => [...], 'subtotal' => 400000],
    ],
    'total_amount' => 1393683,           // 신규
    'bom_tree' => [...]
]

2.2 Price 연동 로직 추가

// FormulaEvaluatorService.php
private function enrichItemWithPrice(array $item, int $tenantId): array
{
    $price = Price::getSalesPriceByItemCode($tenantId, $item['item_code']);

    $item['unit_price'] = $price;
    $item['total_price'] = $item['quantity'] * $price;

    return $item;
}

private function groupItemsByProcess(array $items): array
{
    $grouped = [
        'screen' => ['items' => [], 'subtotal' => 0, 'label' => '스크린 공정'],
        'bending' => ['items' => [], 'subtotal' => 0, 'label' => '절곡 공정'],
        'electric' => ['items' => [], 'subtotal' => 0, 'label' => '전기 공정'],
    ];

    foreach ($items as $item) {
        $process = $item['process_type'] ?? 'etc';
        if (isset($grouped[$process])) {
            $grouped[$process]['items'][] = $item;
            $grouped[$process]['subtotal'] += $item['total_price'];
        }
    }

    return $grouped;
}

파일 변경:

  • mng/app/Services/FormulaEvaluatorService.php
  • mng/app/Models/Item.php (process_type 필드 추가)

Phase 3: 프론트엔드 수정 (2-3일)

3.1 simulator.blade.php UI 개선

현재 UI:

[변수 입력] → [계산] → [변수 결과] [품목 목록] [BOM 트리]

목표 UI:

[입력 섹션]
┌─────────────────────────────────────────────────────────┐
│ 층수 | 부호 | 제품카테고리 | 제품명 | 가로 | 세로 | ... │
│  1층 |  A   |    스크린    | 표준형 | 2000 | 2500 | ... │
└─────────────────────────────────────────────────────────┘

[산출 결과]
┌─────────────────────────────────────────────────────────┐
│ 📦 스크린 공정 (2개 품목)                    558,441원  │
│ ├─ 스크린 원단    5.18m²  @100,000   =    518,415원   │
│ └─ 연기차단재     2.00m   @20,013    =     40,026원   │
├─────────────────────────────────────────────────────────┤
│ 🔧 절곡 공정 (8개 품목)                      385,242원  │
│ ├─ 가이드레일    2750mm   @15,000    =     30,000원   │
│ ...                                                     │
├─────────────────────────────────────────────────────────┤
│ ⚡ 전기 공정 (2개 품목)                      400,000원  │
│ ├─ 모터          1EA     @250,000    =    250,000원   │
│ └─ 제어기        1EA     @150,000    =    150,000원   │
├─────────────────────────────────────────────────────────┤
│ 📊 총합계: 12개 품목                      1,393,683원  │
└─────────────────────────────────────────────────────────┘

3.2 JavaScript 수정

// 공정별 결과 렌더링
function renderProcessResults(data) {
    const processOrder = ['screen', 'bending', 'electric'];
    const processIcons = {
        screen: '📦', bending: '🔧', electric: '⚡'
    };

    let html = '';
    processOrder.forEach(process => {
        const group = data.items_by_process[process];
        if (group.items.length > 0) {
            html += `
                <div class="process-group">
                    <div class="process-header">
                        ${processIcons[process]} ${group.label}
                        (${group.items.length}개 품목)
                        <span class="subtotal">${formatNumber(group.subtotal)}원</span>
                    </div>
                    <div class="process-items">
                        ${renderItems(group.items)}
                    </div>
                </div>
            `;
        }
    });

    html += `
        <div class="total-section">
            📊 총합계: ${data.items.length}개 품목
            <span class="total">${formatNumber(data.total_amount)}원</span>
        </div>
    `;

    return html;
}

파일 변경:

  • mng/resources/views/quote-formulas/simulator.blade.php
  • mng/public/css/simulator.css (스타일 추가)

Phase 4: 테스트 및 검증 (1일)

4.1 테스트 케이스

테스트 입력 예상 결과
기본 테스트 W0=2000, H0=2500 12개 품목, 1,393,683원
크기 변경 W0=3000, H0=3000 금액 증가 확인
수량 변경 QTY=2 총액 2배 확인

4.2 검증 항목

  • 기획 페이지와 동일한 품목 수
  • 기획 페이지와 동일한 공정별 분류
  • 기획 페이지와 동일한 금액 (±1원 오차 허용)
  • 반올림 규칙 일치 확인

3. 파일 변경 목록

백엔드 (mng/)

파일 작업 우선순위
database/migrations/xxxx_add_process_type_to_items.php 신규 P1
database/seeders/ItemProcessTypeSeeder.php 신규 P1
database/seeders/PriceSeeder.php 수정 P1
app/Models/Item.php 수정 (fillable 추가) P1
app/Services/FormulaEvaluatorService.php 수정 (핵심) P1

프론트엔드 (mng/)

파일 작업 우선순위
resources/views/quote-formulas/simulator.blade.php 수정 P2
public/css/simulator.css 신규/수정 P2

4. 일정 (예상 5-7일)

단계 작업 예상 소요
Phase 1 데이터 구조 정비 1-2일
Phase 2 백엔드 수정 2-3일
Phase 3 프론트엔드 수정 2-3일
Phase 4 테스트 및 검증 1일

5. 기술적 고려사항

5.1 가격 계산 정확도

  • 소수점 처리: 4자리까지 계산 후 최종 반올림
  • 반올림 규칙: Price 모델의 rounding_rule 필드 활용
  • 단위: 원 단위로 표시

5.2 공정 분류 확장성

  • process_type enum: 향후 공정 추가 가능하도록 설계
  • 공정 순서: config/constants.php에서 관리

5.3 성능 고려

  • 가격 조회: 배치 조회로 N+1 문제 방지
  • 캐싱: 자주 사용되는 가격 데이터 캐싱 고려

6. 핵심 계산 로직 (2025-12-23 분석 완료)

6.1 변수 계산 규칙 (design.sam.kr 기준)

// AutoCalculationSimulator.tsx:429-444
const calculationVariables = {
  W0: quote.w0,                                           // 입력: 오픈사이즈 폭
  H0: quote.h0,                                           // 입력: 오픈사이즈 높이
  W1: quote.category === '스크린' ? W0 + 140 : W0 + 110,  // 계산: 제작폭
  H1: H0 + 350,                                           // 계산: 제작높이
  W: W1,                                                  // 매핑: W = W1
  H: H1,                                                  // 매핑: H = H1
  M: (W1 * H1) / 1000000,                                 // 계산: 면적 (㎡)
  K: 0                                                    // 미구현: 중량
};

6.2 수식 평가 (formulaEvaluator.ts)

지원 함수: SUM, AVERAGE, MAX, MIN, ROUND, CEIL, FLOOR, ABS, IF, SQRT, POWER

평가 과정:

  1. 변수를 실제 값으로 치환 (W0 → 2000)
  2. 함수를 JavaScript 코드로 변환 (CEIL → Math.ceil)
  3. new Function() 으로 최종 계산

6.3 단가 계산 방식

// 1순위: pricing 테이블
// 2순위: 품목마스터 salesPrice

// 면적 기반 품목 (원단, 패널, 도장, 표면처리)
if (isAreaBased && M > 0) {
  finalUnitPrice = unitPrice * M;  // 면적 × 기본단가
}

6.4 BOM 수량 공식 예시

품목 수량공식 W0=2000, H0=2500 결과
스크린 원단 W*H/1000000 2140×2850/1000000 = 6.099㎡
가이드레일 H/1000 2850/1000 = 2.85m
케이스 (W1+100)/1000 (2140+100)/1000 = 2.24m

7. 참고 자료

7.1 관련 파일

Design (분석 완료):

  • design/src/components/AutoCalculationSimulator.tsx - 메인 시뮬레이터
  • design/src/components/utils/formulaEvaluator.ts - 수식 평가
  • design/src/components/utils/bomCalculatorWithDebug.ts - BOM 계산

MNG (수정 대상):

  • mng/app/Services/Quote/FormulaEvaluatorService.php - 핵심 서비스
  • mng/resources/views/quote-formulas/simulator.blade.php - UI

7.2 기획 데이터

  • 기획 데이터: react/src/contexts/DataContext.tsx.backup
  • 현재 시뮬레이터: mng/resources/views/quote-formulas/simulator.blade.php
  • 수식 평가: mng/app/Services/FormulaEvaluatorService.php
  • 가격 모델: mng/app/Models/Price.php

6.2 DataContext.tsx.backup 핵심 로직

가이드레일 자재 선택 규칙:

// RULE-006: 가이드레일 제작길이 (G) = H0 + 250
// 자재 선택 기준:
// - G <= 3000: SM-GL-3000
// - 3000 < G <= 4000: SM-GL-4000
// - 4000 < G <= 5000: SM-GL-5000
// - G > 5000: SM-GL-6000

수량 공식 예시:

items: [
  { code: 'SCREEN-001', quantityFormula: '(W1 * H1) / 1000000' },  // m²
  { code: 'RAIL-001', quantityFormula: '2' },  // 2개 (좌/우)
  { code: 'MOTOR-001', quantityFormula: '1' },  // 1개
]

7. 체크리스트

시작 전

  • 기획팀과 가격 데이터 확인
  • 공정 분류 기준 확정
  • 반올림 규칙 확정

개발 중

  • Phase 1: 마이그레이션 및 시더 작성
  • Phase 2: FormulaEvaluatorService 수정
  • Phase 3: simulator.blade.php UI 개선
  • Phase 4: 기획 페이지와 결과 비교 검증

완료 후

  • 기획팀 확인
  • CURRENT_WORKS.md 업데이트
  • 문서 업데이트

이 문서는 견적 시뮬레이터와 기획 페이지 동기화 작업의 전체 계획을 담고 있습니다.