Files
sam-docs/plans/qa-bugfix-plan.md
권혁성 352d0d06b6 docs: [QA] V1 완료 처리 + V2 계획 문서 생성
- V1(qa-bugfix-plan.md): 19/43 완료, 상태를 '완료(V2 이관)'로 변경
- V2(qa-bugfix-plan-v2.md): 미완료 17건 + 2차 QA 신규 26건 = 총 43건 통합
2026-03-18 14:53:17 +09:00

39 KiB

경동dev QA 점검 이슈 수정 계획

작성일: 2026-03-13 목적: 경동dev 모듈별 기능 및 UI 점검 결과 43건 이슈 체계적 수정 (1차 QA) 기준 문서: 경동dev_모듈별_기능 및 UI점검 - 시트1.csv 상태: 완료 (V2로 이관)


📍 최종 상태

항목 내용
완료일 2026-03-17
완료 건수 19/43 (코드 검증 완료)
미완료 건수 17건 → V2로 이관
정상 확인 5건 (디펙 아님, 정책 정상)
보류 2건 (기획 확인 필요, mng 선행 필요)
후속 문서 qa-bugfix-plan-v2.md

⚠️ 이 문서는 완료된 참조 문서입니다. 활성 작업은 V2 문서를 참고하세요.


1. 개요

1.1 배경

경동dev 환경에서 모듈별 기능 및 UI를 점검한 결과 37건의 이슈가 발견됨. 2026-03-16 추가 점검으로 6건 추가 등록 (총 43건). Critical 2건은 데이터 정합성에 직접 영향을 주므로 즉시 수정 필요.

1.2 이슈 통계

중요도 건수 비고
Critical 3건 데이터 정합성 파괴 (#6 제어기 계산 오류 추가)
Major 15건 핵심 기능 오류 (#1,#2,#5 추가)
Minor 15건 UI/UX 개선 (#3 추가)
확인 필요 9건 정책 확인 후 결정
보류 (mng 선행) 1건 #4 절곡 바라시
모듈 건수
견적관리 22건
수주관리 5건
거래처관리 2건
단가관리 2건
품목관리 2건
생산/작업지시 3건
재고관리 1건
품질검사 1건
절곡(mng 선행) 1건
공통 4건

1.3 변경 승인 정책

분류 예시 승인
즉시 가능 UI 스타일, 라벨 변경, 필터 수정 불필요
⚠️ 컨펌 필요 상태 전이 로직, 데이터 차단 로직, API 변경 필수
🔴 금지 테이블 구조 변경, 기존 데이터 마이그레이션 별도 협의

1.4 준수 규칙

  • docs/dev/standards/api-rules.md — API 개발 규칙
  • docs/dev/standards/quality-checklist.md — 품질 체크리스트
  • docs/dev/standards/git-conventions.md — Git 커밋 컨벤션
  • docs/features/quotes/README.md — 견적 시스템 문서
  • docs/rules/pricing-policy.md — 단가 정책

2. Phase 구조

2.0 Phase 0: 사전 조사 (확인 필요 9건) — 예상 1일

코드 분석 사전 조사 완료. 정책 결정만 남음.

# 이슈 코드 분석 결과 결과 배정
9 견적 목록 정렬 정책 QuoteManagementClient.tsx:297 customSortFn에서 registrationDate DESC 하드코딩. API도 동일(QuoteService:46). 정책 맞음 정상 -
10 목록 작업 컬럼 빈 값 어떤 데이터가 들어가야 하는지 기획 확인 필요 기획 확인 (보류) -
11 견적 접수일 날짜 하루 밀림 api/config/app.php:60 timezone=Asia/Seoul 정상. QuoteService.php:344 registration_date는 프론트 전달값 사용. React에서 날짜 전송 시 UTC 변환 여부 확인 필요 프론트 확인 Phase 2
12 연락처 필수값 안내 시점 UX 정책 결정 필요: 저장 시 vs 실시간 검증 UX 결정 Phase 4
13 수동 품목 단가 0원 기획 확인 필요: 의도된 동작인지, 단가 입력 필드 필요 여부 기획 확인 Phase 2
15 부가세 값 저장/노출 안 됨 Quote 모델 fillable에 tax_amount 없음. Order 모델에만 존재. 견적에 부가세 저장 로직 자체가 미구현 🔴 버그 확정 Phase 2
17 PDF 생성 안 됨 QuoteDocumentService.php 존재. DomPDF 설정/폰트 확인 필요 (개발서버 환경 이슈 가능) 환경 확인 Phase 3
23 견적 수정→저장=확정 프로세스 결정: 저장/확정 분리 — [저장]=draft 유지, [견적확정]=finalized 전환. 확정 후 수정 시 리비전 생성 결정 완료 Phase 2
29 수주 수정 시 유효성 에러 UpdateOrderRequest.php:40-42에서 items.*.item_name required. 프론트에서 item_name 누락 시 에러 발생 🔴 버그 확정 Phase 3

2.1 Phase 1: Critical 수정 (3건) — 완료

데이터 정합성에 직접 영향. 최우선 수정. 3건 모두 완료 (2026-03-16)

#27 수주등록 - 견적 불러오기 실패 Critical

항목 내용
현상 견적 선택 시 전환 가능한 견적 0건, 데이터 호출 실패
경로 /sales/order-management-sales?mode=new
비고 견적번호 검색으로는 데이터 불러와짐

근본 원인 (코드 분석 완료):

  1. 프론트 호출: react/src/components/orders/QuotationSelectDialog.tsx:46-52

    const handleFetchData = useCallback(async (query: string) => {
      const result = await getQuotesForSelect({ q: query || undefined, size: 50 });
      // ...
    }, []);
    
    • SearchableSelectionModalloadOnOpen 활성화
    • 초기 로드 시 빈 검색어(query: undefined) 전달
  2. API 호출: react/src/components/orders/actions.ts:1246-1266

    url: buildApiUrl('/api/v1/quotes', {
      status: 'finalized',     // ← finalized 상태만
      with_items: 'true',
      for_order: 'true',       // ← 수주 미생성 필터
      q: params?.q,
    }),
    
  3. 백엔드 필터: api/app/Services/Quote/QuoteService.php:48-59,76-81

    // for_order 필터 (라인 48-59)
    if ($forOrder) {
        $query->whereNull('order_id');
        $query->whereDoesntHave('orders');
    }
    // status 필터 (라인 76-81)
    if ($status === Quote::STATUS_CONVERTED) {
        $query->whereNotNull('order_id');
    } elseif ($status) {
        $query->where('status', $status)->whereNull('order_id');
    }
    
    • status=finalized + for_order=truewhereNull('order_id') 2회 적용 (중복이지만 논리적 문제 없음)
    • 실제 원인 추정: status 컬럼에 실제로 'finalized'가 아닌 다른 값으로 저장되어 있거나, whereDoesntHave('orders') 관계가 잘못 설정되어 있을 가능성
    • 검색어(q) 입력 시에는 search() 메서드가 다른 필터를 우회할 수 있음

수정 대상:

파일 라인 변경 내용
api/app/Services/Quote/QuoteService.php 48-81 for_order + status 필터 조건 상호작용 디버깅. whereDoesntHave('orders') 관계 확인
react/src/components/orders/actions.ts 1246-1266 getQuotesForSelect() 파라미터 검증
react/src/components/orders/QuotationSelectDialog.tsx 46-52 초기 로드 시 API 응답 로그 확인

검증:

  • finalized 상태 견적이 DB에 존재하는지 직접 쿼리
  • API 직접 호출하여 응답 확인: GET /api/v1/quotes?status=finalized&for_order=true
  • 검색어 있을 때와 없을 때 쿼리 차이 확인 (DB::enableQueryLog())
  • 견적 선택 → 수주 등록 정상 동작 확인

#31 생산지시 후 견적 수정 가능 → 금액 불일치 Critical

항목 내용
현상 생산지시 완료된 수주의 견적을 수정 가능 → 견적-수주-생산지시 금액 불일치
경로 /sales/order-management-sales/[id] → 견적 수정

컨펌 완료 (2026-03-14): 수정 허용 + 변경 전파 + 리비전 관리. 생산지시 있으면 차단.

결정된 흐름:

견적 수정 클릭
  ├─ 생산지시 존재? → ❌ 차단 "생산지시가 진행된 건은 수정할 수 없습니다"
  └─ 생산지시 없음?
       ├─ 수주 연결? → ⚠️ "연결된 수주건이 함께 변경됩니다" [확인/취소]
       │    ├─ [확인] → 리비전 생성(rev.N) + 견적 수정 + 수주 자동 동기화
       │    └─ [취소] → 변경 안 함
       └─ 수주 미연결 → 리비전 생성(rev.N) + 견적 수정

기존 리비전 시스템 (이미 구현됨):

  • quote_revisions 테이블 존재 (스냅샷 JSON 저장)
  • QuoteRevision 모델 + createRevision() 메서드 구현 완료
  • current_revision 컬럼으로 수정 차수 추적
  • syncFromQuote() 수주 동기화 호출 코드 존재

근본 원인 (코드 분석 완료):

  1. Quote 모델: api/app/Models/Quote/Quote.php:337-340

    public function isEditable(): bool
    {
        return true;  // ← 항상 true! 상태 체크 없음
    }
    
  2. QuoteService update(): api/app/Services/Quote/QuoteService.php:377-379

    • isEditable()이 항상 true이므로 이 검증이 무의미
  3. 프론트 수정 버튼: react/src/components/quotes/QuoteFooterBar.tsx:142-153

    • orderId가 있으면 수정 버튼을 숨기지만, 백엔드에서 차단하지 않음

수정 대상:

파일 라인 변경 내용
api/app/Models/Quote/Quote.php 337-340 isEditable() → 생산지시 존재 시 false 반환
api/app/Services/Quote/QuoteService.php update() 수주 연결 시 syncFromQuote() 동작 검증 + 강화
react/src/components/quotes/QuoteFooterBar.tsx 142-153 생산지시 존재 시 수정 버튼 비활성화. 수주 연결 시 확인 모달 추가
react/src/components/quotes/QuoteRegistration.tsx - 저장 전 "연결된 수주건이 함께 변경됩니다" 확인 모달

수정 코드 (제안):

// Quote.php - isEditable() 수정
public function isEditable(): bool
{
    // 생산지시가 존재하는 수주에 연결된 견적은 수정 불가
    if ($this->orders()->whereHas('workOrders')->exists()) {
        return false;
    }
    return true;  // draft, finalized, converted(수주연결) 모두 수정 가능
}

검증:

  • 생산지시 있는 견적 수정 시 차단 메시지 노출
  • 수주 연결 견적 수정 시 확인 모달 → 수주 자동 동기화
  • 수주 미연결 견적 수정 시 정상 동작
  • 리비전 생성 확인 (revision_number 증가, previous_data 스냅샷)
  • 기존 불일치 데이터 현황 파악 스크립트 실행

#38 견적 금액 제어기 값 오류 (1개소만 계산) Critical

항목 내용
현상 제어기(controller) BOM 금액이 1개소분만 계산됨. 다른 항목(주자재, 모터 등)은 수량에 맞게 정상 계산
기대 제어기도 개소 수량에 맞게 금액 계산
모듈 견적관리

수정 대상:

파일 변경 내용
api/app/Services/Quote/FormulaEvaluatorService.php 제어기(controller) 카테고리 수량 계산 로직 확인 — 1개소 고정 버그
react/src/components/quotes/ 프론트 BOM 결과 표시 시 제어기 수량/금액 확인

검증:

  • 2개소 이상 견적에서 제어기 금액이 개소 수량에 맞게 계산되는지 확인
  • 1개소 견적에서 기존 동작 유지 확인
  • 다른 BOM 카테고리(주자재, 모터 등) 영향 없음 확인

2.2 Phase 2: Major - 견적관리 (9건) — 🔄 진행중 (5/9 완료)

2.2.1 BOM 탭 순서 통일 (#18, #19, #20, #22) — 4건 묶음

항목 내용
현상 등록/상세, 엑셀/수동, 산출 전/후에서 BOM 탭 순서 불일치 + inspection 라벨
기대 모든 케이스에서 동일한 탭 순서

컨펌 완료 (2026-03-14): 주자재 → 모터 → 제어기 → 절곡품 → 부자재 → 검사비 → 기타 (inspection → 검사비 라벨 변경 포함)

근본 원인 (코드 분석 완료):

  1. 프론트 탭 생성: react/src/components/quotes/LocationDetailPanel.tsx:157-179

    const detailTabs = useMemo((): TabDefinition[] => {
      const subtotals = location.bomResult.subtotals;
      const tabs: TabDefinition[] = [];
      Object.entries(subtotals).forEach(([key, value]) => {
        tabs.push({ value: key, label: obj.name || key });
      });
      tabs.push({ value: "etc", label: "기타" });
      return tabs;
    }, [location?.bomResult?.subtotals]);
    
    • Object.entries() 순서가 JavaScript 객체 키 삽입 순서에 의존 → 비결정적
  2. 백엔드 그룹화: api/app/Services/Quote/FormulaEvaluatorService.php:1852-1865

    // Step 8: 카테고리별 그룹화
    $groupedItems = [];
    foreach ($calculatedItems as $item) {
        $category = $item['category_group'];
        if (!isset($groupedItems[$category])) {
            $groupedItems[$category] = [...];
        }
    }
    
    • foreach 루프 순서가 $calculatedItems 배열 순서에 의존 → 비결정적
  3. 카테고리명 하드코딩: FormulaEvaluatorService.php:1923-1933

    private function getTenantCategoryName(string $category): string {
        return match ($category) {
            'material' => '주자재', 'motor' => '모터', 'controller' => '제어기',
            'steel' => '절곡품', 'parts' => '부자재',
            default => $category,  // ← 'inspection' 매핑 없음!
        };
    }
    
    • 'inspection' → '검사비' 매핑이 없음 → 탭 라벨 불일치

수정 대상:

파일 라인 변경 내용
react/src/components/quotes/types.ts 신규 BOM_CATEGORY_ORDER 정렬 순서 상수 정의
react/src/components/quotes/LocationDetailPanel.tsx 157-179 탭 생성 시 BOM_CATEGORY_ORDER 기준 정렬 적용
api/app/Services/Quote/FormulaEvaluatorService.php 1852-1865 $groupedItems를 카테고리 순서대로 정렬 후 반환
api/app/Services/Quote/FormulaEvaluatorService.php 1923-1933 'inspection' => '검사비' 매핑 추가

2.2.2 스크린+스틸 혼합 등록 차단 (#16)

항목 내용
현상 혼합 제품 등록 시 무조건 스크린으로 표기, 혼합 필터 미적용
기대 "혼합"으로 표시스크린+스틸 동시 등록 자체를 차단

컨펌 완료 (2026-03-14): 스크린과 스틸이 동시에 들어오는 수주는 없음 (인정 검사 때문). MIXED 타입 추가 불필요. 프론트에서 혼합 등록 밸리데이션 차단.

수정 방향 변경:

  • MIXED 타입 추가 → 불필요
  • deriveProductCategory() 수정 → 불필요
  • 스크린+스틸 동시 품목 추가 시 프론트 경고 + 차단 추가

수정 대상:

파일 라인 변경 내용
react/src/components/quotes/QuoteRegistration.tsx 품목 추가 핸들러 기존 품목과 다른 카테고리(스크린↔스틸) 추가 시 경고 모달 + 차단
react/src/components/quotes/QuoteManagementClient.tsx 312-323 혼합 필터 옵션 제거 (데드 코드 정리)

2.2.3 견적 저장/확정 분리 (#23)

항목 내용
현상 견적 수정 → 저장하면 바로 확정(finalized)되어 사용자 혼란
기대 저장과 확정을 분리하여 명시적 프로세스 제공

컨펌 완료 (2026-03-14): 저장/확정 분리 — [저장]=draft 유지, [견적확정]=finalized 전환. 확정 후 수정 시 리비전 생성.

결정된 흐름:

견적 등록 → [저장] → draft (자유롭게 수정, 리비전 안 쌓임)
                ↓
            [견적확정] 버튼 클릭 → finalized (리비전 시작, 수주 전환 가능)
                ↓
            수정하면 → rev.N 생성 + 수주 동기화 (#31 규칙 적용)

수정 대상:

파일 라인 변경 내용
react/src/components/quotes/QuoteFooterBar.tsx 142-153 [임시저장] → [저장], [저장] → [견적확정] 버튼 라벨/동작 분리
react/src/components/quotes/QuoteRegistration.tsx submit 핸들러 저장=draft 유지, 견적확정=finalized 전환 API 분리
api/app/Services/Quote/QuoteService.php update() draft 저장 시 리비전 미생성, finalize 시에만 상태 전환
api/app/Http/Controllers/Quote/QuoteController.php - finalize() 액션 추가 (또는 기존 활용)

2.2.4 기타 품목 수동추가 이슈 (#14, #21)

근본 원인: LocationDetailPanel.tsx:175-176

  • 기타 탭은 tabs.push({ value: "etc", label: "기타" })로 항상 마지막 추가
  • TabsList에 overflow/scroll 처리 없음 → 탭이 많으면 화면 밖으로 밀림
  • 수동 추가 시 새로운 카테고리가 생성되어 탭이 분리됨

수정 대상:

파일 라인 변경 내용
react/src/components/quotes/LocationDetailPanel.tsx 157-179 수동 추가 품목을 "기타" 탭에 병합하는 로직
react/src/components/quotes/LocationDetailPanel.tsx 200+ TabsList에 overflow-x-auto + flex-nowrap 스크롤 처리

2.2.5 필터 셀렉트박스 라벨 (#8)

근본 원인: QuoteManagementClient.tsx:312-336

  • 두 셀렉트박스 모두 placeholder 설정은 있음 ("제품분류", "상태")
  • 하지만 초기값이 'all'로 설정되어 "전체" 텍스트만 보임 → 어떤 필터인지 구분 불가

수정 대상:

파일 라인 변경 내용
react/src/components/quotes/QuoteManagementClient.tsx 312-336 SelectTrigger 앞에 라벨 텍스트 추가 또는 "전체" → "제품분류: 전체" / "상태: 전체"로 변경

2.2.6 거래처 선택 시 담당자 자동 채움 (#25)

근본 원인: QuoteRegistration.tsx:776-790

const handleClientChange = (selectedClient: Client | null) => {
  setFormData((prev) => ({
    ...prev,
    clientId: String(selectedClient.id),
    clientName: selectedClient.name,
    manager: selectedClient?.managerName || selectedClient?.representativeName || prev.manager,
    contact: selectedClient?.managerPhone || selectedClient?.mobile || selectedClient?.phone || prev.contact,
  }));
};
  • 자동 채움 로직은 구현되어 있음 (라인 783-784)
  • 원인 추정: 거래처 선택 모달에서 반환하는 Client 객체에 managerName/managerPhone 필드가 포함되지 않음 (목록 조회 시 select 컬럼에서 누락)

수정 대상:

파일 라인 변경 내용
react/src/components/quotes/actions.ts 또는 QuoteRegistration.tsx - 거래처 선택 시 상세 API 추가 호출하여 담당자 정보 로드
또는 api/app/Services/ClientService.php index() 목록 조회 시 contact_person, phone, manager_name 컬럼 포함

2.2.7 견적→수주 변환 시 개소별 분리 (#39)

항목 내용
현상 견적에서는 수량 2개 이상(여러 개소)을 하나로 등록 가능하나, 수주로 변환 시 그대로 넘어감
기대 수주 등록 시 개소별로 쪼개서 각 개소별 견적 단위로 분리되어 들어가야 함
예시 견적 1건(3개소, 수량3) → 수주 등록 시 개소별 3건으로 분리
모듈 견적관리 → 수주관리

수정 대상:

파일 변경 내용
api/app/Services/Order/OrderService.php 견적→수주 변환(store) 시 개소별 분리 로직 추가
react/src/components/orders/ 견적 불러오기 후 개소별 분리 표시 처리

검증:

  • 2개소 이상 견적 → 수주 변환 시 개소별 분리 확인
  • 1개소 견적은 기존 동작 유지
  • 분리된 수주 각각의 금액/수량 정합성 확인

2.3 Phase 3: Major - 기타 모듈 (7건) — 예상 2.5일

#6 거래처 등록 미노출

항목 내용
현상 신규 거래처 등록 시 회계관리 거래처 목록에 미노출

수정 대상:

파일 변경 내용
api/app/Services/ClientService.php 등록 프로세스 확인 (mng DB 동기화 여부)
mng/ 회계관리 거래처 목록 데이터 소스 확인 (samdb vs codebridge DB)

#32, #33 단가 등록/수정 오류 — 2건 묶음

근본 원인 (코드 분석 완료):

  1. PricingService 미구현: api/app/Services/Pricing/PricingService.php

    public function getItemPrice(...): array {
        // TODO: 실제 가격 조회 로직 구현
        // 현재는 임시로 0원 반환
        return ['price' => 0, 'warning' => null];
    }
    
    • store(), update() 메서드가 미구현 (TODO 상태)
  2. 프론트 폼 필드 불일치: react/src/components/pricing/PricingFormClient.tsx:70-76

    const displayItemCode = initialData?.itemCode || itemInfo?.itemCode || '';
    
    • API 응답 필드명(item_code)과 프론트 필드명(itemCode) 간 매핑 불일치 가능
  3. FormRequest 검증 부족: PriceUpdateRequest.php:15-17

    'sales_price' => 'nullable|numeric|min:0',  // 0원 허용, max 제한 없음
    

컨펌 완료 (2026-03-14): 단가 0원 허용. min:0 유지. PricingService 구현만 수정.

수정 대상:

파일 라인 변경 내용
api/app/Services/Pricing/PricingService.php 전체 store(), update() 메서드 구현 (현재 TODO)
react/src/components/pricing/PricingFormClient.tsx 70-76, 152-159 품목코드 매핑 + submit payload 필드명 정렬
api/app/Http/Requests/Pricing/PriceStoreRequest.php - item_id, sales_price 유효성 강화
api/app/Http/Requests/Pricing/PriceUpdateRequest.php 15-17 `nullable

#34 품목목록 규격 컬럼 비어있음 — 🔄 부분 완료

원인: attributes.spec(레거시)에 규격 저장. Item 모델에 accessor 미존재 → API 응답에 미포함. 데이터: 경동 893건 중 604건(68%) 규격 보유.

완료 (2026-03-16):

  • Item.phpspecification accessor 추가 (attributes.specattributes.specification 순서로 조회)
  • $appends = ['specification']으로 API 응답에 자동 포함
  • 품목 목록 페이지 규격 표시 해결

미완료 — 별도 세션 처리 (TODO):

영역 현황 필요 작업
WorkOrder items work_order_items.options에 width/height만 있고 spec 없음 WorkOrder API 응답에 마스터 Item.specification 조인 또는 options에 spec 추가
중간검사 성적서 BendingWipInspectionContent에서 order.items[0].specification 참조 WorkOrder API에서 specification 내려줘야 표시됨
템플릿 검사 성적서 TemplateInspectionContent에서 field.default_value 사용 (실제 규격 무시) WorkOrder.specification으로 대체 필요
QMS 문서 Mock data 사용 중 API 연동 시 함께 처리

#37 작업지시 품목 수량 수정 안 됨

수정 대상:

파일 변경 내용
api/app/Services/WorkOrderService.php updateItem 메서드 확인, 수량 업데이트 로직
react/src/components/production/WorkOrders/WorkOrderDetail.tsx 수량 수정 API 호출 로직 및 파라미터

#29 수주 수정 시 유효성 에러 (Phase 0에서 버그 확정)

근본 원인: api/app/Http/Requests/Order/UpdateOrderRequest.php:40-42

'items.*.item_name' => 'required|string|max:200',
'items.*.quantity' => 'required|numeric|min:0',
'items.*.unit_price' => 'required|numeric|min:0',
  • items 배열 전송 시 모든 item에 item_name 필수 → 프론트에서 누락 가능

수정 대상:

파일 라인 변경 내용
react/src/components/orders/ - 수주 수정 submit 시 items 배열에 item_name 포함 확인
api/app/Http/Requests/Order/UpdateOrderRequest.php 40-42 부분 수정 시 items 유효성 규칙 조정 (sometimes 적용 검토)

#40 작업자 화면 — 자재투입 미터 단위 지원 Major

항목 내용
현상 자재투입 시 EA(개수) 단위는 기본 지원되지만, 미터(m) 단위 설정이 안 되어 있음
대상 품목 원단, 내화실, 슬랫코일 등 미터 단위로 관리되는 자재
운영 방식 미터 단위 차감을 하다가, 특정 개소에서 "소진" 체크로 해당 자재를 일괄 소진 처리
사유 로스가 많고 정확한 측정이 어려워 정밀 차감이 아닌 소진 방식으로 운영
모듈 생산/작업지시 (작업자 화면)

수정 대상:

파일 변경 내용
react/src/components/production/Worker/ 자재투입 UI에 미터(m) 단위 입력 지원 + 소진 체크 기능 추가
api/ (관련 Service) 미터 단위 차감 로직 + 소진 처리 API
items 테이블 options 해당 품목의 단위(unit) 정보 활용 (EA/m 구분)

검증:

  • 미터 단위 품목 투입 시 m 단위로 입력/차감 확인
  • 소진 체크 시 해당 자재 일괄 소진 처리 확인
  • EA 단위 품목은 기존 동작 유지

#41 재고관리 — 재고 조정 API 개발 Major

항목 내용
현상 재고 조정 화면(모달)은 재고관리 또는 입고관리에 이미 개발되어 있으나, API가 미구현
기대 재고 조정 API 개발 + 조정 히스토리(이력) 적재
목적 재고 수정 사유/내역을 추적 가능하게
모듈 재고관리

수정 대상:

파일 변경 내용
api/app/Services/Inventory/ 재고 조정 API (store/update) 구현 + 조정 사유 저장
api/app/Http/Controllers/ 재고 조정 엔드포인트 추가
api/ (마이그레이션) 재고 조정 히스토리 테이블 (또는 기존 audit_logs 활용)

검증:

  • 재고 조정 API 정상 동작 (수량 증가/감소)
  • 조정 히스토리 정상 적재 (사유, 변경 전/후 수량, 작업자)
  • 프론트 모달에서 API 연동 확인

2.4 Phase 4: Minor 수정 (15건) — 예상 1.75일

진행 상태: 8건 완료, 4건 스킵/이미적용, 2건 다른 Phase로 이관, 1건 별도 세션

4.1 공통 UI (4건)

# 이슈 상태 비고
1 리스트 열 너비 정책 ⏭️ 패스 범위 넓어 별도 검토 필요
2 스티키 취소 레이어 이미 적용됨 IntegratedDetailTemplate에 sticky 기본 내장 확인
3 모달 닫힘 정책 ⏭️ 패스 현재 동작 확인 필요 (QA 기대 동작 불명확)
4 밸리데이션 워딩 이미 정리됨 Zod 기반 한글 메시지 통일 확인

4.2 견적 UI (4건)

# 이슈 상태 비고
7 필터 셀렉트박스 라벨 수정 완료 "전체" → "제품분류: 전체" / "상태: 전체"
22 inspection → 검사비 ⏭️ Phase 2 #18-20 BOM 탭 순서와 함께 처리
24 견적상태 3곳 불일치 ⏭️ Phase 2 #23 저장/확정 분리와 함께 처리
26 수식 모달 하단 여백 수정 완료 LocationDetail div에 pb-6 추가

4.3 수주 UI (2건)

# 이슈 상태 비고
28 수신처 필드 수정 완료 Input → PhoneInput 교체 (자동 포맷팅)
30 "만원원" 이중 표시 수정 완료 수주 관련 4파일 19곳 "원" 중복 제거

4.4 품질검사 (1건)

# 이슈 상태 비고
42 제품검사 모달 페이지 이동/검색 ⏭️ 별도 세션 QMS 네비게이션 구조 변경 필요 (Major급)

4.5 기타 (3건)

# 이슈 상태 비고
5 거래처 카운트 불일치 수정 완료 프론트 자체계산 → stats API 호출로 교체. 백엔드 active/inactive 추가
35-1 품목 수정이력 수정 완료 ItemService.update()에 AuditLogger 감사 로그 추가
35-2 생산현황판 데이터 불일치 수정 완료 Dashboard 통계를 work-orders/stats API 활용으로 교체

2.5 보류 — mng 선행 필요 (1건)

#43 절곡 바라시보기 기능 Major 보류

항목 내용
현상 SAM에 절곡 바라시(전개도) 보기 기능이 없음
레퍼런스 5130: viewBendingWork_slat.php → 절곡 바라시 버튼 (bendingview.php)
선행 조건 mng에서 먼저 기능 개발 완료 필요
적용 방식 mng 완료 후 해당 데이터를 호출하는 형태로 react에 적용
모듈 절곡/생산

mng 개발 완료 시점에 Phase 배정 예정


3. 작업 일정

Phase 0: 사전 조사 (대부분 완료) [0.5일] ██
Phase 1: Critical (3건)         [1.5일] ██████
Phase 2: 견적 Major (9건)       [3일]   ████████████
Phase 3: 기타 Major (7건)       [3일]   ████████████
Phase 4: Minor (15건)           [2일]   ████████
보류: mng 선행 (1건)             [TBD]
                                ─────────────────
총계                             약 10일 (+보류)

병렬 처리 가능:

  • Phase 2 (견적) + Phase 3 (기타 모듈): 독립적 → 3일로 단축
  • Phase 4: Phase 1~3 중 동일 파일 수정 시 함께 처리

최적 일정 (병렬 적용):

Day 1-1.5 : Phase 0 마무리 + Phase 1 (Critical 3건)
Day 2-4   : Phase 2 + Phase 3 (병렬)
Day 5-6   : Phase 4 (Minor) + Phase 0 결과 반영
              ─────────────────
최적 총계   약 6일 (+보류 #43)

4. 의존성 맵

Phase 0 (사전 조사)
├─→ #9 (정렬 정책) ────→ ✅ 정상 확인 완료
├─→ #11 (날짜 밀림) ──→ Phase 2로 배정 (프론트 날짜 전송 확인)
├─→ #15 (부가세) ────→ 🔴 Phase 3 배정 (Quote 모델에 tax_amount 미구현)
├─→ #17 (PDF) ──────→ Phase 3 (개발서버 환경 확인 필요)
├─→ #23 (확정 프로세스) → ✅ 결정 완료 → Phase 2 배정 (저장/확정 분리)
└─→ #29 (수주 수정 에러) → 🔴 Phase 3 배정 (UpdateOrderRequest 규칙 수정)

Phase 1 (Critical)
├─→ #27 (견적 불러오기) ── 독립
└─→ #31 (견적 수정 + 전파) ── 생산지시 차단 + 수주 동기화 + 리비전

Phase 2 (견적 Major)
├─→ #18,19,20 (BOM 탭 순서) ── 상호 의존 (함께 수정), #22와 연관
├─→ #23 (저장/확정 분리) ── #31과 연관 (리비전 생성 시점)
├─→ #16 (혼합 등록 차단) ── 독립 (프론트 밸리데이션)
├─→ #14,21 (레이아웃) ── #18-20과 부분 연관 (탭 구조)
├─→ #8 (필터 라벨) ── 독립
└─→ #25 (담당자 자동채움) ── 독립

Phase 3 (기타 Major) ── 모두 독립
Phase 4 (Minor) ── 모두 독립 (#22는 Phase 2에서 함께 처리)

5. 리스크

리스크 영향 대응
BOM 탭 순서 통일 시 기존 데이터 영향 저장된 calculation_inputs의 순서 변경 가능 기존 데이터는 순서 변경 없이 조회 시만 정렬 적용
#16 혼합 등록 차단 프론트 밸리데이션만 추가 → 우회 가능 백엔드에서도 검증 추가 검토 (FormRequest)
#31 견적 수정 + 수주 동기화 syncFromQuote() 동작 검증 필요 개발서버에서 시나리오 테스트 후 반영
#32,33 PricingService 미구현 store/update 전체 구현 필요 → 작업량 증가 기존 패턴(QuoteService) 참고하여 구현
#29 UpdateOrderRequest 수정 items 유효성 규칙 변경 → 다른 수주 기능에 사이드이펙트 sometimes 규칙 적용 시 기존 store도 영향 없는지 확인

6. 컨펌 결과

# 항목 결정 상태
1 #31 견적 수정 범위 수정 허용 + 변경 전파(수주 동기화) + 리비전 관리. 생산지시 있으면 차단 확정
2 #18-20 BOM 탭 순서 주자재→모터→제어기→절곡품→부자재→검사비→기타 (inspection→검사비 라벨 변경) 확정
3 #16 혼합 제품 혼합 수주 없음(인정검사). 스크린+스틸 동시 등록 프론트 차단. MIXED 타입 불필요 확정
4 #23 견적 확정 프로세스 저장/확정 분리 — [저장]=draft, [견적확정]=finalized. 확정 후 수정 시 리비전 확정
5 #33 단가 0원 0원 허용. min:0 유지. PricingService 미구현 버그만 수정 확정
6 #10 작업 컬럼 보류 — 기획 의도 확인 후 결정 보류

7. 변경 이력

날짜 항목 변경 내용 파일 승인
2026-03-13 - 문서 초안 작성 - -
2026-03-13 - 코드 레벨 심층 분석 반영 (4개 에이전트 병렬 분석) - -
2026-03-14 #31,#18-20,#16,#23,#33,#10 컨펌 대기 6건 결정 반영 (5건 확정, 1건 보류) -
2026-03-16 #38~#43 추가 이슈 6건 등록: #38 제어기 계산(Critical), #39 개소분리(Major), #40 미터단위(Major), #41 재고조정API(Major), #42 검사모달(Minor), #43 절곡바라시(보류) -
2026-03-16 Phase 4 Minor 8건 수정 완료: #26 수식모달여백, #7 필터라벨, #30 만원원, #28 수신처, #5 거래처카운트, #35-1 품목이력, #35-2 생산현황판. 4건 스킵(#2 이미적용, #4 이미정리, #1/#3 패스), 2건 이관(#22→P2, #24→P2), 1건 별도(#42 Major급) react aa42360, api 80cd341
2026-03-16 Phase 1 Critical 3건 완료: #27 견적 불러오기(QuoteService 필터 수정+order_id 동기화), #31 생산지시 후 수정 차단(isEditable+프론트 연동), #38 제어기 수량 미반영(FormulaHandler 수량 곱셈) api QuoteService/Quote.php/FormulaHandler, react QuoteFooterBar/QuoteRegistration/types -
2026-03-16 Phase 2 5건 완료: #18-22 BOM 탭 순서(백+프론트 정렬+inspection 라벨), #16 혼합 등록 차단(모델 레벨 필터링), #23 저장/확정 분리(버튼 라벨+조건 분리), #14/#21 품목 수동추가(items.options에 bom_category 추가+API 필터+모달 연동), #8 필터 라벨(접두어 추가) api FormulaEvaluatorService/ItemService/ItemsController/마이그레이션, react LocationDetailPanel/LocationListPanel/QuoteManagementClient/ItemSearchModal/types -

8. 참고 문서

문서 경로
문서 인덱스 docs/INDEX.md
견적 시스템 docs/features/quotes/README.md
단가 정책 docs/rules/pricing-policy.md
API 규칙 docs/dev/standards/api-rules.md
품질 체크리스트 docs/dev/standards/quality-checklist.md
Git 컨벤션 docs/dev/standards/git-conventions.md
프론트엔드 아키텍처 docs/frontend/v1/01-architecture.md
통합 개선 마스터 docs/dev/dev_plans/integrated-master-plan.md

9. 파일 경로 인덱스

견적관리

파일 역할 관련 이슈
react/src/components/quotes/types.ts 타입 정의, 변환 함수 #16(L39,54-59), #18-20(탭순서)
react/src/components/quotes/actions.ts 서버 액션 #25(거래처 API)
react/src/components/quotes/QuoteManagementClient.tsx 목록 페이지 #8(L312-336), #16(L256-262), #24(L115)
react/src/components/quotes/QuoteRegistration.tsx 등록/수정 폼 #25(L776-790)
react/src/components/quotes/LocationListPanel.tsx 개소 목록 (V2) #14,21(엑셀업로드 L228-286)
react/src/components/quotes/LocationDetailPanel.tsx 개소 상세 (V2) #18-20(L157-179), #22(L112)
react/src/components/quotes/QuoteFooterBar.tsx 하단 액션 바 #31(L142-153), #24(L176-187)
api/app/Services/Quote/QuoteService.php CRUD + 상태관리 #27(L48-81), #31(L377-379)
api/app/Services/Quote/FormulaEvaluatorService.php 수식 평가 엔진 #18-20(L1852-1865,1923-1933)
api/app/Models/Quote/Quote.php 견적 모델 #31(L337-340 isEditable)

수주관리

파일 역할 관련 이슈
react/src/components/orders/QuotationSelectDialog.tsx 견적 선택 모달 #27(L46-52)
react/src/components/orders/actions.ts 서버 액션 #27(L1246-1266)
api/app/Http/Requests/Order/UpdateOrderRequest.php 수주 수정 검증 #29(L40-42)

단가관리

파일 역할 관련 이슈
react/src/components/pricing/PricingFormClient.tsx 단가 폼 #32(L70-76), #33(L139-148)
api/app/Services/Pricing/PricingService.php CRUD (미구현!) #32,33(TODO 상태)
api/app/Http/Requests/Pricing/PriceUpdateRequest.php 수정 검증 #33(L15-17)

공통

파일 역할 관련 이슈
react/src/components/templates/UniversalListPage/index.tsx 공통 목록 #1(L227-240,845)
react/src/components/organisms/FormActions.tsx 폼 액션 바 #2(L22-73)
react/src/components/organisms/SearchableSelectionModal/ 공통 모달 #3(L233)
react/src/lib/utils/amount.ts 금액 포맷 #30(L53-61)
api/app/Services/ClientService.php 거래처 CRUD #5(L28-54)

10. 검증 결과

각 Phase 완료 후 이 섹션에 검증 결과 추가

10.1 Phase 1 검증

테스트 예상 결과 실제 결과 상태
GET /api/v1/quotes?status=finalized&for_order=true 직접 호출 전환 가능 견적 N건
DB 직접 조회: SELECT * FROM quotes WHERE status='finalized' AND order_id IS NULL N건 존재
검색어 유무에 따른 쿼리 로그 비교 동일 결과
converted 견적 수정 시도 (API) 403 에러 + "수정 불가" 메시지
converted 견적 상세 (프론트) 수정 버튼 미노출
draft 견적 수정 정상 수정

10.2 Phase 2 검증

테스트 예상 결과 실제 결과 상태
BOM 탭 순서 (수동 등록) 통일된 순서
BOM 탭 순서 (상세 조회) 동일 순서
BOM 탭 순서 (엑셀 업로드) 동일 순서
스크린+스틸 동시 품목 추가 경고 모달 + 차단
혼합 필터 옵션 제거 확인 (데드 코드 정리)
거래처 선택 → 담당자 자동 채움
기타 품목 수동 추가 레이아웃 정상, 기타 탭에 병합

10.3 Phase 3 검증

테스트 예상 결과 실제 결과 상태
단가 등록 → 상세 확인 원본 품목코드/품목명 유지
단가 수정 → 저장 입력 금액 정상 저장
수주 수정 → 저장 유효성 에러 없이 저장

이 문서는 /plan 스킬로 생성되었습니다. (2026-03-13) 코드 레벨 분석: Sequential Thinking MCP + Explore Agent x8 (2회차) 컨펌 반영: 2026-03-14 (6건 중 5건 확정, 1건 보류)