Files
sam-api/CURRENT_WORKS.md
권혁성 400adb7c58 fix(WEB): 방화유리 수량 폴백 제거 및 수주→작업지시 파이프라인 개선
- OrderService: glass_qty에서 quantity 폴백 제거 (투시창 선택 시에만 유효)
- OrderService: createProductionOrder()에서 절곡 공정 bending_info 자동 생성
- OrderService: formula_source 없는 레거시 데이터의 sort_order 기반 개소 분배
- OrderService: note 파싱에서 '-' 단독값 무시 처리
- FormulaEvaluatorService: 철재 W1 계산 (W0+160 → W0+110) 레거시 일치
- WorkOrderService: store()에서 order_node_id null 품목용 rootNodes fallback 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 01:02:23 +09:00

97 KiB
Raw Blame History

2026-02-19 (수) - 작업지시 show() materialInputs eager loading 누락 수정

커밋 내역

  • 23029b1 fix: 작업지시 단건조회(show)에 materialInputs eager loading 추가

수정된 파일

파일명 설명
app/Services/WorkOrderService.php show()에 items.materialInputs + items.materialInputs.stockLot eager loading 추가

원인

  • 목록조회(Line 59-64)에만 items.materialInputs.stockLot 있고, 단건조회 show()에는 누락
  • 프론트 슬랫 작업일지에서 개소별 입고 LOT NO 표시 불가

상태: 완료


2026-02-19 (수) - 슬랫 조인트바 자동 계산 및 데이터 파이프라인 완성

작업 목표

  • 조인트바 수량이 BOM에 생성되지 않는 문제 수정
  • 견적→수주→작업지시 전체 데이터 파이프라인에 조인트바/방화유리 연동

근본 원인

  • joint_bar_qty가 프론트에서 전달되지 않아 항상 0 → 조인트바 BOM 미생성
  • 레거시 5130은 자동 계산: (2 + floor((제작가로 - 500) / 1000)) × 셔터수량

수정된 파일

파일명 설명
app/Services/Quote/Handlers/KyungdongFormulaHandler.php joint_bar_qty 미전달 시 레거시 공식 자동 계산 추가
app/Services/OrderService.php extractSlatInfoFromBom() 및 createWorkOrders()에 동일 폴백 추가

데이터 백필

  • 기존 72건 work_order_items의 slat_info 업데이트 (joint_bar: 0→4, glass_qty: 0→1)

상태: 완료

  • KyungdongFormulaHandler 자동 계산 추가
  • OrderService 3단계 폴백 (nodeOptions → bom_result → width 기반 계산)
  • 기존 데이터 백필 완료
  • 견적 화면에서 조인트바 BOM 항목 표시 확인
  • 전체 파이프라인 E2E 검증 (견적→수주→작업지시→작업자화면)

2026-02-19 (수) - 작업일지 담당자 정보 및 슬랫 데이터 파이프라인 구축

커밋 내역

  • 316d412 fix: 슬랫 작업일지 데이터 파이프라인 구축
  • 1ddef5a fix: 경동 BOM 계산 수정 및 품목-공정 매핑
  • 0c58c52 fix: 견적→수주 변환 시 담당자 정보 누락 수정

수정된 파일

파일명 설명
app/Services/OrderService.php extractSlatInfoFromBom() 신규, createFromQuote/syncFromQuote에 slat_info 추가, createWorkOrders 폴백
app/Services/Quote/Handlers/KyungdongFormulaHandler.php 조인트바 조건: slat → slat+steel 확장

2026-01-30 (목) - 5130↔SAM 견적 교차 검증 완료 + 마이그레이션 검증

작업 목표

  • SAM 견적 계산이 5130 레거시 시스템과 100% 일치하는지 교차 검증
  • FormulaEvaluatorService 슬랫/스틸 지원 완성
  • MigrateBDModelsPrices 커맨드 동작 검증

수정된 파일

파일명 설명
app/Services/Quote/FormulaEvaluatorService.php 제품타입별 면적/중량 공식 분기, 모터/브라켓 입력값 오버라이드, 디버그 포뮬러 동적 표시
app/Services/Quote/Handlers/KyungdongFormulaHandler.php 제품타입별 면적/중량 공식, normalizeGuideType() 추가, guide_rail_spec 파라미터 별칭

핵심 수정 내용

1. 제품타입별 면적/중량 공식 (FormulaEvaluatorService + Handler)

  • Screen: area = (W1 × (H1+550)) / 1M, weight = area×2 + (W0/1000)×14.17
  • Slat: area = (W0 × (H0+50)) / 1M, weight = area×25
  • Steel: area = (W1 × (H1+550)) / 1M, weight = area×25

2. 모터/브라켓 입력값 오버라이드

  • 기존: 항상 자동 계산
  • 수정: MOTOR_CAPACITY, BRACKET_SIZE 입력값이 있으면 우선 사용

3. 가이드타입 정규화

  • normalizeGuideType() 메서드 추가 (벽면↔벽면형, 측면↔측면형, 혼합↔혼합형)
  • guide_rail_spec 파라미터 별칭 지원

검증 결과

전 모델 교차 검증 (Task #6)

16/16 ALL PASS
- 10개 스크린 조합 (KSS01, KSS02, KSE01, KWE01, KTE01, KQTS01, KDSS01 × SUS/EGI)
- 6개 슬랫 조합 (KSS02, KSE01, KTE01 × SUS × 2사이즈)
- 조건: 6800×2700, QTY=1, 300K 모터, 5인치 브라켓

가이드타입 교차 검증 (Task #7)

21/21 ALL PASS
- 벽면/측면/혼합 × 4모델(KSS02, KSE01, KTE01, KDSS01) × screen
- 벽면/측면/혼합 × 3모델(KSS02, KSE01, KTE01) × slat
- 혼합형: 5130은 col6에 "혼합 120*70/120*120" 두 규격 필요

MigrateBDModelsPrices 커맨드 검증 (Task #4, #5)

커맨드 정상 동작 확인
- BD-* (절곡품): 58건 마이그레이션 완료
- EST-* (모터/제어기/원자재 등): 71건 마이그레이션 완료
- chandj 원본 가격 일치: 7/7 검증 통과
- --dry-run, --fresh 옵션 정상 동작

Git 커밋

  • f4a902f - fix: FormulaEvaluatorService 슬랫/스틸 제품타입별 면적/중량/모터/가이드 수정

2026-01-29 (수) - 경동기업 견적 로직 Phase 4 완료

작업 목표

  • 경동기업(tenant_id=287) 전용 견적 계산 로직 구현
  • 5130 레거시 시스템의 BOM/견적 로직을 SAM에 이식
  • 동적 BOM 계산: 모터, 제어기, 절곡품(10종), 부자재(3종)

생성된 파일

파일명 설명
app/Models/Kyungdong/KdPriceTable.php 경동기업 전용 단가 테이블 모델
app/Services/Quote/Handlers/KyungdongFormulaHandler.php 경동기업 견적 계산 핸들러
database/migrations/2026_01_29_004736_create_kd_price_tables_table.php kd_price_tables 마이그레이션
database/seeders/Kyungdong/KdPriceTableSeeder.php 단가 데이터 시더 (47건)

수정된 파일

파일명 설명
app/Services/Quote/FormulaEvaluatorService.php tenant_id=287 라우팅 추가

구현된 기능

기능 설명
모터 용량 계산 제품타입 × 인치 × 중량 3차원 조건
브라켓 크기 결정 중량 기반 530320, 600350, 690*390
주자재 계산 W × (H + 550) / 1,000,000 × 단가
절곡품 계산 (10종) 케이스, 마구리, 가이드레일, 하장바, L바, 평철, 환봉 등
부자재 계산 (3종) 감기샤프트, 각파이프, 앵글

테스트 결과

입력: W0=3000, H0=2500, 철재형, 5인치, KSS01 SUS
출력: 16개 항목, 합계 751,200원 ✅

검증 완료

  • Pint 코드 스타일 통과
  • 마이그레이션 실행 완료 (kd_price_tables)
  • 시더 실행 완료 (47건 단가 데이터)
  • tinker 테스트 통과 (16개 항목 정상 계산)

계획 문서

  • docs/plans/kd-quote-logic-plan.md - Phase 0~4 완료 (100%)

2026-01-21 (화) - TodayIssue 헤더 알림 API (Phase 3 완료)

작업 목표

  • TodayIssue + 알림 시스템 통합 Phase 3: 헤더 알림 API 구현
  • 읽지 않은 이슈 목록/개수 조회, 읽음 처리 API 구현

수정된 파일

파일명 설명
app/Services/TodayIssueService.php getUnreadList(), getUnreadCount(), markAllAsRead() 추가
app/Http/Controllers/Api/V1/TodayIssueController.php unread(), unreadCount(), markAsRead(), markAllAsRead() 추가
routes/api.php 4개 엔드포인트 추가
lang/ko/message.php today_issue.marked_as_read, all_marked_as_read 메시지 추가
app/Swagger/v1/TodayIssueApi.php 4개 엔드포인트 + 스키마 문서화
app/Swagger/v1/ComprehensiveAnalysisApi.php 스키마 이름 충돌 해결 (TodayIssueItem → ComprehensiveTodayIssueItem)

API 엔드포인트

Method Endpoint 설명
GET /api/v1/today-issues/unread 읽지 않은 이슈 목록 (헤더 알림 드롭다운용)
GET /api/v1/today-issues/unread/count 읽지 않은 이슈 개수 (헤더 뱃지용)
POST /api/v1/today-issues/{id}/read 단일 이슈 읽음 처리
POST /api/v1/today-issues/read-all 모든 이슈 읽음 처리

검증 완료

  • Pint 코드 스타일 통과
  • Swagger 문서 생성 완료
  • PHP 문법 검증 통과
  • Service-First 아키텍처 준수
  • Multi-tenancy (tenant_id 필터링) 적용
  • i18n 메시지 키 사용

계획 문서

  • docs/plans/today-issue-notification-integration-plan.md
  • 백엔드 작업 완료 (Phase 1-3: 100%)
  • Phase 4 (React 헤더 연동)는 프론트엔드 담당

2026-01-11 (토) - Labor(노임관리) API 구현

작업 목표

  • 시공관리 > 노임관리 API 백엔드 구현
  • Frontend actions.ts API 연동

생성된 파일

파일명 설명
app/Models/Labor.php 노임 모델 (BelongsToTenant, SoftDeletes)
app/Http/Controllers/Api/V1/LaborController.php 노임 컨트롤러 (7개 메서드)
app/Services/LaborService.php 노임 서비스 (비즈니스 로직)
app/Http/Requests/Labor/LaborIndexRequest.php 목록 조회 검증
app/Http/Requests/Labor/LaborStoreRequest.php 등록 요청 검증
app/Http/Requests/Labor/LaborUpdateRequest.php 수정 요청 검증
app/Http/Requests/Labor/LaborBulkDeleteRequest.php 일괄 삭제 검증
database/migrations/2026_01_11_000000_create_labors_table.php 마이그레이션

수정된 파일

파일명 설명
routes/api.php Labor 라우트 7개 추가 (line 1005-1014)

API 엔드포인트

Method Endpoint 설명
GET /api/v1/labor 목록 조회
GET /api/v1/labor/stats 통계 조회
POST /api/v1/labor 등록
GET /api/v1/labor/{id} 상세 조회
PUT /api/v1/labor/{id} 수정
DELETE /api/v1/labor/{id} 삭제
DELETE /api/v1/labor/bulk 일괄 삭제

검증 완료

  • 마이그레이션 실행 완료
  • Pint 코드 스타일 통과
  • Service-First 아키텍처 준수
  • FormRequest 검증 사용
  • Multi-tenancy (BelongsToTenant) 적용

SAM API 작업 현황

2025-01-09 (목) - 작업지시 코드 리뷰 기반 전면 개선

작업 목표

  • 작업지시(Work Orders) 기능 코드 리뷰 결과 기반 전면 개선
  • Critical, High, Medium 우선순위 항목 전체 수정

수정된 파일

파일명 설명
app/Models/Production/WorkOrderItem.php BelongsToTenant 트레이트 적용
app/Models/Production/WorkOrderBendingDetail.php BelongsToTenant 트레이트 적용
app/Models/Production/WorkOrderIssue.php BelongsToTenant 트레이트 적용
app/Models/Production/WorkOrder.php 상태 전이 규칙 (STATUS_TRANSITIONS, canTransitionTo, transitionTo)
app/Services/WorkOrderService.php 감사 로그, 다중 담당자, 부분 수정 지원

생성된 파일

파일명 설명
app/Models/Production/WorkOrderAssignee.php 다중 담당자 피벗 모델
database/migrations/*_create_work_order_assignees_table.php 다중 담당자 테이블 마이그레이션

주요 변경 내용

  1. Multi-tenancy 적용: 하위 모델 4개에 BelongsToTenant 트레이트 적용
  2. 감사 로그 적용: 품목 삭제, 상태 변경, 이슈 등록/해결, 담당자 배정 시 기록
  3. 상태 전이 규칙: STATUS_TRANSITIONS 상수 + canTransitionTo(), transitionTo() 메서드
  4. 다중 담당자 지원:
    • WorkOrderAssignee 피벗 모델 생성
    • assign() 메서드에서 배열 담당자 지원
    • is_primary 플래그로 주 담당자 구분
  5. 부분 수정 지원: 품목 업데이트 시 ID 기반 upsert/delete (기존 삭제 후 재생성 → ID 기반 부분 수정)

검증 완료

  • Pint 코드 스타일 (3개 파일)
  • Service-First 아키텍처 준수
  • Eager loading에 assignees.user:id,name 추가

Git 커밋

  • 349917f refactor(work-orders): 코드 리뷰 기반 전면 개선

관련 문서

  • 계획: ~/.claude/plans/purring-sparking-pinwheel.md

2026-01-08 (수) - Order Management API Phase 1.1 구현

작업 목표

  • 수주관리(Order Management) API 기본 CRUD 및 상태 관리 기능 구현
  • WorkOrderService/Controller 패턴을 참고하여 SAM API 규칙 준수

생성된 파일

파일명 설명
app/Services/OrderService.php 수주 비즈니스 로직 서비스
app/Http/Controllers/Api/V1/OrderController.php 수주 API 컨트롤러
app/Http/Requests/Order/StoreOrderRequest.php 생성 요청 검증
app/Http/Requests/Order/UpdateOrderRequest.php 수정 요청 검증
app/Http/Requests/Order/UpdateOrderStatusRequest.php 상태 변경 요청 검증
app/Swagger/v1/OrderApi.php Swagger API 문서

수정된 파일

파일명 설명
routes/api.php OrderController import 및 7개 라우트 추가
lang/ko/message.php 수주 관련 메시지 키 추가
lang/en/message.php 수주 관련 메시지 키 추가
lang/ko/error.php 수주 에러 메시지 키 추가
lang/en/error.php 수주 에러 메시지 키 추가

주요 구현 내용

  1. OrderService 메서드: index, stats, show, store, update, destroy, updateStatus
  2. 상태 전환 규칙: DRAFT → CONFIRMED → IN_PROGRESS → COMPLETED/CANCELLED
  3. 수주번호 자동생성: ORD{YYYYMMDD}{0001} 형식
  4. 품목 금액 계산: 공급가, 세액, 합계 자동 계산
  5. Swagger 스키마: Order, OrderItem, OrderPagination, OrderStats 등

검증 완료

  • Pint 코드 스타일 (6개 파일 자동 수정)
  • Swagger 문서 생성
  • Service-First 아키텍처 준수
  • i18n 메시지 키 사용

관련 문서

  • 계획: docs/plans/order-management-plan.md
  • 변경 요약: docs/changes/20250108_order_management_phase1.md

2026-01-02 (목) - 채권현황 동적월 지원 및 버그 수정

작업 목표

  • "최근 1년" 필터 선택 시 동적 월 기간(최근 12개월) 지원
  • year=0 파라미터 처리 버그 수정
  • 거래처별 연체 상태 및 메모 관리 기능 추가

수정된 파일

파일명 설명
app/Http/Controllers/Api/V1/ReceivablesController.php boolean 유효성 검사 수정, 디버깅 로그 추가
app/Services/ReceivablesService.php 동적 월 기간 지원, 이월잔액 계산 추가
app/Models/Orders/Client.php is_overdue, memo 필드 추가
routes/api.php 채권현황 라우트 추가

생성된 파일

파일명 설명
database/migrations/2026_01_02_113722_add_is_overdue_to_clients_table.php clients 테이블 is_overdue 컬럼 추가

주요 변경 내용

  1. Boolean 유효성 검사 수정: 'nullable|boolean''nullable|string|in:true,false,1,0'
    • 쿼리 문자열의 "true" 문자열을 올바르게 처리
  2. 동적 월 기간 지원: recent_year=true 시 최근 12개월 동적 계산
  3. 월별 레이블 동적 생성: 예: ['25.02', '25.03', ...]
  4. 이월잔액(carry_forward_balance) 계산: 선택 기간 이전의 누적 미수금

Git 커밋

  • 4fa38e3 feat(API): 채권현황 동적월 지원 및 year=0 파라미터 버그 수정

남은 작업

  • 디버깅 로그 제거 (테스트 완료 후)
  • 추가 UI 개선사항 확인

2026-01-02 (목) - 견적 BOM 산출 작업 현황 및 Item 모델 주석 추가

작업 목표

  • 견적 BOM 산출 관련 작업 진행 상황 문서화
  • Item 모델 필드 주석 추가

수정된 파일

파일명 설명
app/Models/Items/Item.php item_category 필드 주석 추가

주요 변경 내용

  1. Item 모델 필드 주석:
    • item_category 필드에 설명 주석 추가
    • React 프론트엔드에서 필드 매핑 시 참조용

Git 커밋

  • 02e268e docs(API): 견적 BOM 산출 작업 현황 및 Item 모델 주석 추가

2026-01-02 (목) - Phase 1.2 다건 BOM 기반 자동산출 API 구현

작업 목표

  • React 견적등록 화면에서 여러 품목의 자동산출을 일괄 요청할 수 있는 API 구현
  • React QuoteFormItem 인터페이스 필드명(camelCase)과 API 변수명(약어) 모두 지원

생성된 파일

파일명 설명
app/Http/Requests/Quote/QuoteBomBulkCalculateRequest.php 다건 BOM 산출 FormRequest (필드 변환 포함)
docs/changes/20260102_1300_quote_bom_bulk_calculation.md 변경 내용 문서

수정된 파일

파일명 설명
app/Services/Quote/QuoteCalculationService.php calculateBomBulk() 메서드 추가
app/Http/Controllers/Api/V1/QuoteController.php calculateBomBulk 액션 추가
routes/api.php /calculate/bom/bulk 라우트 추가
app/Swagger/v1/QuoteApi.php 스키마 3개 + 엔드포인트 추가

주요 변경 내용

  1. 다건 BOM 기반 자동산출 API: POST /api/v1/quotes/calculate/bom/bulk
  2. 필드 매핑 지원: React camelCase (openWidth, openHeight) ↔ API 약어 (W0, H0)
  3. 일괄 처리: 여러 품목 동시 계산, 성공/실패 요약 제공
  4. Swagger 문서화: QuoteBomBulkCalculateRequest, QuoteBomBulkItemInput, QuoteBomBulkCalculationResult

필드 매핑 테이블

React 필드 API 변수 설명
openWidth W0 개구부 폭
openHeight H0 개구부 높이
quantity QTY 수량
productCategory PC 제품 카테고리
guideRailType GT 가이드레일 타입
motorPower MP 모터 출력
controller CT 제어반
wingSize WS 날개 크기
inspectionFee INSP 검사비

Git 커밋

  • 4e59bbf feat: Phase 1.2 - 다건 BOM 기반 자동산출 API 구현

관련 문서

  • 계획 문서: docs/plans/quote-calculation-api-plan.md
  • Phase 1.1: docs/changes/20260102_quote_bom_calculation_api.md

2026-01-02 (목) - Phase 1.1 견적 산출 API 엔드포인트 구현

작업 목표

  • React 프론트엔드에서 BOM 기반 견적 계산 API 호출 가능하도록 구현
  • MNG FormulaEvaluatorService.calculateBomWithDebug() 연결

생성된 파일

파일명 설명
app/Http/Requests/Quote/QuoteBomCalculateRequest.php BOM 계산용 FormRequest
docs/changes/20260102_quote_bom_calculation_api.md 변경 내용 문서

수정된 파일

파일명 설명
app/Services/Quote/QuoteCalculationService.php calculateBom 메서드 추가
app/Http/Controllers/Api/V1/QuoteController.php calculateBom 액션 추가
routes/api.php /calculate/bom 라우트 추가
app/Swagger/v1/QuoteApi.php 스키마 및 엔드포인트 문서 추가

주요 변경 내용

  1. BOM 기반 견적 계산 API: POST /api/v1/quotes/calculate/bom
  2. 입력 변수: finished_goods_code, W0, H0, QTY, PC, GT, MP, CT, WS, INSP
  3. 10단계 디버깅: debug=true 옵션으로 계산 과정 확인 가능
  4. Swagger 문서화: QuoteBomCalculateRequest, QuoteBomCalculationResult 스키마

관련 문서

  • 계획 문서: docs/plans/quote-calculation-api-plan.md
  • FormulaEvaluatorService: Phase 1.1에서 구현 완료

2025-12-30 (월) - Phase 1.1 견적 계산 MNG 로직 재구현

작업 목표

  • MNG FormulaEvaluatorService 10단계 BOM 계산 로직을 API로 이식
  • React 프론트엔드에서 MNG와 동일한 견적 계산 기능 사용 가능하도록 구현

생성된 파일

파일명 설명
app/Models/CategoryGroup.php 카테고리별 단가 계산 방식 모델 (신규)
docs/changes/20251230_2339_quote_calculation_mng_logic.md 변경 내용 문서

수정된 파일

파일명 설명
app/Services/Quote/FormulaEvaluatorService.php MNG 10단계 BOM 계산 로직 추가 (537줄→1176줄)

주요 변경 내용

  1. CategoryGroup 모델: 면적/중량/수량 기반 단가 계산 방식 관리
  2. calculateBomWithDebug(): 10단계 BOM 계산 (디버그 모드)
  3. calculateCategoryPrice(): 카테고리 기반 단가 계산
  4. groupItemsByProcess(): 공정별 품목 그룹화
  5. getItemDetails(): 품목 상세 정보 및 BOM 트리

관련 문서

  • docs/plans/quote-calculation-api-plan.md

2025-12-30 (월) - Phase L 설정 및 기준정보 API 개발

작업 목표

  • L-2 권한관리 API 개발
  • L-3 직급관리 + L-4 직책관리 API 개발 (통합 positions 테이블)

생성된 파일

L-2 권한관리

파일명 설명
database/migrations/2025_12_30_160802_add_is_hidden_to_roles_table.php roles 테이블 is_hidden 컬럼 추가
app/Http/Controllers/Api/V1/RoleController.php Role CRUD API
app/Http/Controllers/Api/V1/RolePermissionController.php 권한 매트릭스 API
app/Services/RoleService.php Role 비즈니스 로직
app/Swagger/v1/RoleApi.php Swagger 문서
app/Swagger/v1/RolePermissionApi.php Swagger 문서

L-3/L-4 직급/직책 관리

파일명 설명
database/migrations/2025_12_30_091821_create_positions_table.php positions 테이블 생성
database/migrations/2025_12_30_091822_add_position_type_to_common_codes.php position_type 코드 추가
app/Models/Tenants/Position.php Position 모델
app/Services/PositionService.php Position 비즈니스 로직
app/Http/Controllers/Api/V1/PositionController.php Position CRUD API
app/Http/Requests/PositionRequest.php 생성/수정 요청 검증
app/Http/Requests/PositionReorderRequest.php 순서 변경 요청 검증
app/Swagger/v1/PositionApi.php Swagger 문서

API 엔드포인트

Role API (9개)

GET    /api/v1/roles                    # 역할 목록
POST   /api/v1/roles                    # 역할 생성
GET    /api/v1/roles/{id}               # 역할 상세
PATCH  /api/v1/roles/{id}               # 역할 수정
DELETE /api/v1/roles/{id}               # 역할 삭제
GET    /api/v1/roles/{id}/permissions   # 역할 권한 조회
POST   /api/v1/roles/{id}/permissions   # 권한 추가
DELETE /api/v1/roles/{id}/permissions   # 권한 제거
PUT    /api/v1/roles/{id}/permissions/sync  # 권한 동기화

Position API (6개)

GET    /api/v1/positions?type=rank      # 직급 목록
GET    /api/v1/positions?type=title     # 직책 목록
POST   /api/v1/positions                # 생성 (type 필수)
PUT    /api/v1/positions/{id}           # 수정
DELETE /api/v1/positions/{id}           # 삭제
POST   /api/v1/positions/reorder        # 순서 변경 (bulk)

설계 결정사항

  • 통합 테이블: 직급(rank)과 직책(title)을 positions 테이블로 통합
  • 구분 컬럼: type 컬럼으로 rank/title 구분
  • 정렬 지원: sort_order 컬럼 + reorder API로 드래그 앤 드롭 지원

참고

  • 계획 문서: docs/plans/l2-permission-management-plan.md
  • 세레나 메모리: position-api-development, l2-permission-state

2025-12-28 (토) - 시스템 게시판 tenant_id 및 custom_fields 수정

작업 목표

  • POST /api/v1/system-boards/qna/posts 500 에러 해결
  • 시스템 게시판 tenant_id 처리 로직 개선
  • custom_fields field_key → field_id 매핑 지원
  • 댓글 생성 시 tenant_id 누락 수정

문제 원인

  1. posts.tenant_id NOT NULL 제약조건 위반 (시스템 게시판에서 null 설정 시도)
  2. saveCustomFields()에서 field_key(string)를 field_id(integer)로 사용
  3. createComment()에서 tenant_id 미설정

수정된 파일 (1개)

파일명 변경 내용
app/Services/Boards/PostService.php tenant_id 항상 설정, custom_fields 매핑 개선, 댓글 tenant_id 추가

상세 변경사항

1. HQ_TENANT_ID 상수 추가

private const HQ_TENANT_ID = 1; // 본사 테넌트 ID

2. applySystemBoardScope() 헬퍼 메서드 추가

private function applySystemBoardScope($query): void
{
    $query->where(function ($q) {
        $q->where('tenant_id', self::HQ_TENANT_ID)
            ->orWhere('tenant_id', $this->tenantId());
    });
}

3. createPost() 수정

  • 변경 전: $data['tenant_id'] = $isSystemBoard ? null : $this->tenantId();
  • 변경 후: $data['tenant_id'] = $this->tenantId(); (항상 설정)

4. saveCustomFields() 개선

  • boardId 파라미터 추가
  • field_key → field_id 매핑 로직 추가 (BoardSetting 조회)
  • 모든 호출부 업데이트 (createPost, updatePost 등)

5. createComment() 수정

  • $data['tenant_id'] = $this->tenantId(); 추가

시스템 게시판 조회 조건

(tenant_id = 1) OR (tenant_id = 현재 테넌트)
  • 본사(tenant_id=1)의 글: 모든 테넌트에서 조회 가능
  • 각 테넌트 글: 해당 테넌트만 조회 가능

테스트 결과

  • 게시글 생성 (id=7, id=8 with custom_fields)
  • custom_fields 저장 (inquiry_type → field_id=2 변환)
  • 댓글 생성 (id=1, tenant_id=1)

Git 커밋

4a2c185 fix: 게시판 시스템 tenant_id 및 custom_fields 처리 개선

2025-12-27 (금) - 결재 API 프론트엔드 호환성 개선

작업 목표

  • 프론트엔드에서 form_code, step_type, approver_id 필드명 사용 지원
  • 기존 form_id, type, user_id 필드명과 호환성 유지

수정된 파일 (4개)

파일명 변경 내용
app/Http/Requests/Approval/StoreRequest.php form_code, step_type, approver_id 필드 추가
app/Http/Requests/Approval/UpdateRequest.php form_code, steps 필드 지원 추가
app/Services/ApprovalService.php store()/update() - form_code→form_id 변환, createApprovalSteps() - step_type/approver_id 지원
lang/ko/error.php approval.form_required 에러 메시지 추가

필드 호환성 매핑

프론트엔드 API (이전) 설명
form_code form_id 양식 코드로 form_id 자동 조회
step_type type 결재/참조 구분
approver_id user_id 결재자 사용자 ID
step_order - 결재 순서 (자동 증가)

2025-12-26 (목) - 휴가관리 휴직 직원 표시 수정

작업 목표

  • 휴가 사용현황에 휴직(leave) 상태 직원도 표시되도록 수정

수정된 파일 (1개)

파일명 변경 내용
app/Services/LeaveService.php getAllBalances()에서 employee_status 필터를 'active'에서 ['active', 'leave']로 변경

영향받는 직원

  • 최준호(46), 한지민(50), 오태양(51) - 휴직 상태

Git 커밋

defe971 fix(leave): 휴직 직원도 휴가 사용현황에 표시되도록 수정

2025-12-24 (화) - 매입 세금계산서 수취 토글 기능 추가

작업 목표

  • 매입(Purchase) 테이블에 세금계산서 수취 여부(tax_invoice_received) 필드 추가
  • React 프론트엔드에서 토글 API 호출 가능하도록 지원

생성된 마이그레이션 (1개)

파일명 설명
2025_12_24_160000_add_tax_invoice_received_to_purchases_table.php purchases 테이블에 tax_invoice_received 컬럼 추가

수정된 파일 (3개)

파일명 변경 내용
app/Models/Tenants/Purchase.php fillable에 tax_invoice_received 추가, casts에 boolean 타입 추가
app/Services/PurchaseService.php toggleTaxInvoice() 메서드 추가
app/Http/Requests/V1/Purchase/UpdatePurchaseRequest.php tax_invoice_received 필드 검증 규칙 추가

마이그레이션 실행

php artisan migrate
# 2025_12_24_160000_add_tax_invoice_received_to_purchases_table ... DONE

테스트 결과

  • 세금계산서 수취 토글 API 정상 동작
  • React 매입 관리 페이지에서 토글 기능 정상 작동

2025-12-22 (일) - 견적수식 시더 업데이트 (5130 연동)

작업 목표

  • 5130 레거시 데이터 기반 견적수식 시더 업데이트
  • 케이스(셔터박스) 3600mm, 6000mm 품목 및 범위 추가

수정된 파일 (2개)

파일명 변경 내용
database/seeders/QuoteFormulaSeeder.php CASE_AUTO_SELECT 범위에 3600, 6000 구간 추가
database/seeders/QuoteFormulaItemSeeder.php PT-CASE-3600, PT-CASE-6000 품목 추가

테스트 결과

  • W0=3000, H0=2500 입력 시:
    • S=3270 → PT-CASE-3600 정상 선택
    • H1=2770 → PT-GR-3000 정상 선택
    • K=41.21kg → PT-MOTOR-150 정상 선택

Git 커밋

  • eeca8d3 feat: 견적수식 케이스 3600/6000 품목 및 범위 추가

2025-12-19 (목) - Phase 7.2 보완 - 나의 게시글 API 추가

작업 목표

  • Phase 7 게시판 연동 분석 결과, 7.1/7.2 대부분 구현 완료 확인
  • 누락된 /posts/my (나의 게시글) API 추가

수정된 파일 (4개)

파일명 변경 내용
app/Services/Boards/PostService.php getMyPosts() 메서드 추가
app/Http/Controllers/Api/V1/PostController.php myPosts() 액션 추가
routes/api.php GET /v1/posts/my 라우트 추가
app/Swagger/v1/PostApi.php /posts/my Swagger 문서 추가

API 라우트 (1개)

Method Endpoint 설명
GET /v1/posts/my 나의 게시글 목록 (게시판 코드/검색/상태 필터 지원)

Git 커밋

  • c15a245 feat: Phase 7.2 보완 - 나의 게시글 API 추가

2025-12-19 (목) - Phase 6.1 악성채권 추심관리 API 개발

작업 목표

  • docs/plans/erp-api-development-plan-d1.0-changes.md Phase 6.1 악성채권 추심관리
  • 악성채권 CRUD, 서류 첨부, 메모 관리 API 구현

생성된 마이그레이션 (3개)

파일명 설명
2025_12_19_160001_create_bad_debts_table.php 악성채권 테이블
2025_12_19_160002_create_bad_debt_documents_table.php 악성채권 서류 테이블
2025_12_19_160003_create_bad_debt_memos_table.php 악성채권 메모 테이블

생성된 모델 (3개)

app/Models/BadDebts/BadDebt.php:

  • 악성채권 모델 (BelongsToTenant, SoftDeletes)
  • 상태: collecting(추심중), legal_action(법적조치), recovered(회수완료), bad_debt(대손처리)
  • Relations: client(), assignedUser(), creator(), documents(), memos()

app/Models/BadDebts/BadDebtDocument.php:

  • 서류 모델 (document_type: business_license, tax_invoice, additional)
  • Relations: badDebt(), file()

app/Models/BadDebts/BadDebtMemo.php:

  • 메모 모델
  • Relations: badDebt(), creator()

생성된 서비스 (1개)

app/Services/BadDebtService.php:

  • CRUD: index, show, store, update, destroy
  • 토글: toggle (is_active)
  • 요약: summary (상태별 통계)
  • 서류: addDocument, removeDocument
  • 메모: addMemo, removeMemo

생성된 FormRequest (4개)

파일명 설명
StoreBadDebtRequest.php 등록 (client_id, amount, status, assigned_user_id 등)
UpdateBadDebtRequest.php 수정 (선택적 필드)
StoreBadDebtDocumentRequest.php 서류 첨부 (document_type, file_id)
StoreBadDebtMemoRequest.php 메모 추가 (content)

생성된 컨트롤러 (1개)

app/Http/Controllers/Api/V1/BadDebtController.php:

  • index, summary, store, show, update, destroy, toggle
  • addDocument, removeDocument, addMemo, removeMemo

API 라우트 (11개)

Method Endpoint 설명
GET /v1/bad-debts 목록
POST /v1/bad-debts 등록
GET /v1/bad-debts/summary 요약 통계
GET /v1/bad-debts/{id} 상세
PUT /v1/bad-debts/{id} 수정
DELETE /v1/bad-debts/{id} 삭제
PATCH /v1/bad-debts/{id}/toggle 활성화 토글
POST /v1/bad-debts/{id}/documents 서류 첨부
DELETE /v1/bad-debts/{id}/documents/{documentId} 서류 삭제
POST /v1/bad-debts/{id}/memos 메모 추가
DELETE /v1/bad-debts/{id}/memos/{memoId} 메모 삭제

Swagger 문서

app/Swagger/v1/BadDebtApi.php:

  • BadDebt, BadDebtDocument, BadDebtMemo 스키마
  • 모든 엔드포인트 문서화 완료

Git 커밋

  • c0af888 feat: Phase 6.1 악성채권 추심관리 API 구현

2025-12-18 (수) - 가지급금 관리 API 개발

작업 목표

  • docs/plans/erp-api-development-plan.md Phase 3의 3.5 가지급금 관리
  • 가지급금 CRUD, 정산 처리, 인정이자 계산/리포트 API 구현

생성된 마이그레이션 (1개)

파일명 설명
2025_12_18_120001_create_loans_table.php 가지급금 테이블 (tenant_id, user_id, loan_date, amount, purpose, settlement_date, settlement_amount, status, withdrawal_id)

생성된 모델 (1개)

app/Models/Tenants/Loan.php:

  • 가지급금 모델 (BelongsToTenant, SoftDeletes)
  • 상태: outstanding(미정산), settled(정산완료), partial(부분정산)
  • 인정이자율: 연 4.6% (2024/2025), DEFAULT_INTEREST_RATE
  • 세금: 법인세 19%, 소득세 35%, 주민세 10%
  • Relations: user(), withdrawal(), creator(), updater()
  • Methods: calculateRecognizedInterest(), calculateTaxes(), isEditable(), isSettleable()

생성된 서비스 (1개)

app/Services/LoanService.php:

  • CRUD: index, show, store, update, destroy
  • 요약: summary (미정산 건수/금액, 정산완료 금액)
  • 정산: settle (전액/부분 정산)
  • 인정이자: calculateInterest, interestReport

생성된 FormRequest (5개)

파일명 설명
LoanIndexRequest.php 목록 조회 (user_id, status, date_from, date_to, per_page)
LoanStoreRequest.php 등록 (user_id, loan_date, amount, purpose, withdrawal_id)
LoanUpdateRequest.php 수정 (선택적 필드)
LoanSettleRequest.php 정산 (settlement_date, settlement_amount)
LoanCalculateInterestRequest.php 인정이자 계산 (year, user_id)

생성된 컨트롤러 (1개)

app/Http/Controllers/Api/V1/LoanController.php:

  • index, summary, store, show, update, destroy, settle, calculateInterest, interestReport

API 라우트 (9개)

Method Endpoint 설명
GET /v1/loans 목록 조회
POST /v1/loans 등록
GET /v1/loans/summary 요약 조회
POST /v1/loans/calculate-interest 인정이자 계산
GET /v1/loans/interest-report/{year} 인정이자 리포트
GET /v1/loans/{id} 상세 조회
PUT /v1/loans/{id} 수정
DELETE /v1/loans/{id} 삭제
POST /v1/loans/{id}/settle 정산

i18n 키 추가

lang/ko/message.php:

  • loan.fetched, loan.created, loan.updated, loan.deleted
  • loan.settled, loan.summary_fetched
  • loan.interest_calculated, loan.interest_report_fetched

lang/ko/error.php:

  • loan.not_found, loan.not_editable, loan.not_deletable
  • loan.not_settleable, loan.settlement_exceeds
  • loan.invalid_withdrawal, loan.user_not_found

lang/ko/validation.php (attributes):

  • loan_date, amount, purpose, settlement_date, settlement_amount, year

Git 커밋

  • af83319 - feat: 가지급금 관리 API 구현

2025-12-17 (화) - 전자결재 모듈 API 개발

작업 목표

  • docs/plans/erp-api-development-plan.md Phase 2의 3.1 전자결재 모듈
  • 결재 양식 (approval_forms), 결재선 템플릿 (approval_lines), 결재 문서 (approvals) CRUD API 구현
  • 기안함, 결재함, 참조함 조회 및 결재 액션 (상신, 승인, 반려, 회수) 기능

생성된 마이그레이션 (4개)

파일명 설명
2025_12_17_200001_create_approval_forms_table.php 결재 양식 테이블 (name, code, category, template JSON)
2025_12_17_200002_create_approval_lines_table.php 결재선 템플릿 테이블 (name, steps JSON, is_default)
2025_12_17_200003_create_approvals_table.php 결재 문서 테이블 (form_id, drafter_id, title, content JSON, status)
2025_12_17_200004_create_approval_steps_table.php 결재 단계 테이블 (approval_id, step_order, type, user_id, status)

생성된 모델 (4개)

app/Models/Tenants/ApprovalForm.php:

  • 결재 양식 모델 (BelongsToTenant, SoftDeletes)
  • Relations: creator(), approvals()
  • Scopes: active()

app/Models/Tenants/ApprovalLine.php:

  • 결재선 템플릿 모델 (BelongsToTenant, SoftDeletes)
  • Relations: creator()
  • Methods: getStepCountAttribute()

app/Models/Tenants/Approval.php:

  • 결재 문서 모델 (BelongsToTenant, SoftDeletes)
  • 상태: draft → pending → approved/rejected/cancelled
  • Relations: form(), drafter(), steps(), currentStepApprover()
  • Methods: canEdit(), canDelete(), canSubmit(), canAction(), canCancel()

app/Models/Tenants/ApprovalStep.php:

  • 결재 단계 모델 (SoftDeletes)
  • 단계 유형: approval, agreement, reference
  • Relations: approval(), user()
  • Methods: isPending(), isApproved(), isRejected()

생성된 서비스 (1개)

app/Services/ApprovalService.php:

  • Form CRUD: formIndex, formShow, formStore, formUpdate, formDestroy, formActive
  • Line CRUD: lineIndex, lineShow, lineStore, lineUpdate, lineDestroy
  • Approval CRUD: show, store, update, destroy
  • 기안함: drafts, draftsSummary
  • 결재함: inbox, inboxSummary
  • 참조함: referenceList
  • 액션: submit, approve, reject, cancel, markRead, markUnread

생성된 FormRequest (13개)

파일 설명
FormIndexRequest.php 양식 목록 조회 파라미터
FormStoreRequest.php 양식 생성 검증 (name, code, template)
FormUpdateRequest.php 양식 수정 검증
LineIndexRequest.php 결재선 목록 조회 파라미터
LineStoreRequest.php 결재선 생성 검증 (name, steps)
LineUpdateRequest.php 결재선 수정 검증
IndexRequest.php 기안함 조회 파라미터
InboxIndexRequest.php 결재함 조회 파라미터
ReferenceIndexRequest.php 참조함 조회 파라미터
StoreRequest.php 문서 생성 검증 (form_id, title)
UpdateRequest.php 문서 수정 검증
SubmitRequest.php 상신 검증 (steps 필수)
RejectRequest.php 반려 검증 (comment 필수)

생성된 컨트롤러 (3개)

파일 엔드포인트
ApprovalFormController.php index, active, show, store, update, destroy
ApprovalLineController.php index, show, store, update, destroy
ApprovalController.php drafts, draftsSummary, inbox, inboxSummary, reference, show, store, update, destroy, submit, approve, reject, cancel, markRead, markUnread

수정된 파일

routes/api.php:

  • Approval Forms 라우트 그룹 추가 (6개 라우트)
  • Approval Lines 라우트 그룹 추가 (5개 라우트)
  • Approvals 라우트 그룹 추가 (15개 라우트)

lang/ko/message.php:

  • approval 섹션 추가 (16개 키)

lang/ko/error.php:

  • approval 섹션 추가 (15개 키)

생성된 Swagger 문서 (3개)

파일 설명
app/Swagger/v1/ApprovalFormApi.php 결재 양식 API 문서 (6개 엔드포인트)
app/Swagger/v1/ApprovalLineApi.php 결재선 API 문서 (5개 엔드포인트)
app/Swagger/v1/ApprovalApi.php 전자결재 API 문서 (15개 엔드포인트)

API 엔드포인트

결재 양식 API (Approval Forms):

  • GET /api/v1/approval-forms - 목록 조회
  • POST /api/v1/approval-forms - 생성
  • GET /api/v1/approval-forms/active - 활성 양식 (셀렉트박스용)
  • GET /api/v1/approval-forms/{id} - 상세 조회
  • PATCH /api/v1/approval-forms/{id} - 수정
  • DELETE /api/v1/approval-forms/{id} - 삭제

결재선 API (Approval Lines):

  • GET /api/v1/approval-lines - 목록 조회
  • POST /api/v1/approval-lines - 생성
  • GET /api/v1/approval-lines/{id} - 상세 조회
  • PATCH /api/v1/approval-lines/{id} - 수정
  • DELETE /api/v1/approval-lines/{id} - 삭제

전자결재 API (Approvals):

  • GET /api/v1/approvals/drafts - 기안함
  • GET /api/v1/approvals/drafts/summary - 기안함 현황
  • GET /api/v1/approvals/inbox - 결재함
  • GET /api/v1/approvals/inbox/summary - 결재함 현황
  • GET /api/v1/approvals/reference - 참조함
  • POST /api/v1/approvals - 문서 생성
  • GET /api/v1/approvals/{id} - 상세 조회
  • PATCH /api/v1/approvals/{id} - 수정
  • DELETE /api/v1/approvals/{id} - 삭제
  • POST /api/v1/approvals/{id}/submit - 상신
  • POST /api/v1/approvals/{id}/approve - 승인
  • POST /api/v1/approvals/{id}/reject - 반려
  • POST /api/v1/approvals/{id}/cancel - 회수
  • POST /api/v1/approvals/{id}/read - 열람
  • POST /api/v1/approvals/{id}/unread - 미열람

검증 완료

  • Pint 스타일 검사 통과 (19개 파일)
  • 라우트 등록 확인 (26개)
  • Swagger 문서 생성 완료

2025-12-17 (화) - 매출/매입 관리 API 개발

작업 목표

  • docs/plans/erp-api-development-plan.md Phase 1의 2.5 매출/매입 관리
  • 매출(Sale) 및 매입(Purchase) CRUD API 구현
  • 확정(confirm) 기능 및 요약(summary) 조회 기능 포함

생성된 마이그레이션 (2개)

파일명 설명
2025_12_17_100001_create_sales_table.php 매출 테이블 (sale_number, sale_date, client_id, 금액, 상태)
2025_12_17_100002_create_purchases_table.php 매입 테이블 (purchase_number, purchase_date, client_id, 금액, 상태)

생성된 모델 (2개)

app/Models/Tenants/Sale.php:

  • 매출 모델 (BelongsToTenant, SoftDeletes)
  • 상태: draft → confirmed → invoiced
  • Relations: client(), deposit(), creator()
  • Methods: canConfirm(), canEdit(), canDelete()

app/Models/Tenants/Purchase.php:

  • 매입 모델 (BelongsToTenant, SoftDeletes)
  • 상태: draft → confirmed
  • Relations: client(), withdrawal(), creator()
  • Methods: canConfirm(), canEdit(), canDelete()

생성된 서비스 (2개)

app/Services/SaleService.php:

  • CRUD, confirm(), summary()
  • 문서번호 자동 생성: SL{YYYYMMDD}{0001}
  • 상태 검증 (수정/삭제는 draft만 가능)

app/Services/PurchaseService.php:

  • CRUD, confirm(), summary()
  • 문서번호 자동 생성: PU{YYYYMMDD}{0001}
  • 상태 검증 (수정/삭제는 draft만 가능)

생성된 FormRequest (4개)

파일 설명
app/Http/Requests/V1/Sale/StoreSaleRequest.php 매출 등록 검증
app/Http/Requests/V1/Sale/UpdateSaleRequest.php 매출 수정 검증
app/Http/Requests/V1/Purchase/StorePurchaseRequest.php 매입 등록 검증
app/Http/Requests/V1/Purchase/UpdatePurchaseRequest.php 매입 수정 검증

생성된 컨트롤러 (2개)

파일 엔드포인트
SaleController.php index, store, show, update, destroy, confirm, summary
PurchaseController.php index, store, show, update, destroy, confirm, summary

수정된 파일

routes/api.php:

  • Sales 라우트 그룹 추가 (7개 라우트)
  • Purchases 라우트 그룹 추가 (7개 라우트)

생성된 Swagger 문서 (2개)

파일 설명
app/Swagger/v1/SaleApi.php 매출 API 문서 (전체 엔드포인트)
app/Swagger/v1/PurchaseApi.php 매입 API 문서 (전체 엔드포인트)

API 엔드포인트

매출 API (Sales):

  • GET /api/v1/sales - 목록 조회
  • POST /api/v1/sales - 등록
  • GET /api/v1/sales/{id} - 상세 조회
  • PUT /api/v1/sales/{id} - 수정
  • DELETE /api/v1/sales/{id} - 삭제
  • POST /api/v1/sales/{id}/confirm - 확정
  • GET /api/v1/sales/summary - 요약

매입 API (Purchases):

  • GET /api/v1/purchases - 목록 조회
  • POST /api/v1/purchases - 등록
  • GET /api/v1/purchases/{id} - 상세 조회
  • PUT /api/v1/purchases/{id} - 수정
  • DELETE /api/v1/purchases/{id} - 삭제
  • POST /api/v1/purchases/{id}/confirm - 확정
  • GET /api/v1/purchases/summary - 요약

검증 완료

  • Pint 스타일 검사 통과
  • 라우트 등록 확인 (14개)
  • 마이그레이션 실행 성공
  • Swagger 문서 생성 완료

2025-12-13 (금) - Items 테이블 통합 마이그레이션 작성

작업 목표

  • docs/plans/items-table-unification-plan.md 기반 작업
  • products + materials 테이블을 items 단일 테이블로 통합
  • BOM 관리 단순화 (child_item_type + child_item_id → child_item_id만)

생성된 마이그레이션 파일 (6개)

순서 파일명 Phase 설명
1 2025_12_13_152423_normalize_item_types_before_unification.php 0 비표준 item_type 삭제 (PRODUCT, SUBASSEMBLY, PART 등)
2 2025_12_13_152507_create_items_table.php 1.1 items 테이블 생성
3 2025_12_13_152553_create_item_details_table.php 1.2 item_details 테이블 생성 (1:1 확장 필드)
4 2025_12_13_152631_migrate_products_materials_to_items.php 1.3 데이터 이관 + item_id_mappings 매핑 테이블
5 2025_12_13_153116_update_item_pages_source_table_to_items.php 3 item_pages.source_table 업데이트
6 2025_12_13_153544_update_reference_tables_to_items.php 5 참조 테이블 item_id 컬럼 추가 및 매핑

생성된 모델 (2개)

app/Models/Items/Item.php:

  • 통합 품목 모델 (FG, PT, SM, RM, CS)
  • BelongsToTenant, SoftDeletes
  • 스코프: products(), materials(), type(), active()
  • BOM 헬퍼: getBomChildIds(), loadBomChildren()

app/Models/Items/ItemDetail.php:

  • 품목 상세 정보 (1:1 관계)
  • Products 전용: is_sellable, is_purchasable, is_producible, 파일 필드
  • Materials 전용: is_inspection, specification

생성된 서비스

app/Services/ItemService.php:

  • items 단일 테이블 CRUD
  • 동적 필드 → options JSON 병합
  • 카테고리 트리 조회
  • 활성/비활성 토글

수정된 파일

app/Models/ItemMaster/ItemPage.php:

  • getTargetModelClass(): items 테이블 지원 추가
  • isItemPage(), isProductType(), isMaterialType() 헬퍼 추가

ID 매핑 전략

item_id_mappings 테이블:

  • 기존 products/materials ID → 새 items ID 매핑
  • Phase 5 참조 테이블 마이그레이션에서 활용

참조 테이블 업데이트 대상 (Phase 5)

테이블 기존 추가 컬럼
product_components ref_type + ref_id item_id, parent_item_id
bom_template_items ref_type + ref_id item_id
orders product_id item_id
order_items product_id item_id
material_receipts material_id item_id
lots material_id item_id
price_histories item_type + item_id new_item_id
item_fields source_table → 'items'

실행 명령어

# 마이그레이션 실행 (순서대로)
php artisan migrate

# 롤백 (전체)
php artisan migrate:rollback --step=6

Phase 6: 마이그레이션 실행 완료

실행 결과:

items: 362개 (FG:4, PT:4, RM:133, SM:217, CS:4)
item_details: 362개 (1:1 관계)
item_id_mappings: 362개 (ID 매핑 완료)
item_pages: 47개 → source_table='items'로 통합
product_components: 4개 중 2개 item_id 매핑 완료

수정 사항:

  • migrate_products_materials_to_items.php: null material_code 자동 생성 로직 추가

다음 작업 (Phase 7 이후)

  • ItemsController → ItemService 교체
  • CRUD 테스트 (전체 item_type)
  • BOM 계산 테스트
  • Item-Master 연동 테스트
  • 기존 products/materials 테이블 삭제 (확인 후)

참조 문서

  • docs/plans/items-table-unification-plan.md
  • docs/INDEX.md

2025-12-09 (월) - HR API 개발 완료 (Employee, Attendance, Department Tree)

작업 목표

  • docs/features/HR_API_ANALYSIS.md 기반 HR API 구현
  • Employee 관리 API (tenant_user_profiles 활용)
  • Attendance 근태 관리 API (attendances 테이블)
  • Department 트리 조회 API

Phase 1: 마이그레이션

추가된 마이그레이션:

  • 2025_12_09_084138_add_employee_status_to_tenant_user_profiles_table.php

    • employee_status ENUM('active', 'leave', 'resigned') DEFAULT 'active'
    • json_extra JSON nullable (유연한 사원 정보)
  • 2025_12_09_084231_create_attendances_table.php

    • user_id, base_date, status, json_details, remarks
    • json_details: check_in, check_out, gps_data, work_minutes 등

Phase 2: Employee API

수정된 파일:

  • app/Models/Tenants/TenantUserProfile.php - employee_status, json_extra 헬퍼 추가

추가된 파일:

  • app/Services/EmployeeService.php - 사원 CRUD, 통계, 계정 생성
  • app/Http/Controllers/Api/V1/EmployeeController.php
  • app/Http/Requests/Employee/ (5개 FormRequest)
    • IndexRequest, StoreRequest, UpdateRequest, BulkDeleteRequest, CreateAccountRequest
  • app/Swagger/v1/EmployeeApi.php - 8개 엔드포인트 문서

API 엔드포인트 (8개):

Method Endpoint 설명
GET /v1/employees 사원 목록
POST /v1/employees 사원 등록
GET /v1/employees/stats 사원 통계
GET /v1/employees/{id} 사원 상세
PATCH /v1/employees/{id} 사원 수정
DELETE /v1/employees/{id} 사원 삭제
POST /v1/employees/bulk-delete 일괄 삭제
POST /v1/employees/{id}/create-account 계정 생성

Phase 3: Department Tree API

수정된 파일:

  • app/Services/DepartmentService.php - getTree(), buildTreeNode() 추가
  • app/Http/Controllers/Api/V1/DepartmentController.php - tree() 액션 추가
  • routes/api.php - /v1/departments/tree 라우트 추가

Phase 4: Attendance API

추가된 파일:

  • app/Models/Tenants/Attendance.php - 근태 모델

    • BelongsToTenant, SoftDeletes
    • json_details 헬퍼 (check_in, check_out, gps_data, work_minutes 등)
    • 스코프: onDate, betweenDates, forUser, withStatus
  • app/Services/AttendanceService.php - 근태 CRUD, 출퇴근, 월간 통계

  • app/Http/Controllers/Api/V1/AttendanceController.php

  • app/Http/Requests/Attendance/ (6개 FormRequest)

    • IndexRequest, StoreRequest, UpdateRequest, CheckInRequest, CheckOutRequest, MonthlyStatsRequest
  • app/Swagger/v1/AttendanceApi.php - 9개 엔드포인트 문서

API 엔드포인트 (9개):

Method Endpoint 설명
GET /v1/attendances 근태 목록
POST /v1/attendances 근태 등록
GET /v1/attendances/monthly-stats 월간 통계
POST /v1/attendances/check-in 출근 기록
POST /v1/attendances/check-out 퇴근 기록
GET /v1/attendances/{id} 근태 상세
PATCH /v1/attendances/{id} 근태 수정
DELETE /v1/attendances/{id} 근태 삭제
POST /v1/attendances/bulk-delete 일괄 삭제

검증 결과

  • Pint 코드 포맷팅 완료 (13개 파일 수정)
  • 마이그레이션 실행 완료 (Batch 48)
  • 라우트 등록 확인 (Employee 8개, Attendance 9개, Department Tree 1개)
  • Swagger 문서 생성 완료

수정된 파일 목록

routes/api.php:

  • EmployeeController, AttendanceController import 추가
  • Employee API 라우트 그룹 (8개)
  • Attendance API 라우트 그룹 (9개)
  • Department /tree 라우트 추가

버그 수정:

  • app/Models/Tenants/Attendance.php - BelongsToTenant 경로 수정
    • App\Models\Scopes\BelongsToTenantApp\Traits\BelongsToTenant

다음 작업

  • React 프론트엔드 연동
  • 휴가 관리 API 구현 (향후)

2025-12-08 (일) - Flow Tester Error #61 해결 (GET query 파라미터 처리)

문제

  • GET /pricing/cost 요청 시 422 에러 발생
  • 에러: item_iditem_type_code가 필수 항목인데 누락

원인

  • Flow 정의에 query 필드로 파라미터 정의되어 있음
  • FlowExecutorquery 필드를 처리하지 않고 body만 처리
  • GET 요청은 query string으로 파라미터를 전달해야 하는데 누락됨

해결

수정 파일: mng/app/Services/FlowTester/FlowExecutor.php

// Line 226: query 변수 바인딩 추가
$query = $this->binder->bind($step['query'] ?? []);

// Line 230-234: HTTP 요청에 query 옵션 전달
$response = $this->httpClient->request($method, $endpoint, [
    'headers' => $headers,
    'body' => $body,
    'query' => $query,  // 추가
]);

// 결과 로그에도 query 정보 포함
'request' => [
    'method' => $method,
    'endpoint' => $endpoint,
    'headers' => $headers,
    'body' => $body,
    'query' => $query,  // 추가
],

검증

  • PHP 문법 검사: 통과

2025-12-08 (일) - Flow Tester Error #60 해결 (중복 테스트 데이터 삭제)

문제

  • Flow Tester 재실행 시 error.duplicate_key 오류 발생
  • 이전 테스트 실행(Error #59)이 중간에 실패하면서 테스트 데이터가 남아있음
  • 동적 날짜({{$date}})가 정상 작동 중이지만, 같은 날 재실행 시 중복 발생

원인

  • Error #59 실행 시 create_price 단계까지 진행 후 실패
  • cleanup 단계(delete_price)에 도달하지 못해 테스트 데이터 잔류
  • checkDuplicate() 메서드가 기존 데이터 발견

해결

# 잔류 테스트 데이터 삭제
docker exec -i sam-api-1 php artisan tinker --execute="
  use App\Models\Products\Price;
  Price::where('effective_from', '2025-12-08')->delete();
"
# 결과: 1건 삭제 완료

추가 권장사항

  • Flow Tester 재실행 전 cleanup 또는 setup 스크립트 추가 고려
  • 또는 effective_from에 {{$uuid}}{{$timestamp}}를 조합하여 고유성 보장

2025-12-08 (일) - User 모델 경로 오류 수정 (500 에러 해결)

문제

  • Flow Tester #59 실행 시 GET /pricing/{id}/revisions 에서 500 에러
  • 에러: Class "App\Models\User" not found

원인

  • PriceRevision.php, Board.php에서 잘못된 User 모델 경로 참조
  • 실제 경로: App\Models\Members\User

수정된 파일

  • app/Models/Products/PriceRevision.php - \App\Models\User\App\Models\Members\User
  • app/Models/Boards/Board.php - use App\Models\Useruse App\Models\Members\User

2025-12-08 (일) - Flow Tester 동적 날짜 변수 적용 (duplicate key 해결)

문제

  • Flow Tester #58 실행 시 error.duplicate_key 에러 발생
  • 원인: 하드코딩된 effective_from: "2025-01-01"로 인해 이전 테스트 데이터와 중복

해결

{{$date}} 동적 변수를 사용하여 매번 테스트 시 오늘 날짜 사용

수정 내용 (Flow ID: 8 - 단가 관리 CRUD 테스트)

스텝 필드 변경 전 변경 후
create_price effective_from "2025-01-01" "{{$date}}"
create_price effective_to "2025-12-31" "2099-12-31"
create_price_for_finalize effective_from "2025-01-01" "{{$date}}"
get_cost date "2025-06-15" "{{$date}}"
by_items date "2025-06-15" "{{$date}}"

VariableBinder 지원 변수

변수 설명 예시
{{$date}} 현재 날짜 (Y-m-d) 2025-12-08
{{$datetime}} 현재 날짜시간 (Y-m-d H:i:s) 2025-12-08 14:30:00
{{$timestamp}} Unix 타임스탬프 1733637000
{{$uuid}} 랜덤 UUID 550e8400-e29b-41d4-a716-...
{{$random:N}} N자리 랜덤 숫자 123456
{{$faker.xxx}} Faker 랜덤 데이터 회사명, 이름 등

해결 원리

  • 매번 테스트 시 오늘 날짜가 사용되어 다른 날의 중복 데이터와 충돌 없음
  • 플로우 마지막에 delete_pricecleanup_finalized 스텝이 테스트 데이터 정리
  • 별도의 cleanup 스텝 없이도 반복 실행 가능

2025-12-08 (일) - Flow Tester HTTP 상태 코드 수정

문제

  • Flow Tester POST /pricing 요청에서 예상 상태 코드 201, 실제 200 반환
  • HTTP 표준: POST 리소스 생성 시 201 Created 반환 필요

수정 내용

ApiResponse 클래스 개선:

  • ApiResponse::success(): $statusCode 파라미터 추가 (기본값 200)
  • ApiResponse::handle(): 콜백에서 statusCode 키로 상태 코드 지정 가능

PricingController 수정:

  • store() 메서드: 'statusCode' => 201 반환

수정된 파일

  • app/Helpers/ApiResponse.php - 상태 코드 파라미터 추가
  • app/Http/Controllers/Api/V1/PricingController.php - store 201 반환

사용 예시

// Controller에서 201 반환
return ApiResponse::handle(function () use ($request) {
    $data = $this->service->store($request->validated());
    return ['data' => $data, 'message' => __('message.created'), 'statusCode' => 201];
});

검증 필요

  • Flow Tester 재실행하여 201 응답 확인
  • duplicate key 에러 해결 (동적 날짜 변수 적용)

남은 작업 (TODO)

다른 Controller store 메서드 일괄 수정 (27개):

  • AdminController, BoardController, CategoryController, CategoryFieldController
  • CategoryTemplateController, ClassificationController, ClientGroupController
  • CommonController, DesignModelController, EstimateController, FolderController
  • ItemsController, MaterialController, MenuController, ModelSetController
  • PostController, QuoteController, RoleController, TenantController
  • TenantOptionGroupController, TenantOptionValueController, TenantStatFieldController
  • ItemMaster 하위: CustomTabController, ItemBomItemController, ItemFieldController
  • ItemMaster 하위: ItemSectionController, UnitOptionController

2025-12-08 (일) - 단가 관리 API 전면 재설계 (prices + price_revisions)

작업 목표

  • price_historiesprices + price_revisions 구조로 전면 재설계
  • 원가 조회 시 수입검사 입고단가 우선, 표준원가 폴백 로직 구현
  • 가격 확정(finalize) 기능으로 불변성 보장
  • 리비전 관리로 변경 이력 추적

테이블 구조 변경

prices (단가 마스터):

컬럼 설명
item_type_code 품목유형 (PRODUCT/MATERIAL)
item_id 품목 ID
client_group_id 고객그룹 ID (NULL=기본가)
purchase_price 매입단가 (표준원가)
processing_cost 가공비
loss_rate LOSS율 (%)
margin_rate 마진율 (%)
sales_price 판매단가
rounding_rule 반올림 규칙 (round/ceil/floor)
rounding_unit 반올림 단위 (1,10,100,1000)
effective_from/to 적용 기간
status 상태 (draft/active/inactive/finalized)
is_final 최종 확정 여부

price_revisions (변경 이력):

컬럼 설명
price_id 단가 FK
revision_number 리비전 번호
changed_at 변경 일시
changed_by 변경자 ID
change_reason 변경 사유
before_snapshot 변경 전 JSON
after_snapshot 변경 후 JSON

생성된 파일

마이그레이션 (4개):

  • 2025_12_08_154633_create_prices_table.php
  • 2025_12_08_154634_create_price_revisions_table.php
  • 2025_12_08_154635_migrate_price_histories_to_prices.php
  • 2025_12_08_154636_drop_price_histories_table.php

모델 (2개):

  • app/Models/Price.php - BelongsToTenant, SoftDeletes
  • app/Models/PriceRevision.php

서비스 (1개):

  • app/Services/PricingService.php
    • index, show, store, update, destroy (CRUD)
    • byItems: 다중 품목 단가 조회
    • revisions: 변경 이력 조회
    • finalize: 가격 확정 (불변 처리)
    • getCost: 원가 조회 (receipt > standard 폴백)

FormRequest (5개):

  • app/Http/Requests/Pricing/PriceIndexRequest.php
  • app/Http/Requests/Pricing/PriceStoreRequest.php
  • app/Http/Requests/Pricing/PriceUpdateRequest.php
  • app/Http/Requests/Pricing/PriceByItemsRequest.php
  • app/Http/Requests/Pricing/PriceCostRequest.php

Swagger (1개):

  • app/Swagger/v1/PricingApi.php - 전면 재작성

삭제된 파일

  • app/Models/PriceHistory.php

API 엔드포인트 (9개)

Method Endpoint 설명
GET /api/v1/pricing 단가 목록 (페이지네이션)
POST /api/v1/pricing 단가 생성
GET /api/v1/pricing/cost 원가 조회 (receipt > standard)
POST /api/v1/pricing/by-items 다중 품목 단가 조회
GET /api/v1/pricing/{id} 단가 상세
PUT /api/v1/pricing/{id} 단가 수정
DELETE /api/v1/pricing/{id} 단가 삭제
POST /api/v1/pricing/{id}/finalize 가격 확정
GET /api/v1/pricing/{id}/revisions 변경 이력 조회

원가 조회 로직 (getCost)

1순위: 자재인 경우 → material_receipts.purchase_price_excl_vat
       (수입검사 완료된 최신 입고단가)

2순위: prices.purchase_price (표준원가)

총원가 계산:
  total_cost = (purchase_price + processing_cost) × (1 + loss_rate/100)

판매가 계산:
  sales_price = round(total_cost × (1 + margin_rate/100), rounding_unit, rounding_rule)

검증 결과

  • PHP 문법 검사: 모든 파일 통과
  • Pint 코드 포맷팅: 14개 파일 수정 완료
  • Swagger 문서 생성: 완료

다음 작업

  • 마이그레이션 실행 (php artisan migrate)
  • API 테스트 (Swagger UI)
  • React 프론트엔드 연동

참조 문서

  • docs/front/[API-2025-12-08] pricing-api-enhancement-request.md
  • docs/rules/pricing-policy.md

2025-12-04 (수) - 견적 API Phase 3: Controller + FormRequest + Routes + Swagger 완료

작업 목표

  • 견적 API Phase 3 Controller Layer 구현
  • 16개 API 엔드포인트 구현 완료

생성된 파일

Controller (1개):

  • app/Http/Controllers/Api/V1/QuoteController.php
    • 16개 메서드: index, show, store, update, destroy, bulkDestroy, finalize, cancelFinalize, convertToOrder, previewNumber, calculate, calculationSchema, generatePdf, sendEmail, sendKakao, sendHistory
    • 4개 Service DI: QuoteService, QuoteNumberService, QuoteCalculationService, QuoteDocumentService
    • ApiResponse::handle() 패턴 적용

FormRequest (7개):

파일 설명
QuoteIndexRequest.php 목록 조회 파라미터 검증
QuoteStoreRequest.php 견적 생성 검증 (items 배열 포함)
QuoteUpdateRequest.php 견적 수정 검증
QuoteBulkDeleteRequest.php 일괄 삭제 IDs 검증
QuoteCalculateRequest.php 자동산출 입력값 검증
QuoteSendEmailRequest.php 이메일 발송 검증
QuoteSendKakaoRequest.php 카카오 발송 검증

Swagger (1개):

  • app/Swagger/v1/QuoteApi.php
    • 12개 스키마: Quote, QuoteItem, QuotePagination, QuoteCreateRequest, QuoteUpdateRequest 등
    • 16개 엔드포인트 문서화

API 엔드포인트 (16개)

Method Endpoint 설명
GET /api/v1/quotes 견적 목록 (페이지네이션)
POST /api/v1/quotes 견적 생성
GET /api/v1/quotes/number/preview 견적번호 미리보기
POST /api/v1/quotes/calculate 자동산출
GET /api/v1/quotes/calculate/schema 산출 스키마 조회
DELETE /api/v1/quotes/bulk 일괄 삭제
GET /api/v1/quotes/{id} 견적 상세
PUT /api/v1/quotes/{id} 견적 수정
DELETE /api/v1/quotes/{id} 견적 삭제
POST /api/v1/quotes/{id}/finalize 확정
POST /api/v1/quotes/{id}/cancel-finalize 확정 취소
POST /api/v1/quotes/{id}/convert 주문 전환
GET /api/v1/quotes/{id}/pdf PDF 생성
POST /api/v1/quotes/{id}/send/email 이메일 발송
POST /api/v1/quotes/{id}/send/kakao 카카오 발송
GET /api/v1/quotes/{id}/send/history 발송 이력

검증 결과

  • PHP 문법 검사: 9개 파일 통과
  • Pint 코드 포맷팅: 완료
  • Swagger 문서 생성: 완료
  • 라우트 등록: 16개 라우트 확인

다음 작업 (Phase 4)

  • 단위 테스트 작성
  • 통합 테스트 작성
  • 마이그레이션 실행 및 실제 데이터 검증

2025-12-04 (수) - 견적 API Phase 2: Service Layer 구현 완료

작업 목표

  • 견적 API Phase 2 Service Layer 구현
  • 5개 Service 파일 생성 완료

생성된 파일

Service Layer (5개):

파일 설명 주요 기능
QuoteService.php 견적 CRUD + 상태관리 index, show, store, update, destroy, bulkDestroy, finalize, cancelFinalize, convertToOrder
QuoteNumberService.php 견적번호 채번 generate, preview, validate, parse, isUnique
FormulaEvaluatorService.php 수식 평가 엔진 validateFormula, evaluate, evaluateMultiple, evaluateRange, evaluateMapping
QuoteCalculationService.php 견적 자동산출 calculate, preview, recalculate, getInputSchema
QuoteDocumentService.php 문서 생성/발송 generatePdf, sendEmail, sendKakao, getSendHistory

견적번호 형식

KD-{PREFIX}-{YYMMDD}-{SEQ}
예: KD-SC-251204-01 (스크린), KD-ST-251204-01 (철재)

FormulaEvaluatorService 지원 함수

  • 수학: SUM, ROUND, CEIL, FLOOR, ABS, MIN, MAX
  • 논리: IF, AND, OR, NOT

QuoteCalculationService 입력 스키마

공통 입력:

  • W0: 개구부 폭 (mm)
  • H0: 개구부 높이 (mm)
  • QTY: 수량

스크린 제품 추가:

  • INSTALL_TYPE: 설치 유형 (wall/ceiling/floor)
  • MOTOR_TYPE: 모터 유형 (standard/heavy)
  • CONTROL_TYPE: 제어 방식 (switch/remote/smart)
  • CHAIN_SIDE: 체인 위치 (left/right)

철재 제품 추가:

  • MATERIAL: 재질 (ss304/ss316/galvanized)
  • THICKNESS: 두께 (mm)
  • FINISH: 표면처리 (hairline/mirror/matte)
  • WELDING: 용접 방식 (tig/mig/spot)

i18n 키 추가

에러 메시지 (error.php):

  • quote_not_found, quote_not_editable, quote_not_deletable
  • quote_not_finalizable, quote_not_finalized, quote_already_converted
  • quote_not_convertible, quote_email_not_found, quote_phone_not_found
  • formula_empty, formula_parentheses_mismatch, formula_unsupported_function, formula_calculation_error

성공 메시지 (message.php):

  • quote.fetched, quote.created, quote.updated, quote.deleted
  • quote.bulk_deleted, quote.finalized, quote.finalize_cancelled
  • quote.converted, quote.calculated, quote.pdf_generated
  • quote_email_sent, quote_kakao_sent

검증 결과

  • PHP 문법 검사: 5개 파일 통과
  • Pint 코드 포맷팅: 완료

다음 작업 (Phase 3)

  • QuoteController.php 생성
  • FormRequest 생성 (QuoteStoreRequest, QuoteUpdateRequest 등)
  • Swagger 문서 작성 (QuoteApi.php)
  • 라우트 등록

2025-12-04 (수) - 거래처 API 2차 필드 추가 및 견적 API 계획 업데이트

작업 목표

  • 거래처 API에 2차 필드 추가 (17개 신규 필드)
  • 견적 API 변경사항 분석 및 계획 문서 업데이트

거래처 API 2차 필드 추가

추가된 필드 (7개 섹션, 20개 필드):

섹션 필드 설명
거래처 유형 client_type 매입/매출/매입매출
연락처 mobile, fax 모바일, 팩스
담당자 manager_name, manager_tel, system_manager 담당자 정보
발주처 설정 account_id, account_password, purchase_payment_day, sales_payment_day 계정 및 결제일
약정 세금 tax_agreement, tax_amount, tax_start_date, tax_end_date 세금 약정 정보
악성채권 bad_debt, bad_debt_amount, bad_debt_receive_date, bad_debt_end_date, bad_debt_progress 채권 정보
기타 memo 메모

수정된 파일:

  • database/migrations/2025_12_04_205603_add_extended_fields_to_clients_table.php (NEW)
  • app/Models/Orders/Client.php - fillable, casts, hidden 업데이트
  • app/Http/Requests/Client/ClientStoreRequest.php - 검증 규칙 추가
  • app/Http/Requests/Client/ClientUpdateRequest.php - 검증 규칙 추가
  • app/Services/ClientService.php - store/update 검증 추가
  • app/Swagger/v1/ClientApi.php - 3개 스키마 업데이트

견적 API 계획 업데이트

신규 요청 - 문서 발송 API (Section 3.5):

Method Endpoint 설명
POST /api/v1/quotes/{id}/send/email 이메일 발송
POST /api/v1/quotes/{id}/send/fax 팩스 발송
POST /api/v1/quotes/{id}/send/kakao 카카오톡 발송

계획 문서 업데이트 내용:

  • Phase 2: QuoteDocumentService 추가
  • Phase 3: QuoteSendEmailRequest, QuoteSendFaxRequest, QuoteSendKakaoRequest 추가
  • Service 5개, FormRequest 8개로 조정

Git 커밋

commit d164bb4
feat: [client] 거래처 API 2차 필드 추가 및 견적 계획 업데이트

다음 작업

  • 견적 API Phase 2: Service Layer 구현

2025-12-04 (수) - 견적수식 시드 데이터 구현

작업 목표

  • design/src/components/utils/formulaSampleData.ts의 데이터를 MNG에서 관리할 수 있도록 시드 데이터 구현
  • 26개 수식 규칙, 11개 카테고리를 DB에 입력

추가된 파일

Seeder (2개):

  • database/seeders/QuoteFormulaCategorySeeder.php

    • 11개 카테고리 시드 (OPEN_SIZE, MAKE_SIZE, AREA, WEIGHT, GUIDE_RAIL, CASE, MOTOR, CONTROLLER, EDGE_WING, INSPECTION, PRICE_FORMULA)
    • updateOrInsert 패턴으로 멱등성 보장
  • database/seeders/QuoteFormulaSeeder.php

    • 29개 수식 시드 (input 2개, calculation 18개, range 3개, mapping 1개, 단가수식 8개)
    • 8개 범위 데이터 (quote_formula_ranges)
    • 카테고리 코드 → ID 매핑으로 FK 참조

시드 데이터 상세

카테고리 코드 수식 수 설명
오픈사이즈 OPEN_SIZE 2 W0, H0 입력
제작사이즈 MAKE_SIZE 4 W1/H1 (스크린/철재)
면적 AREA 1 W1 × H1 / 1000000
중량 WEIGHT 2 스크린/철재 중량 계산
가이드레일 GUIDE_RAIL 5 길이, 자동선택, 설치유형별 수량
케이스 CASE 3 사이즈, 자재 자동선택
모터 MOTOR 1 중량 기반 자동선택
제어기 CONTROLLER 1 유형별 자동선택
마구리 EDGE_WING 1 날개 수량 계산
검사 INSPECTION 1 검사비 고정
단가수식 PRICE_FORMULA 8 품목별 단가 계산

실행 명령어

# 순서대로 실행
php artisan db:seed --class=QuoteFormulaCategorySeeder
php artisan db:seed --class=QuoteFormulaSeeder

검증 결과

  • 카테고리: 11개 생성 완료
  • 수식: 29개 생성 완료
  • 범위 데이터: 8개 생성 완료

참조 문서

  • mng/docs/QUOTE_FORMULA_SEED_PLAN.md - 구현 계획서
  • design/src/components/utils/formulaSampleData.ts - 소스 데이터

2025-12-02 (월) - 메뉴 통합관리 시스템 구현 (Phase 1-2)

작업 목표

  • 글로벌 메뉴-테넌트 메뉴 연결 시스템 구현
  • Phase 1: DB 스키마 변경 및 모델 수정
  • Phase 2: 서비스 및 API 엔드포인트 개발

Phase 1 완료 (DB 스키마 및 모델)

추가된 파일:

  • database/migrations/2025_12_02_100000_add_global_menu_link_columns_to_menus_table.php
    • global_menu_id 컬럼: 원본 글로벌 메뉴 ID
    • is_customized 컬럼: 테넌트 커스터마이징 여부
    • 인덱스 추가

수정된 파일:

  • app/Models/Commons/Menu.php

    • fillable: global_menu_id, is_customized 추가
    • casts: is_customized => boolean 등 추가
    • 관계 메서드: globalMenu(), tenantMenus()
    • 헬퍼 메서드: isGlobal(), isClonedFromGlobal(), isCustomized()
    • 스코프: scopeGlobal(), scopeActive(), scopeVisible(), scopeRoots()
    • getSyncFields(): 동기화 비교 대상 필드 목록
  • app/Services/MenuBootstrapService.php

    • cloneGlobalMenusForTenant(): global_menu_id 저장 추가
    • 활성 메뉴만 복제 (is_active=true)

Phase 2 완료 (서비스 및 API)

추가된 파일:

  • app/Services/GlobalMenuService.php (신규)

    • 글로벌 메뉴 CRUD (tenant_id = NULL)
    • syncToAllTenants(): 특정 메뉴를 모든 테넌트에 동기화
    • stats(): 글로벌 메뉴 통계
  • app/Services/MenuSyncService.php (신규)

    • 동기화 상태 상수: NEW, UP_TO_DATE, UPDATABLE, CUSTOMIZED, DELETED
    • getSyncStatus(): 동기화 상태 목록 조회
    • syncMenus(): 선택 동기화 (신규/업데이트)
    • importNewMenus(): 신규 글로벌 메뉴 일괄 가져오기
    • syncUpdates(): 변경된 메뉴 일괄 업데이트 (커스텀 제외)
    • getAvailableGlobalMenus(): 복제 가능한 글로벌 메뉴 목록
  • app/Http/Controllers/Api/Admin/GlobalMenuController.php (신규)

    • 시스템 관리자용 글로벌 메뉴 관리
    • index, tree, show, store, update, destroy, reorder
    • syncToTenants, stats

수정된 파일:

  • app/Services/MenuService.php

    • update(): 글로벌 복제 메뉴 수정 시 is_customized=true 자동 설정
    • restore(): 삭제된 메뉴 복원 추가
    • trashedList(): 삭제된 메뉴 목록 조회 추가
  • app/Http/Controllers/Api/V1/MenuController.php

    • MenuSyncService DI 추가
    • restore, trashed, availableGlobal, syncStatus, sync, syncNew, syncUpdates 메서드 추가
  • routes/api.php

    • GlobalMenuController use 문 추가
    • 테넌트 메뉴 동기화 라우트 6개 추가 (trashed, available-global, sync-status, sync, sync-new, sync-updates, restore)
    • 글로벌 메뉴 관리 라우트 9개 추가 (admin/global-menus/*)

API 엔드포인트

테넌트 메뉴 동기화 (V1):

Method Path 설명
GET /v1/menus/trashed 삭제된 메뉴 목록
GET /v1/menus/available-global 복제 가능한 글로벌 메뉴
GET /v1/menus/sync-status 동기화 상태 조회
POST /v1/menus/sync 선택 동기화
POST /v1/menus/sync-new 신규 메뉴 일괄 가져오기
POST /v1/menus/sync-updates 변경된 메뉴 일괄 업데이트
POST /v1/menus/{id}/restore 삭제된 메뉴 복원

글로벌 메뉴 관리 (Admin):

Method Path 설명
GET /v1/admin/global-menus 글로벌 메뉴 목록
POST /v1/admin/global-menus 글로벌 메뉴 생성
GET /v1/admin/global-menus/tree 글로벌 메뉴 트리
GET /v1/admin/global-menus/stats 통계 조회
POST /v1/admin/global-menus/reorder 순서 변경
GET /v1/admin/global-menus/{id} 단건 조회
PUT /v1/admin/global-menus/{id} 수정
DELETE /v1/admin/global-menus/{id} 삭제
POST /v1/admin/global-menus/{id}/sync-to-tenants 모든 테넌트에 동기화

검증 결과

  • PHP 문법 검사: 모든 파일 통과
  • 라우트 등록: 9개 글로벌 메뉴 + 7개 테넌트 동기화 라우트 확인

다음 작업 (Phase 3-4)

  • Phase 3: MNG 글로벌 메뉴 관리 화면
  • Phase 3: MNG 동기화 센터 화면
  • Phase 4: 마이그레이션 실행 및 테스트

2025-12-01 (일) - 메뉴 통합관리 시스템 설계

작업 목표

  • PDF 기획서(SAM_ERP_인사관리전자결재_Storyboard)에서 메뉴 추출
  • 글로벌 메뉴와 테넌트 메뉴 간의 연결(링크) 시스템 설계
  • 메뉴 추가 SQL 쿼리 생성

추가된 파일

  • claudedocs/MENU_INTEGRATION_SYSTEM_DESIGN.md (신규)

    • 글로벌-테넌트 메뉴 연결 시스템 설계서
    • global_menu_id, is_customized 컬럼 추가 계획
    • API 엔드포인트 설계 (글로벌/테넌트 메뉴 관리)
    • MNG 화면 설계 (복제, 동기화 기능)
    • 구현 Phase 1~4 계획
  • claudedocs/MENU_INSERT_QUERIES.sql (신규)

    • PDF 기획서 기반 신규 메뉴 23개 INSERT 쿼리
    • 인사관리 (근태/휴가/급여)
    • 전자결재 (기안함/결재함/참조함)
    • 게시판, 보고서, 계정정보, 회사정보, 구독관리, 결제내역, 고객센터
    • 기준정보 관리 하위 8개 메뉴

정책 결정 사항

항목 결정 내용
글로벌 메뉴 삭제 시 테넌트 메뉴 유지 (global_menu_id = NULL)
활성 메뉴 (is_active=1) 새 테넌트 생성 시 자동 복사
비활성 메뉴 (is_active=0) 테넌트가 수동으로 복제 가능
숨김 메뉴 (hidden=1) 복사되지만 테넌트에서 안 보임
기존 데이터 신규 테넌트부터 적용

다음 작업 (Phase별)

  • Phase 1: 마이그레이션 (global_menu_id, is_customized)
  • Phase 1: Menu 모델 수정
  • Phase 1: MenuBootstrapService 수정
  • Phase 2: GlobalMenuService 생성
  • Phase 2: MenuService 메서드 추가
  • Phase 2: API 엔드포인트 추가
  • Phase 3: MNG 글로벌 메뉴 관리 화면
  • Phase 3: MNG 테넌트 메뉴 관리 화면 개선
  • Phase 4: 테스트

Git 커밋

commit d7fdfa8
docs: 메뉴 통합관리 시스템 설계서 및 SQL 쿼리 추가

참고 문서

  • PDF: SAM_ERP_인사관리전자결재_Storyboard_D0.6_251201.pdf
  • 설계서: claudedocs/MENU_INTEGRATION_SYSTEM_DESIGN.md

2025-11-27 (수) - 시스템 게시판 기능 확장

작업 목표

  • 기존 boards 테이블에 시스템 게시판 지원 추가
  • mng에서 시스템 게시판 생성, sam에서 테넌트 게시판 + 시스템 게시판 조회

수정된 파일

Migration:

  • database/migrations/2025_11_27_205429_add_system_fields_to_boards_table.php (NEW)
    • tenant_id nullable 변경
    • is_system boolean 컬럼 추가
    • board_type VARCHAR(50) 컬럼 추가
    • deleted_at, deleted_by SoftDeletes 추가
    • 인덱스 추가

Model:

  • app/Models/Boards/Board.php

    • SoftDeletes 추가
    • fillable, casts 업데이트
    • scopeAccessible(tenantId), scopeSystemOnly(), scopeTenantOnly(tenantId) 스코프 추가
    • 권한 헬퍼 메서드 (canRead, canWrite, canManage)
  • app/Models/Boards/BoardSetting.php

    • casts, fillable 업데이트
    • getMeta() 헬퍼 추가

Service:

  • app/Services/Boards/BoardService.php (NEW)
    • 시스템/테넌트 게시판 CRUD
    • 필드 관리 (add, update, delete, reorder)
    • 유틸리티 메서드

DB 스키마 변경

-- boards 테이블 추가 컬럼
is_system TINYINT(1) DEFAULT 0 COMMENT '시스템 게시판 여부'
board_type VARCHAR(50) NULL COMMENT '게시판 유형'
deleted_at TIMESTAMP NULL
deleted_by BIGINT UNSIGNED NULL

다음 작업

  • mng 게시판 관리 화면 개발
  • sam API 개발 (Swagger 포함)
  • 검증 및 테스트

2025-11-27 (수) - sam API 게시판/게시글 API 개발

작업 목표

  • 테넌트용 게시판/게시글 V1 API 개발
  • Swagger 문서 작성

추가된 파일

Service:

  • app/Services/Boards/PostService.php (NEW)
    • 게시글 CRUD (boardCode 기반)
    • 댓글 CRUD
    • 커스텀 필드 관리
    • 조회수 증가

Controller:

  • app/Http/Controllers/Api/V1/BoardController.php (NEW)

    • index: 접근 가능한 게시판 목록 (시스템 + 테넌트)
    • tenantBoards: 테넌트 게시판만
    • show: 게시판 상세 (코드 기반)
    • store/update/destroy: 테넌트 게시판 CRUD
    • fields: 게시판 필드 목록
  • app/Http/Controllers/Api/V1/PostController.php (NEW)

    • index/show/store/update/destroy: 게시글 CRUD
    • comments/storeComment/updateComment/destroyComment: 댓글 CRUD

FormRequest:

  • app/Http/Requests/Boards/BoardStoreRequest.php (NEW)
  • app/Http/Requests/Boards/BoardUpdateRequest.php (NEW)
  • app/Http/Requests/Boards/PostStoreRequest.php (NEW)
  • app/Http/Requests/Boards/PostUpdateRequest.php (NEW)
  • app/Http/Requests/Boards/CommentStoreRequest.php (NEW)

Swagger:

  • app/Swagger/v1/BoardApi.php (NEW)

    • Board, BoardField 스키마
    • BoardCreateRequest, BoardUpdateRequest 스키마
    • 7개 엔드포인트 문서화
  • app/Swagger/v1/PostApi.php (NEW)

    • Post, PostPagination, Comment 스키마
    • PostCreateRequest, PostUpdateRequest, CommentCreateRequest 스키마
    • 9개 엔드포인트 문서화

수정된 파일

  • routes/api.php
    • BoardController, PostController import 추가
    • /v1/boards 프리픽스로 16개 라우트 등록

API 엔드포인트 (16개)

게시판 관리:

Method Path 설명
GET /v1/boards 접근 가능한 게시판 목록
GET /v1/boards/tenant 테넌트 게시판만
POST /v1/boards 테넌트 게시판 생성
GET /v1/boards/{code} 게시판 상세 (코드 기반)
PUT /v1/boards/{id} 테넌트 게시판 수정
DELETE /v1/boards/{id} 테넌트 게시판 삭제
GET /v1/boards/{code}/fields 게시판 필드 목록

게시글 관리:

Method Path 설명
GET /v1/boards/{code}/posts 게시글 목록
POST /v1/boards/{code}/posts 게시글 작성
GET /v1/boards/{code}/posts/{id} 게시글 상세
PUT /v1/boards/{code}/posts/{id} 게시글 수정
DELETE /v1/boards/{code}/posts/{id} 게시글 삭제

댓글 관리:

Method Path 설명
GET /v1/boards/{code}/posts/{postId}/comments 댓글 목록
POST /v1/boards/{code}/posts/{postId}/comments 댓글 작성
PUT /v1/boards/{code}/posts/{postId}/comments/{commentId} 댓글 수정
DELETE /v1/boards/{code}/posts/{postId}/comments/{commentId} 댓글 삭제

검증 결과

  • PHP 문법 검사: 통과
  • Pint 코드 포맷팅: 완료
  • Swagger 문서 생성: 완료
  • 라우트 등록: 16개 라우트 확인

2025-11-27 (수) - ItemMaster API group_id(계층번호) 추가 및 Swagger 보완

작업 목표

  • FormRequest에 누락된 group_id 필드 추가
  • Swagger 스키마에 group_id description="계층번호" 추가
  • API 문서 정확도 개선

배경

  • POST /api/v1/item-master/pages/{pageId}/sections Swagger 문서 점검 중 group_id 누락 발견
  • Service에서 $data['group_id'] ?? 1로 사용하지만 FormRequest와 Swagger에 미정의
  • group_id는 "계층번호"로 통일하여 주석 및 description 작성

수정된 파일 (4개)

FormRequest (group_id 필드 추가):

  • app/Http/Requests/ItemMaster/ItemSectionStoreRequest.php
  • app/Http/Requests/ItemMaster/ItemFieldStoreRequest.php
  • app/Http/Requests/ItemMaster/ItemBomItemStoreRequest.php

Swagger:

  • app/Swagger/v1/ItemMasterApi.php

Swagger 스키마 업데이트 상세

모델 스키마 (3개) - description="계층번호" 추가:

  • ItemSection
  • ItemField
  • ItemBomItem

Request 스키마 (6개) - group_id 추가 또는 description 추가:

  • ItemSectionStoreRequest - group_id 신규 추가
  • ItemFieldStoreRequest - group_id 신규 추가
  • ItemBomItemStoreRequest - group_id 신규 추가
  • IndependentSectionStoreRequest - description 추가
  • IndependentFieldStoreRequest - description 추가
  • IndependentBomItemStoreRequest - description 추가

12개 EntityRelationship 엔드포인트

EntityRelationshipApi.php에 이미 문서화 완료 확인:

  • POST/DELETE /pages/{pageId}/link-section, /unlink-section/{sectionId}
  • POST/DELETE /pages/{pageId}/link-field, /unlink-field/{fieldId}
  • GET /pages/{pageId}/relationships, /structure
  • POST/DELETE /sections/{sectionId}/link-field, /unlink-field/{fieldId}
  • POST/DELETE /sections/{sectionId}/link-bom, /unlink-bom/{bomId}
  • GET /sections/{sectionId}/relationships
  • POST /relationships/reorder

검증 결과

  • Pint 코드 포맷팅: 통과
  • Swagger 문서 생성: 완료

Git 커밋

commit 4f78eed
feat: ItemMaster API group_id(계층번호) 추가 및 Swagger 보완

2025-11-26 (화) - AccessService permission_overrides 테이블 사용으로 수정

주요 작업

  • API AccessService가 존재하지 않는 테이블(user_permission_overrides, department_permissions)을 참조하던 문제 수정
  • 실제 DB의 permission_overrides 테이블을 사용하도록 수정

수정된 파일:

  • app/Services/Authz/AccessService.php
    • hasUserOverride(): user_permission_overridespermission_overrides (model_type='App\Models\Members\User')
    • departmentAllows(): department_permissionspermission_overrides (model_type='App\Models\Tenants\Department')
    • 필드명 변경: is_allowedeffect, user_idmodel_id

기술 상세:

permission_overrides 테이블 구조:

  • model_type: 폴리모픽 타입 (User, Department)
  • model_id: 대상 ID
  • permission_id: 권한 ID
  • effect: 0=DENY, 1=ALLOW
  • effective_from, effective_to: 유효 기간

코드 품질:

  • PHP 문법 검사 통과
  • Pint 포맷팅 통과

2025-11-26 (화) - Item Master 독립 엔티티 API 추가 완료

작업 목표

  • 독립 엔티티(섹션, 필드, BOM) CRUD API 10개 추가
  • SectionTemplate 모델 삭제 → ItemSection.is_template 플래그로 통합
  • Swagger 문서 업데이트

변경 내용

1. SectionTemplate → ItemSection 통합

  • section_templates 테이블 삭제
  • item_sections 테이블에 is_template 컬럼 추가
  • 기존 /section-templates API는 유지 (내부적으로 is_template=true 사용)

2. 10개 독립 API 추가

API 메서드 설명
/sections GET 섹션 목록 (is_template 필터)
/sections POST 독립 섹션 생성
/sections/{id}/clone POST 섹션 복제
/sections/{id}/usage GET 섹션 사용처 조회
/fields GET 필드 목록
/fields POST 독립 필드 생성
/fields/{id}/clone POST 필드 복제
/fields/{id}/usage GET 필드 사용처 조회
/bom-items GET BOM 항목 목록
/bom-items POST 독립 BOM 생성

추가된 파일

  • app/Http/Requests/ItemMaster/IndependentSectionStoreRequest.php
  • app/Http/Requests/ItemMaster/IndependentFieldStoreRequest.php
  • app/Http/Requests/ItemMaster/IndependentBomItemStoreRequest.php

삭제된 파일

  • app/Models/ItemMaster/SectionTemplate.php

수정된 파일

  • app/Http/Controllers/Api/V1/ItemMaster/ItemSectionController.php
  • app/Http/Controllers/Api/V1/ItemMaster/ItemFieldController.php
  • app/Http/Controllers/Api/V1/ItemMaster/ItemBomItemController.php
  • app/Services/ItemMaster/ItemSectionService.php
  • app/Services/ItemMaster/ItemFieldService.php
  • app/Services/ItemMaster/ItemBomItemService.php
  • app/Models/ItemMaster/ItemSection.php (is_template, scopeTemplates 추가)
  • routes/api.php
  • app/Swagger/v1/ItemMasterApi.php

마이그레이션

# 실행된 마이그레이션
2025_11_26_120000_add_is_template_to_item_sections_and_drop_section_templates.php

검증 결과

  • PHP 문법 검사: 통과
  • Pint 코드 포맷팅: 통과
  • Swagger 문서 생성: 완료

2025-11-26 (화) - Item Master 하이브리드 구조 전환 (독립 엔티티 + 링크 테이블) 완료

작업 목표

기존 CASCADE FK 기반 계층 구조를 독립 엔티티 + 링크 테이블 구조로 전환

배경

  • 문제점: 현재 구조에서 섹션 삭제 시 항목(필드)도 함께 삭제됨 (CASCADE)
  • 요구사항:
    • 페이지, 섹션, 항목은 독립적으로 존재
    • 관계는 링크 테이블로 관리 (Many-to-Many)
    • 페이지에서 섹션/항목 모두 직접 연결 가능
    • 섹션에서 항목 연결 가능
    • 엔티티 삭제 시 링크만 제거, 다른 엔티티는 유지
    • group_id로 카테고리 격리 (품목관리=1, 향후 확장)

변경 구조

Before (CASCADE FK):

item_pages
  ↓ page_id FK (CASCADE)
item_sections
  ↓ section_id FK (CASCADE)
item_fields / item_bom_items

After (독립 + 링크):

item_pages (독립)
item_sections (독립)
item_fields (독립)
item_bom_items (독립)
  ⇄ entity_relationships (링크 테이블)

Phase 계획

Phase 작업 내용 상태
1 마이그레이션: FK 제거 + group_id 추가 완료
2 마이그레이션: entity_relationships 테이블 생성 완료
3 마이그레이션: 기존 데이터 이관 완료
4 모델 및 Service 수정 완료
5 새로운 API 엔드포인트 추가 완료
6 Swagger 문서 업데이트 완료
7 테스트 및 검증 완료

추가된 파일

마이그레이션 (Batch 26으로 실행):

  • database/migrations/2025_11_26_100001_convert_item_tables_to_independent_entities.php
  • database/migrations/2025_11_26_100002_create_entity_relationships_table.php
  • database/migrations/2025_11_26_100003_migrate_existing_relationships_to_entity_relationships.php

모델:

  • app/Models/ItemMaster/EntityRelationship.php (신규)

서비스:

  • app/Services/ItemMaster/EntityRelationshipService.php (신규)

컨트롤러:

  • app/Http/Controllers/Api/V1/ItemMaster/EntityRelationshipController.php (신규)

Request:

  • app/Http/Requests/ItemMaster/LinkEntityRequest.php (신규)
  • app/Http/Requests/ItemMaster/ReorderRelationshipsRequest.php (신규)

Swagger:

  • app/Swagger/v1/EntityRelationshipApi.php (신규)

수정된 파일

모델 (group_id 추가 + relationship 메서드):

  • app/Models/ItemMaster/ItemPage.php
  • app/Models/ItemMaster/ItemSection.php
  • app/Models/ItemMaster/ItemField.php
  • app/Models/ItemMaster/ItemBomItem.php
  • app/Models/ItemMaster/SectionTemplate.php
  • app/Models/ItemMaster/ItemMasterField.php

라우트:

  • routes/api.php (새로운 엔드포인트 추가)

언어 파일:

  • lang/ko/message.php (linked, unlinked 추가)
  • lang/ko/error.php (page_not_found, section_not_found, field_not_found, bom_not_found 추가)

새로운 API 엔드포인트 (14개)

페이지-섹션 연결:

  • POST /api/v1/item-master/pages/{pageId}/link-section
  • DELETE /api/v1/item-master/pages/{pageId}/unlink-section/{sectionId}

페이지-필드 직접 연결:

  • POST /api/v1/item-master/pages/{pageId}/link-field
  • DELETE /api/v1/item-master/pages/{pageId}/unlink-field/{fieldId}

페이지 관계 조회:

  • GET /api/v1/item-master/pages/{pageId}/relationships
  • GET /api/v1/item-master/pages/{pageId}/structure

섹션-필드 연결:

  • POST /api/v1/item-master/sections/{sectionId}/link-field
  • DELETE /api/v1/item-master/sections/{sectionId}/unlink-field/{fieldId}

섹션-BOM 연결:

  • POST /api/v1/item-master/sections/{sectionId}/link-bom
  • DELETE /api/v1/item-master/sections/{sectionId}/unlink-bom/{bomId}

섹션 관계 조회:

  • GET /api/v1/item-master/sections/{sectionId}/relationships

관계 순서 변경:

  • POST /api/v1/item-master/relationships/reorder

검증 결과

  • PHP 문법 검사: 통과
  • Pint 코드 포맷팅: 통과 (9개 신규 파일)
  • Swagger 문서 생성: 완료
  • 라우트 등록: 44개 item-master 라우트 확인

롤백 방법

php artisan migrate:rollback --step=3

다음 작업 (옵션)

  • 기존 API (POST /pages/{pageId}/sections 등) 내부적으로 entity_relationships 사용하도록 수정
  • 독립 엔티티 CRUD API 추가 (POST /sections, POST /fields 등)

2025-11-25 (월) - API 인증 에러 처리 개선 및 요청 로그 강화

문제 상황

  • GET /item-master/init 요청 시 Route [login] not defined 에러 발생
  • user_id null (인증 실패 상태)
  • API Request 로그에 헤더 정보 누락

근본 원인

  1. Handler.php: expectsJson() 체크만 있어서, Accept 헤더 없는 API 요청이 기본 동작(로그인 리다이렉트)으로 처리됨
  2. ApiKeyMiddleware: 요청 로그에 헤더 정보 (X-API-KEY, Authorization) 미포함

해결 방안

Handler.php (app/Exceptions/Handler.php)

  • Line 60: API 라우트 체크 추가 (str_starts_with($request->path(), 'api/'))
  • Line 62: $request->expectsJson() || $isApiRoute 조건으로 변경
  • 효과: Accept 헤더 없어도 API 라우트는 무조건 JSON 응답 반환

ApiKeyMiddleware (app/Http/Middleware/ApiKeyMiddleware.php)

  • Line 52-57: headers 필드 추가
    • X-API-KEY: 마스킹 ('***')
    • Authorization: Bearer 토큰 마스킹 ('Bearer ***')
    • Accept, Content-Type: 원본 그대로
  • 효과: 인증 문제 디버깅 용이

수정된 파일

  • app/Exceptions/Handler.php
  • app/Http/Middleware/ApiKeyMiddleware.php

검증 결과

  • PHP 문법 체크: 통과
  • Pint 코드 포맷팅: 통과

기대 효과

  1. API 요청 시 Accept 헤더 없어도 정상 JSON 응답
  2. 인증 실패 시 로그인 리다이렉트 대신 401 JSON 응답
  3. 요청 로그에서 헤더 정보 확인 가능 (디버깅 개선)

2025-11-24 (일) - 소프트삭제 및 타임스탬프 감사 컬럼 추가

작업 목표

  • deleted_at이 있는 테이블에 deleted_by 컬럼 추가
  • created_at, updated_at이 있는 테이블에 created_by, updated_by 컬럼 추가

작업 내용

1. DB 스키마 분석 (INFORMATION_SCHEMA 쿼리)

  • deleted_at은 있지만 deleted_by가 없는 테이블: 30개
  • created_at은 있지만 created_by, updated_by가 없는 테이블: 45개

2. 마이그레이션 생성

  • 2025_11_24_192518_add_deleted_by_to_soft_delete_tables.php

    • 30개 테이블에 deleted_by 추가
    • nullable, COMMENT('삭제자 사용자 ID')
    • after('deleted_at') 배치
  • 2025_11_24_192518_add_audit_columns_to_tables.php

    • 38개 비즈니스 테이블에 created_by, updated_by 추가
    • 시스템 테이블 제외 (jobs, job_batches, password_reset_tokens, personal_access_tokens, taggables, tags)
    • nullable, COMMENT
    • after('updated_at'), after('created_by') 배치

3. 마이그레이션 실행 및 검증

  • 실행 시간: deleted_by (429.53ms), audit_columns (1초)
  • 샘플 테이블 검증: users, products, models, bom_templates, department_user 모두 정상

추가된 파일

  • database/migrations/2025_11_24_192518_add_deleted_by_to_soft_delete_tables.php
  • database/migrations/2025_11_24_192518_add_audit_columns_to_tables.php

마이그레이션 상태

  • Batch 25로 실행 완료
  • 롤백 가능 (down 메서드 구현)

2025-11-24 (일) - CORS Preflight 문제 해결

문제 상황

  • React 프론트엔드(http://192.0.0.2:3001)에서 API 호출 시 CORS 에러
  • 에러: Request header field x-api-key is not allowed by Access-Control-Allow-Headers in preflight response
  • 서버 로그: OPTIONS 요청만 있고 Response 로그 없음 (401 차단)

근본 원인 (root-cause-analyst 스킬 활용)

  1. CorsMiddleware에서 Access-Control-Allow-HeadersX-API-KEY 누락
  2. OPTIONS 요청(Preflight)이 ApiKeyMiddleware에서 401로 차단
  3. 브라우저는 커스텀 헤더 사용 시 Preflight 요청을 자동 전송

해결 방안

CorsMiddleware 수정:

  • OPTIONS 요청을 미들웨어 체인 진입 전에 즉시 200 OK 처리
  • Access-Control-Allow-HeadersX-API-KEY 추가
  • PATCH 메서드 추가, Max-Age 86400초 설정

ApiKeyMiddleware 정리:

  • 불필요한 OPTIONS 체크 제거

CORS 설정 업데이트:

  • config/cors.php: exposed_headers, max_age 설정

수정된 파일

  • app/Http/Middleware/CorsMiddleware.php
  • app/Http/Middleware/ApiKeyMiddleware.php
  • config/cors.php

Git 커밋

  • 2e96660 - CORS preflight 요청 처리 개선 및 X-API-KEY 헤더 허용
  • 8e8ab65 - CORS preflight 응답에 x-api-key 헤더 허용 추가

다음 작업

  • React에서 API 호출 테스트
  • 개발 서버 로그 확인 (Request/Response 쌍 기록 여부)

2025-11-20 (수) - ItemMaster API 테스트 및 버그 수정

주요 작업

  • ItemMaster API 통합 테스트 작성 (12개 테스트, 82개 assertion)
  • 누락된 마이그레이션 실행 (section_templates, tab_columns)
  • API Key 미들웨어 수정 (로그인 엔드포인트 API Key 필수화)
  • ReorderRequest validation 수정 (범용성 확보)
  • 네임스페이스 오류 수정 (5개 Controller)
  • Route 순서 수정 (specific route 우선)

테스트 결과

12/12 테스트 통과 (100%)


2025-11-19 (화) - ItemMaster API Swagger 문서 작성

주요 작업

  • ItemMaster 전체 API (32개 엔드포인트) Swagger 문서화 완료
  • OpenAPI 3.0 표준 준수
  • Model Schemas 8개, Request Schemas 12개 작성

2025-11-18 (월) - Category API 테스트 및 개선

주요 작업

  • Category CRUD 테스트 작성 (9개 테스트, 98개 assertion)
  • 계층 구조 및 필드 관리 테스트
  • Validation 로직 개선

2025-12-19 (목) - Phase 6.2 팝업관리 API 구현

주요 작업

  • Phase 6.2 팝업관리 기능 구현 완료
  • PROJECT_DEVELOPMENT_POLICY.md 정책 준수 (string 타입, options JSON 가변 컬럼)

추가된 파일

마이그레이션:

  • database/migrations/2025_12_19_170001_create_popups_table.php

모델:

  • app/Models/Popups/Popup.php
    • BelongsToTenant, SoftDeletes 적용
    • target_type: all(전사), department(부서)
    • status: active(사용), inactive(사용안함)
    • 스코프: active(), status(), targetType(), forUser()
    • 관계: department(), creator(), updater()

서비스:

  • app/Services/PopupService.php
    • index(): 관리자용 목록 (페이지네이션)
    • getActivePopups(): 사용자용 활성 팝업
    • show(): 상세 조회
    • store(): 등록
    • update(): 수정
    • destroy(): 삭제 (Soft Delete)

FormRequest:

  • app/Http/Requests/V1/Popup/StorePopupRequest.php
  • app/Http/Requests/V1/Popup/UpdatePopupRequest.php

컨트롤러:

  • app/Http/Controllers/Api/V1/PopupController.php

Swagger:

  • app/Swagger/v1/PopupApi.php

수정된 파일

  • routes/api.php: Popup 라우트 추가 (6개 엔드포인트)

API 엔드포인트 (6개)

Method Path Description
GET /api/v1/popups 팝업 목록 (관리자용)
POST /api/v1/popups 팝업 등록
GET /api/v1/popups/active 활성 팝업 (사용자용)
GET /api/v1/popups/{id} 팝업 상세
PUT /api/v1/popups/{id} 팝업 수정
DELETE /api/v1/popups/{id} 팝업 삭제

정책 준수 사항

  • 기존 테이블 확인 후 신규 생성
  • string 타입 사용 (enum 대신)
  • options JSON 가변 컬럼
  • BelongsToTenant, SoftDeletes 적용
  • Service-First 아키텍처
  • FormRequest 검증