Files
sam-docs/dev/dev_plans/archive/mng-quote-formula-development-plan.md
권혁성 db63fcff85 refactor: [docs] 팀별 폴더 구조 재편 (공유/개발/프론트/기획)
- 개발팀 전용 폴더 dev/ 생성 (standards, guides, quickstart, changes, deploys, data, history, dev_plans 이동)
- 프론트엔드 전용 폴더 frontend/ 생성 (api/ → frontend/api-specs/)
- 기획팀 폴더 requests/ 생성
- plans/ → dev/dev_plans/ 이름 변경
- README.md 신규 (사람용 안내), INDEX.md 재작성 (Claude Code용)
- resources.md 신규 (노션 링크용, assets/brochure 이관 예정)
- CURRENT_WORKS.md 삭제, TODO.md → dev/ 이동
- 전체 참조 경로 업데이트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:46:03 +09:00

16 KiB

MNG 견적수식 관리 개발 계획

작성일: 2025-12-22 상태: 완료 대상: mng.sam.kr/quote-formulas


1. 현황 분석

1.1 MNG 프로젝트 현재 상태

구현된 기능 (mng)

기능 상태 설명
수식 목록 완료 페이지네이션, 필터링, HTMX 테이블
수식 생성 완료 카테고리, 유형, 변수명, 수식 입력
수식 수정 완료 편집 폼, API 연동
수식 삭제 완료 Soft Delete, 복원, 영구삭제
수식 복제 완료 수식 복사 기능
활성/비활성 완료 토글 기능
카테고리 관리 완료 CRUD 구현
시뮬레이터 완료 입력값 → 계산 결과 미리보기
변수 참조 완료 사용 가능한 변수 목록 표시
수식 검증 완료 문법 검증 API
범위(Range) 관리 UI 완료 범위별 결과 설정 화면 (Phase 1)
매핑(Mapping) 관리 UI 완료 매핑 규칙 설정 화면 (Phase 2)
품목(Item) 관리 UI 완료 출력 품목 설정 화면 (Phase 3)

1.2 API 프로젝트 현재 상태

모델 구조 (api)

QuoteFormulaCategory (카테고리)
└── QuoteFormula (수식)
    ├── QuoteFormulaRange (범위 조건)
    ├── QuoteFormulaMapping (매핑 규칙)
    └── QuoteFormulaItem (출력 품목)

시더 데이터 (api)

시더 데이터 수 설명
QuoteFormulaCategorySeeder 11개 카테고리 (오픈사이즈~단가수식)
QuoteFormulaSeeder 30개 수식, 18개 범위 스크린 계산 수식
QuoteFormulaItemSeeder 25개 품목 마스터

서비스 (api)

서비스 역할
QuoteCalculationService 자동산출 실행 엔진
FormulaEvaluatorService 수식 평가, 범위/매핑 처리
QuoteService 견적 CRUD, 상태 관리
QuoteNumberService 견적번호 생성
QuoteDocumentService PDF/이메일/카카오 발송 (TODO)

2. MNG vs API 비교 분석

2.1 데이터 구조 비교

항목 MNG API 일치
quote_formula_categories
quote_formulas
quote_formula_ranges
quote_formula_mappings
quote_formula_items

결론: 모델 구조는 동일함 (같은 DB 사용)

2.2 기능 비교

기능 MNG API 비고
수식 CRUD 동일
카테고리 CRUD 동일
범위 관리 UI (시더) Phase 1 완료
매핑 관리 UI (시더) Phase 2 완료
품목 관리 UI (시더) Phase 3 완료
시뮬레이터 동일
자동산출 API - API 전용

3. 개발 계획 (완료)

3.1 목표

MNG에서 범위(Range), 매핑(Mapping), 품목(Item) 관리 UI를 추가하여:

  1. 시더 없이도 관리자가 직접 수식 규칙 설정 가능
  2. SAM 자체 품목 마스터로 가격 설정
  3. 실시간 시뮬레이션으로 설정 검증 가능

3.2 개발 범위 (완료)

Phase 1: 범위(Range) 관리 UI

우선순위: 높음 이유: 모터, 가이드레일, 케이스 자동 선택에 필수

기능 목록:

  1. 수식 상세 페이지에 범위 관리 탭 추가
  2. 범위 목록 표시 (min ~ max → 결과)
  3. 범위 추가/수정/삭제
  4. 드래그앤드롭 순서 변경
  5. item_code 연결 (품목 선택)

화면 설계:

[수식 수정] 페이지
├── [기본 정보] 탭 (기존)
├── [범위 설정] 탭 ← 추가
│   ├── 조건 변수: [K (중량)] ▼
│   ├── 범위 목록
│   │   ┌─────────────────────────────────────────────────┐
│   │   │ # │ 최소값 │ 최대값 │ 결과값      │ 품목코드    │
│   │   ├─────────────────────────────────────────────────┤
│   │   │ 1 │ 0      │ 150    │ 150K        │ PT-MOTOR-150│
│   │   │ 2 │ 150    │ 300    │ 300K        │ PT-MOTOR-300│
│   │   │ 3 │ 300    │ 400    │ 400K        │ PT-MOTOR-400│
│   │   └─────────────────────────────────────────────────┘
│   └── [+ 범위 추가]
├── [매핑 설정] 탭
└── [품목 설정] 탭

API 엔드포인트 (MNG 내부):

GET    /api/admin/quote-formulas/formulas/{id}/ranges
POST   /api/admin/quote-formulas/formulas/{id}/ranges
PUT    /api/admin/quote-formulas/formulas/{id}/ranges/{rangeId}
DELETE /api/admin/quote-formulas/formulas/{id}/ranges/{rangeId}
POST   /api/admin/quote-formulas/formulas/{id}/ranges/reorder

Phase 2: 매핑(Mapping) 관리 UI

우선순위: 중간 이유: 제어기 유형 등 코드 매핑에 사용

기능 목록:

  1. 수식 상세 페이지에 매핑 관리 탭 추가
  2. 매핑 목록 표시 (소스값 → 결과값)
  3. 매핑 추가/수정/삭제

화면 설계:

[매핑 설정] 탭
├── 소스 변수: [CONTROL_TYPE] ▼
├── 매핑 목록
│   ┌──────────────────────────────────────────────────┐
│   │ # │ 소스값  │ 결과값     │ 품목코드        │
│   ├──────────────────────────────────────────────────┤
│   │ 1 │ EMB     │ 매립형     │ PT-CTRL-EMB     │
│   │ 2 │ EXP     │ 노출형     │ PT-CTRL-EXP     │
│   │ 3 │ BOX_1P  │ 콘트롤박스 │ PT-CTRL-BOX-1P  │
│   └──────────────────────────────────────────────────┘
└── [+ 매핑 추가]

Phase 3: 품목(Item) 관리 UI

우선순위: 중간 이유: 수식 결과로 생성되는 품목 정의

기능 목록:

  1. 수식 상세 페이지에 품목 관리 탭 추가
  2. 품목 목록 표시
  3. 품목 추가/수정/삭제
  4. 수량/단가 수식 입력
  5. SAM 품목 마스터에서 가격 참조

화면 설계:

[품목 설정] 탭
├── 품목 목록
│   ┌───────────────────────────────────────────────────────────┐
│   │ 품목코드     │ 품목명          │ 규격    │ 수량식 │ 단가식│
│   ├───────────────────────────────────────────────────────────┤
│   │ PT-MOTOR-150 │ 개폐전동기 150kg│ 150K(S) │ 1      │ 285000│
│   │ PT-GR-3000   │ 가이드레일 3000 │ 3000mm  │ 2      │ 42000 │
│   └───────────────────────────────────────────────────────────┘
└── [+ 품목 추가]

3.3 파일 구조 (구현 완료)

Controllers

app/Http/Controllers/
├── QuoteFormulaController.php (수정: 탭 추가)
└── Api/Admin/Quote/
    ├── QuoteFormulaController.php
    ├── QuoteFormulaRangeController.php ✅
    ├── QuoteFormulaMappingController.php ✅
    ├── QuoteFormulaItemController.php ✅
    └── QuoteFormulaCategoryController.php

Services

app/Services/Quote/
├── QuoteFormulaService.php
├── QuoteFormulaRangeService.php ✅
├── QuoteFormulaMappingService.php ✅
├── QuoteFormulaItemService.php ✅
└── QuoteFormulaCategoryService.php

Views

resources/views/quote-formulas/
├── index.blade.php
├── create.blade.php
├── edit.blade.php (수정: 탭 구조)
├── simulator.blade.php
└── partials/
    ├── basic-info-tab.blade.php ✅
    ├── ranges-tab.blade.php ✅
    ├── mappings-tab.blade.php ✅
    └── items-tab.blade.php ✅

4. 기술 스택

4.1 Frontend (MNG)

  • Framework: Laravel Blade + Alpine.js
  • Styling: Tailwind CSS + DaisyUI
  • AJAX: HTMX (hx-get, hx-post, hx-delete)
  • Modal: DaisyUI modal 컴포넌트

4.2 Backend (MNG)

  • Framework: Laravel 12
  • ORM: Eloquent
  • DB: MySQL (samdb)
  • Auth: Session 기반

4.3 API 연동

  • MNG 내부 API (/api/admin/quote-formulas/*)

5. 검증 계획

5.1 시뮬레이터 테스트

입력: W0=3000, H0=2500
예상 결과:
  - CASE: PT-CASE-3600 (S=3270)
  - GR: PT-GR-3000 (H1=2770)
  - MOTOR: PT-MOTOR-150 (K=41.21kg)

5.2 CRUD 테스트

  • 범위 추가/수정/삭제 후 시뮬레이터 결과 확인
  • 품목 가격 변경 후 합계 확인

6. 참고 자료

6.1 파일 위치 (MNG)

mng/
├── app/Http/Controllers/
│   ├── QuoteFormulaController.php
│   └── Api/Admin/Quote/
│       ├── QuoteFormulaController.php
│       ├── QuoteFormulaRangeController.php
│       ├── QuoteFormulaMappingController.php
│       ├── QuoteFormulaItemController.php
│       └── QuoteFormulaCategoryController.php
├── app/Services/Quote/
│   ├── QuoteFormulaService.php
│   ├── QuoteFormulaRangeService.php
│   ├── QuoteFormulaMappingService.php
│   ├── QuoteFormulaItemService.php
│   └── QuoteFormulaCategoryService.php
├── app/Models/Quote/
│   ├── QuoteFormula.php
│   ├── QuoteFormulaCategory.php
│   ├── QuoteFormulaRange.php
│   ├── QuoteFormulaMapping.php
│   └── QuoteFormulaItem.php
└── resources/views/quote-formulas/
    ├── index.blade.php
    ├── create.blade.php
    ├── edit.blade.php
    ├── simulator.blade.php
    └── partials/
        ├── basic-info-tab.blade.php
        ├── ranges-tab.blade.php
        ├── mappings-tab.blade.php
        └── items-tab.blade.php

6.2 API 시더 위치

api/database/seeders/
├── QuoteFormulaCategorySeeder.php
├── QuoteFormulaSeeder.php
└── QuoteFormulaItemSeeder.php

7. 코딩 컨벤션 및 예시 코드

7.1 API Controller 패턴 (MNG)

<?php
// 파일: app/Http/Controllers/Api/Admin/Quote/QuoteFormulaRangeController.php

namespace App\Http\Controllers\Api\Admin\Quote;

use App\Http\Controllers\Controller;
use App\Services\Quote\QuoteFormulaRangeService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class QuoteFormulaRangeController extends Controller
{
    public function __construct(
        private readonly QuoteFormulaRangeService $rangeService
    ) {}

    /**
     * 범위 목록 조회
     */
    public function index(int $formulaId): JsonResponse
    {
        $ranges = $this->rangeService->getRangesByFormula($formulaId);

        return response()->json([
            'success' => true,
            'data' => $ranges,
        ]);
    }

    /**
     * 범위 생성
     */
    public function store(Request $request, int $formulaId): JsonResponse
    {
        $validated = $request->validate([
            'min_value' => 'nullable|numeric',
            'max_value' => 'nullable|numeric',
            'condition_variable' => 'required|string|max:50',
            'result_value' => 'required|string',
            'result_type' => 'in:fixed,formula',
            'sort_order' => 'nullable|integer',
        ]);

        $range = $this->rangeService->createRange($formulaId, $validated);

        return response()->json([
            'success' => true,
            'message' => '범위가 추가되었습니다.',
            'data' => $range,
        ]);
    }

    /**
     * 범위 수정
     */
    public function update(Request $request, int $formulaId, int $rangeId): JsonResponse
    {
        $validated = $request->validate([
            'min_value' => 'nullable|numeric',
            'max_value' => 'nullable|numeric',
            'result_value' => 'required|string',
            'result_type' => 'in:fixed,formula',
        ]);

        $this->rangeService->updateRange($rangeId, $validated);

        return response()->json([
            'success' => true,
            'message' => '범위가 수정되었습니다.',
        ]);
    }

    /**
     * 범위 삭제
     */
    public function destroy(int $formulaId, int $rangeId): JsonResponse
    {
        $this->rangeService->deleteRange($rangeId);

        return response()->json([
            'success' => true,
            'message' => '범위가 삭제되었습니다.',
        ]);
    }

    /**
     * 순서 변경
     */
    public function reorder(Request $request, int $formulaId): JsonResponse
    {
        $validated = $request->validate([
            'range_ids' => 'required|array',
            'range_ids.*' => 'integer',
        ]);

        $this->rangeService->reorder($validated['range_ids']);

        return response()->json([
            'success' => true,
            'message' => '순서가 변경되었습니다.',
        ]);
    }
}

7.2 Service 패턴 (MNG)

<?php
// 파일: app/Services/Quote/QuoteFormulaRangeService.php

namespace App\Services\Quote;

use App\Models\Quote\QuoteFormulaRange;
use Illuminate\Support\Collection;

class QuoteFormulaRangeService
{
    /**
     * 수식별 범위 조회
     */
    public function getRangesByFormula(int $formulaId): Collection
    {
        return QuoteFormulaRange::where('formula_id', $formulaId)
            ->orderBy('sort_order')
            ->get();
    }

    /**
     * 범위 생성
     */
    public function createRange(int $formulaId, array $data): QuoteFormulaRange
    {
        $data['formula_id'] = $formulaId;

        // 순서 자동 설정
        if (!isset($data['sort_order'])) {
            $maxOrder = QuoteFormulaRange::where('formula_id', $formulaId)->max('sort_order') ?? 0;
            $data['sort_order'] = $maxOrder + 1;
        }

        return QuoteFormulaRange::create($data);
    }

    /**
     * 범위 수정
     */
    public function updateRange(int $rangeId, array $data): QuoteFormulaRange
    {
        $range = QuoteFormulaRange::findOrFail($rangeId);
        $range->update($data);

        return $range->fresh();
    }

    /**
     * 범위 삭제
     */
    public function deleteRange(int $rangeId): void
    {
        QuoteFormulaRange::destroy($rangeId);
    }

    /**
     * 순서 변경
     */
    public function reorder(array $rangeIds): void
    {
        foreach ($rangeIds as $order => $id) {
            QuoteFormulaRange::where('id', $id)->update(['sort_order' => $order + 1]);
        }
    }
}

7.3 API 응답 형식

// 성공 응답
{
    "success": true,
    "message": "범위가 추가되었습니다.",
    "data": { ... }
}

// 실패 응답
{
    "success": false,
    "message": "이미 사용 중인 변수명입니다."
}

// 목록 응답
{
    "success": true,
    "data": [
        {
            "id": 1,
            "formula_id": 5,
            "min_value": "0.0000",
            "max_value": "150.0000",
            "condition_variable": "K",
            "result_value": "{\"value\":\"150K\",\"item_code\":\"PT-MOTOR-150\"}",
            "result_type": "fixed",
            "sort_order": 1
        }
    ]
}

8. 체크리스트 (완료)

개발 완료 확인

  • mng 프로젝트 디렉토리: /Users/hskwon/Works/@KD_SAM/SAM/mng
  • QuoteFormulaRangeController.php 생성
  • QuoteFormulaRangeService.php 생성
  • QuoteFormulaMappingController.php 생성
  • QuoteFormulaMappingService.php 생성
  • QuoteFormulaItemController.php 생성
  • QuoteFormulaItemService.php 생성
  • routes/api.php에 라우트 추가
  • edit.blade.php 탭 구조로 수정
  • partials/ranges-tab.blade.php 생성
  • partials/mappings-tab.blade.php 생성
  • partials/items-tab.blade.php 생성

문서 버전: 2.0 작성자: Claude Code 검토자: - 최종 업데이트: 2025-12-22 (Phase 1-3 완료, 5130 연동 제거)