Files
sam-api/CURRENT_WORKS.md

66 KiB
Raw Blame History

SAM 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 로직 개선