2025-07-17 10:05:47 +09:00
< ? php
feat: [quote] 견적 API Phase 2-3 완료 (Service + Controller Layer)
Phase 2 - Service Layer:
- QuoteService: 견적 CRUD + 상태관리 (확정/전환)
- QuoteNumberService: 견적번호 채번 (KD-{PREFIX}-YYMMDD-SEQ)
- FormulaEvaluatorService: 수식 평가 엔진 (SUM, IF, ROUND 등)
- QuoteCalculationService: 자동산출 (스크린/철재 제품)
- QuoteDocumentService: PDF 생성 및 이메일/카카오 발송
Phase 3 - Controller Layer:
- QuoteController: 16개 엔드포인트
- FormRequest 7개: Index, Store, Update, BulkDelete, Calculate, SendEmail, SendKakao
- QuoteApi.php: Swagger 문서 (12개 스키마, 16개 엔드포인트)
- routes/api.php: 16개 라우트 등록
i18n 키 추가:
- error.php: quote_not_found, formula_* 등
- message.php: quote.* 성공 메시지
2025-12-04 22:03:40 +09:00
use App\Http\Controllers\Api\Admin\GlobalMenuController ;
feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능:
- UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 초대 발송/수락/취소/재발송 API
5.2 알림설정 확장:
- NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 채널별/유형별 알림 설정 관리
5.3 계정정보 수정 API:
- 회원탈퇴, 사용중지, 약관동의 관리
- AccountService, AccountController, Swagger
5.4 매출 거래명세서 API:
- 거래명세서 조회/발행/이메일발송
- SaleService 확장, Swagger 문서화
2025-12-19 14:52:53 +09:00
use App\Http\Controllers\Api\V1\AccountController ;
2026-01-19 19:42:05 +09:00
use App\Http\Controllers\Api\V1\Admin\FcmController ;
2025-11-06 17:24:42 +09:00
use App\Http\Controllers\Api\V1\AdminController ;
2025-12-18 14:21:37 +09:00
use App\Http\Controllers\Api\V1\AiReportController ;
2025-11-06 17:24:42 +09:00
use App\Http\Controllers\Api\V1\ApiController ;
2025-12-18 11:40:49 +09:00
use App\Http\Controllers\Api\V1\ApprovalController ;
use App\Http\Controllers\Api\V1\ApprovalFormController ;
use App\Http\Controllers\Api\V1\ApprovalLineController ;
2025-12-09 21:51:46 +09:00
use App\Http\Controllers\Api\V1\AttendanceController ;
feat: Phase 6.1 악성채권 추심관리 API 구현
- 테이블 3개: bad_debts, bad_debt_documents, bad_debt_memos
- 모델 3개: BadDebt, BadDebtDocument, BadDebtMemo
- BadDebtService: CRUD, 요약 통계, 서류/메모 관리
- API 엔드포인트 11개 (목록, 등록, 상세, 수정, 삭제, 토글, 서류/메모 CRUD)
- Swagger 문서 작성 완료
2025-12-19 15:57:04 +09:00
use App\Http\Controllers\Api\V1\BadDebtController ;
feat: 매출/매입 관리 API 구현
- 매출(Sale) 및 매입(Purchase) CRUD API 구현
- 문서번호 자동 생성 (SL/PU + YYYYMMDD + 시퀀스)
- 상태 관리 (draft → confirmed → invoiced)
- 확정(confirm) 및 요약(summary) 기능 추가
- BelongsToTenant, SoftDeletes 적용
- Swagger API 문서 작성 완료
추가된 파일:
- 마이그레이션: sales, purchases 테이블
- 모델: Sale, Purchase
- 서비스: SaleService, PurchaseService
- 컨트롤러: SaleController, PurchaseController
- FormRequest: Store/Update 4개
- Swagger: SaleApi.php, PurchaseApi.php
API 엔드포인트 (14개):
- GET/POST /v1/sales, /v1/purchases
- GET/PUT/DELETE /v1/{sales,purchases}/{id}
- POST /v1/{sales,purchases}/{id}/confirm
- GET /v1/{sales,purchases}/summary
2025-12-17 22:14:48 +09:00
use App\Http\Controllers\Api\V1\BankAccountController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\BankTransactionController ;
feat: 구독/결제 API 확장 (Plan, Subscription, Payment)
- Plan/Subscription/Payment 모델에 상태 상수, 스코프, 헬퍼 메서드 추가
- PlanService, SubscriptionService, PaymentService 생성
- PlanController, SubscriptionController, PaymentController 생성
- FormRequest 9개 생성 (Plan 3개, Subscription 3개, Payment 3개)
- Swagger 문서 3개 생성 (PlanApi, SubscriptionApi, PaymentApi)
- API 라우트 22개 등록 (Plan 7개, Subscription 8개, Payment 7개)
- Pint 코드 스타일 정리
2025-12-18 16:20:29 +09:00
use App\Http\Controllers\Api\V1\BarobillSettingController ;
2026-01-19 19:42:05 +09:00
use App\Http\Controllers\Api\V1\BiddingController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\BillController ;
2025-11-30 21:06:22 +09:00
use App\Http\Controllers\Api\V1\BoardController ;
2026-01-21 10:25:18 +09:00
use App\Http\Controllers\Api\V1\CalendarController ;
feat: 매출/매입 관리 API 구현
- 매출(Sale) 및 매입(Purchase) CRUD API 구현
- 문서번호 자동 생성 (SL/PU + YYYYMMDD + 시퀀스)
- 상태 관리 (draft → confirmed → invoiced)
- 확정(confirm) 및 요약(summary) 기능 추가
- BelongsToTenant, SoftDeletes 적용
- Swagger API 문서 작성 완료
추가된 파일:
- 마이그레이션: sales, purchases 테이블
- 모델: Sale, Purchase
- 서비스: SaleService, PurchaseService
- 컨트롤러: SaleController, PurchaseController
- FormRequest: Store/Update 4개
- Swagger: SaleApi.php, PurchaseApi.php
API 엔드포인트 (14개):
- GET/POST /v1/sales, /v1/purchases
- GET/PUT/DELETE /v1/{sales,purchases}/{id}
- POST /v1/{sales,purchases}/{id}/confirm
- GET /v1/{sales,purchases}/summary
2025-12-17 22:14:48 +09:00
use App\Http\Controllers\Api\V1\CardController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\CardTransactionController ;
2025-11-06 17:24:42 +09:00
use App\Http\Controllers\Api\V1\CategoryController ;
use App\Http\Controllers\Api\V1\CategoryFieldController ;
2025-08-25 17:46:34 +09:00
use App\Http\Controllers\Api\V1\CategoryLogController ;
2025-11-06 17:24:42 +09:00
use App\Http\Controllers\Api\V1\CategoryTemplateController ;
use App\Http\Controllers\Api\V1\ClassificationController ;
use App\Http\Controllers\Api\V1\ClientController ;
use App\Http\Controllers\Api\V1\ClientGroupController ;
2025-07-18 11:37:07 +09:00
use App\Http\Controllers\Api\V1\CommonController ;
2025-12-22 17:42:59 +09:00
use App\Http\Controllers\Api\V1\CompanyController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\ComprehensiveAnalysisController ;
use App\Http\Controllers\Api\V1\Construction\ContractController ;
use App\Http\Controllers\Api\V1\Construction\HandoverReportController ;
2026-01-09 21:31:27 +09:00
use App\Http\Controllers\Api\V1\Construction\StructureReviewController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\DailyReportController ;
feat: 대시보드 API 및 FCM 푸시 알림 API 구현
Dashboard API:
- DashboardController, DashboardService 추가
- /dashboard/summary, /charts, /approvals 엔드포인트
Push Notification API:
- FCM 토큰 관리 (등록/해제/목록)
- 알림 설정 관리 (유형별 on/off, 알림음 설정)
- 알림 유형: deposit, withdrawal, order, approval, attendance, notice, system
- 알림음: default, deposit, withdrawal, order, approval, urgent
- PushDeviceToken, PushNotificationSetting 모델
- Swagger 문서 추가
2025-12-18 11:16:24 +09:00
use App\Http\Controllers\Api\V1\DashboardController ;
2025-11-06 17:24:42 +09:00
use App\Http\Controllers\Api\V1\DepartmentController ;
feat: 매출/매입 관리 API 구현
- 매출(Sale) 및 매입(Purchase) CRUD API 구현
- 문서번호 자동 생성 (SL/PU + YYYYMMDD + 시퀀스)
- 상태 관리 (draft → confirmed → invoiced)
- 확정(confirm) 및 요약(summary) 기능 추가
- BelongsToTenant, SoftDeletes 적용
- Swagger API 문서 작성 완료
추가된 파일:
- 마이그레이션: sales, purchases 테이블
- 모델: Sale, Purchase
- 서비스: SaleService, PurchaseService
- 컨트롤러: SaleController, PurchaseController
- FormRequest: Store/Update 4개
- Swagger: SaleApi.php, PurchaseApi.php
API 엔드포인트 (14개):
- GET/POST /v1/sales, /v1/purchases
- GET/PUT/DELETE /v1/{sales,purchases}/{id}
- POST /v1/{sales,purchases}/{id}/confirm
- GET /v1/{sales,purchases}/summary
2025-12-17 22:14:48 +09:00
use App\Http\Controllers\Api\V1\DepositController ;
2025-11-06 17:24:42 +09:00
use App\Http\Controllers\Api\V1\Design\AuditLogController as DesignAuditLogController ;
use App\Http\Controllers\Api\V1\Design\BomCalculationController ;
use App\Http\Controllers\Api\V1\Design\BomTemplateController as DesignBomTemplateController ;
use App\Http\Controllers\Api\V1\Design\DesignModelController ;
use App\Http\Controllers\Api\V1\Design\ModelVersionController as DesignModelVersionController ;
2025-12-09 21:51:46 +09:00
use App\Http\Controllers\Api\V1\EmployeeController ;
2026-01-21 10:25:18 +09:00
use App\Http\Controllers\Api\V1\EntertainmentController ;
2025-11-06 17:24:42 +09:00
use App\Http\Controllers\Api\V1\EstimateController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\ExpectedExpenseController ;
feat: 파일 저장 시스템 DB 마이그레이션
- enhance_files_table: 이중 파일명 시스템 (display_name/stored_name), 폴더 관리, 문서 연결 지원
- create_folders_table: 동적 폴더 관리 시스템 (tenant별 커스터마이징 가능)
- 5개 stub 마이그레이션 생성 (file_share_links, file_deletion_logs, storage_usage_history, add_storage_columns_to_tenants)
- FolderSeeder stub 생성
- CURRENT_WORKS.md에 Phase 1 진행상황 문서화
fix: 파일 공유 및 삭제 기능 버그 수정
- ShareLinkRequest: PATH 파라미터 {id}를 file_id로 자동 병합
- routes/api.php: 공유 링크 다운로드를 auth.apikey 그룹 밖으로 이동 (인증 불필요)
- FileShareLink: File, Tenant 클래스 import 추가
- File 모델: softDeleteFile()에서 SoftDeletes의 delete() 메서드 사용
- FileStorageService: getTrash(), restoreFile(), permanentDelete()에서 onlyTrashed() 사용
- File 모델: Tenant 네임스페이스 수정 (App\Models\Tenants\Tenant)
refactor: Swagger 문서 정리 - File 태그를 Files로 통합
- FileApi.php의 모든 태그를 Files로 변경
- 구 파일 시스템 라우트 삭제 (prefix 'file')
- 구 FileController.php 삭제
- 신규 파일 저장소 시스템으로 완전 통합
fix: 모든 legacy 파일 컬럼 nullable 일괄 처리
- 5개 legacy 컬럼을 한 번에 nullable로 변경
* original_name, file_name, file_name_old (string)
* fileable_id, fileable_type (polymorphic)
- foreach 루프로 반복 작업 자동화
- 신규/기존 시스템 간 완전한 하위 호환성 확보
fix: legacy 파일 컬럼 nullable 처리 완료
- file_name, file_name_old 컬럼도 nullable로 변경
- 기존 시스템과 신규 시스템 간 완전한 하위 호환성 확보
- Legacy: original_name, file_name, file_name_old (nullable)
- New: display_name, stored_name (required)
fix: original_name 컬럼 nullable 처리
- original_name을 nullable로 변경하여 하위 호환성 유지
- 새 시스템에서는 display_name 사용, 기존 시스템은 original_name 사용 가능
fix: 파일 업로드 DB 컬럼 누락 및 메시지 구조 개선
- files 테이블에 감사 컬럼 추가 (created_by, updated_by, uploaded_by)
- ApiResponse::handle() 메시지 로직 개선 (접미사 제거)
- 다국어 지원을 위한 완성된 문장 구조 유지
- FileUploadRequest 파일 검증 규칙 수정
fix: 파일 저장소 버그 수정 및 신규 테넌트 폴더 자동 생성
- FolderSeeder 네임스페이스 수정 (App\Models\Tenant → App\Models\Tenants\Tenant)
- FileStorageController use 문 구문 오류 수정 (/ → \)
- TenantObserver에 신규 테넌트 기본 폴더 자동 생성 로직 추가
- 5개 기본 폴더 (생산관리, 품질관리, 회계, 인사, 일반)
- 에러 처리 및 로깅
- 회원가입 시 자동 실행
2025-11-10 19:08:56 +09:00
use App\Http\Controllers\Api\V1\FileStorageController ;
use App\Http\Controllers\Api\V1\FolderController ;
2026-01-14 17:08:59 +09:00
use App\Http\Controllers\Api\V1\InspectionController ;
2025-12-18 14:21:37 +09:00
use App\Http\Controllers\Api\V1\InternalController ;
2025-11-20 17:16:03 +09:00
use App\Http\Controllers\Api\V1\ItemMaster\CustomTabController ;
2025-11-26 14:09:31 +09:00
use App\Http\Controllers\Api\V1\ItemMaster\EntityRelationshipController ;
2025-11-20 17:07:40 +09:00
use App\Http\Controllers\Api\V1\ItemMaster\ItemBomItemController ;
2025-11-20 16:55:57 +09:00
use App\Http\Controllers\Api\V1\ItemMaster\ItemFieldController ;
use App\Http\Controllers\Api\V1\ItemMaster\ItemMasterController ;
feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능:
- UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 초대 발송/수락/취소/재발송 API
5.2 알림설정 확장:
- NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 채널별/유형별 알림 설정 관리
5.3 계정정보 수정 API:
- 회원탈퇴, 사용중지, 약관동의 관리
- AccountService, AccountController, Swagger
5.4 매출 거래명세서 API:
- 거래명세서 조회/발행/이메일발송
- SaleService 확장, Swagger 문서화
2025-12-19 14:52:53 +09:00
use App\Http\Controllers\Api\V1\ItemMaster\ItemPageController ;
feat: 구독/결제 API 확장 (Plan, Subscription, Payment)
- Plan/Subscription/Payment 모델에 상태 상수, 스코프, 헬퍼 메서드 추가
- PlanService, SubscriptionService, PaymentService 생성
- PlanController, SubscriptionController, PaymentController 생성
- FormRequest 9개 생성 (Plan 3개, Subscription 3개, Payment 3개)
- Swagger 문서 3개 생성 (PlanApi, SubscriptionApi, PaymentApi)
- API 라우트 22개 등록 (Plan 7개, Subscription 8개, Payment 7개)
- Pint 코드 스타일 정리
2025-12-18 16:20:29 +09:00
use App\Http\Controllers\Api\V1\ItemMaster\ItemSectionController ;
2025-11-20 17:07:40 +09:00
use App\Http\Controllers\Api\V1\ItemMaster\SectionTemplateController ;
2025-11-20 17:16:03 +09:00
use App\Http\Controllers\Api\V1\ItemMaster\UnitOptionController ;
2026-01-09 16:58:41 +09:00
// use App\Http\Controllers\Api\V1\MaterialController; // REMOVED: materials 테이블 삭제됨
2026-01-19 19:42:05 +09:00
use App\Http\Controllers\Api\V1\ItemsBomController ;
2025-12-09 20:27:44 +09:00
use App\Http\Controllers\Api\V1\ItemsController ;
use App\Http\Controllers\Api\V1\ItemsFileController ;
2026-01-13 16:00:47 +09:00
use App\Http\Controllers\Api\V1\LaborController ;
feat: 근무/출퇴근 설정 및 현장 관리 API 구현
- 근무 설정 API (GET/PUT /settings/work)
- 근무유형, 소정근로시간, 연장근로시간, 근무요일, 출퇴근시간, 휴게시간
- 출퇴근 설정 API (GET/PUT /settings/attendance)
- GPS 출퇴근, 허용 반경, 본사 위치 설정
- 현장 관리 API (CRUD /sites)
- 현장 등록/수정/삭제, 활성화된 현장 목록(셀렉트박스용)
- GPS 좌표 기반 위치 관리
마이그레이션: work_settings, attendance_settings, sites 테이블
모델: WorkSetting, AttendanceSetting, Site (BelongsToTenant, SoftDeletes)
서비스: WorkSettingService, SiteService
Swagger 문서 및 i18n 메시지 키 추가
2025-12-17 20:46:37 +09:00
use App\Http\Controllers\Api\V1\LeaveController ;
2025-12-26 15:18:08 +09:00
use App\Http\Controllers\Api\V1\LeavePolicyController ;
2025-12-18 14:21:37 +09:00
use App\Http\Controllers\Api\V1\LoanController ;
2025-08-16 03:25:06 +09:00
use App\Http\Controllers\Api\V1\MenuController ;
2025-11-06 17:24:42 +09:00
use App\Http\Controllers\Api\V1\ModelSetController ;
feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능:
- UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 초대 발송/수락/취소/재발송 API
5.2 알림설정 확장:
- NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 채널별/유형별 알림 설정 관리
5.3 계정정보 수정 API:
- 회원탈퇴, 사용중지, 약관동의 관리
- AccountService, AccountController, Swagger
5.4 매출 거래명세서 API:
- 거래명세서 조회/발행/이메일발송
- SaleService 확장, Swagger 문서화
2025-12-19 14:52:53 +09:00
use App\Http\Controllers\Api\V1\NotificationSettingController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\OrderController ;
feat: 구독/결제 API 확장 (Plan, Subscription, Payment)
- Plan/Subscription/Payment 모델에 상태 상수, 스코프, 헬퍼 메서드 추가
- PlanService, SubscriptionService, PaymentService 생성
- PlanController, SubscriptionController, PaymentController 생성
- FormRequest 9개 생성 (Plan 3개, Subscription 3개, Payment 3개)
- Swagger 문서 3개 생성 (PlanApi, SubscriptionApi, PaymentApi)
- API 라우트 22개 등록 (Plan 7개, Subscription 8개, Payment 7개)
- Pint 코드 스타일 정리
2025-12-18 16:20:29 +09:00
use App\Http\Controllers\Api\V1\PaymentController ;
feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능:
- UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 초대 발송/수락/취소/재발송 API
5.2 알림설정 확장:
- NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 채널별/유형별 알림 설정 관리
5.3 계정정보 수정 API:
- 회원탈퇴, 사용중지, 약관동의 관리
- AccountService, AccountController, Swagger
5.4 매출 거래명세서 API:
- 거래명세서 조회/발행/이메일발송
- SaleService 확장, Swagger 문서화
2025-12-19 14:52:53 +09:00
use App\Http\Controllers\Api\V1\PayrollController ;
2025-12-18 14:21:37 +09:00
use App\Http\Controllers\Api\V1\PermissionController ;
2026-01-09 16:58:41 +09:00
// use App\Http\Controllers\Api\V1\ProductBomItemController; // REMOVED: products 테이블 삭제됨
// use App\Http\Controllers\Api\V1\ProductController; // REMOVED: products 테이블 삭제됨
2026-01-19 19:42:05 +09:00
use App\Http\Controllers\Api\V1\PlanController ;
2026-01-13 16:00:47 +09:00
use App\Http\Controllers\Api\V1\PopupController ;
2025-12-30 11:18:17 +09:00
use App\Http\Controllers\Api\V1\PositionController ;
2025-12-18 11:40:49 +09:00
use App\Http\Controllers\Api\V1\PostController ;
2025-11-06 17:24:42 +09:00
use App\Http\Controllers\Api\V1\PricingController ;
feat: 매출/매입 관리 API 구현
- 매출(Sale) 및 매입(Purchase) CRUD API 구현
- 문서번호 자동 생성 (SL/PU + YYYYMMDD + 시퀀스)
- 상태 관리 (draft → confirmed → invoiced)
- 확정(confirm) 및 요약(summary) 기능 추가
- BelongsToTenant, SoftDeletes 적용
- Swagger API 문서 작성 완료
추가된 파일:
- 마이그레이션: sales, purchases 테이블
- 모델: Sale, Purchase
- 서비스: SaleService, PurchaseService
- 컨트롤러: SaleController, PurchaseController
- FormRequest: Store/Update 4개
- Swagger: SaleApi.php, PurchaseApi.php
API 엔드포인트 (14개):
- GET/POST /v1/sales, /v1/purchases
- GET/PUT/DELETE /v1/{sales,purchases}/{id}
- POST /v1/{sales,purchases}/{id}/confirm
- GET /v1/{sales,purchases}/summary
2025-12-17 22:14:48 +09:00
use App\Http\Controllers\Api\V1\PurchaseController ;
2025-12-18 11:40:49 +09:00
use App\Http\Controllers\Api\V1\PushNotificationController ;
feat: [quote] 견적 API Phase 2-3 완료 (Service + Controller Layer)
Phase 2 - Service Layer:
- QuoteService: 견적 CRUD + 상태관리 (확정/전환)
- QuoteNumberService: 견적번호 채번 (KD-{PREFIX}-YYMMDD-SEQ)
- FormulaEvaluatorService: 수식 평가 엔진 (SUM, IF, ROUND 등)
- QuoteCalculationService: 자동산출 (스크린/철재 제품)
- QuoteDocumentService: PDF 생성 및 이메일/카카오 발송
Phase 3 - Controller Layer:
- QuoteController: 16개 엔드포인트
- FormRequest 7개: Index, Store, Update, BulkDelete, Calculate, SendEmail, SendKakao
- QuoteApi.php: Swagger 문서 (12개 스키마, 16개 엔드포인트)
- routes/api.php: 16개 라우트 등록
i18n 키 추가:
- error.php: quote_not_found, formula_* 등
- message.php: quote.* 성공 메시지
2025-12-04 22:03:40 +09:00
use App\Http\Controllers\Api\V1\QuoteController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\ReceivablesController ;
use App\Http\Controllers\Api\V1\ReceivingController ;
feat: 파일 저장 시스템 DB 마이그레이션
- enhance_files_table: 이중 파일명 시스템 (display_name/stored_name), 폴더 관리, 문서 연결 지원
- create_folders_table: 동적 폴더 관리 시스템 (tenant별 커스터마이징 가능)
- 5개 stub 마이그레이션 생성 (file_share_links, file_deletion_logs, storage_usage_history, add_storage_columns_to_tenants)
- FolderSeeder stub 생성
- CURRENT_WORKS.md에 Phase 1 진행상황 문서화
fix: 파일 공유 및 삭제 기능 버그 수정
- ShareLinkRequest: PATH 파라미터 {id}를 file_id로 자동 병합
- routes/api.php: 공유 링크 다운로드를 auth.apikey 그룹 밖으로 이동 (인증 불필요)
- FileShareLink: File, Tenant 클래스 import 추가
- File 모델: softDeleteFile()에서 SoftDeletes의 delete() 메서드 사용
- FileStorageService: getTrash(), restoreFile(), permanentDelete()에서 onlyTrashed() 사용
- File 모델: Tenant 네임스페이스 수정 (App\Models\Tenants\Tenant)
refactor: Swagger 문서 정리 - File 태그를 Files로 통합
- FileApi.php의 모든 태그를 Files로 변경
- 구 파일 시스템 라우트 삭제 (prefix 'file')
- 구 FileController.php 삭제
- 신규 파일 저장소 시스템으로 완전 통합
fix: 모든 legacy 파일 컬럼 nullable 일괄 처리
- 5개 legacy 컬럼을 한 번에 nullable로 변경
* original_name, file_name, file_name_old (string)
* fileable_id, fileable_type (polymorphic)
- foreach 루프로 반복 작업 자동화
- 신규/기존 시스템 간 완전한 하위 호환성 확보
fix: legacy 파일 컬럼 nullable 처리 완료
- file_name, file_name_old 컬럼도 nullable로 변경
- 기존 시스템과 신규 시스템 간 완전한 하위 호환성 확보
- Legacy: original_name, file_name, file_name_old (nullable)
- New: display_name, stored_name (required)
fix: original_name 컬럼 nullable 처리
- original_name을 nullable로 변경하여 하위 호환성 유지
- 새 시스템에서는 display_name 사용, 기존 시스템은 original_name 사용 가능
fix: 파일 업로드 DB 컬럼 누락 및 메시지 구조 개선
- files 테이블에 감사 컬럼 추가 (created_by, updated_by, uploaded_by)
- ApiResponse::handle() 메시지 로직 개선 (접미사 제거)
- 다국어 지원을 위한 완성된 문장 구조 유지
- FileUploadRequest 파일 검증 규칙 수정
fix: 파일 저장소 버그 수정 및 신규 테넌트 폴더 자동 생성
- FolderSeeder 네임스페이스 수정 (App\Models\Tenant → App\Models\Tenants\Tenant)
- FileStorageController use 문 구문 오류 수정 (/ → \)
- TenantObserver에 신규 테넌트 기본 폴더 자동 생성 로직 추가
- 5개 기본 폴더 (생산관리, 품질관리, 회계, 인사, 일반)
- 에러 처리 및 로깅
- 회원가입 시 자동 실행
2025-11-10 19:08:56 +09:00
use App\Http\Controllers\Api\V1\RefreshController ;
2025-11-06 17:24:42 +09:00
use App\Http\Controllers\Api\V1\RegisterController ;
2025-12-17 22:51:17 +09:00
use App\Http\Controllers\Api\V1\ReportController ;
2025-08-16 03:25:06 +09:00
use App\Http\Controllers\Api\V1\RoleController ;
use App\Http\Controllers\Api\V1\RolePermissionController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\SalaryController ;
feat: 매출/매입 관리 API 구현
- 매출(Sale) 및 매입(Purchase) CRUD API 구현
- 문서번호 자동 생성 (SL/PU + YYYYMMDD + 시퀀스)
- 상태 관리 (draft → confirmed → invoiced)
- 확정(confirm) 및 요약(summary) 기능 추가
- BelongsToTenant, SoftDeletes 적용
- Swagger API 문서 작성 완료
추가된 파일:
- 마이그레이션: sales, purchases 테이블
- 모델: Sale, Purchase
- 서비스: SaleService, PurchaseService
- 컨트롤러: SaleController, PurchaseController
- FormRequest: Store/Update 4개
- Swagger: SaleApi.php, PurchaseApi.php
API 엔드포인트 (14개):
- GET/POST /v1/sales, /v1/purchases
- GET/PUT/DELETE /v1/{sales,purchases}/{id}
- POST /v1/{sales,purchases}/{id}/confirm
- GET /v1/{sales,purchases}/summary
2025-12-17 22:14:48 +09:00
use App\Http\Controllers\Api\V1\SaleController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\ShipmentController ;
2026-01-13 16:00:47 +09:00
use App\Http\Controllers\Api\V1\SiteBriefingController ;
feat: 근무/출퇴근 설정 및 현장 관리 API 구현
- 근무 설정 API (GET/PUT /settings/work)
- 근무유형, 소정근로시간, 연장근로시간, 근무요일, 출퇴근시간, 휴게시간
- 출퇴근 설정 API (GET/PUT /settings/attendance)
- GPS 출퇴근, 허용 반경, 본사 위치 설정
- 현장 관리 API (CRUD /sites)
- 현장 등록/수정/삭제, 활성화된 현장 목록(셀렉트박스용)
- GPS 좌표 기반 위치 관리
마이그레이션: work_settings, attendance_settings, sites 테이블
모델: WorkSetting, AttendanceSetting, Site (BelongsToTenant, SoftDeletes)
서비스: WorkSettingService, SiteService
Swagger 문서 및 i18n 메시지 키 추가
2025-12-17 20:46:37 +09:00
use App\Http\Controllers\Api\V1\SiteController ;
2026-01-21 10:25:18 +09:00
use App\Http\Controllers\Api\V1\StatusBoardController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\StockController ;
feat: 구독/결제 API 확장 (Plan, Subscription, Payment)
- Plan/Subscription/Payment 모델에 상태 상수, 스코프, 헬퍼 메서드 추가
- PlanService, SubscriptionService, PaymentService 생성
- PlanController, SubscriptionController, PaymentController 생성
- FormRequest 9개 생성 (Plan 3개, Subscription 3개, Payment 3개)
- Swagger 문서 3개 생성 (PlanApi, SubscriptionApi, PaymentApi)
- API 라우트 22개 등록 (Plan 7개, Subscription 8개, Payment 7개)
- Pint 코드 스타일 정리
2025-12-18 16:20:29 +09:00
use App\Http\Controllers\Api\V1\SubscriptionController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\SystemBoardController ;
use App\Http\Controllers\Api\V1\SystemPostController ;
feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능:
- UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 초대 발송/수락/취소/재발송 API
5.2 알림설정 확장:
- NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 채널별/유형별 알림 설정 관리
5.3 계정정보 수정 API:
- 회원탈퇴, 사용중지, 약관동의 관리
- AccountService, AccountController, Swagger
5.4 매출 거래명세서 API:
- 거래명세서 조회/발행/이메일발송
- SaleService 확장, Swagger 문서화
2025-12-19 14:52:53 +09:00
use App\Http\Controllers\Api\V1\TaxInvoiceController ;
2026-01-22 22:40:00 +09:00
// 설계 전용 (디자인 네임스페이스)
2025-12-18 14:21:37 +09:00
use App\Http\Controllers\Api\V1\TenantController ;
2025-12-18 11:40:49 +09:00
use App\Http\Controllers\Api\V1\TenantFieldSettingController ;
2025-12-17 22:51:17 +09:00
use App\Http\Controllers\Api\V1\TenantOptionGroupController ;
2025-08-18 19:03:46 +09:00
use App\Http\Controllers\Api\V1\TenantOptionValueController ;
2026-01-26 20:29:22 +09:00
use App\Http\Controllers\Api\V1\TenantSettingController ;
feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능:
- UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 초대 발송/수락/취소/재발송 API
5.2 알림설정 확장:
- NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 채널별/유형별 알림 설정 관리
5.3 계정정보 수정 API:
- 회원탈퇴, 사용중지, 약관동의 관리
- AccountService, AccountController, Swagger
5.4 매출 거래명세서 API:
- 거래명세서 조회/발행/이메일발송
- SaleService 확장, Swagger 문서화
2025-12-19 14:52:53 +09:00
use App\Http\Controllers\Api\V1\TenantStatFieldController ;
2026-01-22 22:40:00 +09:00
// 모델셋 관리 (견적 시스템)
2025-12-18 14:21:37 +09:00
use App\Http\Controllers\Api\V1\TenantUserProfileController ;
2026-01-22 22:40:00 +09:00
use App\Http\Controllers\Api\V1\TodayIssueController ;
2025-12-18 11:40:49 +09:00
use App\Http\Controllers\Api\V1\UserController ;
feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능:
- UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 초대 발송/수락/취소/재발송 API
5.2 알림설정 확장:
- NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 채널별/유형별 알림 설정 관리
5.3 계정정보 수정 API:
- 회원탈퇴, 사용중지, 약관동의 관리
- AccountService, AccountController, Swagger
5.4 매출 거래명세서 API:
- 거래명세서 조회/발행/이메일발송
- SaleService 확장, Swagger 문서화
2025-12-19 14:52:53 +09:00
use App\Http\Controllers\Api\V1\UserInvitationController ;
2025-12-17 22:51:17 +09:00
use App\Http\Controllers\Api\V1\UserRoleController ;
2026-01-21 10:25:18 +09:00
use App\Http\Controllers\Api\V1\VatController ;
2026-01-09 16:58:41 +09:00
use App\Http\Controllers\Api\V1\VendorLedgerController ;
2026-01-21 10:25:18 +09:00
use App\Http\Controllers\Api\V1\WelfareController ;
feat: 매출/매입 관리 API 구현
- 매출(Sale) 및 매입(Purchase) CRUD API 구현
- 문서번호 자동 생성 (SL/PU + YYYYMMDD + 시퀀스)
- 상태 관리 (draft → confirmed → invoiced)
- 확정(confirm) 및 요약(summary) 기능 추가
- BelongsToTenant, SoftDeletes 적용
- Swagger API 문서 작성 완료
추가된 파일:
- 마이그레이션: sales, purchases 테이블
- 모델: Sale, Purchase
- 서비스: SaleService, PurchaseService
- 컨트롤러: SaleController, PurchaseController
- FormRequest: Store/Update 4개
- Swagger: SaleApi.php, PurchaseApi.php
API 엔드포인트 (14개):
- GET/POST /v1/sales, /v1/purchases
- GET/PUT/DELETE /v1/{sales,purchases}/{id}
- POST /v1/{sales,purchases}/{id}/confirm
- GET /v1/{sales,purchases}/summary
2025-12-17 22:14:48 +09:00
use App\Http\Controllers\Api\V1\WithdrawalController ;
2025-12-26 13:23:07 +09:00
use App\Http\Controllers\Api\V1\WorkOrderController ;
2025-12-26 15:18:08 +09:00
use App\Http\Controllers\Api\V1\WorkResultController ;
feat: 근무/출퇴근 설정 및 현장 관리 API 구현
- 근무 설정 API (GET/PUT /settings/work)
- 근무유형, 소정근로시간, 연장근로시간, 근무요일, 출퇴근시간, 휴게시간
- 출퇴근 설정 API (GET/PUT /settings/attendance)
- GPS 출퇴근, 허용 반경, 본사 위치 설정
- 현장 관리 API (CRUD /sites)
- 현장 등록/수정/삭제, 활성화된 현장 목록(셀렉트박스용)
- GPS 좌표 기반 위치 관리
마이그레이션: work_settings, attendance_settings, sites 테이블
모델: WorkSetting, AttendanceSetting, Site (BelongsToTenant, SoftDeletes)
서비스: WorkSettingService, SiteService
Swagger 문서 및 i18n 메시지 키 추가
2025-12-17 20:46:37 +09:00
use App\Http\Controllers\Api\V1\WorkSettingController ;
2025-12-26 18:56:24 +09:00
use App\Http\Controllers\V1\ProcessController ;
2025-11-06 17:24:42 +09:00
use Illuminate\Support\Facades\Route ;
2025-09-24 17:41:26 +09:00
2025-07-18 11:37:07 +09:00
// V1 초기 개발
Route :: prefix ( 'v1' ) -> group ( function () {
2025-07-17 10:05:47 +09:00
2025-12-18 13:51:40 +09:00
// 내부 서버간 통신 (API Key, Bearer 인증 제외 - HMAC 인증 사용)
Route :: prefix ( 'internal' ) -> group ( function () {
Route :: post ( '/exchange-token' , [ InternalController :: class , 'exchangeToken' ]) -> name ( 'v1.internal.exchange-token' );
});
2025-11-24 16:00:30 +09:00
// API KEY 인증 (글로벌 미들웨어로 이미 적용됨)
Route :: get ( '/debug-apikey' , [ ApiController :: class , 'debugApikey' ]);
2025-08-04 08:38:47 +09:00
2025-11-24 16:00:30 +09:00
// SAM API (글로벌 미들웨어로 이미 적용됨)
Route :: group ([], function () {
2025-07-17 10:05:47 +09:00
2025-11-06 17:24:42 +09:00
// Auth API
2025-08-18 16:37:02 +09:00
Route :: post ( 'login' , [ ApiController :: class , 'login' ]) -> name ( 'v1.users.login' );
Route :: middleware ( 'auth:sanctum' ) -> post ( 'logout' , [ ApiController :: class , 'logout' ]) -> name ( 'v1.users.logout' );
Route :: post ( 'signup' , [ ApiController :: class , 'signup' ]) -> name ( 'v1.users.signup' );
2025-12-20 13:43:04 +09:00
Route :: post ( 'token-login' , [ ApiController :: class , 'tokenLogin' ]) -> name ( 'v1.auth.token-login' ); // MNG → DEV 자동 로그인
2025-11-10 11:17:32 +09:00
Route :: post ( 'refresh' , [ RefreshController :: class , 'refresh' ]) -> name ( 'v1.token.refresh' );
2025-11-06 17:24:42 +09:00
Route :: post ( 'register' , [ RegisterController :: class , 'register' ]) -> name ( 'v1.register' );
2025-07-23 18:06:33 +09:00
2025-08-15 16:32:11 +09:00
// Tenant Admin API
Route :: prefix ( 'admin' ) -> group ( function () {
// 목록/생성
Route :: get ( 'users' , [ AdminController :: class , 'index' ]) -> name ( 'v1.admin.users.index' ); // 테넌트 사용자 목록 조회
Route :: post ( 'users' , [ AdminController :: class , 'store' ]) -> name ( 'v1.admin.users.store' ); // 테넌트 사용자 생성
// 단건
Route :: get ( 'users/{id}' , [ AdminController :: class , 'show' ]) -> name ( 'v1.admin.users.show' ); // 테넌트 사용자 단건 조회
Route :: put ( 'users/{id}' , [ AdminController :: class , 'update' ]) -> name ( 'v1.admin.users.update' ); // 테넌트 사용자 수정
// 소프트 삭제 복구
Route :: delete ( 'users/{id}' , [ AdminController :: class , 'destroy' ]) -> name ( 'v1.admin.users.destroy' ); // 테넌트 사용자 삭제(연결 삭제)
Route :: post ( 'users/{id}/restore' , [ AdminController :: class , 'restore' ]) -> name ( 'v1.admin.users.restore' ); // 테넌트 사용자 삭제 복구
// 상태 토글
Route :: patch ( 'users/{id}/status' , [ AdminController :: class , 'toggle' ]) -> name ( 'v1.admin.users.status.toggle' ); // 테넌트 사용자 활성/비활성
// 역할 부여/해제
Route :: post ( 'users/{id}/roles' , [ AdminController :: class , 'attach' ]) -> name ( 'v1.admin.users.roles.attach' ); // 테넌트 사용자 역할 부여
Route :: delete ( 'users/{id}/roles/{role}' , [ AdminController :: class , 'detach' ]) -> name ( 'v1.admin.users.roles.detach' ); // 테넌트 사용자 역할 해제
// 비밀번호 초기화
Route :: post ( 'users/{id}/reset-password' , [ AdminController :: class , 'reset' ]) -> name ( 'v1.admin.users.password.reset' ); // 테넌트 사용자 비밀번호 초기화
2025-12-02 22:11:08 +09:00
// 글로벌 메뉴 관리 (시스템 관리자용)
Route :: prefix ( 'global-menus' ) -> group ( function () {
Route :: get ( '/' , [ GlobalMenuController :: class , 'index' ]) -> name ( 'v1.admin.global-menus.index' ); // 글로벌 메뉴 목록
Route :: post ( '/' , [ GlobalMenuController :: class , 'store' ]) -> name ( 'v1.admin.global-menus.store' ); // 글로벌 메뉴 생성
Route :: get ( '/tree' , [ GlobalMenuController :: class , 'tree' ]) -> name ( 'v1.admin.global-menus.tree' ); // 글로벌 메뉴 트리
Route :: get ( '/stats' , [ GlobalMenuController :: class , 'stats' ]) -> name ( 'v1.admin.global-menus.stats' ); // 글로벌 메뉴 통계
Route :: post ( '/reorder' , [ GlobalMenuController :: class , 'reorder' ]) -> name ( 'v1.admin.global-menus.reorder' ); // 글로벌 메뉴 순서 변경
Route :: get ( '/{id}' , [ GlobalMenuController :: class , 'show' ]) -> name ( 'v1.admin.global-menus.show' ); // 글로벌 메뉴 단건 조회
Route :: put ( '/{id}' , [ GlobalMenuController :: class , 'update' ]) -> name ( 'v1.admin.global-menus.update' ); // 글로벌 메뉴 수정
Route :: delete ( '/{id}' , [ GlobalMenuController :: class , 'destroy' ]) -> name ( 'v1.admin.global-menus.destroy' ); // 글로벌 메뉴 삭제
Route :: post ( '/{id}/sync-to-tenants' , [ GlobalMenuController :: class , 'syncToTenants' ]) -> name ( 'v1.admin.global-menus.sync-to-tenants' ); // 테넌트에 동기화
});
2025-08-15 16:32:11 +09:00
});
2025-07-18 11:37:07 +09:00
// Member API
2025-08-14 00:55:08 +09:00
Route :: prefix ( 'users' ) -> group ( function () {
Route :: get ( 'index' , [ UserController :: class , 'index' ]) -> name ( 'v1.users.index' ); // 회원 목록 조회
Route :: get ( 'show/{user_no}' , [ UserController :: class , 'show' ]) -> name ( 'v1.users.show' ); // 회원 상세 조회
Route :: get ( 'me' , [ UserController :: class , 'me' ]) -> name ( 'v1.users.users.me' ); // 내 정보 조회
2025-08-18 16:37:02 +09:00
Route :: put ( 'me' , [ UserController :: class , 'meUpdate' ]) -> name ( 'v1.users.me.update' ); // 내 정보 수정
2025-08-14 00:55:08 +09:00
Route :: put ( 'me/password' , [ UserController :: class , 'changePassword' ]) -> name ( 'v1.users.me.password' ); // 비밀번호 변겅
2025-08-18 16:37:02 +09:00
Route :: get ( 'me/tenants' , [ UserController :: class , 'tenants' ]) -> name ( 'v1.users.me.tenants.index' ); // 내 테넌트 목록
2025-08-13 18:34:28 +09:00
Route :: patch ( 'me/tenants/switch' , [ UserController :: class , 'switchTenant' ]) -> name ( 'v1.users.me.tenants.switch' ); // 활성 테넌트 전환
feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능:
- UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 초대 발송/수락/취소/재발송 API
5.2 알림설정 확장:
- NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 채널별/유형별 알림 설정 관리
5.3 계정정보 수정 API:
- 회원탈퇴, 사용중지, 약관동의 관리
- AccountService, AccountController, Swagger
5.4 매출 거래명세서 API:
- 거래명세서 조회/발행/이메일발송
- SaleService 확장, Swagger 문서화
2025-12-19 14:52:53 +09:00
// 사용자 초대 API
Route :: get ( 'invitations' , [ UserInvitationController :: class , 'index' ]) -> name ( 'v1.users.invitations.index' ); // 초대 목록
Route :: post ( 'invite' , [ UserInvitationController :: class , 'invite' ]) -> name ( 'v1.users.invite' ); // 초대 발송
Route :: post ( 'invitations/{token}/accept' , [ UserInvitationController :: class , 'accept' ]) -> name ( 'v1.users.invitations.accept' ); // 초대 수락
Route :: delete ( 'invitations/{id}' , [ UserInvitationController :: class , 'cancel' ]) -> whereNumber ( 'id' ) -> name ( 'v1.users.invitations.cancel' ); // 초대 취소
Route :: post ( 'invitations/{id}/resend' , [ UserInvitationController :: class , 'resend' ]) -> whereNumber ( 'id' ) -> name ( 'v1.users.invitations.resend' ); // 초대 재발송
// 알림 설정 API (auth:sanctum 필수)
Route :: middleware ( 'auth:sanctum' ) -> group ( function () {
Route :: get ( 'me/notification-settings' , [ NotificationSettingController :: class , 'index' ]) -> name ( 'v1.users.me.notification-settings.index' ); // 알림 설정 조회
Route :: put ( 'me/notification-settings' , [ NotificationSettingController :: class , 'update' ]) -> name ( 'v1.users.me.notification-settings.update' ); // 알림 설정 수정
Route :: put ( 'me/notification-settings/bulk' , [ NotificationSettingController :: class , 'bulkUpdate' ]) -> name ( 'v1.users.me.notification-settings.bulk' ); // 알림 일괄 설정
});
});
// Account API (계정 관리 - 탈퇴, 사용중지, 약관동의)
Route :: prefix ( 'account' ) -> middleware ( 'auth:sanctum' ) -> group ( function () {
Route :: post ( 'withdraw' , [ AccountController :: class , 'withdraw' ]) -> name ( 'v1.account.withdraw' ); // 회원 탈퇴
Route :: post ( 'suspend' , [ AccountController :: class , 'suspend' ]) -> name ( 'v1.account.suspend' ); // 사용 중지 (테넌트)
Route :: get ( 'agreements' , [ AccountController :: class , 'getAgreements' ]) -> name ( 'v1.account.agreements.index' ); // 약관 동의 조회
Route :: put ( 'agreements' , [ AccountController :: class , 'updateAgreements' ]) -> name ( 'v1.account.agreements.update' ); // 약관 동의 수정
2025-08-14 00:55:08 +09:00
});
2025-07-17 10:05:47 +09:00
2025-08-14 17:20:28 +09:00
// Tenant API
Route :: prefix ( 'tenants' ) -> group ( function () {
Route :: get ( 'list' , [ TenantController :: class , 'index' ]) -> name ( 'v1.tenant.index' ); // 테넌트 목록 조회
Route :: get ( '/' , [ TenantController :: class , 'show' ]) -> name ( 'v1.tenant.show' ); // 테넌트 정보 조회
Route :: put ( '/' , [ TenantController :: class , 'update' ]) -> name ( 'v1.tenant.update' ); // 테넌트 정보 수정
Route :: post ( '/' , [ TenantController :: class , 'store' ]) -> name ( 'v1.tenant.store' ); // 테넌트 등록
Route :: delete ( '/' , [ TenantController :: class , 'destroy' ]) -> name ( 'v1.tenant.destroy' ); // 테넌트 삭제(탈퇴)
Route :: put ( '/restore/{tenant_id}' , [ TenantController :: class , 'restore' ]) -> name ( 'v1.tenant.restore' ); // 테넌트 복구
2025-12-30 22:58:50 +09:00
Route :: post ( '/logo' , [ TenantController :: class , 'uploadLogo' ]) -> name ( 'v1.tenant.upload-logo' ); // 로고 업로드
2025-08-14 17:20:28 +09:00
});
2025-11-14 14:09:53 +09:00
// Tenant Statistics Field API
Route :: prefix ( 'tenant-stat-fields' ) -> group ( function () {
Route :: get ( '/' , [ TenantStatFieldController :: class , 'index' ]) -> name ( 'v1.tenant-stat-fields.index' ); // 목록 조회
Route :: post ( '/' , [ TenantStatFieldController :: class , 'store' ]) -> name ( 'v1.tenant-stat-fields.store' ); // 생성
Route :: get ( '/{id}' , [ TenantStatFieldController :: class , 'show' ]) -> name ( 'v1.tenant-stat-fields.show' ); // 단건 조회
Route :: patch ( '/{id}' , [ TenantStatFieldController :: class , 'update' ]) -> name ( 'v1.tenant-stat-fields.update' ); // 수정
Route :: delete ( '/{id}' , [ TenantStatFieldController :: class , 'destroy' ]) -> name ( 'v1.tenant-stat-fields.destroy' ); // 삭제
Route :: post ( '/reorder' , [ TenantStatFieldController :: class , 'reorder' ]) -> name ( 'v1.tenant-stat-fields.reorder' ); // 정렬 변경
Route :: put ( '/bulk-upsert' , [ TenantStatFieldController :: class , 'bulkUpsert' ]) -> name ( 'v1.tenant-stat-fields.bulk-upsert' ); // 일괄 저장
});
2026-01-26 20:29:22 +09:00
// Tenant Settings API (테넌트별 설정)
Route :: prefix ( 'tenant-settings' ) -> group ( function () {
Route :: get ( '/' , [ TenantSettingController :: class , 'index' ]) -> name ( 'v1.tenant-settings.index' ); // 전체 설정 조회
Route :: post ( '/' , [ TenantSettingController :: class , 'store' ]) -> name ( 'v1.tenant-settings.store' ); // 설정 저장
Route :: put ( '/bulk' , [ TenantSettingController :: class , 'bulkUpdate' ]) -> name ( 'v1.tenant-settings.bulk' ); // 일괄 저장
Route :: post ( '/initialize' , [ TenantSettingController :: class , 'initialize' ]) -> name ( 'v1.tenant-settings.initialize' ); // 기본 설정 초기화
Route :: get ( '/{group}/{key}' , [ TenantSettingController :: class , 'show' ]) -> name ( 'v1.tenant-settings.show' ); // 단일 설정 조회
Route :: delete ( '/{group}/{key}' , [ TenantSettingController :: class , 'destroy' ]) -> name ( 'v1.tenant-settings.destroy' ); // 설정 삭제
});
2025-08-16 03:25:06 +09:00
// Menu API
Route :: middleware ([ 'perm.map' , 'permission' ]) -> prefix ( 'menus' ) -> group ( function () {
2025-08-22 18:08:57 +09:00
Route :: get ( '/' , [ MenuController :: class , 'index' ]) -> name ( 'v1.menus.index' );
Route :: post ( '/' , [ MenuController :: class , 'store' ]) -> name ( 'v1.menus.store' );
2025-12-02 22:11:08 +09:00
Route :: post ( '/reorder' , [ MenuController :: class , 'reorder' ]) -> name ( 'v1.menus.reorder' );
// 동기화 관련 라우트 (/{id} 전에 위치해야 함)
Route :: get ( '/trashed' , [ MenuController :: class , 'trashed' ]) -> name ( 'v1.menus.trashed' );
Route :: get ( '/available-global' , [ MenuController :: class , 'availableGlobal' ]) -> name ( 'v1.menus.available-global' );
Route :: get ( '/sync-status' , [ MenuController :: class , 'syncStatus' ]) -> name ( 'v1.menus.sync-status' );
Route :: post ( '/sync' , [ MenuController :: class , 'sync' ]) -> name ( 'v1.menus.sync' );
Route :: post ( '/sync-new' , [ MenuController :: class , 'syncNew' ]) -> name ( 'v1.menus.sync-new' );
Route :: post ( '/sync-updates' , [ MenuController :: class , 'syncUpdates' ]) -> name ( 'v1.menus.sync-updates' );
// 단일 메뉴 관련 라우트
Route :: get ( '/{id}' , [ MenuController :: class , 'show' ]) -> name ( 'v1.menus.show' );
2025-08-22 18:08:57 +09:00
Route :: patch ( '/{id}' , [ MenuController :: class , 'update' ]) -> name ( 'v1.menus.update' );
Route :: delete ( '/{id}' , [ MenuController :: class , 'destroy' ]) -> name ( 'v1.menus.destroy' );
Route :: post ( '/{id}/toggle' , [ MenuController :: class , 'toggle' ]) -> name ( 'v1.menus.toggle' );
2025-12-02 22:11:08 +09:00
Route :: post ( '/{id}/restore' , [ MenuController :: class , 'restore' ]) -> name ( 'v1.menus.restore' );
2025-08-16 03:25:06 +09:00
});
2025-07-17 10:05:47 +09:00
2025-08-16 03:25:06 +09:00
// Role API
Route :: prefix ( 'roles' ) -> group ( function () {
2025-08-22 18:08:57 +09:00
Route :: get ( '/' , [ RoleController :: class , 'index' ]) -> name ( 'v1.roles.index' ); // view
Route :: post ( '/' , [ RoleController :: class , 'store' ]) -> name ( 'v1.roles.store' ); // create
2025-12-30 17:25:29 +09:00
Route :: get ( '/stats' , [ RoleController :: class , 'stats' ]) -> name ( 'v1.roles.stats' ); // stats
Route :: get ( '/active' , [ RoleController :: class , 'active' ]) -> name ( 'v1.roles.active' ); // active list
2025-08-22 18:08:57 +09:00
Route :: get ( '/{id}' , [ RoleController :: class , 'show' ]) -> name ( 'v1.roles.show' ); // view
Route :: patch ( '/{id}' , [ RoleController :: class , 'update' ]) -> name ( 'v1.roles.update' ); // update
2025-11-06 17:24:42 +09:00
Route :: delete ( '/{id}' , [ RoleController :: class , 'destroy' ]) -> name ( 'v1.roles.destroy' ); // delete
2025-08-16 03:25:06 +09:00
});
2025-08-14 00:55:08 +09:00
2025-12-30 17:25:29 +09:00
// Role Permission API - 공통
Route :: get ( '/role-permissions/menus' , [ RolePermissionController :: class , 'menus' ]) -> name ( 'v1.roles.perms.menus' ); // 메뉴 트리
// Role Permission API - 역할별
2025-08-16 03:25:06 +09:00
Route :: prefix ( 'roles/{id}/permissions' ) -> group ( function () {
2025-08-22 18:08:57 +09:00
Route :: get ( '/' , [ RolePermissionController :: class , 'index' ]) -> name ( 'v1.roles.perms.index' ); // list
Route :: post ( '/' , [ RolePermissionController :: class , 'grant' ]) -> name ( 'v1.roles.perms.grant' ); // grant
Route :: delete ( '/' , [ RolePermissionController :: class , 'revoke' ]) -> name ( 'v1.roles.perms.revoke' ); // revoke
Route :: put ( '/sync' , [ RolePermissionController :: class , 'sync' ]) -> name ( 'v1.roles.perms.sync' ); // sync
2025-12-30 17:25:29 +09:00
// 권한 매트릭스 API
Route :: get ( '/matrix' , [ RolePermissionController :: class , 'matrix' ]) -> name ( 'v1.roles.perms.matrix' ); // 권한 매트릭스 조회
Route :: post ( '/toggle' , [ RolePermissionController :: class , 'toggle' ]) -> name ( 'v1.roles.perms.toggle' ); // 개별 권한 토글
Route :: post ( '/allow-all' , [ RolePermissionController :: class , 'allowAll' ]) -> name ( 'v1.roles.perms.allowAll' ); // 전체 허용
Route :: post ( '/deny-all' , [ RolePermissionController :: class , 'denyAll' ]) -> name ( 'v1.roles.perms.denyAll' ); // 전체 거부
Route :: post ( '/reset' , [ RolePermissionController :: class , 'reset' ]) -> name ( 'v1.roles.perms.reset' ); // 기본값 초기화
2025-08-16 03:25:06 +09:00
});
// User Role API
Route :: prefix ( 'users/{id}/roles' ) -> group ( function () {
2025-08-22 18:08:57 +09:00
Route :: get ( '/' , [ UserRoleController :: class , 'index' ]) -> name ( 'v1.users.roles.index' ); // list
Route :: post ( '/' , [ UserRoleController :: class , 'grant' ]) -> name ( 'v1.users.roles.grant' ); // grant
Route :: delete ( '/' , [ UserRoleController :: class , 'revoke' ]) -> name ( 'v1.users.roles.revoke' ); // revoke
Route :: put ( '/sync' , [ UserRoleController :: class , 'sync' ]) -> name ( 'v1.users.roles.sync' ); // sync
2025-08-16 03:25:06 +09:00
});
// Department API
Route :: prefix ( 'departments' ) -> group ( function () {
2025-09-24 20:35:17 +09:00
Route :: get ( '' , [ DepartmentController :: class , 'index' ]) -> name ( 'v1.departments.index' ); // 목록
Route :: post ( '' , [ DepartmentController :: class , 'store' ]) -> name ( 'v1.departments.store' ); // 생성
2025-12-09 20:27:44 +09:00
Route :: get ( '/tree' , [ DepartmentController :: class , 'tree' ]) -> name ( 'v1.departments.tree' ); // 트리
2025-09-24 20:35:17 +09:00
Route :: get ( '/{id}' , [ DepartmentController :: class , 'show' ]) -> name ( 'v1.departments.show' ); // 단건
Route :: patch ( '/{id}' , [ DepartmentController :: class , 'update' ]) -> name ( 'v1.departments.update' ); // 수정
Route :: delete ( '/{id}' , [ DepartmentController :: class , 'destroy' ]) -> name ( 'v1.departments.destroy' ); // 삭제(soft)
2025-08-16 03:25:06 +09:00
// 부서-사용자
2025-09-24 20:35:17 +09:00
Route :: get ( '/{id}/users' , [ DepartmentController :: class , 'listUsers' ]) -> name ( 'v1.departments.users.index' ); // 부서 사용자 목록
Route :: post ( '/{id}/users' , [ DepartmentController :: class , 'attachUser' ]) -> name ( 'v1.departments.users.attach' ); // 사용자 배정(주/부서)
Route :: delete ( '/{id}/users/{user}' , [ DepartmentController :: class , 'detachUser' ]) -> name ( 'v1.departments.users.detach' ); // 사용자 제거
Route :: patch ( '/{id}/users/{user}/primary' , [ DepartmentController :: class , 'setPrimary' ]) -> name ( 'v1.departments.users.primary' ); // 주부서 설정/해제
2025-08-16 03:25:06 +09:00
// 부서-권한
2025-09-24 20:35:17 +09:00
Route :: get ( '/{id}/permissions' , [ DepartmentController :: class , 'listPermissions' ]) -> name ( 'v1.departments.permissions.index' ); // 권한 목록
Route :: post ( '/{id}/permissions' , [ DepartmentController :: class , 'upsertPermissions' ]) -> name ( 'v1.departments.permissions.upsert' ); // 권한 부여/차단(메뉴별 가능)
Route :: delete ( '/{id}/permissions/{permission}' , [ DepartmentController :: class , 'revokePermissions' ]) -> name ( 'v1.departments.permissions.revoke' ); // 권한 제거(해당 메뉴 범위까지)
2025-08-16 03:25:06 +09:00
});
2025-12-30 11:18:17 +09:00
// Position API (직급/직책 통합 관리)
Route :: prefix ( 'positions' ) -> group ( function () {
Route :: get ( '' , [ PositionController :: class , 'index' ]) -> name ( 'v1.positions.index' );
Route :: post ( '' , [ PositionController :: class , 'store' ]) -> name ( 'v1.positions.store' );
Route :: put ( '/reorder' , [ PositionController :: class , 'reorder' ]) -> name ( 'v1.positions.reorder' );
Route :: get ( '/{id}' , [ PositionController :: class , 'show' ]) -> name ( 'v1.positions.show' );
Route :: put ( '/{id}' , [ PositionController :: class , 'update' ]) -> name ( 'v1.positions.update' );
Route :: delete ( '/{id}' , [ PositionController :: class , 'destroy' ]) -> name ( 'v1.positions.destroy' );
});
2025-08-14 00:55:08 +09:00
2025-12-09 20:27:44 +09:00
// Employee API (사원 관리)
Route :: prefix ( 'employees' ) -> group ( function () {
Route :: get ( '' , [ EmployeeController :: class , 'index' ]) -> name ( 'v1.employees.index' );
Route :: post ( '' , [ EmployeeController :: class , 'store' ]) -> name ( 'v1.employees.store' );
Route :: get ( '/stats' , [ EmployeeController :: class , 'stats' ]) -> name ( 'v1.employees.stats' );
Route :: get ( '/{id}' , [ EmployeeController :: class , 'show' ]) -> name ( 'v1.employees.show' );
Route :: patch ( '/{id}' , [ EmployeeController :: class , 'update' ]) -> name ( 'v1.employees.update' );
Route :: delete ( '/{id}' , [ EmployeeController :: class , 'destroy' ]) -> name ( 'v1.employees.destroy' );
Route :: post ( '/bulk-delete' , [ EmployeeController :: class , 'bulkDelete' ]) -> name ( 'v1.employees.bulkDelete' );
Route :: post ( '/{id}/create-account' , [ EmployeeController :: class , 'createAccount' ]) -> name ( 'v1.employees.createAccount' );
2025-12-26 00:34:21 +09:00
Route :: post ( '/{id}/revoke-account' , [ EmployeeController :: class , 'revokeAccount' ]) -> name ( 'v1.employees.revokeAccount' );
2025-12-09 20:27:44 +09:00
});
// Attendance API (근태 관리)
Route :: prefix ( 'attendances' ) -> group ( function () {
Route :: get ( '' , [ AttendanceController :: class , 'index' ]) -> name ( 'v1.attendances.index' );
Route :: post ( '' , [ AttendanceController :: class , 'store' ]) -> name ( 'v1.attendances.store' );
Route :: get ( '/monthly-stats' , [ AttendanceController :: class , 'monthlyStats' ]) -> name ( 'v1.attendances.monthlyStats' );
2026-01-15 17:14:04 +09:00
Route :: get ( '/export' , [ AttendanceController :: class , 'export' ]) -> name ( 'v1.attendances.export' );
2025-12-09 20:27:44 +09:00
Route :: post ( '/check-in' , [ AttendanceController :: class , 'checkIn' ]) -> name ( 'v1.attendances.checkIn' );
Route :: post ( '/check-out' , [ AttendanceController :: class , 'checkOut' ]) -> name ( 'v1.attendances.checkOut' );
Route :: get ( '/{id}' , [ AttendanceController :: class , 'show' ]) -> name ( 'v1.attendances.show' );
Route :: patch ( '/{id}' , [ AttendanceController :: class , 'update' ]) -> name ( 'v1.attendances.update' );
Route :: delete ( '/{id}' , [ AttendanceController :: class , 'destroy' ]) -> name ( 'v1.attendances.destroy' );
Route :: post ( '/bulk-delete' , [ AttendanceController :: class , 'bulkDelete' ]) -> name ( 'v1.attendances.bulkDelete' );
});
2025-12-17 20:13:48 +09:00
// Leave API (휴가 관리)
Route :: prefix ( 'leaves' ) -> group ( function () {
Route :: get ( '' , [ LeaveController :: class , 'index' ]) -> name ( 'v1.leaves.index' );
Route :: post ( '' , [ LeaveController :: class , 'store' ]) -> name ( 'v1.leaves.store' );
2025-12-24 19:29:15 +09:00
Route :: get ( '/balances' , [ LeaveController :: class , 'balances' ]) -> name ( 'v1.leaves.balances' );
2025-12-17 20:13:48 +09:00
Route :: get ( '/balance' , [ LeaveController :: class , 'balance' ]) -> name ( 'v1.leaves.balance' );
Route :: get ( '/balance/{userId}' , [ LeaveController :: class , 'userBalance' ]) -> name ( 'v1.leaves.userBalance' );
Route :: put ( '/balance' , [ LeaveController :: class , 'setBalance' ]) -> name ( 'v1.leaves.setBalance' );
2025-12-24 19:39:33 +09:00
Route :: get ( '/grants' , [ LeaveController :: class , 'grants' ]) -> name ( 'v1.leaves.grants' );
Route :: post ( '/grants' , [ LeaveController :: class , 'storeGrant' ]) -> name ( 'v1.leaves.grants.store' );
Route :: delete ( '/grants/{id}' , [ LeaveController :: class , 'destroyGrant' ]) -> name ( 'v1.leaves.grants.destroy' );
2025-12-17 20:13:48 +09:00
Route :: get ( '/{id}' , [ LeaveController :: class , 'show' ]) -> name ( 'v1.leaves.show' );
Route :: patch ( '/{id}' , [ LeaveController :: class , 'update' ]) -> name ( 'v1.leaves.update' );
Route :: delete ( '/{id}' , [ LeaveController :: class , 'destroy' ]) -> name ( 'v1.leaves.destroy' );
Route :: post ( '/{id}/approve' , [ LeaveController :: class , 'approve' ]) -> name ( 'v1.leaves.approve' );
Route :: post ( '/{id}/reject' , [ LeaveController :: class , 'reject' ]) -> name ( 'v1.leaves.reject' );
Route :: post ( '/{id}/cancel' , [ LeaveController :: class , 'cancel' ]) -> name ( 'v1.leaves.cancel' );
});
2025-12-26 15:18:08 +09:00
// Leave Policy API (휴가 정책)
Route :: get ( '/leave-policy' , [ LeavePolicyController :: class , 'show' ]) -> name ( 'v1.leave-policy.show' );
Route :: put ( '/leave-policy' , [ LeavePolicyController :: class , 'update' ]) -> name ( 'v1.leave-policy.update' );
feat: [approval] 전자결재 모듈 API 구현
- 마이그레이션 4개 (approval_forms, approval_lines, approvals, approval_steps)
- 모델 4개 (ApprovalForm, ApprovalLine, Approval, ApprovalStep)
- ApprovalService 비즈니스 로직 (양식/결재선 CRUD, 기안함/결재함/참조함, 결재 액션)
- 컨트롤러 3개 (ApprovalFormController, ApprovalLineController, ApprovalController)
- FormRequest 13개 (양식/결재선/문서 검증)
- Swagger 문서 3개 (26개 엔드포인트)
- i18n 메시지/에러 키 추가
- 라우트 26개 등록
2025-12-17 23:23:20 +09:00
// Approval Form API (결재 양식)
Route :: prefix ( 'approval-forms' ) -> group ( function () {
Route :: get ( '' , [ ApprovalFormController :: class , 'index' ]) -> name ( 'v1.approval-forms.index' );
Route :: post ( '' , [ ApprovalFormController :: class , 'store' ]) -> name ( 'v1.approval-forms.store' );
Route :: get ( '/active' , [ ApprovalFormController :: class , 'active' ]) -> name ( 'v1.approval-forms.active' );
Route :: get ( '/{id}' , [ ApprovalFormController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approval-forms.show' );
Route :: patch ( '/{id}' , [ ApprovalFormController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approval-forms.update' );
Route :: delete ( '/{id}' , [ ApprovalFormController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approval-forms.destroy' );
});
// Approval Line API (결재선)
Route :: prefix ( 'approval-lines' ) -> group ( function () {
Route :: get ( '' , [ ApprovalLineController :: class , 'index' ]) -> name ( 'v1.approval-lines.index' );
Route :: post ( '' , [ ApprovalLineController :: class , 'store' ]) -> name ( 'v1.approval-lines.store' );
Route :: get ( '/{id}' , [ ApprovalLineController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approval-lines.show' );
Route :: patch ( '/{id}' , [ ApprovalLineController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approval-lines.update' );
Route :: delete ( '/{id}' , [ ApprovalLineController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approval-lines.destroy' );
});
// Approval API (전자결재)
Route :: prefix ( 'approvals' ) -> group ( function () {
// 기안함
Route :: get ( '/drafts' , [ ApprovalController :: class , 'drafts' ]) -> name ( 'v1.approvals.drafts' );
Route :: get ( '/drafts/summary' , [ ApprovalController :: class , 'draftsSummary' ]) -> name ( 'v1.approvals.drafts.summary' );
// 결재함
Route :: get ( '/inbox' , [ ApprovalController :: class , 'inbox' ]) -> name ( 'v1.approvals.inbox' );
Route :: get ( '/inbox/summary' , [ ApprovalController :: class , 'inboxSummary' ]) -> name ( 'v1.approvals.inbox.summary' );
// 참조함
Route :: get ( '/reference' , [ ApprovalController :: class , 'reference' ]) -> name ( 'v1.approvals.reference' );
// CRUD
Route :: post ( '' , [ ApprovalController :: class , 'store' ]) -> name ( 'v1.approvals.store' );
Route :: get ( '/{id}' , [ ApprovalController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approvals.show' );
Route :: patch ( '/{id}' , [ ApprovalController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approvals.update' );
Route :: delete ( '/{id}' , [ ApprovalController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approvals.destroy' );
// 액션
Route :: post ( '/{id}/submit' , [ ApprovalController :: class , 'submit' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approvals.submit' );
Route :: post ( '/{id}/approve' , [ ApprovalController :: class , 'approve' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approvals.approve' );
Route :: post ( '/{id}/reject' , [ ApprovalController :: class , 'reject' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approvals.reject' );
Route :: post ( '/{id}/cancel' , [ ApprovalController :: class , 'cancel' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approvals.cancel' );
// 참조 열람
Route :: post ( '/{id}/read' , [ ApprovalController :: class , 'markRead' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approvals.read' );
Route :: post ( '/{id}/unread' , [ ApprovalController :: class , 'markUnread' ]) -> whereNumber ( 'id' ) -> name ( 'v1.approvals.unread' );
});
feat: 근무/출퇴근 설정 및 현장 관리 API 구현
- 근무 설정 API (GET/PUT /settings/work)
- 근무유형, 소정근로시간, 연장근로시간, 근무요일, 출퇴근시간, 휴게시간
- 출퇴근 설정 API (GET/PUT /settings/attendance)
- GPS 출퇴근, 허용 반경, 본사 위치 설정
- 현장 관리 API (CRUD /sites)
- 현장 등록/수정/삭제, 활성화된 현장 목록(셀렉트박스용)
- GPS 좌표 기반 위치 관리
마이그레이션: work_settings, attendance_settings, sites 테이블
모델: WorkSetting, AttendanceSetting, Site (BelongsToTenant, SoftDeletes)
서비스: WorkSettingService, SiteService
Swagger 문서 및 i18n 메시지 키 추가
2025-12-17 20:46:37 +09:00
// Site API (현장 관리)
Route :: prefix ( 'sites' ) -> group ( function () {
Route :: get ( '' , [ SiteController :: class , 'index' ]) -> name ( 'v1.sites.index' );
Route :: post ( '' , [ SiteController :: class , 'store' ]) -> name ( 'v1.sites.store' );
2026-01-09 16:34:59 +09:00
Route :: get ( '/stats' , [ SiteController :: class , 'stats' ]) -> name ( 'v1.sites.stats' );
feat: 근무/출퇴근 설정 및 현장 관리 API 구현
- 근무 설정 API (GET/PUT /settings/work)
- 근무유형, 소정근로시간, 연장근로시간, 근무요일, 출퇴근시간, 휴게시간
- 출퇴근 설정 API (GET/PUT /settings/attendance)
- GPS 출퇴근, 허용 반경, 본사 위치 설정
- 현장 관리 API (CRUD /sites)
- 현장 등록/수정/삭제, 활성화된 현장 목록(셀렉트박스용)
- GPS 좌표 기반 위치 관리
마이그레이션: work_settings, attendance_settings, sites 테이블
모델: WorkSetting, AttendanceSetting, Site (BelongsToTenant, SoftDeletes)
서비스: WorkSettingService, SiteService
Swagger 문서 및 i18n 메시지 키 추가
2025-12-17 20:46:37 +09:00
Route :: get ( '/active' , [ SiteController :: class , 'active' ]) -> name ( 'v1.sites.active' );
2026-01-09 16:34:59 +09:00
Route :: delete ( '/bulk' , [ SiteController :: class , 'bulkDestroy' ]) -> name ( 'v1.sites.bulk-destroy' );
feat: 근무/출퇴근 설정 및 현장 관리 API 구현
- 근무 설정 API (GET/PUT /settings/work)
- 근무유형, 소정근로시간, 연장근로시간, 근무요일, 출퇴근시간, 휴게시간
- 출퇴근 설정 API (GET/PUT /settings/attendance)
- GPS 출퇴근, 허용 반경, 본사 위치 설정
- 현장 관리 API (CRUD /sites)
- 현장 등록/수정/삭제, 활성화된 현장 목록(셀렉트박스용)
- GPS 좌표 기반 위치 관리
마이그레이션: work_settings, attendance_settings, sites 테이블
모델: WorkSetting, AttendanceSetting, Site (BelongsToTenant, SoftDeletes)
서비스: WorkSettingService, SiteService
Swagger 문서 및 i18n 메시지 키 추가
2025-12-17 20:46:37 +09:00
Route :: get ( '/{id}' , [ SiteController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.sites.show' );
Route :: put ( '/{id}' , [ SiteController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.sites.update' );
Route :: delete ( '/{id}' , [ SiteController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.sites.destroy' );
});
2026-01-13 16:00:47 +09:00
// Site Briefing API (현장설명회 관리)
Route :: prefix ( 'site-briefings' ) -> group ( function () {
Route :: get ( '' , [ SiteBriefingController :: class , 'index' ]) -> name ( 'v1.site-briefings.index' );
Route :: post ( '' , [ SiteBriefingController :: class , 'store' ]) -> name ( 'v1.site-briefings.store' );
Route :: get ( '/stats' , [ SiteBriefingController :: class , 'stats' ]) -> name ( 'v1.site-briefings.stats' );
Route :: delete ( '/bulk' , [ SiteBriefingController :: class , 'bulkDestroy' ]) -> name ( 'v1.site-briefings.bulk-destroy' );
Route :: get ( '/{id}' , [ SiteBriefingController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.site-briefings.show' );
Route :: put ( '/{id}' , [ SiteBriefingController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.site-briefings.update' );
Route :: delete ( '/{id}' , [ SiteBriefingController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.site-briefings.destroy' );
});
2026-01-09 10:18:43 +09:00
// Construction API (시공관리)
Route :: prefix ( 'construction' ) -> group ( function () {
// Contract API (계약관리)
Route :: prefix ( 'contracts' ) -> group ( function () {
Route :: get ( '' , [ ContractController :: class , 'index' ]) -> name ( 'v1.construction.contracts.index' );
Route :: post ( '' , [ ContractController :: class , 'store' ]) -> name ( 'v1.construction.contracts.store' );
Route :: get ( '/stats' , [ ContractController :: class , 'stats' ]) -> name ( 'v1.construction.contracts.stats' );
Route :: get ( '/stage-counts' , [ ContractController :: class , 'stageCounts' ]) -> name ( 'v1.construction.contracts.stage-counts' );
Route :: delete ( '/bulk' , [ ContractController :: class , 'bulkDestroy' ]) -> name ( 'v1.construction.contracts.bulk-destroy' );
2026-01-19 20:53:36 +09:00
Route :: post ( '/from-bidding/{biddingId}' , [ ContractController :: class , 'storeFromBidding' ]) -> whereNumber ( 'biddingId' ) -> name ( 'v1.construction.contracts.store-from-bidding' );
2026-01-09 10:18:43 +09:00
Route :: get ( '/{id}' , [ ContractController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.construction.contracts.show' );
Route :: put ( '/{id}' , [ ContractController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.construction.contracts.update' );
Route :: delete ( '/{id}' , [ ContractController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.construction.contracts.destroy' );
});
2026-01-09 16:34:59 +09:00
// HandoverReport API (인수인계보고서관리)
Route :: prefix ( 'handover-reports' ) -> group ( function () {
Route :: get ( '' , [ HandoverReportController :: class , 'index' ]) -> name ( 'v1.construction.handover-reports.index' );
Route :: post ( '' , [ HandoverReportController :: class , 'store' ]) -> name ( 'v1.construction.handover-reports.store' );
Route :: get ( '/stats' , [ HandoverReportController :: class , 'stats' ]) -> name ( 'v1.construction.handover-reports.stats' );
Route :: delete ( '/bulk' , [ HandoverReportController :: class , 'bulkDestroy' ]) -> name ( 'v1.construction.handover-reports.bulk-destroy' );
Route :: get ( '/{id}' , [ HandoverReportController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.construction.handover-reports.show' );
Route :: put ( '/{id}' , [ HandoverReportController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.construction.handover-reports.update' );
Route :: delete ( '/{id}' , [ HandoverReportController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.construction.handover-reports.destroy' );
});
2026-01-09 21:31:27 +09:00
// StructureReview API (구조검토관리)
Route :: prefix ( 'structure-reviews' ) -> group ( function () {
Route :: get ( '' , [ StructureReviewController :: class , 'index' ]) -> name ( 'v1.construction.structure-reviews.index' );
Route :: post ( '' , [ StructureReviewController :: class , 'store' ]) -> name ( 'v1.construction.structure-reviews.store' );
Route :: get ( '/stats' , [ StructureReviewController :: class , 'stats' ]) -> name ( 'v1.construction.structure-reviews.stats' );
Route :: delete ( '/bulk' , [ StructureReviewController :: class , 'bulkDestroy' ]) -> name ( 'v1.construction.structure-reviews.bulk-destroy' );
Route :: get ( '/{id}' , [ StructureReviewController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.construction.structure-reviews.show' );
Route :: put ( '/{id}' , [ StructureReviewController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.construction.structure-reviews.update' );
Route :: delete ( '/{id}' , [ StructureReviewController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.construction.structure-reviews.destroy' );
});
2026-01-09 10:18:43 +09:00
});
2025-12-17 21:02:20 +09:00
// Card API (카드 관리)
Route :: prefix ( 'cards' ) -> group ( function () {
Route :: get ( '' , [ CardController :: class , 'index' ]) -> name ( 'v1.cards.index' );
Route :: post ( '' , [ CardController :: class , 'store' ]) -> name ( 'v1.cards.store' );
Route :: get ( '/active' , [ CardController :: class , 'active' ]) -> name ( 'v1.cards.active' );
Route :: get ( '/{id}' , [ CardController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.cards.show' );
Route :: put ( '/{id}' , [ CardController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.cards.update' );
Route :: delete ( '/{id}' , [ CardController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.cards.destroy' );
Route :: patch ( '/{id}/toggle' , [ CardController :: class , 'toggle' ]) -> whereNumber ( 'id' ) -> name ( 'v1.cards.toggle' );
});
// BankAccount API (계좌 관리)
Route :: prefix ( 'bank-accounts' ) -> group ( function () {
Route :: get ( '' , [ BankAccountController :: class , 'index' ]) -> name ( 'v1.bank-accounts.index' );
Route :: post ( '' , [ BankAccountController :: class , 'store' ]) -> name ( 'v1.bank-accounts.store' );
Route :: get ( '/active' , [ BankAccountController :: class , 'active' ]) -> name ( 'v1.bank-accounts.active' );
Route :: get ( '/{id}' , [ BankAccountController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bank-accounts.show' );
Route :: put ( '/{id}' , [ BankAccountController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bank-accounts.update' );
Route :: delete ( '/{id}' , [ BankAccountController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bank-accounts.destroy' );
Route :: patch ( '/{id}/toggle' , [ BankAccountController :: class , 'toggle' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bank-accounts.toggle' );
Route :: patch ( '/{id}/set-primary' , [ BankAccountController :: class , 'setPrimary' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bank-accounts.set-primary' );
});
2025-12-17 21:47:15 +09:00
// Deposit API (입금 관리)
Route :: prefix ( 'deposits' ) -> group ( function () {
Route :: get ( '' , [ DepositController :: class , 'index' ]) -> name ( 'v1.deposits.index' );
Route :: post ( '' , [ DepositController :: class , 'store' ]) -> name ( 'v1.deposits.store' );
Route :: get ( '/summary' , [ DepositController :: class , 'summary' ]) -> name ( 'v1.deposits.summary' );
2026-01-15 17:14:04 +09:00
Route :: post ( '/bulk-update-account-code' , [ DepositController :: class , 'bulkUpdateAccountCode' ]) -> name ( 'v1.deposits.bulk-update-account-code' );
2025-12-17 21:47:15 +09:00
Route :: get ( '/{id}' , [ DepositController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.deposits.show' );
Route :: put ( '/{id}' , [ DepositController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.deposits.update' );
Route :: delete ( '/{id}' , [ DepositController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.deposits.destroy' );
});
// Withdrawal API (출금 관리)
Route :: prefix ( 'withdrawals' ) -> group ( function () {
Route :: get ( '' , [ WithdrawalController :: class , 'index' ]) -> name ( 'v1.withdrawals.index' );
Route :: post ( '' , [ WithdrawalController :: class , 'store' ]) -> name ( 'v1.withdrawals.store' );
Route :: get ( '/summary' , [ WithdrawalController :: class , 'summary' ]) -> name ( 'v1.withdrawals.summary' );
2026-01-15 17:14:04 +09:00
Route :: post ( '/bulk-update-account-code' , [ WithdrawalController :: class , 'bulkUpdateAccountCode' ]) -> name ( 'v1.withdrawals.bulk-update-account-code' );
2025-12-17 21:47:15 +09:00
Route :: get ( '/{id}' , [ WithdrawalController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.withdrawals.show' );
Route :: put ( '/{id}' , [ WithdrawalController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.withdrawals.update' );
Route :: delete ( '/{id}' , [ WithdrawalController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.withdrawals.destroy' );
});
2025-12-18 10:56:16 +09:00
// Payroll API (급여 관리)
Route :: prefix ( 'payrolls' ) -> group ( function () {
Route :: get ( '' , [ PayrollController :: class , 'index' ]) -> name ( 'v1.payrolls.index' );
Route :: post ( '' , [ PayrollController :: class , 'store' ]) -> name ( 'v1.payrolls.store' );
Route :: get ( '/summary' , [ PayrollController :: class , 'summary' ]) -> name ( 'v1.payrolls.summary' );
Route :: post ( '/calculate' , [ PayrollController :: class , 'calculate' ]) -> name ( 'v1.payrolls.calculate' );
Route :: post ( '/bulk-confirm' , [ PayrollController :: class , 'bulkConfirm' ]) -> name ( 'v1.payrolls.bulk-confirm' );
Route :: get ( '/{id}' , [ PayrollController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.payrolls.show' );
Route :: put ( '/{id}' , [ PayrollController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.payrolls.update' );
Route :: delete ( '/{id}' , [ PayrollController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.payrolls.destroy' );
Route :: post ( '/{id}/confirm' , [ PayrollController :: class , 'confirm' ]) -> whereNumber ( 'id' ) -> name ( 'v1.payrolls.confirm' );
Route :: post ( '/{id}/pay' , [ PayrollController :: class , 'pay' ]) -> whereNumber ( 'id' ) -> name ( 'v1.payrolls.pay' );
Route :: get ( '/{id}/payslip' , [ PayrollController :: class , 'payslip' ]) -> whereNumber ( 'id' ) -> name ( 'v1.payrolls.payslip' );
});
2025-12-25 03:48:32 +09:00
// Salary API (급여 관리 - React 연동)
Route :: prefix ( 'salaries' ) -> group ( function () {
Route :: get ( '' , [ SalaryController :: class , 'index' ]) -> name ( 'v1.salaries.index' );
Route :: post ( '' , [ SalaryController :: class , 'store' ]) -> name ( 'v1.salaries.store' );
Route :: get ( '/statistics' , [ SalaryController :: class , 'statistics' ]) -> name ( 'v1.salaries.statistics' );
2026-01-15 17:14:04 +09:00
Route :: get ( '/export' , [ SalaryController :: class , 'export' ]) -> name ( 'v1.salaries.export' );
2025-12-25 03:48:32 +09:00
Route :: post ( '/bulk-update-status' , [ SalaryController :: class , 'bulkUpdateStatus' ]) -> name ( 'v1.salaries.bulk-update-status' );
Route :: get ( '/{id}' , [ SalaryController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.salaries.show' );
Route :: put ( '/{id}' , [ SalaryController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.salaries.update' );
Route :: delete ( '/{id}' , [ SalaryController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.salaries.destroy' );
Route :: patch ( '/{id}/status' , [ SalaryController :: class , 'updateStatus' ]) -> whereNumber ( 'id' ) -> name ( 'v1.salaries.update-status' );
});
2025-12-26 13:23:07 +09:00
// Expected Expense API (미지급비용 관리)
Route :: prefix ( 'expected-expenses' ) -> group ( function () {
Route :: get ( '' , [ ExpectedExpenseController :: class , 'index' ]) -> name ( 'v1.expected-expenses.index' );
Route :: post ( '' , [ ExpectedExpenseController :: class , 'store' ]) -> name ( 'v1.expected-expenses.store' );
Route :: get ( '/summary' , [ ExpectedExpenseController :: class , 'summary' ]) -> name ( 'v1.expected-expenses.summary' );
2026-01-22 22:47:59 +09:00
Route :: get ( '/dashboard-detail' , [ ExpectedExpenseController :: class , 'dashboardDetail' ]) -> name ( 'v1.expected-expenses.dashboard-detail' );
2025-12-26 13:23:07 +09:00
Route :: delete ( '' , [ ExpectedExpenseController :: class , 'destroyMany' ]) -> name ( 'v1.expected-expenses.destroy-many' );
Route :: put ( '/update-payment-date' , [ ExpectedExpenseController :: class , 'updateExpectedPaymentDate' ]) -> name ( 'v1.expected-expenses.update-payment-date' );
Route :: get ( '/{id}' , [ ExpectedExpenseController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.expected-expenses.show' );
Route :: put ( '/{id}' , [ ExpectedExpenseController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.expected-expenses.update' );
Route :: delete ( '/{id}' , [ ExpectedExpenseController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.expected-expenses.destroy' );
});
2025-12-18 14:21:37 +09:00
// Loan API (가지급금 관리)
Route :: prefix ( 'loans' ) -> group ( function () {
Route :: get ( '' , [ LoanController :: class , 'index' ]) -> name ( 'v1.loans.index' );
Route :: post ( '' , [ LoanController :: class , 'store' ]) -> name ( 'v1.loans.store' );
Route :: get ( '/summary' , [ LoanController :: class , 'summary' ]) -> name ( 'v1.loans.summary' );
2026-01-22 22:40:00 +09:00
Route :: get ( '/dashboard' , [ LoanController :: class , 'dashboard' ]) -> name ( 'v1.loans.dashboard' );
2026-01-22 22:47:59 +09:00
Route :: get ( '/tax-simulation' , [ LoanController :: class , 'taxSimulation' ]) -> name ( 'v1.loans.tax-simulation' );
2025-12-18 14:21:37 +09:00
Route :: post ( '/calculate-interest' , [ LoanController :: class , 'calculateInterest' ]) -> name ( 'v1.loans.calculate-interest' );
2025-12-18 14:27:10 +09:00
Route :: get ( '/interest-report/{year}' , [ LoanController :: class , 'interestReport' ]) -> whereNumber ( 'year' ) -> name ( 'v1.loans.interest-report' );
2025-12-18 14:21:37 +09:00
Route :: get ( '/{id}' , [ LoanController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.loans.show' );
Route :: put ( '/{id}' , [ LoanController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.loans.update' );
Route :: delete ( '/{id}' , [ LoanController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.loans.destroy' );
Route :: post ( '/{id}/settle' , [ LoanController :: class , 'settle' ]) -> whereNumber ( 'id' ) -> name ( 'v1.loans.settle' );
});
2025-12-26 13:57:42 +09:00
// Vendor Ledger API (거래처원장)
Route :: prefix ( 'vendor-ledger' ) -> group ( function () {
Route :: get ( '' , [ VendorLedgerController :: class , 'index' ]) -> name ( 'v1.vendor-ledger.index' );
Route :: get ( '/summary' , [ VendorLedgerController :: class , 'summary' ]) -> name ( 'v1.vendor-ledger.summary' );
Route :: get ( '/{clientId}' , [ VendorLedgerController :: class , 'show' ]) -> whereNumber ( 'clientId' ) -> name ( 'v1.vendor-ledger.show' );
});
2026-01-22 20:49:29 +09:00
// Card Transaction API (카드 거래)
2025-12-26 13:57:42 +09:00
Route :: prefix ( 'card-transactions' ) -> group ( function () {
Route :: get ( '' , [ CardTransactionController :: class , 'index' ]) -> name ( 'v1.card-transactions.index' );
Route :: get ( '/summary' , [ CardTransactionController :: class , 'summary' ]) -> name ( 'v1.card-transactions.summary' );
2026-01-22 22:28:41 +09:00
Route :: get ( '/dashboard' , [ CardTransactionController :: class , 'dashboard' ]) -> name ( 'v1.card-transactions.dashboard' );
2026-01-22 20:49:29 +09:00
Route :: post ( '' , [ CardTransactionController :: class , 'store' ]) -> name ( 'v1.card-transactions.store' );
2025-12-26 13:57:42 +09:00
Route :: put ( '/bulk-update-account' , [ CardTransactionController :: class , 'bulkUpdateAccountCode' ]) -> name ( 'v1.card-transactions.bulk-update-account' );
Route :: get ( '/{id}' , [ CardTransactionController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.card-transactions.show' );
2026-01-22 20:49:29 +09:00
Route :: put ( '/{id}' , [ CardTransactionController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.card-transactions.update' );
Route :: delete ( '/{id}' , [ CardTransactionController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.card-transactions.destroy' );
2025-12-26 13:57:42 +09:00
});
// Bank Transaction API (은행 거래 조회)
Route :: prefix ( 'bank-transactions' ) -> group ( function () {
Route :: get ( '' , [ BankTransactionController :: class , 'index' ]) -> name ( 'v1.bank-transactions.index' );
Route :: get ( '/summary' , [ BankTransactionController :: class , 'summary' ]) -> name ( 'v1.bank-transactions.summary' );
Route :: get ( '/accounts' , [ BankTransactionController :: class , 'accounts' ]) -> name ( 'v1.bank-transactions.accounts' );
});
2025-12-26 15:18:08 +09:00
// Receivables API (채권 현황)
Route :: prefix ( 'receivables' ) -> group ( function () {
Route :: get ( '' , [ ReceivablesController :: class , 'index' ]) -> name ( 'v1.receivables.index' );
Route :: get ( '/summary' , [ ReceivablesController :: class , 'summary' ]) -> name ( 'v1.receivables.summary' );
2026-01-02 13:13:50 +09:00
Route :: put ( '/overdue-status' , [ ReceivablesController :: class , 'updateOverdueStatus' ]) -> name ( 'v1.receivables.update-overdue-status' );
2026-01-02 14:47:51 +09:00
Route :: put ( '/memos' , [ ReceivablesController :: class , 'updateMemos' ]) -> name ( 'v1.receivables.update-memos' );
2025-12-26 15:18:08 +09:00
});
// Daily Report API (일일 보고서)
Route :: prefix ( 'daily-report' ) -> group ( function () {
Route :: get ( '/note-receivables' , [ DailyReportController :: class , 'noteReceivables' ]) -> name ( 'v1.daily-report.note-receivables' );
Route :: get ( '/daily-accounts' , [ DailyReportController :: class , 'dailyAccounts' ]) -> name ( 'v1.daily-report.daily-accounts' );
Route :: get ( '/summary' , [ DailyReportController :: class , 'summary' ]) -> name ( 'v1.daily-report.summary' );
});
// Comprehensive Analysis API (종합 분석 보고서)
Route :: get ( '/comprehensive-analysis' , [ ComprehensiveAnalysisController :: class , 'index' ]) -> name ( 'v1.comprehensive-analysis.index' );
2026-01-21 10:25:18 +09:00
// Status Board API (CEO 대시보드 현황판)
Route :: get ( '/status-board/summary' , [ StatusBoardController :: class , 'summary' ]) -> name ( 'v1.status-board.summary' );
// Today Issue API (CEO 대시보드 오늘의 이슈 리스트)
Route :: get ( '/today-issues/summary' , [ TodayIssueController :: class , 'summary' ]) -> name ( 'v1.today-issues.summary' );
2026-01-22 09:47:29 +09:00
Route :: get ( '/today-issues/unread' , [ TodayIssueController :: class , 'unread' ]) -> name ( 'v1.today-issues.unread' );
Route :: get ( '/today-issues/unread/count' , [ TodayIssueController :: class , 'unreadCount' ]) -> name ( 'v1.today-issues.unread.count' );
Route :: post ( '/today-issues/{id}/read' , [ TodayIssueController :: class , 'markAsRead' ]) -> whereNumber ( 'id' ) -> name ( 'v1.today-issues.read' );
Route :: post ( '/today-issues/read-all' , [ TodayIssueController :: class , 'markAllAsRead' ]) -> name ( 'v1.today-issues.read-all' );
2026-01-21 10:25:18 +09:00
// Calendar API (CEO 대시보드 캘린더)
Route :: get ( '/calendar/schedules' , [ CalendarController :: class , 'summary' ]) -> name ( 'v1.calendar.schedules' );
// Vat API (CEO 대시보드 부가세 현황)
Route :: get ( '/vat/summary' , [ VatController :: class , 'summary' ]) -> name ( 'v1.vat.summary' );
// Entertainment API (CEO 대시보드 접대비 현황)
Route :: get ( '/entertainment/summary' , [ EntertainmentController :: class , 'summary' ]) -> name ( 'v1.entertainment.summary' );
// Welfare API (CEO 대시보드 복리후생비 현황)
Route :: get ( '/welfare/summary' , [ WelfareController :: class , 'summary' ]) -> name ( 'v1.welfare.summary' );
2026-01-22 22:35:20 +09:00
Route :: get ( '/welfare/detail' , [ WelfareController :: class , 'detail' ]) -> name ( 'v1.welfare.detail' );
2026-01-21 10:25:18 +09:00
feat: 구독/결제 API 확장 (Plan, Subscription, Payment)
- Plan/Subscription/Payment 모델에 상태 상수, 스코프, 헬퍼 메서드 추가
- PlanService, SubscriptionService, PaymentService 생성
- PlanController, SubscriptionController, PaymentController 생성
- FormRequest 9개 생성 (Plan 3개, Subscription 3개, Payment 3개)
- Swagger 문서 3개 생성 (PlanApi, SubscriptionApi, PaymentApi)
- API 라우트 22개 등록 (Plan 7개, Subscription 8개, Payment 7개)
- Pint 코드 스타일 정리
2025-12-18 16:20:29 +09:00
// Plan API (요금제 관리)
Route :: prefix ( 'plans' ) -> group ( function () {
Route :: get ( '' , [ PlanController :: class , 'index' ]) -> name ( 'v1.plans.index' );
Route :: post ( '' , [ PlanController :: class , 'store' ]) -> name ( 'v1.plans.store' );
Route :: get ( '/active' , [ PlanController :: class , 'active' ]) -> name ( 'v1.plans.active' );
Route :: get ( '/{id}' , [ PlanController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.plans.show' );
Route :: put ( '/{id}' , [ PlanController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.plans.update' );
Route :: delete ( '/{id}' , [ PlanController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.plans.destroy' );
Route :: patch ( '/{id}/toggle' , [ PlanController :: class , 'toggle' ]) -> whereNumber ( 'id' ) -> name ( 'v1.plans.toggle' );
});
// Subscription API (구독 관리)
Route :: prefix ( 'subscriptions' ) -> group ( function () {
Route :: get ( '' , [ SubscriptionController :: class , 'index' ]) -> name ( 'v1.subscriptions.index' );
Route :: post ( '' , [ SubscriptionController :: class , 'store' ]) -> name ( 'v1.subscriptions.store' );
Route :: get ( '/current' , [ SubscriptionController :: class , 'current' ]) -> name ( 'v1.subscriptions.current' );
2025-12-19 16:53:49 +09:00
Route :: get ( '/usage' , [ SubscriptionController :: class , 'usage' ]) -> name ( 'v1.subscriptions.usage' );
Route :: post ( '/export' , [ SubscriptionController :: class , 'export' ]) -> name ( 'v1.subscriptions.export' );
Route :: get ( '/export/{id}' , [ SubscriptionController :: class , 'exportStatus' ]) -> whereNumber ( 'id' ) -> name ( 'v1.subscriptions.export.status' );
feat: 구독/결제 API 확장 (Plan, Subscription, Payment)
- Plan/Subscription/Payment 모델에 상태 상수, 스코프, 헬퍼 메서드 추가
- PlanService, SubscriptionService, PaymentService 생성
- PlanController, SubscriptionController, PaymentController 생성
- FormRequest 9개 생성 (Plan 3개, Subscription 3개, Payment 3개)
- Swagger 문서 3개 생성 (PlanApi, SubscriptionApi, PaymentApi)
- API 라우트 22개 등록 (Plan 7개, Subscription 8개, Payment 7개)
- Pint 코드 스타일 정리
2025-12-18 16:20:29 +09:00
Route :: get ( '/{id}' , [ SubscriptionController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.subscriptions.show' );
Route :: post ( '/{id}/cancel' , [ SubscriptionController :: class , 'cancel' ]) -> whereNumber ( 'id' ) -> name ( 'v1.subscriptions.cancel' );
Route :: post ( '/{id}/renew' , [ SubscriptionController :: class , 'renew' ]) -> whereNumber ( 'id' ) -> name ( 'v1.subscriptions.renew' );
Route :: post ( '/{id}/suspend' , [ SubscriptionController :: class , 'suspend' ]) -> whereNumber ( 'id' ) -> name ( 'v1.subscriptions.suspend' );
Route :: post ( '/{id}/resume' , [ SubscriptionController :: class , 'resume' ]) -> whereNumber ( 'id' ) -> name ( 'v1.subscriptions.resume' );
});
// Payment API (결제 관리)
Route :: prefix ( 'payments' ) -> group ( function () {
Route :: get ( '' , [ PaymentController :: class , 'index' ]) -> name ( 'v1.payments.index' );
Route :: post ( '' , [ PaymentController :: class , 'store' ]) -> name ( 'v1.payments.store' );
Route :: get ( '/summary' , [ PaymentController :: class , 'summary' ]) -> name ( 'v1.payments.summary' );
Route :: get ( '/{id}' , [ PaymentController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.payments.show' );
Route :: post ( '/{id}/complete' , [ PaymentController :: class , 'complete' ]) -> whereNumber ( 'id' ) -> name ( 'v1.payments.complete' );
Route :: post ( '/{id}/cancel' , [ PaymentController :: class , 'cancel' ]) -> whereNumber ( 'id' ) -> name ( 'v1.payments.cancel' );
Route :: post ( '/{id}/refund' , [ PaymentController :: class , 'refund' ]) -> whereNumber ( 'id' ) -> name ( 'v1.payments.refund' );
2025-12-19 16:53:49 +09:00
Route :: get ( '/{id}/statement' , [ PaymentController :: class , 'statement' ]) -> whereNumber ( 'id' ) -> name ( 'v1.payments.statement' );
feat: 구독/결제 API 확장 (Plan, Subscription, Payment)
- Plan/Subscription/Payment 모델에 상태 상수, 스코프, 헬퍼 메서드 추가
- PlanService, SubscriptionService, PaymentService 생성
- PlanController, SubscriptionController, PaymentController 생성
- FormRequest 9개 생성 (Plan 3개, Subscription 3개, Payment 3개)
- Swagger 문서 3개 생성 (PlanApi, SubscriptionApi, PaymentApi)
- API 라우트 22개 등록 (Plan 7개, Subscription 8개, Payment 7개)
- Pint 코드 스타일 정리
2025-12-18 16:20:29 +09:00
});
2025-12-22 15:30:38 +09:00
// Company API (회사 추가 관리)
Route :: prefix ( 'companies' ) -> group ( function () {
Route :: post ( '/check' , [ CompanyController :: class , 'check' ]) -> name ( 'v1.companies.check' ); // 사업자등록번호 검증
Route :: post ( '/request' , [ CompanyController :: class , 'request' ]) -> name ( 'v1.companies.request' ); // 회사 추가 신청
Route :: get ( '/requests' , [ CompanyController :: class , 'requests' ]) -> name ( 'v1.companies.requests.index' ); // 신청 목록 (관리자)
Route :: get ( '/requests/{id}' , [ CompanyController :: class , 'showRequest' ]) -> whereNumber ( 'id' ) -> name ( 'v1.companies.requests.show' ); // 신청 상세
Route :: post ( '/requests/{id}/approve' , [ CompanyController :: class , 'approve' ]) -> whereNumber ( 'id' ) -> name ( 'v1.companies.requests.approve' ); // 승인
Route :: post ( '/requests/{id}/reject' , [ CompanyController :: class , 'reject' ]) -> whereNumber ( 'id' ) -> name ( 'v1.companies.requests.reject' ); // 반려
Route :: get ( '/my-requests' , [ CompanyController :: class , 'myRequests' ]) -> name ( 'v1.companies.my-requests' ); // 내 신청 목록
});
feat: 매출/매입 관리 API 구현
- 매출(Sale) 및 매입(Purchase) CRUD API 구현
- 문서번호 자동 생성 (SL/PU + YYYYMMDD + 시퀀스)
- 상태 관리 (draft → confirmed → invoiced)
- 확정(confirm) 및 요약(summary) 기능 추가
- BelongsToTenant, SoftDeletes 적용
- Swagger API 문서 작성 완료
추가된 파일:
- 마이그레이션: sales, purchases 테이블
- 모델: Sale, Purchase
- 서비스: SaleService, PurchaseService
- 컨트롤러: SaleController, PurchaseController
- FormRequest: Store/Update 4개
- Swagger: SaleApi.php, PurchaseApi.php
API 엔드포인트 (14개):
- GET/POST /v1/sales, /v1/purchases
- GET/PUT/DELETE /v1/{sales,purchases}/{id}
- POST /v1/{sales,purchases}/{id}/confirm
- GET /v1/{sales,purchases}/summary
2025-12-17 22:14:48 +09:00
// Sale API (매출 관리)
Route :: prefix ( 'sales' ) -> group ( function () {
Route :: get ( '' , [ SaleController :: class , 'index' ]) -> name ( 'v1.sales.index' );
Route :: post ( '' , [ SaleController :: class , 'store' ]) -> name ( 'v1.sales.store' );
Route :: get ( '/summary' , [ SaleController :: class , 'summary' ]) -> name ( 'v1.sales.summary' );
Route :: get ( '/{id}' , [ SaleController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.sales.show' );
Route :: put ( '/{id}' , [ SaleController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.sales.update' );
Route :: delete ( '/{id}' , [ SaleController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.sales.destroy' );
Route :: post ( '/{id}/confirm' , [ SaleController :: class , 'confirm' ]) -> whereNumber ( 'id' ) -> name ( 'v1.sales.confirm' );
2026-01-16 15:38:52 +09:00
Route :: put ( '/bulk-update-account' , [ SaleController :: class , 'bulkUpdateAccountCode' ]) -> name ( 'v1.sales.bulk-update-account' );
feat: Phase 5 API 개발 완료 (사용자 초대, 알림설정, 계정관리, 거래명세서)
5.1 사용자 초대 기능:
- UserInvitation 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 초대 발송/수락/취소/재발송 API
5.2 알림설정 확장:
- NotificationSetting 마이그레이션, 모델, 서비스, 컨트롤러, Swagger
- 채널별/유형별 알림 설정 관리
5.3 계정정보 수정 API:
- 회원탈퇴, 사용중지, 약관동의 관리
- AccountService, AccountController, Swagger
5.4 매출 거래명세서 API:
- 거래명세서 조회/발행/이메일발송
- SaleService 확장, Swagger 문서화
2025-12-19 14:52:53 +09:00
// 거래명세서 API
Route :: get ( '/{id}/statement' , [ SaleController :: class , 'getStatement' ]) -> whereNumber ( 'id' ) -> name ( 'v1.sales.statement.show' );
Route :: post ( '/{id}/statement/issue' , [ SaleController :: class , 'issueStatement' ]) -> whereNumber ( 'id' ) -> name ( 'v1.sales.statement.issue' );
Route :: post ( '/{id}/statement/send' , [ SaleController :: class , 'sendStatement' ]) -> whereNumber ( 'id' ) -> name ( 'v1.sales.statement.send' );
2026-01-19 20:53:36 +09:00
Route :: post ( '/bulk-issue-statement' , [ SaleController :: class , 'bulkIssueStatement' ]) -> name ( 'v1.sales.bulk-issue-statement' );
feat: 매출/매입 관리 API 구현
- 매출(Sale) 및 매입(Purchase) CRUD API 구현
- 문서번호 자동 생성 (SL/PU + YYYYMMDD + 시퀀스)
- 상태 관리 (draft → confirmed → invoiced)
- 확정(confirm) 및 요약(summary) 기능 추가
- BelongsToTenant, SoftDeletes 적용
- Swagger API 문서 작성 완료
추가된 파일:
- 마이그레이션: sales, purchases 테이블
- 모델: Sale, Purchase
- 서비스: SaleService, PurchaseService
- 컨트롤러: SaleController, PurchaseController
- FormRequest: Store/Update 4개
- Swagger: SaleApi.php, PurchaseApi.php
API 엔드포인트 (14개):
- GET/POST /v1/sales, /v1/purchases
- GET/PUT/DELETE /v1/{sales,purchases}/{id}
- POST /v1/{sales,purchases}/{id}/confirm
- GET /v1/{sales,purchases}/summary
2025-12-17 22:14:48 +09:00
});
// Purchase API (매입 관리)
Route :: prefix ( 'purchases' ) -> group ( function () {
Route :: get ( '' , [ PurchaseController :: class , 'index' ]) -> name ( 'v1.purchases.index' );
Route :: post ( '' , [ PurchaseController :: class , 'store' ]) -> name ( 'v1.purchases.store' );
Route :: get ( '/summary' , [ PurchaseController :: class , 'summary' ]) -> name ( 'v1.purchases.summary' );
2026-01-22 22:28:41 +09:00
Route :: get ( '/dashboard-detail' , [ PurchaseController :: class , 'dashboardDetail' ]) -> name ( 'v1.purchases.dashboard-detail' );
2026-01-19 19:42:05 +09:00
Route :: post ( '/bulk-update-type' , [ PurchaseController :: class , 'bulkUpdatePurchaseType' ]) -> name ( 'v1.purchases.bulk-update-type' );
Route :: post ( '/bulk-update-tax-received' , [ PurchaseController :: class , 'bulkUpdateTaxReceived' ]) -> name ( 'v1.purchases.bulk-update-tax-received' );
feat: 매출/매입 관리 API 구현
- 매출(Sale) 및 매입(Purchase) CRUD API 구현
- 문서번호 자동 생성 (SL/PU + YYYYMMDD + 시퀀스)
- 상태 관리 (draft → confirmed → invoiced)
- 확정(confirm) 및 요약(summary) 기능 추가
- BelongsToTenant, SoftDeletes 적용
- Swagger API 문서 작성 완료
추가된 파일:
- 마이그레이션: sales, purchases 테이블
- 모델: Sale, Purchase
- 서비스: SaleService, PurchaseService
- 컨트롤러: SaleController, PurchaseController
- FormRequest: Store/Update 4개
- Swagger: SaleApi.php, PurchaseApi.php
API 엔드포인트 (14개):
- GET/POST /v1/sales, /v1/purchases
- GET/PUT/DELETE /v1/{sales,purchases}/{id}
- POST /v1/{sales,purchases}/{id}/confirm
- GET /v1/{sales,purchases}/summary
2025-12-17 22:14:48 +09:00
Route :: get ( '/{id}' , [ PurchaseController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.purchases.show' );
Route :: put ( '/{id}' , [ PurchaseController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.purchases.update' );
Route :: delete ( '/{id}' , [ PurchaseController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.purchases.destroy' );
Route :: post ( '/{id}/confirm' , [ PurchaseController :: class , 'confirm' ]) -> whereNumber ( 'id' ) -> name ( 'v1.purchases.confirm' );
});
2025-12-26 13:23:07 +09:00
// Receiving API (입고 관리)
Route :: prefix ( 'receivings' ) -> group ( function () {
Route :: get ( '' , [ ReceivingController :: class , 'index' ]) -> name ( 'v1.receivings.index' );
Route :: post ( '' , [ ReceivingController :: class , 'store' ]) -> name ( 'v1.receivings.store' );
Route :: get ( '/stats' , [ ReceivingController :: class , 'stats' ]) -> name ( 'v1.receivings.stats' );
Route :: get ( '/{id}' , [ ReceivingController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.receivings.show' );
Route :: put ( '/{id}' , [ ReceivingController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.receivings.update' );
Route :: delete ( '/{id}' , [ ReceivingController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.receivings.destroy' );
Route :: post ( '/{id}/process' , [ ReceivingController :: class , 'process' ]) -> whereNumber ( 'id' ) -> name ( 'v1.receivings.process' );
});
2025-12-26 13:57:42 +09:00
// Stock API (재고 현황)
Route :: prefix ( 'stocks' ) -> group ( function () {
Route :: get ( '' , [ StockController :: class , 'index' ]) -> name ( 'v1.stocks.index' );
Route :: get ( '/stats' , [ StockController :: class , 'stats' ]) -> name ( 'v1.stocks.stats' );
Route :: get ( '/stats-by-type' , [ StockController :: class , 'statsByItemType' ]) -> name ( 'v1.stocks.stats-by-type' );
Route :: get ( '/{id}' , [ StockController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.stocks.show' );
});
2025-12-26 15:48:37 +09:00
// Shipment API (출하 관리)
Route :: prefix ( 'shipments' ) -> group ( function () {
Route :: get ( '' , [ ShipmentController :: class , 'index' ]) -> name ( 'v1.shipments.index' );
Route :: get ( '/stats' , [ ShipmentController :: class , 'stats' ]) -> name ( 'v1.shipments.stats' );
Route :: get ( '/stats-by-status' , [ ShipmentController :: class , 'statsByStatus' ]) -> name ( 'v1.shipments.stats-by-status' );
Route :: get ( '/options/lots' , [ ShipmentController :: class , 'lotOptions' ]) -> name ( 'v1.shipments.options.lots' );
Route :: get ( '/options/logistics' , [ ShipmentController :: class , 'logisticsOptions' ]) -> name ( 'v1.shipments.options.logistics' );
Route :: get ( '/options/vehicle-tonnage' , [ ShipmentController :: class , 'vehicleTonnageOptions' ]) -> name ( 'v1.shipments.options.vehicle-tonnage' );
Route :: post ( '' , [ ShipmentController :: class , 'store' ]) -> name ( 'v1.shipments.store' );
Route :: get ( '/{id}' , [ ShipmentController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.shipments.show' );
Route :: put ( '/{id}' , [ ShipmentController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.shipments.update' );
Route :: patch ( '/{id}/status' , [ ShipmentController :: class , 'updateStatus' ]) -> whereNumber ( 'id' ) -> name ( 'v1.shipments.status' );
Route :: delete ( '/{id}' , [ ShipmentController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.shipments.destroy' );
});
2025-12-18 15:31:59 +09:00
// Barobill Setting API (바로빌 설정)
Route :: prefix ( 'barobill-settings' ) -> group ( function () {
Route :: get ( '' , [ BarobillSettingController :: class , 'show' ]) -> name ( 'v1.barobill-settings.show' );
Route :: put ( '' , [ BarobillSettingController :: class , 'save' ]) -> name ( 'v1.barobill-settings.save' );
Route :: post ( '/test-connection' , [ BarobillSettingController :: class , 'testConnection' ]) -> name ( 'v1.barobill-settings.test-connection' );
});
// Tax Invoice API (세금계산서)
Route :: prefix ( 'tax-invoices' ) -> group ( function () {
Route :: get ( '' , [ TaxInvoiceController :: class , 'index' ]) -> name ( 'v1.tax-invoices.index' );
Route :: post ( '' , [ TaxInvoiceController :: class , 'store' ]) -> name ( 'v1.tax-invoices.store' );
Route :: get ( '/summary' , [ TaxInvoiceController :: class , 'summary' ]) -> name ( 'v1.tax-invoices.summary' );
Route :: get ( '/{id}' , [ TaxInvoiceController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.tax-invoices.show' );
Route :: put ( '/{id}' , [ TaxInvoiceController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.tax-invoices.update' );
Route :: delete ( '/{id}' , [ TaxInvoiceController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.tax-invoices.destroy' );
Route :: post ( '/{id}/issue' , [ TaxInvoiceController :: class , 'issue' ]) -> whereNumber ( 'id' ) -> name ( 'v1.tax-invoices.issue' );
Route :: post ( '/{id}/cancel' , [ TaxInvoiceController :: class , 'cancel' ]) -> whereNumber ( 'id' ) -> name ( 'v1.tax-invoices.cancel' );
Route :: get ( '/{id}/check-status' , [ TaxInvoiceController :: class , 'checkStatus' ]) -> whereNumber ( 'id' ) -> name ( 'v1.tax-invoices.check-status' );
2026-01-19 20:53:36 +09:00
Route :: post ( '/bulk-issue' , [ TaxInvoiceController :: class , 'bulkIssue' ]) -> name ( 'v1.tax-invoices.bulk-issue' );
2025-12-18 15:31:59 +09:00
});
feat: Phase 6.1 악성채권 추심관리 API 구현
- 테이블 3개: bad_debts, bad_debt_documents, bad_debt_memos
- 모델 3개: BadDebt, BadDebtDocument, BadDebtMemo
- BadDebtService: CRUD, 요약 통계, 서류/메모 관리
- API 엔드포인트 11개 (목록, 등록, 상세, 수정, 삭제, 토글, 서류/메모 CRUD)
- Swagger 문서 작성 완료
2025-12-19 15:57:04 +09:00
// Bad Debt API (악성채권 추심관리)
Route :: prefix ( 'bad-debts' ) -> group ( function () {
Route :: get ( '' , [ BadDebtController :: class , 'index' ]) -> name ( 'v1.bad-debts.index' );
Route :: post ( '' , [ BadDebtController :: class , 'store' ]) -> name ( 'v1.bad-debts.store' );
Route :: get ( '/summary' , [ BadDebtController :: class , 'summary' ]) -> name ( 'v1.bad-debts.summary' );
Route :: get ( '/{id}' , [ BadDebtController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bad-debts.show' );
Route :: put ( '/{id}' , [ BadDebtController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bad-debts.update' );
Route :: delete ( '/{id}' , [ BadDebtController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bad-debts.destroy' );
Route :: patch ( '/{id}/toggle' , [ BadDebtController :: class , 'toggle' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bad-debts.toggle' );
// 서류
Route :: post ( '/{id}/documents' , [ BadDebtController :: class , 'addDocument' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bad-debts.documents.store' );
Route :: delete ( '/{id}/documents/{documentId}' , [ BadDebtController :: class , 'removeDocument' ]) -> whereNumber ([ 'id' , 'documentId' ]) -> name ( 'v1.bad-debts.documents.destroy' );
// 메모
Route :: post ( '/{id}/memos' , [ BadDebtController :: class , 'addMemo' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bad-debts.memos.store' );
Route :: delete ( '/{id}/memos/{memoId}' , [ BadDebtController :: class , 'removeMemo' ]) -> whereNumber ([ 'id' , 'memoId' ]) -> name ( 'v1.bad-debts.memos.destroy' );
});
2025-12-23 23:42:02 +09:00
// Bill API (어음관리)
Route :: prefix ( 'bills' ) -> group ( function () {
Route :: get ( '' , [ BillController :: class , 'index' ]) -> name ( 'v1.bills.index' );
Route :: post ( '' , [ BillController :: class , 'store' ]) -> name ( 'v1.bills.store' );
Route :: get ( '/summary' , [ BillController :: class , 'summary' ]) -> name ( 'v1.bills.summary' );
2026-01-22 22:40:00 +09:00
Route :: get ( '/dashboard-detail' , [ BillController :: class , 'dashboardDetail' ]) -> name ( 'v1.bills.dashboard-detail' );
2025-12-23 23:42:02 +09:00
Route :: get ( '/{id}' , [ BillController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bills.show' );
Route :: put ( '/{id}' , [ BillController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bills.update' );
Route :: delete ( '/{id}' , [ BillController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bills.destroy' );
Route :: patch ( '/{id}/status' , [ BillController :: class , 'updateStatus' ]) -> whereNumber ( 'id' ) -> name ( 'v1.bills.update-status' );
});
2025-12-19 16:14:04 +09:00
// Popup API (팝업관리)
Route :: prefix ( 'popups' ) -> group ( function () {
Route :: get ( '' , [ PopupController :: class , 'index' ]) -> name ( 'v1.popups.index' );
Route :: post ( '' , [ PopupController :: class , 'store' ]) -> name ( 'v1.popups.store' );
Route :: get ( '/active' , [ PopupController :: class , 'active' ]) -> name ( 'v1.popups.active' );
Route :: get ( '/{id}' , [ PopupController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.popups.show' );
Route :: put ( '/{id}' , [ PopupController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.popups.update' );
Route :: delete ( '/{id}' , [ PopupController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.popups.destroy' );
});
2025-12-17 22:51:17 +09:00
// Report API (보고서)
Route :: prefix ( 'reports' ) -> group ( function () {
Route :: get ( '/daily' , [ ReportController :: class , 'daily' ]) -> name ( 'v1.reports.daily' );
Route :: get ( '/daily/export' , [ ReportController :: class , 'dailyExport' ]) -> name ( 'v1.reports.daily.export' );
Route :: get ( '/expense-estimate' , [ ReportController :: class , 'expenseEstimate' ]) -> name ( 'v1.reports.expense-estimate' );
Route :: get ( '/expense-estimate/export' , [ ReportController :: class , 'expenseEstimateExport' ]) -> name ( 'v1.reports.expense-estimate.export' );
2025-12-18 13:51:40 +09:00
// AI Report API (AI 리포트)
Route :: get ( '/ai' , [ AiReportController :: class , 'index' ]) -> name ( 'v1.reports.ai.index' );
Route :: post ( '/ai/generate' , [ AiReportController :: class , 'generate' ]) -> name ( 'v1.reports.ai.generate' );
Route :: get ( '/ai/{id}' , [ AiReportController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.reports.ai.show' );
Route :: delete ( '/ai/{id}' , [ AiReportController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.reports.ai.destroy' );
2025-12-17 22:51:17 +09:00
});
feat: 대시보드 API 및 FCM 푸시 알림 API 구현
Dashboard API:
- DashboardController, DashboardService 추가
- /dashboard/summary, /charts, /approvals 엔드포인트
Push Notification API:
- FCM 토큰 관리 (등록/해제/목록)
- 알림 설정 관리 (유형별 on/off, 알림음 설정)
- 알림 유형: deposit, withdrawal, order, approval, attendance, notice, system
- 알림음: default, deposit, withdrawal, order, approval, urgent
- PushDeviceToken, PushNotificationSetting 모델
- Swagger 문서 추가
2025-12-18 11:16:24 +09:00
// Dashboard API (대시보드)
Route :: prefix ( 'dashboard' ) -> group ( function () {
Route :: get ( '/summary' , [ DashboardController :: class , 'summary' ]) -> name ( 'v1.dashboard.summary' );
Route :: get ( '/charts' , [ DashboardController :: class , 'charts' ]) -> name ( 'v1.dashboard.charts' );
Route :: get ( '/approvals' , [ DashboardController :: class , 'approvals' ]) -> name ( 'v1.dashboard.approvals' );
});
2025-08-21 20:48:39 +09:00
// Permission API
Route :: prefix ( 'permissions' ) -> group ( function () {
2025-09-24 20:35:17 +09:00
Route :: get ( 'departments/{dept_id}/menu-matrix' , [ PermissionController :: class , 'deptMenuMatrix' ]) -> name ( 'v1.permissions.deptMenuMatrix' ); // 부서별 권한 메트릭스
Route :: get ( 'roles/{role_id}/menu-matrix' , [ PermissionController :: class , 'roleMenuMatrix' ]) -> name ( 'v1.permissions.roleMenuMatrix' ); // 부서별 권한 메트릭스
Route :: get ( 'users/{user_id}/menu-matrix' , [ PermissionController :: class , 'userMenuMatrix' ]) -> name ( 'v1.permissions.userMenuMatrix' ); // 부서별 권한 메트릭스
2025-08-21 20:48:39 +09:00
});
2025-09-24 20:35:17 +09:00
// Settings & Configuration (설정 및 환경설정 통합 관리)
Route :: prefix ( 'settings' ) -> group ( function () {
feat: 근무/출퇴근 설정 및 현장 관리 API 구현
- 근무 설정 API (GET/PUT /settings/work)
- 근무유형, 소정근로시간, 연장근로시간, 근무요일, 출퇴근시간, 휴게시간
- 출퇴근 설정 API (GET/PUT /settings/attendance)
- GPS 출퇴근, 허용 반경, 본사 위치 설정
- 현장 관리 API (CRUD /sites)
- 현장 등록/수정/삭제, 활성화된 현장 목록(셀렉트박스용)
- GPS 좌표 기반 위치 관리
마이그레이션: work_settings, attendance_settings, sites 테이블
모델: WorkSetting, AttendanceSetting, Site (BelongsToTenant, SoftDeletes)
서비스: WorkSettingService, SiteService
Swagger 문서 및 i18n 메시지 키 추가
2025-12-17 20:46:37 +09:00
// 근무 설정
Route :: get ( '/work' , [ WorkSettingController :: class , 'showWorkSetting' ]) -> name ( 'v1.settings.work.show' );
Route :: put ( '/work' , [ WorkSettingController :: class , 'updateWorkSetting' ]) -> name ( 'v1.settings.work.update' );
// 출퇴근 설정
Route :: get ( '/attendance' , [ WorkSettingController :: class , 'showAttendanceSetting' ]) -> name ( 'v1.settings.attendance.show' );
Route :: put ( '/attendance' , [ WorkSettingController :: class , 'updateAttendanceSetting' ]) -> name ( 'v1.settings.attendance.update' );
2025-12-18 10:56:16 +09:00
// 급여 설정
Route :: get ( '/payroll' , [ PayrollController :: class , 'getSettings' ]) -> name ( 'v1.settings.payroll.show' );
Route :: put ( '/payroll' , [ PayrollController :: class , 'updateSettings' ]) -> name ( 'v1.settings.payroll.update' );
2025-09-24 20:35:17 +09:00
// 테넌트 필드 설정 (기존 fields에서 이동)
Route :: get ( '/fields' , [ TenantFieldSettingController :: class , 'index' ]) -> name ( 'v1.settings.fields.index' ); // 필드 설정 목록(전역+테넌트 병합 효과값)
Route :: put ( '/fields/bulk' , [ TenantFieldSettingController :: class , 'bulkUpsert' ]) -> name ( 'v1.settings.fields.bulk' ); // 필드 설정 대량 저장(트랜잭션 처리)
Route :: patch ( '/fields/{key}' , [ TenantFieldSettingController :: class , 'updateOne' ]) -> name ( 'v1.settings.fields.update' ); // 필드 설정 단건 수정/업데이트
// 옵션 그룹/값 (기존 opt-groups에서 이동)
Route :: get ( '/options' , [ TenantOptionGroupController :: class , 'index' ]) -> name ( 'v1.settings.options.index' ); // 옵션 그룹 목록
Route :: post ( '/options' , [ TenantOptionGroupController :: class , 'store' ]) -> name ( 'v1.settings.options.store' ); // 옵션 그룹 생성
Route :: get ( '/options/{id}' , [ TenantOptionGroupController :: class , 'show' ]) -> name ( 'v1.settings.options.show' ); // 옵션 그룹 단건 조회
Route :: patch ( '/options/{id}' , [ TenantOptionGroupController :: class , 'update' ]) -> name ( 'v1.settings.options.update' ); // 옵션 그룹 수정
Route :: delete ( '/options/{id}' , [ TenantOptionGroupController :: class , 'destroy' ]) -> name ( 'v1.settings.options.destroy' ); // 옵션 그룹 삭제
Route :: get ( '/options/{gid}/values' , [ TenantOptionValueController :: class , 'index' ]) -> name ( 'v1.settings.options.values.index' ); // 옵션 값 목록
Route :: post ( '/options/{gid}/values' , [ TenantOptionValueController :: class , 'store' ]) -> name ( 'v1.settings.options.values.store' ); // 옵션 값 생성
Route :: get ( '/options/{gid}/values/{id}' , [ TenantOptionValueController :: class , 'show' ]) -> name ( 'v1.settings.options.values.show' ); // 옵션 값 단건 조회
Route :: patch ( '/options/{gid}/values/{id}' , [ TenantOptionValueController :: class , 'update' ]) -> name ( 'v1.settings.options.values.update' ); // 옵션 값 수정
Route :: delete ( '/options/{gid}/values/{id}' , [ TenantOptionValueController :: class , 'destroy' ]) -> name ( 'v1.settings.options.values.destroy' ); // 옵션 값 삭제
Route :: patch ( '/options/{gid}/values/reorder' , [ TenantOptionValueController :: class , 'reorder' ]) -> name ( 'v1.settings.options.values.reorder' ); // 옵션 값 정렬순서 재배치
// 공통 코드 관리 (기존 common에서 이동)
Route :: get ( '/common/code' , [ CommonController :: class , 'getComeCode' ]) -> name ( 'v1.settings.common.code' ); // 공통코드 조회 (기존 v1.common.code에서 이동)
Route :: get ( '/common' , [ CommonController :: class , 'list' ]) -> name ( 'v1.settings.common.list' ); // 공통 코드 목록
Route :: get ( '/common/{group}' , [ CommonController :: class , 'index' ]) -> name ( 'v1.settings.common.index' ); // 특정 그룹 코드 목록
Route :: post ( '/common' , [ CommonController :: class , 'store' ]) -> name ( 'v1.settings.common.store' ); // 공통 코드 생성
Route :: patch ( '/common/{id}' , [ CommonController :: class , 'update' ]) -> name ( 'v1.settings.common.update' ); // 공통 코드 수정
Route :: delete ( '/common/{id}' , [ CommonController :: class , 'destroy' ]) -> name ( 'v1.settings.common.destroy' ); // 공통 코드 삭제
2025-12-22 17:42:59 +09:00
// 알림 설정 (그룹 기반, React 호환) - auth:sanctum 필수
Route :: middleware ( 'auth:sanctum' ) -> group ( function () {
Route :: get ( '/notifications' , [ NotificationSettingController :: class , 'indexGrouped' ]) -> name ( 'v1.settings.notifications.index' ); // 알림 설정 조회 (그룹 기반)
Route :: put ( '/notifications' , [ NotificationSettingController :: class , 'updateGrouped' ]) -> name ( 'v1.settings.notifications.update' ); // 알림 설정 수정 (그룹 기반)
});
2025-08-18 19:03:46 +09:00
});
2025-12-18 21:08:35 +09:00
// Push Notification API (FCM 푸시 알림) - auth:sanctum 필수 (tenantId, apiUserId 필요)
Route :: prefix ( 'push' ) -> middleware ( 'auth:sanctum' ) -> group ( function () {
feat: 대시보드 API 및 FCM 푸시 알림 API 구현
Dashboard API:
- DashboardController, DashboardService 추가
- /dashboard/summary, /charts, /approvals 엔드포인트
Push Notification API:
- FCM 토큰 관리 (등록/해제/목록)
- 알림 설정 관리 (유형별 on/off, 알림음 설정)
- 알림 유형: deposit, withdrawal, order, approval, attendance, notice, system
- 알림음: default, deposit, withdrawal, order, approval, urgent
- PushDeviceToken, PushNotificationSetting 모델
- Swagger 문서 추가
2025-12-18 11:16:24 +09:00
Route :: post ( '/register-token' , [ PushNotificationController :: class , 'registerToken' ]) -> name ( 'v1.push.register-token' ); // FCM 토큰 등록
Route :: post ( '/unregister-token' , [ PushNotificationController :: class , 'unregisterToken' ]) -> name ( 'v1.push.unregister-token' ); // FCM 토큰 해제
Route :: get ( '/tokens' , [ PushNotificationController :: class , 'getTokens' ]) -> name ( 'v1.push.tokens' ); // 등록된 토큰 목록
Route :: get ( '/settings' , [ PushNotificationController :: class , 'getSettings' ]) -> name ( 'v1.push.settings' ); // 알림 설정 조회
Route :: put ( '/settings' , [ PushNotificationController :: class , 'updateSettings' ]) -> name ( 'v1.push.settings.update' ); // 알림 설정 수정
Route :: get ( '/notification-types' , [ PushNotificationController :: class , 'getNotificationTypes' ]) -> name ( 'v1.push.notification-types' ); // 알림 유형 목록
});
2025-12-23 12:45:28 +09:00
// Admin FCM API (MNG 관리자용 FCM 발송) - API Key 인증만 사용
Route :: prefix ( 'admin/fcm' ) -> group ( function () {
2026-01-19 19:42:05 +09:00
Route :: post ( '/send' , [ FcmController :: class , 'send' ]) -> name ( 'v1.admin.fcm.send' ); // FCM 발송
Route :: get ( '/preview-count' , [ FcmController :: class , 'previewCount' ]) -> name ( 'v1.admin.fcm.preview' ); // 대상 토큰 수 미리보기
Route :: get ( '/tokens' , [ FcmController :: class , 'tokens' ]) -> name ( 'v1.admin.fcm.tokens' ); // 토큰 목록
Route :: get ( '/tokens/stats' , [ FcmController :: class , 'tokenStats' ]) -> name ( 'v1.admin.fcm.tokens.stats' ); // 토큰 통계
Route :: patch ( '/tokens/{id}/toggle' , [ FcmController :: class , 'toggleToken' ]) -> name ( 'v1.admin.fcm.tokens.toggle' ); // 토큰 상태 토글
Route :: delete ( '/tokens/{id}' , [ FcmController :: class , 'deleteToken' ]) -> name ( 'v1.admin.fcm.tokens.delete' ); // 토큰 삭제
Route :: get ( '/history' , [ FcmController :: class , 'history' ]) -> name ( 'v1.admin.fcm.history' ); // 발송 이력
2025-12-23 12:45:28 +09:00
});
2025-08-18 19:03:46 +09:00
// 회원 프로필(테넌트 기준)
Route :: prefix ( 'profiles' ) -> group ( function () {
2025-08-22 18:08:57 +09:00
Route :: get ( '' , [ TenantUserProfileController :: class , 'index' ]) -> name ( 'v1.profiles.index' ); // 프로필 목록(테넌트 기준)
2025-12-18 20:29:30 +09:00
// /me 라우트는 /{userId} 와일드카드보다 먼저 정의해야 함
// auth:sanctum 미들웨어로 Bearer 토큰 인증 필요
Route :: middleware ( 'auth:sanctum' ) -> group ( function () {
Route :: get ( '/me' , [ TenantUserProfileController :: class , 'me' ]) -> name ( 'v1.profiles.me' ); // 내 프로필 조회
Route :: patch ( '/me' , [ TenantUserProfileController :: class , 'updateMe' ]) -> name ( 'v1.profiles.me.update' ); // 내 프로필 수정
});
2025-08-22 18:08:57 +09:00
Route :: get ( '/{userId}' , [ TenantUserProfileController :: class , 'show' ]) -> name ( 'v1.profiles.show' ); // 특정 사용자 프로필 조회
Route :: patch ( '/{userId}' , [ TenantUserProfileController :: class , 'update' ]) -> name ( 'v1.profiles.update' ); // 특정 사용자 프로필 수정(관리자)
});
2025-09-24 20:35:17 +09:00
// Category API (통합)
2025-08-22 18:08:57 +09:00
Route :: prefix ( 'categories' ) -> group ( function () {
2025-09-24 20:35:17 +09:00
// === 기본 Category CRUD ===
2025-08-22 18:08:57 +09:00
Route :: get ( '' , [ CategoryController :: class , 'index' ]) -> name ( 'v1.categories.index' ); // 목록(페이징)
Route :: post ( '' , [ CategoryController :: class , 'store' ]) -> name ( 'v1.categories.store' ); // 생성
Route :: get ( '/{id}' , [ CategoryController :: class , 'show' ]) -> name ( 'v1.categories.show' ); // 단건
Route :: patch ( '/{id}' , [ CategoryController :: class , 'update' ]) -> name ( 'v1.categories.update' ); // 수정
Route :: delete ( '/{id}' , [ CategoryController :: class , 'destroy' ]) -> name ( 'v1.categories.destroy' ); // 삭제(soft)
2025-08-18 19:03:46 +09:00
2025-09-24 20:35:17 +09:00
// === 확장 기능 ===
Route :: get ( '/tree' , [ CategoryController :: class , 'tree' ]) -> name ( 'v1.categories.tree' ); // 트리
Route :: post ( '/reorder' , [ CategoryController :: class , 'reorder' ]) -> name ( 'v1.categories.reorder' ); // 정렬 일괄
Route :: post ( '/{id}/toggle' , [ CategoryController :: class , 'toggle' ]) -> name ( 'v1.categories.toggle' ); // 활성 토글
Route :: patch ( '/{id}/move' , [ CategoryController :: class , 'move' ]) -> name ( 'v1.categories.move' ); // 부모/순서 이동
// === Category Fields ===
2025-08-25 17:46:34 +09:00
// 목록/생성 (카테고리 기준)
2025-11-06 17:24:42 +09:00
Route :: get ( '/{id}/fields' , [ CategoryFieldController :: class , 'index' ]) -> name ( 'v1.categories.fields.index' ); // ?page&size&sort&order
Route :: post ( '/{id}/fields' , [ CategoryFieldController :: class , 'store' ]) -> name ( 'v1.categories.fields.store' );
2025-08-25 17:46:34 +09:00
// 단건
2025-11-06 17:24:42 +09:00
Route :: get ( '/fields/{field}' , [ CategoryFieldController :: class , 'show' ]) -> name ( 'v1.categories.fields.show' );
Route :: patch ( '/fields/{field}' , [ CategoryFieldController :: class , 'update' ]) -> name ( 'v1.categories.fields.update' );
Route :: delete ( '/fields/{field}' , [ CategoryFieldController :: class , 'destroy' ]) -> name ( 'v1.categories.fields.destroy' );
2025-08-25 17:46:34 +09:00
// 일괄 정렬/업서트
2025-11-06 17:24:42 +09:00
Route :: post ( '/{id}/fields/reorder' , [ CategoryFieldController :: class , 'reorder' ]) -> name ( 'v1.categories.fields.reorder' ); // [{id,sort_order}]
Route :: put ( '/{id}/fields/bulk-upsert' , [ CategoryFieldController :: class , 'bulkUpsert' ]) -> name ( 'v1.categories.fields.bulk' ); // [{id?,field_key,...}]
2025-08-25 17:46:34 +09:00
2025-09-24 20:35:17 +09:00
// === Category Templates ===
2025-08-25 17:46:34 +09:00
// 버전 목록/생성 (카테고리 기준)
2025-11-06 17:24:42 +09:00
Route :: get ( '/{id}/templates' , [ CategoryTemplateController :: class , 'index' ]) -> name ( 'v1.categories.templates.index' ); // ?page&size
Route :: post ( '/{id}/templates' , [ CategoryTemplateController :: class , 'store' ]) -> name ( 'v1.categories.templates.store' ); // 새 버전 등록
2025-08-25 17:46:34 +09:00
// 단건
2025-11-06 17:24:42 +09:00
Route :: get ( '/templates/{tpl}' , [ CategoryTemplateController :: class , 'show' ]) -> name ( 'v1.categories.templates.show' );
Route :: patch ( '/templates/{tpl}' , [ CategoryTemplateController :: class , 'update' ]) -> name ( 'v1.categories.templates.update' ); // remarks 등 메타 수정
Route :: delete ( '/templates/{tpl}' , [ CategoryTemplateController :: class , 'destroy' ]) -> name ( 'v1.categories.templates.destroy' );
2025-08-25 17:46:34 +09:00
// 운영 편의
2025-11-06 17:24:42 +09:00
Route :: post ( '/{id}/templates/{tpl}/apply' , [ CategoryTemplateController :: class , 'apply' ]) -> name ( 'v1.categories.templates.apply' ); // 해당 버전 활성화
Route :: get ( '/{id}/templates/{tpl}/preview' , [ CategoryTemplateController :: class , 'preview' ]) -> name ( 'v1.categories.templates.preview' ); // 렌더용 스냅샷
2025-08-25 17:46:34 +09:00
// (선택) 버전 간 diff
2025-11-06 17:24:42 +09:00
Route :: get ( '/{id}/templates/diff' , [ CategoryTemplateController :: class , 'diff' ]) -> name ( 'v1.categories.templates.diff' ); // ?a=ver&b=ver
2025-08-25 17:46:34 +09:00
2025-09-24 20:35:17 +09:00
// === Category Logs ===
2025-11-06 17:24:42 +09:00
Route :: get ( '/{id}/logs' , [ CategoryLogController :: class , 'index' ]) -> name ( 'v1.categories.logs.index' ); // ?action=&from=&to=&page&size
Route :: get ( '/logs/{log}' , [ CategoryLogController :: class , 'show' ]) -> name ( 'v1.categories.logs.show' );
2025-08-25 17:46:34 +09:00
// (선택) 특정 변경 시점으로 카테고리 복구(템플릿/필드와 별개)
// Route::post('{id}/logs/{log}/restore', [CategoryLogController::class, 'restore'])->name('v1.categories.logs.restore');
});
// Classifications API
2025-08-22 19:30:05 +09:00
Route :: prefix ( 'classifications' ) -> group ( function () {
2025-11-06 17:24:42 +09:00
Route :: get ( '' , [ ClassificationController :: class , 'index' ]) -> name ( 'v1.classifications.index' ); // 목록
Route :: post ( '' , [ ClassificationController :: class , 'store' ]) -> name ( 'v1.classifications.store' ); // 생성
Route :: get ( '/{id}' , [ ClassificationController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.classifications.show' ); // 단건
Route :: patch ( '/{id}' , [ ClassificationController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.classifications.update' ); // 수정
Route :: delete ( '/{id}' , [ ClassificationController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.classifications.destroy' ); // 삭제
2025-08-22 19:30:05 +09:00
});
2025-10-13 21:52:34 +09:00
// Clients (거래처 관리)
Route :: prefix ( 'clients' ) -> group ( function () {
2026-01-09 16:46:25 +09:00
Route :: get ( '/stats' , [ ClientController :: class , 'stats' ]) -> name ( 'v1.clients.stats' ); // 통계
Route :: delete ( '/bulk' , [ ClientController :: class , 'bulkDestroy' ]) -> name ( 'v1.clients.bulk-destroy' ); // 일괄 삭제
2025-11-06 17:24:42 +09:00
Route :: get ( '' , [ ClientController :: class , 'index' ]) -> name ( 'v1.clients.index' ); // 목록
Route :: post ( '' , [ ClientController :: class , 'store' ]) -> name ( 'v1.clients.store' ); // 생성
Route :: get ( '/{id}' , [ ClientController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.clients.show' ); // 단건
Route :: put ( '/{id}' , [ ClientController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.clients.update' ); // 수정
Route :: delete ( '/{id}' , [ ClientController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.clients.destroy' ); // 삭제
Route :: patch ( '/{id}/toggle' , [ ClientController :: class , 'toggle' ]) -> whereNumber ( 'id' ) -> name ( 'v1.clients.toggle' ); // 활성/비활성
2025-10-13 21:52:34 +09:00
});
2025-10-13 22:06:42 +09:00
// Client Groups (고객 그룹 관리)
Route :: prefix ( 'client-groups' ) -> group ( function () {
2025-11-06 17:24:42 +09:00
Route :: get ( '' , [ ClientGroupController :: class , 'index' ]) -> name ( 'v1.client-groups.index' ); // 목록
Route :: post ( '' , [ ClientGroupController :: class , 'store' ]) -> name ( 'v1.client-groups.store' ); // 생성
Route :: get ( '/{id}' , [ ClientGroupController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.client-groups.show' ); // 단건
Route :: put ( '/{id}' , [ ClientGroupController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.client-groups.update' ); // 수정
Route :: delete ( '/{id}' , [ ClientGroupController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.client-groups.destroy' ); // 삭제
Route :: patch ( '/{id}/toggle' , [ ClientGroupController :: class , 'toggle' ]) -> whereNumber ( 'id' ) -> name ( 'v1.client-groups.toggle' ); // 활성/비활성
2025-10-13 22:06:42 +09:00
});
feat: [quote] 견적 API Phase 2-3 완료 (Service + Controller Layer)
Phase 2 - Service Layer:
- QuoteService: 견적 CRUD + 상태관리 (확정/전환)
- QuoteNumberService: 견적번호 채번 (KD-{PREFIX}-YYMMDD-SEQ)
- FormulaEvaluatorService: 수식 평가 엔진 (SUM, IF, ROUND 등)
- QuoteCalculationService: 자동산출 (스크린/철재 제품)
- QuoteDocumentService: PDF 생성 및 이메일/카카오 발송
Phase 3 - Controller Layer:
- QuoteController: 16개 엔드포인트
- FormRequest 7개: Index, Store, Update, BulkDelete, Calculate, SendEmail, SendKakao
- QuoteApi.php: Swagger 문서 (12개 스키마, 16개 엔드포인트)
- routes/api.php: 16개 라우트 등록
i18n 키 추가:
- error.php: quote_not_found, formula_* 등
- message.php: quote.* 성공 메시지
2025-12-04 22:03:40 +09:00
// Quotes (견적 관리)
Route :: prefix ( 'quotes' ) -> group ( function () {
// 기본 CRUD
Route :: get ( '' , [ QuoteController :: class , 'index' ]) -> name ( 'v1.quotes.index' ); // 목록
Route :: post ( '' , [ QuoteController :: class , 'store' ]) -> name ( 'v1.quotes.store' ); // 생성
Route :: get ( '/{id}' , [ QuoteController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.quotes.show' ); // 단건
Route :: put ( '/{id}' , [ QuoteController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.quotes.update' ); // 수정
Route :: delete ( '/{id}' , [ QuoteController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.quotes.destroy' ); // 삭제
// 일괄 삭제
Route :: delete ( '/bulk' , [ QuoteController :: class , 'bulkDestroy' ]) -> name ( 'v1.quotes.bulk-destroy' ); // 일괄 삭제
// 상태 관리
Route :: post ( '/{id}/finalize' , [ QuoteController :: class , 'finalize' ]) -> whereNumber ( 'id' ) -> name ( 'v1.quotes.finalize' ); // 확정
Route :: post ( '/{id}/cancel-finalize' , [ QuoteController :: class , 'cancelFinalize' ]) -> whereNumber ( 'id' ) -> name ( 'v1.quotes.cancel-finalize' ); // 확정 취소
Route :: post ( '/{id}/convert' , [ QuoteController :: class , 'convertToOrder' ]) -> whereNumber ( 'id' ) -> name ( 'v1.quotes.convert' ); // 수주 전환
// 견적번호 미리보기
Route :: get ( '/number/preview' , [ QuoteController :: class , 'previewNumber' ]) -> name ( 'v1.quotes.number-preview' ); // 번호 미리보기
// 자동산출
Route :: get ( '/calculation/schema' , [ QuoteController :: class , 'calculationSchema' ]) -> name ( 'v1.quotes.calculation-schema' ); // 입력 스키마
Route :: post ( '/calculate' , [ QuoteController :: class , 'calculate' ]) -> name ( 'v1.quotes.calculate' ); // 자동산출 실행
2026-01-02 11:24:22 +09:00
Route :: post ( '/calculate/bom' , [ QuoteController :: class , 'calculateBom' ]) -> name ( 'v1.quotes.calculate-bom' ); // BOM 기반 자동산출
2026-01-02 13:13:50 +09:00
Route :: post ( '/calculate/bom/bulk' , [ QuoteController :: class , 'calculateBomBulk' ]) -> name ( 'v1.quotes.calculate-bom-bulk' ); // 다건 BOM 자동산출
feat: [quote] 견적 API Phase 2-3 완료 (Service + Controller Layer)
Phase 2 - Service Layer:
- QuoteService: 견적 CRUD + 상태관리 (확정/전환)
- QuoteNumberService: 견적번호 채번 (KD-{PREFIX}-YYMMDD-SEQ)
- FormulaEvaluatorService: 수식 평가 엔진 (SUM, IF, ROUND 등)
- QuoteCalculationService: 자동산출 (스크린/철재 제품)
- QuoteDocumentService: PDF 생성 및 이메일/카카오 발송
Phase 3 - Controller Layer:
- QuoteController: 16개 엔드포인트
- FormRequest 7개: Index, Store, Update, BulkDelete, Calculate, SendEmail, SendKakao
- QuoteApi.php: Swagger 문서 (12개 스키마, 16개 엔드포인트)
- routes/api.php: 16개 라우트 등록
i18n 키 추가:
- error.php: quote_not_found, formula_* 등
- message.php: quote.* 성공 메시지
2025-12-04 22:03:40 +09:00
// 문서 관리
Route :: post ( '/{id}/pdf' , [ QuoteController :: class , 'generatePdf' ]) -> whereNumber ( 'id' ) -> name ( 'v1.quotes.pdf' ); // PDF 생성
Route :: post ( '/{id}/send/email' , [ QuoteController :: class , 'sendEmail' ]) -> whereNumber ( 'id' ) -> name ( 'v1.quotes.send-email' ); // 이메일 발송
Route :: post ( '/{id}/send/kakao' , [ QuoteController :: class , 'sendKakao' ]) -> whereNumber ( 'id' ) -> name ( 'v1.quotes.send-kakao' ); // 카카오톡 발송
Route :: get ( '/{id}/send/history' , [ QuoteController :: class , 'sendHistory' ]) -> whereNumber ( 'id' ) -> name ( 'v1.quotes.send-history' ); // 발송 이력
2026-01-19 19:42:05 +09:00
// 입찰 전환
Route :: post ( '/{id}/convert-to-bidding' , [ QuoteController :: class , 'convertToBidding' ]) -> whereNumber ( 'id' ) -> name ( 'v1.quotes.convert-to-bidding' ); // 입찰 전환
});
// Biddings (입찰관리)
Route :: prefix ( 'biddings' ) -> group ( function () {
Route :: get ( '' , [ BiddingController :: class , 'index' ]) -> name ( 'v1.biddings.index' ); // 목록
Route :: post ( '' , [ BiddingController :: class , 'store' ]) -> name ( 'v1.biddings.store' ); // 생성
Route :: get ( '/stats' , [ BiddingController :: class , 'stats' ]) -> name ( 'v1.biddings.stats' ); // 통계
Route :: delete ( '/bulk' , [ BiddingController :: class , 'bulkDestroy' ]) -> name ( 'v1.biddings.bulk-destroy' ); // 일괄 삭제
Route :: get ( '/{id}' , [ BiddingController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.biddings.show' ); // 단건
Route :: put ( '/{id}' , [ BiddingController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.biddings.update' ); // 수정
Route :: delete ( '/{id}' , [ BiddingController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.biddings.destroy' ); // 삭제
Route :: patch ( '/{id}/status' , [ BiddingController :: class , 'updateStatus' ]) -> whereNumber ( 'id' ) -> name ( 'v1.biddings.status' ); // 상태 변경
feat: [quote] 견적 API Phase 2-3 완료 (Service + Controller Layer)
Phase 2 - Service Layer:
- QuoteService: 견적 CRUD + 상태관리 (확정/전환)
- QuoteNumberService: 견적번호 채번 (KD-{PREFIX}-YYMMDD-SEQ)
- FormulaEvaluatorService: 수식 평가 엔진 (SUM, IF, ROUND 등)
- QuoteCalculationService: 자동산출 (스크린/철재 제품)
- QuoteDocumentService: PDF 생성 및 이메일/카카오 발송
Phase 3 - Controller Layer:
- QuoteController: 16개 엔드포인트
- FormRequest 7개: Index, Store, Update, BulkDelete, Calculate, SendEmail, SendKakao
- QuoteApi.php: Swagger 문서 (12개 스키마, 16개 엔드포인트)
- routes/api.php: 16개 라우트 등록
i18n 키 추가:
- error.php: quote_not_found, formula_* 등
- message.php: quote.* 성공 메시지
2025-12-04 22:03:40 +09:00
});
2025-12-08 19:03:50 +09:00
// Pricing (단가 관리)
2025-10-13 22:06:42 +09:00
Route :: prefix ( 'pricing' ) -> group ( function () {
2025-12-08 19:03:50 +09:00
Route :: get ( '' , [ PricingController :: class , 'index' ]) -> name ( 'v1.pricing.index' ); // 목록
2026-01-09 22:19:11 +09:00
Route :: get ( '/stats' , [ PricingController :: class , 'stats' ]) -> name ( 'v1.pricing.stats' ); // 통계
2025-12-08 19:03:50 +09:00
Route :: get ( '/cost' , [ PricingController :: class , 'cost' ]) -> name ( 'v1.pricing.cost' ); // 원가 조회
Route :: post ( '/by-items' , [ PricingController :: class , 'byItems' ]) -> name ( 'v1.pricing.by-items' ); // 품목별 단가 현황
2026-01-09 22:19:11 +09:00
Route :: delete ( '/bulk' , [ PricingController :: class , 'bulkDestroy' ]) -> name ( 'v1.pricing.bulk-destroy' ); // 일괄 삭제
2025-12-08 19:03:50 +09:00
Route :: post ( '' , [ PricingController :: class , 'store' ]) -> name ( 'v1.pricing.store' ); // 등록
Route :: get ( '/{id}' , [ PricingController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.pricing.show' ); // 상세
Route :: put ( '/{id}' , [ PricingController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.pricing.update' ); // 수정
Route :: delete ( '/{id}' , [ PricingController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.pricing.destroy' ); // 삭제
Route :: post ( '/{id}/finalize' , [ PricingController :: class , 'finalize' ]) -> whereNumber ( 'id' ) -> name ( 'v1.pricing.finalize' ); // 확정
Route :: get ( '/{id}/revisions' , [ PricingController :: class , 'revisions' ]) -> whereNumber ( 'id' ) -> name ( 'v1.pricing.revisions' ); // 변경이력
2025-10-13 22:06:42 +09:00
});
2026-01-11 23:29:32 +09:00
// Labor (노임관리)
Route :: prefix ( 'labor' ) -> group ( function () {
Route :: get ( '' , [ LaborController :: class , 'index' ]) -> name ( 'v1.labor.index' ); // 목록
Route :: get ( '/stats' , [ LaborController :: class , 'stats' ]) -> name ( 'v1.labor.stats' ); // 통계
Route :: delete ( '/bulk' , [ LaborController :: class , 'bulkDestroy' ]) -> name ( 'v1.labor.bulk-destroy' ); // 일괄 삭제
Route :: post ( '' , [ LaborController :: class , 'store' ]) -> name ( 'v1.labor.store' ); // 등록
Route :: get ( '/{id}' , [ LaborController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.labor.show' ); // 상세
Route :: put ( '/{id}' , [ LaborController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.labor.update' ); // 수정
Route :: delete ( '/{id}' , [ LaborController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.labor.destroy' ); // 삭제
});
refactor: products/materials 테이블 및 관련 코드 삭제
- products, materials, product_components 테이블 삭제 마이그레이션
- FK 제약조건 정리 (orders, order_items, material_receipts, lots)
- 관련 Models 삭제: Product, Material, ProductComponent 등
- 관련 Controllers 삭제: ProductController, MaterialController, ProductBomItemController
- 관련 Services 삭제: ProductService, MaterialService, ProductBomService
- 관련 Requests, Swagger 파일 삭제
- 라우트 정리: /products, /materials 엔드포인트 제거
모든 품목 관리는 /items 엔드포인트로 통합됨
item_id_mappings 테이블에 ID 매핑 보존
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-14 00:20:09 +09:00
// REMOVED: Products & Materials 라우트 삭제됨 (products/materials 테이블 삭제)
// 모든 품목 관리는 /items 엔드포인트 사용
2025-09-05 17:59:34 +09:00
refactor: products/materials 테이블 및 관련 코드 삭제
- products, materials, product_components 테이블 삭제 마이그레이션
- FK 제약조건 정리 (orders, order_items, material_receipts, lots)
- 관련 Models 삭제: Product, Material, ProductComponent 등
- 관련 Controllers 삭제: ProductController, MaterialController, ProductBomItemController
- 관련 Services 삭제: ProductService, MaterialService, ProductBomService
- 관련 Requests, Swagger 파일 삭제
- 라우트 정리: /products, /materials 엔드포인트 제거
모든 품목 관리는 /items 엔드포인트로 통합됨
item_id_mappings 테이블에 ID 매핑 보존
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-14 00:20:09 +09:00
// Items (통합 품목 관리 - items 테이블)
2025-11-11 11:30:17 +09:00
Route :: prefix ( 'items' ) -> group ( function () {
2025-12-09 20:27:44 +09:00
Route :: get ( '' , [ ItemsController :: class , 'index' ]) -> name ( 'v1.items.index' ); // 통합 목록
2026-01-09 16:58:41 +09:00
Route :: get ( '/stats' , [ ItemsController :: class , 'stats' ]) -> name ( 'v1.items.stats' ); // 통계
2025-12-09 20:27:44 +09:00
Route :: post ( '' , [ ItemsController :: class , 'store' ]) -> name ( 'v1.items.store' ); // 품목 생성
Route :: get ( '/code/{code}' , [ ItemsController :: class , 'showByCode' ]) -> name ( 'v1.items.show_by_code' ); // code 기반 조회
Route :: get ( '/{id}' , [ ItemsController :: class , 'show' ]) -> name ( 'v1.items.show' ); // 단건 (item_type 파라미터 필수)
Route :: put ( '/{id}' , [ ItemsController :: class , 'update' ]) -> name ( 'v1.items.update' ); // 품목 수정
Route :: delete ( '/batch' , [ ItemsController :: class , 'batchDestroy' ]) -> name ( 'v1.items.batch_destroy' ); // 품목 일괄 삭제
Route :: delete ( '/{id}' , [ ItemsController :: class , 'destroy' ]) -> name ( 'v1.items.destroy' ); // 품목 삭제
2025-11-11 11:30:17 +09:00
});
2025-12-21 16:06:13 +09:00
// Items BOM - 전체 BOM 목록 (item_id 없이)
// 주의: /items/{id}/bom 보다 먼저 정의해야 함 ('bom'이 {id}로 인식되지 않도록)
Route :: get ( 'items/bom' , [ ItemsBomController :: class , 'listAll' ]) -> name ( 'v1.items.bom.list-all' );
2025-11-30 21:06:22 +09:00
// Items BOM (ID-based BOM API)
Route :: prefix ( 'items/{id}/bom' ) -> group ( function () {
2025-12-09 20:27:44 +09:00
Route :: get ( '' , [ ItemsBomController :: class , 'index' ]) -> name ( 'v1.items.bom.index' ); // BOM 목록 (flat)
Route :: get ( '/tree' , [ ItemsBomController :: class , 'tree' ]) -> name ( 'v1.items.bom.tree' ); // BOM 트리 (계층)
Route :: post ( '' , [ ItemsBomController :: class , 'store' ]) -> name ( 'v1.items.bom.store' ); // BOM 추가 (bulk)
Route :: put ( '/{lineId}' , [ ItemsBomController :: class , 'update' ]) -> name ( 'v1.items.bom.update' ); // BOM 수정
Route :: delete ( '/{lineId}' , [ ItemsBomController :: class , 'destroy' ]) -> name ( 'v1.items.bom.destroy' ); // BOM 삭제
Route :: get ( '/summary' , [ ItemsBomController :: class , 'summary' ]) -> name ( 'v1.items.bom.summary' ); // BOM 요약
Route :: get ( '/validate' , [ ItemsBomController :: class , 'validate' ]) -> name ( 'v1.items.bom.validate' ); // BOM 검증
Route :: post ( '/replace' , [ ItemsBomController :: class , 'replace' ]) -> name ( 'v1.items.bom.replace' ); // BOM 전체 교체
Route :: post ( '/reorder' , [ ItemsBomController :: class , 'reorder' ]) -> name ( 'v1.items.bom.reorder' ); // BOM 정렬
Route :: get ( '/categories' , [ ItemsBomController :: class , 'listCategories' ]) -> name ( 'v1.items.bom.categories' ); // 카테고리 목록
2025-11-17 11:45:16 +09:00
});
2025-12-12 17:38:22 +09:00
// Items Files (group_id 기반 파일 관리, 동적 field_key 지원)
2025-11-30 21:06:22 +09:00
Route :: prefix ( 'items/{id}/files' ) -> group ( function () {
2025-12-12 17:38:22 +09:00
Route :: get ( '' , [ ItemsFileController :: class , 'index' ]) -> name ( 'v1.items.files.index' ); // 파일 조회 (field_key별 그룹핑)
Route :: post ( '' , [ ItemsFileController :: class , 'upload' ]) -> name ( 'v1.items.files.upload' ); // 파일 업로드 (field_key 동적)
Route :: delete ( '/{fileId}' , [ ItemsFileController :: class , 'delete' ]) -> name ( 'v1.items.files.delete' ); // 파일 삭제 (file_id)
feat: 품목 파일 업로드 API 구현 (절곡도, 시방서, 인정서)
- Products 테이블에 9개 파일 관련 필드 추가
- bending_diagram, bending_details (JSON)
- specification_file, specification_file_name
- certification_file, certification_file_name
- certification_number, certification_start_date, certification_end_date
- ItemsFileController 구현 (Code-based API)
- POST /items/{code}/files - 파일 업로드
- DELETE /items/{code}/files/{type} - 파일 삭제
- 파일 타입: bending_diagram, specification, certification
- ItemsFileUploadRequest 검증
- 파일 타입별 MIME 검증 (이미지/문서)
- 파일 크기 제한 (10MB/20MB)
- 인증 정보 및 절곡 상세 정보 검증
- Swagger 문서 작성 (ItemsFileApi.php)
- 업로드/삭제 API 스펙
- 스키마: ItemFileUploadResponse, ItemFileDeleteResponse
2025-11-17 13:40:07 +09:00
});
refactor: products/materials 테이블 및 관련 코드 삭제
- products, materials, product_components 테이블 삭제 마이그레이션
- FK 제약조건 정리 (orders, order_items, material_receipts, lots)
- 관련 Models 삭제: Product, Material, ProductComponent 등
- 관련 Controllers 삭제: ProductController, MaterialController, ProductBomItemController
- 관련 Services 삭제: ProductService, MaterialService, ProductBomService
- 관련 Requests, Swagger 파일 삭제
- 라우트 정리: /products, /materials 엔드포인트 제거
모든 품목 관리는 /items 엔드포인트로 통합됨
item_id_mappings 테이블에 ID 매핑 보존
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-14 00:20:09 +09:00
// REMOVED: products/{id}/bom 라우트 삭제됨 (product_components 테이블 삭제)
// BOM 관리는 /items/{id}/bom 엔드포인트 사용
2025-08-25 17:46:34 +09:00
2025-09-05 17:59:34 +09:00
// 설계 전용 (Design) - 운영과 분리된 네임스페이스/경로
Route :: prefix ( 'design' ) -> group ( function () {
2025-11-06 17:24:42 +09:00
Route :: get ( '/models' , [ DesignModelController :: class , 'index' ]) -> name ( 'v1.design.models.index' );
Route :: post ( '/models' , [ DesignModelController :: class , 'store' ]) -> name ( 'v1.design.models.store' );
Route :: get ( '/models/{id}' , [ DesignModelController :: class , 'show' ]) -> name ( 'v1.design.models.show' );
Route :: put ( '/models/{id}' , [ DesignModelController :: class , 'update' ]) -> name ( 'v1.design.models.update' );
2025-09-05 17:59:34 +09:00
Route :: delete ( '/models/{id}' , [ DesignModelController :: class , 'destroy' ]) -> name ( 'v1.design.models.destroy' );
2025-11-06 17:24:42 +09:00
Route :: get ( '/models/{modelId}/versions' , [ DesignModelVersionController :: class , 'index' ]) -> name ( 'v1.design.models.versions.index' );
Route :: post ( '/models/{modelId}/versions' , [ DesignModelVersionController :: class , 'createDraft' ]) -> name ( 'v1.design.models.versions.store' );
Route :: post ( '/versions/{versionId}/release' , [ DesignModelVersionController :: class , 'release' ]) -> name ( 'v1.design.versions.release' );
2025-09-05 17:59:34 +09:00
2025-11-06 17:24:42 +09:00
Route :: get ( '/versions/{versionId}/bom-templates' , [ DesignBomTemplateController :: class , 'listByVersion' ]) -> name ( 'v1.design.bom.templates.index' );
Route :: post ( '/versions/{versionId}/bom-templates' , [ DesignBomTemplateController :: class , 'upsertTemplate' ]) -> name ( 'v1.design.bom.templates.store' );
Route :: get ( '/bom-templates/{templateId}' , [ DesignBomTemplateController :: class , 'show' ]) -> name ( 'v1.design.bom.templates.show' );
Route :: put ( '/bom-templates/{templateId}/items' , [ DesignBomTemplateController :: class , 'replaceItems' ]) -> name ( 'v1.design.bom.templates.items.replace' );
Route :: get ( '/bom-templates/{templateId}/diff' , [ DesignBomTemplateController :: class , 'diff' ]) -> name ( 'v1.design.bom.templates.diff' );
Route :: post ( '/bom-templates/{templateId}/clone' , [ DesignBomTemplateController :: class , 'cloneTemplate' ]) -> name ( 'v1.design.bom.templates.clone' );
2025-09-11 14:39:55 +09:00
// 감사 로그 조회
2025-11-06 17:24:42 +09:00
Route :: get ( '/audit-logs' , [ DesignAuditLogController :: class , 'index' ]) -> name ( 'v1.design.audit-logs.index' );
2025-09-22 22:09:42 +09:00
// BOM 계산 시스템
2025-11-06 17:24:42 +09:00
Route :: get ( '/models/{modelId}/estimate-parameters' , [ BomCalculationController :: class , 'getEstimateParameters' ]) -> name ( 'v1.design.models.estimate-parameters' );
Route :: post ( '/bom-templates/{bomTemplateId}/calculate-bom' , [ BomCalculationController :: class , 'calculateBom' ]) -> name ( 'v1.design.bom-templates.calculate-bom' );
Route :: get ( '/companies/{companyName}/formulas' , [ BomCalculationController :: class , 'getCompanyFormulas' ]) -> name ( 'v1.design.companies.formulas' );
Route :: post ( '/companies/{companyName}/formulas/{formulaType}' , [ BomCalculationController :: class , 'saveCompanyFormula' ]) -> name ( 'v1.design.companies.formulas.save' );
Route :: post ( '/formulas/test' , [ BomCalculationController :: class , 'testFormula' ]) -> name ( 'v1.design.formulas.test' );
2025-09-05 17:59:34 +09:00
});
2025-09-24 17:41:26 +09:00
// 모델셋 관리 API (견적 시스템)
Route :: prefix ( 'model-sets' ) -> group ( function () {
2025-11-06 17:24:42 +09:00
Route :: get ( '/' , [ ModelSetController :: class , 'index' ]) -> name ( 'v1.model-sets.index' ); // 모델셋 목록
Route :: post ( '/' , [ ModelSetController :: class , 'store' ]) -> name ( 'v1.model-sets.store' ); // 모델셋 생성
Route :: get ( '/{id}' , [ ModelSetController :: class , 'show' ]) -> name ( 'v1.model-sets.show' ); // 모델셋 상세
Route :: put ( '/{id}' , [ ModelSetController :: class , 'update' ]) -> name ( 'v1.model-sets.update' ); // 모델셋 수정
2025-09-24 17:41:26 +09:00
Route :: delete ( '/{id}' , [ ModelSetController :: class , 'destroy' ]) -> name ( 'v1.model-sets.destroy' ); // 모델셋 삭제
2025-11-06 17:24:42 +09:00
Route :: post ( '/{id}/clone' , [ ModelSetController :: class , 'clone' ]) -> name ( 'v1.model-sets.clone' ); // 모델셋 복제
2025-09-24 17:41:26 +09:00
// 모델셋 세부 기능
2025-11-06 17:24:42 +09:00
Route :: get ( '/{id}/fields' , [ ModelSetController :: class , 'getCategoryFields' ]) -> name ( 'v1.model-sets.fields' ); // 카테고리 필드 조회
Route :: get ( '/{id}/bom-templates' , [ ModelSetController :: class , 'getBomTemplates' ]) -> name ( 'v1.model-sets.bom-templates' ); // BOM 템플릿 조회
Route :: get ( '/{id}/estimate-parameters' , [ ModelSetController :: class , 'getEstimateParameters' ]) -> name ( 'v1.model-sets.estimate-parameters' ); // 견적 파라미터
Route :: post ( '/{id}/calculate-bom' , [ ModelSetController :: class , 'calculateBom' ]) -> name ( 'v1.model-sets.calculate-bom' ); // BOM 계산
2025-09-24 17:41:26 +09:00
});
// 견적 관리 API
Route :: prefix ( 'estimates' ) -> group ( function () {
2025-11-06 17:24:42 +09:00
Route :: get ( '/' , [ EstimateController :: class , 'index' ]) -> name ( 'v1.estimates.index' ); // 견적 목록
2026-01-13 16:00:47 +09:00
Route :: get ( '/stats' , [ EstimateController :: class , 'stats' ]) -> name ( 'v1.estimates.stats' ); // 견적 통계
2025-11-06 17:24:42 +09:00
Route :: post ( '/' , [ EstimateController :: class , 'store' ]) -> name ( 'v1.estimates.store' ); // 견적 생성
Route :: get ( '/{id}' , [ EstimateController :: class , 'show' ]) -> name ( 'v1.estimates.show' ); // 견적 상세
Route :: put ( '/{id}' , [ EstimateController :: class , 'update' ]) -> name ( 'v1.estimates.update' ); // 견적 수정
2025-09-24 17:41:26 +09:00
Route :: delete ( '/{id}' , [ EstimateController :: class , 'destroy' ]) -> name ( 'v1.estimates.destroy' ); // 견적 삭제
2025-11-06 17:24:42 +09:00
Route :: post ( '/{id}/clone' , [ EstimateController :: class , 'clone' ]) -> name ( 'v1.estimates.clone' ); // 견적 복제
Route :: put ( '/{id}/status' , [ EstimateController :: class , 'changeStatus' ]) -> name ( 'v1.estimates.status' ); // 견적 상태 변경
2025-09-24 17:41:26 +09:00
// 견적 폼 및 계산 기능
2025-11-06 17:24:42 +09:00
Route :: get ( '/form-schema/{model_set_id}' , [ EstimateController :: class , 'getFormSchema' ]) -> name ( 'v1.estimates.form-schema' ); // 견적 폼 스키마
Route :: post ( '/preview/{model_set_id}' , [ EstimateController :: class , 'previewCalculation' ]) -> name ( 'v1.estimates.preview' ); // 견적 계산 미리보기
2025-09-24 17:41:26 +09:00
});
2025-12-26 18:56:24 +09:00
// 공정 관리 API (Process Management)
Route :: prefix ( 'processes' ) -> group ( function () {
Route :: get ( '' , [ ProcessController :: class , 'index' ]) -> name ( 'v1.processes.index' );
Route :: get ( '/options' , [ ProcessController :: class , 'options' ]) -> name ( 'v1.processes.options' );
Route :: get ( '/stats' , [ ProcessController :: class , 'stats' ]) -> name ( 'v1.processes.stats' );
Route :: post ( '' , [ ProcessController :: class , 'store' ]) -> name ( 'v1.processes.store' );
Route :: delete ( '' , [ ProcessController :: class , 'destroyMany' ]) -> name ( 'v1.processes.destroy-many' );
Route :: get ( '/{id}' , [ ProcessController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.processes.show' );
Route :: put ( '/{id}' , [ ProcessController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.processes.update' );
Route :: delete ( '/{id}' , [ ProcessController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.processes.destroy' );
Route :: patch ( '/{id}/toggle' , [ ProcessController :: class , 'toggleActive' ]) -> whereNumber ( 'id' ) -> name ( 'v1.processes.toggle' );
});
2026-01-08 11:11:54 +09:00
// 수주관리 API (Sales)
Route :: prefix ( 'orders' ) -> group ( function () {
// 기본 CRUD
Route :: get ( '' , [ OrderController :: class , 'index' ]) -> name ( 'v1.orders.index' ); // 목록
Route :: get ( '/stats' , [ OrderController :: class , 'stats' ]) -> name ( 'v1.orders.stats' ); // 통계
Route :: post ( '' , [ OrderController :: class , 'store' ]) -> name ( 'v1.orders.store' ); // 생성
Route :: get ( '/{id}' , [ OrderController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.orders.show' ); // 상세
Route :: put ( '/{id}' , [ OrderController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.orders.update' ); // 수정
Route :: delete ( '/{id}' , [ OrderController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.orders.destroy' ); // 삭제
// 상태 관리
Route :: patch ( '/{id}/status' , [ OrderController :: class , 'updateStatus' ]) -> whereNumber ( 'id' ) -> name ( 'v1.orders.status' ); // 상태 변경
2026-01-08 20:17:40 +09:00
// 견적에서 수주 생성
Route :: post ( '/from-quote/{quoteId}' , [ OrderController :: class , 'createFromQuote' ]) -> whereNumber ( 'quoteId' ) -> name ( 'v1.orders.from-quote' );
// 생산지시 생성
Route :: post ( '/{id}/production-order' , [ OrderController :: class , 'createProductionOrder' ]) -> whereNumber ( 'id' ) -> name ( 'v1.orders.production-order' );
2026-01-16 21:58:57 +09:00
// 수주확정 되돌리기
Route :: post ( '/{id}/revert-confirmation' , [ OrderController :: class , 'revertOrderConfirmation' ]) -> whereNumber ( 'id' ) -> name ( 'v1.orders.revert-confirmation' );
// 생산지시 되돌리기
Route :: post ( '/{id}/revert-production' , [ OrderController :: class , 'revertProductionOrder' ]) -> whereNumber ( 'id' ) -> name ( 'v1.orders.revert-production' );
2026-01-08 11:11:54 +09:00
});
2025-12-26 13:23:07 +09:00
// 작업지시 관리 API (Production)
Route :: prefix ( 'work-orders' ) -> group ( function () {
// 기본 CRUD
Route :: get ( '' , [ WorkOrderController :: class , 'index' ]) -> name ( 'v1.work-orders.index' ); // 목록
Route :: get ( '/stats' , [ WorkOrderController :: class , 'stats' ]) -> name ( 'v1.work-orders.stats' ); // 통계
Route :: post ( '' , [ WorkOrderController :: class , 'store' ]) -> name ( 'v1.work-orders.store' ); // 생성
Route :: get ( '/{id}' , [ WorkOrderController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-orders.show' ); // 상세
Route :: put ( '/{id}' , [ WorkOrderController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-orders.update' ); // 수정
Route :: delete ( '/{id}' , [ WorkOrderController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-orders.destroy' ); // 삭제
// 상태 및 담당자 관리
Route :: patch ( '/{id}/status' , [ WorkOrderController :: class , 'updateStatus' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-orders.status' ); // 상태 변경
Route :: patch ( '/{id}/assign' , [ WorkOrderController :: class , 'assign' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-orders.assign' ); // 담당자 배정
// 벤딩 공정 상세 토글
Route :: patch ( '/{id}/bending/toggle' , [ WorkOrderController :: class , 'toggleBendingField' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-orders.bending-toggle' );
// 이슈 관리
Route :: post ( '/{id}/issues' , [ WorkOrderController :: class , 'addIssue' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-orders.issues.store' ); // 이슈 등록
Route :: patch ( '/{id}/issues/{issueId}/resolve' , [ WorkOrderController :: class , 'resolveIssue' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-orders.issues.resolve' ); // 이슈 해결
2026-01-13 16:00:47 +09:00
// 품목 상태 변경
Route :: patch ( '/{id}/items/{itemId}/status' , [ WorkOrderController :: class , 'updateItemStatus' ]) -> whereNumber ( 'id' ) -> whereNumber ( 'itemId' ) -> name ( 'v1.work-orders.items.status' );
2026-01-16 21:59:06 +09:00
// 자재 관리
Route :: get ( '/{id}/materials' , [ WorkOrderController :: class , 'materials' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-orders.materials' ); // 자재 목록 조회
Route :: post ( '/{id}/material-inputs' , [ WorkOrderController :: class , 'registerMaterialInput' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-orders.material-inputs' ); // 자재 투입 등록
2025-12-26 13:23:07 +09:00
});
2025-12-26 15:18:08 +09:00
// 작업실적 관리 API (Production)
Route :: prefix ( 'work-results' ) -> group ( function () {
// 기본 CRUD
Route :: get ( '' , [ WorkResultController :: class , 'index' ]) -> name ( 'v1.work-results.index' ); // 목록
Route :: get ( '/stats' , [ WorkResultController :: class , 'stats' ]) -> name ( 'v1.work-results.stats' ); // 통계
Route :: post ( '' , [ WorkResultController :: class , 'store' ]) -> name ( 'v1.work-results.store' ); // 생성
Route :: get ( '/{id}' , [ WorkResultController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-results.show' ); // 상세
Route :: put ( '/{id}' , [ WorkResultController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-results.update' ); // 수정
Route :: delete ( '/{id}' , [ WorkResultController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-results.destroy' ); // 삭제
// 상태 토글
Route :: patch ( '/{id}/inspection' , [ WorkResultController :: class , 'toggleInspection' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-results.inspection' ); // 검사 상태 토글
Route :: patch ( '/{id}/packaging' , [ WorkResultController :: class , 'togglePackaging' ]) -> whereNumber ( 'id' ) -> name ( 'v1.work-results.packaging' ); // 포장 상태 토글
});
2026-01-14 17:08:59 +09:00
// 검사 관리 API (Quality)
Route :: prefix ( 'inspections' ) -> group ( function () {
Route :: get ( '' , [ InspectionController :: class , 'index' ]) -> name ( 'v1.inspections.index' ); // 목록
Route :: get ( '/stats' , [ InspectionController :: class , 'stats' ]) -> name ( 'v1.inspections.stats' ); // 통계
Route :: post ( '' , [ InspectionController :: class , 'store' ]) -> name ( 'v1.inspections.store' ); // 생성
Route :: get ( '/{id}' , [ InspectionController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.inspections.show' ); // 상세
Route :: put ( '/{id}' , [ InspectionController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.inspections.update' ); // 수정
Route :: delete ( '/{id}' , [ InspectionController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.inspections.destroy' ); // 삭제
Route :: patch ( '/{id}/complete' , [ InspectionController :: class , 'complete' ]) -> whereNumber ( 'id' ) -> name ( 'v1.inspections.complete' ); // 완료 처리
});
feat: 파일 저장 시스템 DB 마이그레이션
- enhance_files_table: 이중 파일명 시스템 (display_name/stored_name), 폴더 관리, 문서 연결 지원
- create_folders_table: 동적 폴더 관리 시스템 (tenant별 커스터마이징 가능)
- 5개 stub 마이그레이션 생성 (file_share_links, file_deletion_logs, storage_usage_history, add_storage_columns_to_tenants)
- FolderSeeder stub 생성
- CURRENT_WORKS.md에 Phase 1 진행상황 문서화
fix: 파일 공유 및 삭제 기능 버그 수정
- ShareLinkRequest: PATH 파라미터 {id}를 file_id로 자동 병합
- routes/api.php: 공유 링크 다운로드를 auth.apikey 그룹 밖으로 이동 (인증 불필요)
- FileShareLink: File, Tenant 클래스 import 추가
- File 모델: softDeleteFile()에서 SoftDeletes의 delete() 메서드 사용
- FileStorageService: getTrash(), restoreFile(), permanentDelete()에서 onlyTrashed() 사용
- File 모델: Tenant 네임스페이스 수정 (App\Models\Tenants\Tenant)
refactor: Swagger 문서 정리 - File 태그를 Files로 통합
- FileApi.php의 모든 태그를 Files로 변경
- 구 파일 시스템 라우트 삭제 (prefix 'file')
- 구 FileController.php 삭제
- 신규 파일 저장소 시스템으로 완전 통합
fix: 모든 legacy 파일 컬럼 nullable 일괄 처리
- 5개 legacy 컬럼을 한 번에 nullable로 변경
* original_name, file_name, file_name_old (string)
* fileable_id, fileable_type (polymorphic)
- foreach 루프로 반복 작업 자동화
- 신규/기존 시스템 간 완전한 하위 호환성 확보
fix: legacy 파일 컬럼 nullable 처리 완료
- file_name, file_name_old 컬럼도 nullable로 변경
- 기존 시스템과 신규 시스템 간 완전한 하위 호환성 확보
- Legacy: original_name, file_name, file_name_old (nullable)
- New: display_name, stored_name (required)
fix: original_name 컬럼 nullable 처리
- original_name을 nullable로 변경하여 하위 호환성 유지
- 새 시스템에서는 display_name 사용, 기존 시스템은 original_name 사용 가능
fix: 파일 업로드 DB 컬럼 누락 및 메시지 구조 개선
- files 테이블에 감사 컬럼 추가 (created_by, updated_by, uploaded_by)
- ApiResponse::handle() 메시지 로직 개선 (접미사 제거)
- 다국어 지원을 위한 완성된 문장 구조 유지
- FileUploadRequest 파일 검증 규칙 수정
fix: 파일 저장소 버그 수정 및 신규 테넌트 폴더 자동 생성
- FolderSeeder 네임스페이스 수정 (App\Models\Tenant → App\Models\Tenants\Tenant)
- FileStorageController use 문 구문 오류 수정 (/ → \)
- TenantObserver에 신규 테넌트 기본 폴더 자동 생성 로직 추가
- 5개 기본 폴더 (생산관리, 품질관리, 회계, 인사, 일반)
- 에러 처리 및 로깅
- 회원가입 시 자동 실행
2025-11-10 19:08:56 +09:00
// 파일 저장소 API
Route :: prefix ( 'files' ) -> group ( function () {
Route :: post ( '/upload' , [ FileStorageController :: class , 'upload' ]) -> name ( 'v1.files.upload' ); // 파일 업로드 (임시)
Route :: post ( '/move' , [ FileStorageController :: class , 'move' ]) -> name ( 'v1.files.move' ); // 파일 이동 (temp → folder)
Route :: get ( '/' , [ FileStorageController :: class , 'index' ]) -> name ( 'v1.files.index' ); // 파일 목록
Route :: get ( '/trash' , [ FileStorageController :: class , 'trash' ]) -> name ( 'v1.files.trash' ); // 휴지통 목록
Route :: get ( '/{id}' , [ FileStorageController :: class , 'show' ]) -> name ( 'v1.files.show' ); // 파일 상세
Route :: get ( '/{id}/download' , [ FileStorageController :: class , 'download' ]) -> name ( 'v1.files.download' ); // 파일 다운로드
Route :: delete ( '/{id}' , [ FileStorageController :: class , 'destroy' ]) -> name ( 'v1.files.destroy' ); // 파일 삭제 (soft)
Route :: post ( '/{id}/restore' , [ FileStorageController :: class , 'restore' ]) -> name ( 'v1.files.restore' ); // 파일 복구
Route :: delete ( '/{id}/permanent' , [ FileStorageController :: class , 'permanentDelete' ]) -> name ( 'v1.files.permanent' ); // 파일 영구 삭제
Route :: post ( '/{id}/share' , [ FileStorageController :: class , 'createShareLink' ]) -> name ( 'v1.files.share' ); // 공유 링크 생성
});
// 저장소 사용량
Route :: get ( '/storage/usage' , [ FileStorageController :: class , 'storageUsage' ]) -> name ( 'v1.storage.usage' );
// 폴더 관리 API
Route :: prefix ( 'folders' ) -> group ( function () {
Route :: get ( '/' , [ FolderController :: class , 'index' ]) -> name ( 'v1.folders.index' ); // 폴더 목록
Route :: post ( '/' , [ FolderController :: class , 'store' ]) -> name ( 'v1.folders.store' ); // 폴더 생성
Route :: get ( '/{id}' , [ FolderController :: class , 'show' ]) -> name ( 'v1.folders.show' ); // 폴더 상세
Route :: put ( '/{id}' , [ FolderController :: class , 'update' ]) -> name ( 'v1.folders.update' ); // 폴더 수정
Route :: delete ( '/{id}' , [ FolderController :: class , 'destroy' ]) -> name ( 'v1.folders.destroy' ); // 폴더 삭제/비활성화
Route :: post ( '/reorder' , [ FolderController :: class , 'reorder' ]) -> name ( 'v1.folders.reorder' ); // 폴더 순서 변경
});
2025-11-20 16:55:57 +09:00
// 품목기준관리 (ItemMaster) API
Route :: prefix ( 'item-master' ) -> group ( function () {
// 초기화
Route :: get ( '/init' , [ ItemMasterController :: class , 'init' ]) -> name ( 'v1.item-master.init' );
// 페이지 관리
Route :: get ( '/pages' , [ ItemPageController :: class , 'index' ]) -> name ( 'v1.item-master.pages.index' );
Route :: post ( '/pages' , [ ItemPageController :: class , 'store' ]) -> name ( 'v1.item-master.pages.store' );
Route :: put ( '/pages/{id}' , [ ItemPageController :: class , 'update' ]) -> name ( 'v1.item-master.pages.update' );
Route :: delete ( '/pages/{id}' , [ ItemPageController :: class , 'destroy' ]) -> name ( 'v1.item-master.pages.destroy' );
2025-11-26 14:09:31 +09:00
// 독립 섹션 관리
Route :: get ( '/sections' , [ ItemSectionController :: class , 'index' ]) -> name ( 'v1.item-master.sections.index' );
Route :: post ( '/sections' , [ ItemSectionController :: class , 'storeIndependent' ]) -> name ( 'v1.item-master.sections.store-independent' );
Route :: post ( '/sections/{id}/clone' , [ ItemSectionController :: class , 'clone' ]) -> name ( 'v1.item-master.sections.clone' );
Route :: get ( '/sections/{id}/usage' , [ ItemSectionController :: class , 'getUsage' ]) -> name ( 'v1.item-master.sections.usage' );
// 섹션 관리 (페이지 연결)
2025-11-20 16:55:57 +09:00
Route :: post ( '/pages/{pageId}/sections' , [ ItemSectionController :: class , 'store' ]) -> name ( 'v1.item-master.sections.store' );
Route :: put ( '/sections/{id}' , [ ItemSectionController :: class , 'update' ]) -> name ( 'v1.item-master.sections.update' );
Route :: delete ( '/sections/{id}' , [ ItemSectionController :: class , 'destroy' ]) -> name ( 'v1.item-master.sections.destroy' );
Route :: put ( '/pages/{pageId}/sections/reorder' , [ ItemSectionController :: class , 'reorder' ]) -> name ( 'v1.item-master.sections.reorder' );
2025-11-26 14:09:31 +09:00
// 독립 필드 관리
Route :: get ( '/fields' , [ ItemFieldController :: class , 'index' ]) -> name ( 'v1.item-master.fields.index' );
Route :: post ( '/fields' , [ ItemFieldController :: class , 'storeIndependent' ]) -> name ( 'v1.item-master.fields.store-independent' );
Route :: post ( '/fields/{id}/clone' , [ ItemFieldController :: class , 'clone' ]) -> name ( 'v1.item-master.fields.clone' );
Route :: get ( '/fields/{id}/usage' , [ ItemFieldController :: class , 'getUsage' ]) -> name ( 'v1.item-master.fields.usage' );
// 필드 관리 (섹션 연결)
2025-11-20 16:55:57 +09:00
Route :: post ( '/sections/{sectionId}/fields' , [ ItemFieldController :: class , 'store' ]) -> name ( 'v1.item-master.fields.store' );
Route :: put ( '/fields/{id}' , [ ItemFieldController :: class , 'update' ]) -> name ( 'v1.item-master.fields.update' );
Route :: delete ( '/fields/{id}' , [ ItemFieldController :: class , 'destroy' ]) -> name ( 'v1.item-master.fields.destroy' );
Route :: put ( '/sections/{sectionId}/fields/reorder' , [ ItemFieldController :: class , 'reorder' ]) -> name ( 'v1.item-master.fields.reorder' );
2025-11-20 17:07:40 +09:00
2025-11-26 14:09:31 +09:00
// 독립 BOM 항목 관리
Route :: get ( '/bom-items' , [ ItemBomItemController :: class , 'index' ]) -> name ( 'v1.item-master.bom-items.index' );
Route :: post ( '/bom-items' , [ ItemBomItemController :: class , 'storeIndependent' ]) -> name ( 'v1.item-master.bom-items.store-independent' );
// BOM 항목 관리 (섹션 연결)
2025-11-20 17:07:40 +09:00
Route :: post ( '/sections/{sectionId}/bom-items' , [ ItemBomItemController :: class , 'store' ]) -> name ( 'v1.item-master.bom-items.store' );
Route :: put ( '/bom-items/{id}' , [ ItemBomItemController :: class , 'update' ]) -> name ( 'v1.item-master.bom-items.update' );
Route :: delete ( '/bom-items/{id}' , [ ItemBomItemController :: class , 'destroy' ]) -> name ( 'v1.item-master.bom-items.destroy' );
// 섹션 템플릿
Route :: get ( '/section-templates' , [ SectionTemplateController :: class , 'index' ]) -> name ( 'v1.item-master.section-templates.index' );
Route :: post ( '/section-templates' , [ SectionTemplateController :: class , 'store' ]) -> name ( 'v1.item-master.section-templates.store' );
Route :: put ( '/section-templates/{id}' , [ SectionTemplateController :: class , 'update' ]) -> name ( 'v1.item-master.section-templates.update' );
Route :: delete ( '/section-templates/{id}' , [ SectionTemplateController :: class , 'destroy' ]) -> name ( 'v1.item-master.section-templates.destroy' );
2025-11-20 17:16:03 +09:00
// 커스텀 탭
Route :: get ( '/custom-tabs' , [ CustomTabController :: class , 'index' ]) -> name ( 'v1.item-master.custom-tabs.index' );
Route :: post ( '/custom-tabs' , [ CustomTabController :: class , 'store' ]) -> name ( 'v1.item-master.custom-tabs.store' );
2025-11-20 20:28:33 +09:00
Route :: put ( '/custom-tabs/reorder' , [ CustomTabController :: class , 'reorder' ]) -> name ( 'v1.item-master.custom-tabs.reorder' );
2025-11-20 17:16:03 +09:00
Route :: put ( '/custom-tabs/{id}' , [ CustomTabController :: class , 'update' ]) -> name ( 'v1.item-master.custom-tabs.update' );
Route :: delete ( '/custom-tabs/{id}' , [ CustomTabController :: class , 'destroy' ]) -> name ( 'v1.item-master.custom-tabs.destroy' );
// 단위 옵션
Route :: get ( '/unit-options' , [ UnitOptionController :: class , 'index' ]) -> name ( 'v1.item-master.unit-options.index' );
Route :: post ( '/unit-options' , [ UnitOptionController :: class , 'store' ]) -> name ( 'v1.item-master.unit-options.store' );
Route :: delete ( '/unit-options/{id}' , [ UnitOptionController :: class , 'destroy' ]) -> name ( 'v1.item-master.unit-options.destroy' );
2025-11-26 14:09:31 +09:00
// 엔티티 관계 관리 (독립 엔티티 + 링크 테이블)
// 페이지-섹션 연결
Route :: post ( '/pages/{pageId}/link-section' , [ EntityRelationshipController :: class , 'linkSectionToPage' ]) -> name ( 'v1.item-master.pages.link-section' );
Route :: delete ( '/pages/{pageId}/unlink-section/{sectionId}' , [ EntityRelationshipController :: class , 'unlinkSectionFromPage' ]) -> name ( 'v1.item-master.pages.unlink-section' );
// 페이지-필드 직접 연결
Route :: post ( '/pages/{pageId}/link-field' , [ EntityRelationshipController :: class , 'linkFieldToPage' ]) -> name ( 'v1.item-master.pages.link-field' );
Route :: delete ( '/pages/{pageId}/unlink-field/{fieldId}' , [ EntityRelationshipController :: class , 'unlinkFieldFromPage' ]) -> name ( 'v1.item-master.pages.unlink-field' );
// 페이지 관계 조회
Route :: get ( '/pages/{pageId}/relationships' , [ EntityRelationshipController :: class , 'getPageRelationships' ]) -> name ( 'v1.item-master.pages.relationships' );
Route :: get ( '/pages/{pageId}/structure' , [ EntityRelationshipController :: class , 'getPageStructure' ]) -> name ( 'v1.item-master.pages.structure' );
// 섹션-필드 연결
Route :: post ( '/sections/{sectionId}/link-field' , [ EntityRelationshipController :: class , 'linkFieldToSection' ]) -> name ( 'v1.item-master.sections.link-field' );
Route :: delete ( '/sections/{sectionId}/unlink-field/{fieldId}' , [ EntityRelationshipController :: class , 'unlinkFieldFromSection' ]) -> name ( 'v1.item-master.sections.unlink-field' );
// 섹션-BOM 연결
Route :: post ( '/sections/{sectionId}/link-bom' , [ EntityRelationshipController :: class , 'linkBomToSection' ]) -> name ( 'v1.item-master.sections.link-bom' );
Route :: delete ( '/sections/{sectionId}/unlink-bom/{bomId}' , [ EntityRelationshipController :: class , 'unlinkBomFromSection' ]) -> name ( 'v1.item-master.sections.unlink-bom' );
// 섹션 관계 조회
Route :: get ( '/sections/{sectionId}/relationships' , [ EntityRelationshipController :: class , 'getSectionRelationships' ]) -> name ( 'v1.item-master.sections.relationships' );
// 관계 순서 변경
Route :: post ( '/relationships/reorder' , [ EntityRelationshipController :: class , 'reorderRelationships' ]) -> name ( 'v1.item-master.relationships.reorder' );
2025-11-20 16:55:57 +09:00
});
2025-12-28 00:53:26 +09:00
// 시스템 게시판 API (is_system=true, tenant_id=null)
Route :: prefix ( 'system-boards' ) -> group ( function () {
// 시스템 게시판 목록/상세
Route :: get ( '/' , [ SystemBoardController :: class , 'index' ]) -> name ( 'v1.system-boards.index' );
Route :: get ( '/{code}' , [ SystemBoardController :: class , 'show' ]) -> name ( 'v1.system-boards.show' );
Route :: get ( '/{code}/fields' , [ SystemBoardController :: class , 'fields' ]) -> name ( 'v1.system-boards.fields' );
// 시스템 게시글 API
Route :: get ( '/{code}/posts' , [ SystemPostController :: class , 'index' ]) -> name ( 'v1.system-boards.posts.index' );
Route :: post ( '/{code}/posts' , [ SystemPostController :: class , 'store' ]) -> name ( 'v1.system-boards.posts.store' );
Route :: get ( '/{code}/posts/{id}' , [ SystemPostController :: class , 'show' ]) -> name ( 'v1.system-boards.posts.show' );
Route :: put ( '/{code}/posts/{id}' , [ SystemPostController :: class , 'update' ]) -> name ( 'v1.system-boards.posts.update' );
Route :: delete ( '/{code}/posts/{id}' , [ SystemPostController :: class , 'destroy' ]) -> name ( 'v1.system-boards.posts.destroy' );
// 시스템 댓글 API
Route :: get ( '/{code}/posts/{postId}/comments' , [ SystemPostController :: class , 'comments' ]) -> name ( 'v1.system-boards.posts.comments.index' );
Route :: post ( '/{code}/posts/{postId}/comments' , [ SystemPostController :: class , 'storeComment' ]) -> name ( 'v1.system-boards.posts.comments.store' );
Route :: put ( '/{code}/posts/{postId}/comments/{commentId}' , [ SystemPostController :: class , 'updateComment' ]) -> name ( 'v1.system-boards.posts.comments.update' );
Route :: delete ( '/{code}/posts/{postId}/comments/{commentId}' , [ SystemPostController :: class , 'destroyComment' ]) -> name ( 'v1.system-boards.posts.comments.destroy' );
});
2025-11-30 21:06:22 +09:00
// 게시판 관리 API (테넌트용)
Route :: prefix ( 'boards' ) -> group ( function () {
// 게시판 목록/상세
Route :: get ( '/' , [ BoardController :: class , 'index' ]) -> name ( 'v1.boards.index' ); // 접근 가능한 게시판 목록
Route :: get ( '/tenant' , [ BoardController :: class , 'tenantBoards' ]) -> name ( 'v1.boards.tenant' ); // 테넌트 게시판만
Route :: post ( '/' , [ BoardController :: class , 'store' ]) -> name ( 'v1.boards.store' ); // 테넌트 게시판 생성
2025-12-27 18:27:19 +09:00
Route :: get ( '/{id}' , [ BoardController :: class , 'show' ]) -> whereNumber ( 'id' ) -> name ( 'v1.boards.show' ); // 게시판 상세 (ID 기반)
2025-11-30 21:06:22 +09:00
Route :: put ( '/{id}' , [ BoardController :: class , 'update' ]) -> whereNumber ( 'id' ) -> name ( 'v1.boards.update' ); // 테넌트 게시판 수정
Route :: delete ( '/{id}' , [ BoardController :: class , 'destroy' ]) -> whereNumber ( 'id' ) -> name ( 'v1.boards.destroy' ); // 테넌트 게시판 삭제
Route :: get ( '/{code}/fields' , [ BoardController :: class , 'fields' ]) -> name ( 'v1.boards.fields' ); // 게시판 필드 목록
// 게시글 API
Route :: get ( '/{code}/posts' , [ PostController :: class , 'index' ]) -> name ( 'v1.boards.posts.index' ); // 게시글 목록
Route :: post ( '/{code}/posts' , [ PostController :: class , 'store' ]) -> name ( 'v1.boards.posts.store' ); // 게시글 작성
Route :: get ( '/{code}/posts/{id}' , [ PostController :: class , 'show' ]) -> name ( 'v1.boards.posts.show' ); // 게시글 상세
Route :: put ( '/{code}/posts/{id}' , [ PostController :: class , 'update' ]) -> name ( 'v1.boards.posts.update' ); // 게시글 수정
Route :: delete ( '/{code}/posts/{id}' , [ PostController :: class , 'destroy' ]) -> name ( 'v1.boards.posts.destroy' ); // 게시글 삭제
// 댓글 API
Route :: get ( '/{code}/posts/{postId}/comments' , [ PostController :: class , 'comments' ]) -> name ( 'v1.boards.posts.comments.index' ); // 댓글 목록
Route :: post ( '/{code}/posts/{postId}/comments' , [ PostController :: class , 'storeComment' ]) -> name ( 'v1.boards.posts.comments.store' ); // 댓글 작성
Route :: put ( '/{code}/posts/{postId}/comments/{commentId}' , [ PostController :: class , 'updateComment' ]) -> name ( 'v1.boards.posts.comments.update' ); // 댓글 수정
Route :: delete ( '/{code}/posts/{postId}/comments/{commentId}' , [ PostController :: class , 'destroyComment' ]) -> name ( 'v1.boards.posts.comments.destroy' ); // 댓글 삭제
});
2025-12-19 16:27:36 +09:00
// 게시글 API (사용자 중심)
Route :: prefix ( 'posts' ) -> group ( function () {
Route :: get ( '/my' , [ PostController :: class , 'myPosts' ]) -> name ( 'v1.posts.my' ); // 나의 게시글 목록
});
2025-08-14 00:55:08 +09:00
});
feat: 파일 저장 시스템 DB 마이그레이션
- enhance_files_table: 이중 파일명 시스템 (display_name/stored_name), 폴더 관리, 문서 연결 지원
- create_folders_table: 동적 폴더 관리 시스템 (tenant별 커스터마이징 가능)
- 5개 stub 마이그레이션 생성 (file_share_links, file_deletion_logs, storage_usage_history, add_storage_columns_to_tenants)
- FolderSeeder stub 생성
- CURRENT_WORKS.md에 Phase 1 진행상황 문서화
fix: 파일 공유 및 삭제 기능 버그 수정
- ShareLinkRequest: PATH 파라미터 {id}를 file_id로 자동 병합
- routes/api.php: 공유 링크 다운로드를 auth.apikey 그룹 밖으로 이동 (인증 불필요)
- FileShareLink: File, Tenant 클래스 import 추가
- File 모델: softDeleteFile()에서 SoftDeletes의 delete() 메서드 사용
- FileStorageService: getTrash(), restoreFile(), permanentDelete()에서 onlyTrashed() 사용
- File 모델: Tenant 네임스페이스 수정 (App\Models\Tenants\Tenant)
refactor: Swagger 문서 정리 - File 태그를 Files로 통합
- FileApi.php의 모든 태그를 Files로 변경
- 구 파일 시스템 라우트 삭제 (prefix 'file')
- 구 FileController.php 삭제
- 신규 파일 저장소 시스템으로 완전 통합
fix: 모든 legacy 파일 컬럼 nullable 일괄 처리
- 5개 legacy 컬럼을 한 번에 nullable로 변경
* original_name, file_name, file_name_old (string)
* fileable_id, fileable_type (polymorphic)
- foreach 루프로 반복 작업 자동화
- 신규/기존 시스템 간 완전한 하위 호환성 확보
fix: legacy 파일 컬럼 nullable 처리 완료
- file_name, file_name_old 컬럼도 nullable로 변경
- 기존 시스템과 신규 시스템 간 완전한 하위 호환성 확보
- Legacy: original_name, file_name, file_name_old (nullable)
- New: display_name, stored_name (required)
fix: original_name 컬럼 nullable 처리
- original_name을 nullable로 변경하여 하위 호환성 유지
- 새 시스템에서는 display_name 사용, 기존 시스템은 original_name 사용 가능
fix: 파일 업로드 DB 컬럼 누락 및 메시지 구조 개선
- files 테이블에 감사 컬럼 추가 (created_by, updated_by, uploaded_by)
- ApiResponse::handle() 메시지 로직 개선 (접미사 제거)
- 다국어 지원을 위한 완성된 문장 구조 유지
- FileUploadRequest 파일 검증 규칙 수정
fix: 파일 저장소 버그 수정 및 신규 테넌트 폴더 자동 생성
- FolderSeeder 네임스페이스 수정 (App\Models\Tenant → App\Models\Tenants\Tenant)
- FileStorageController use 문 구문 오류 수정 (/ → \)
- TenantObserver에 신규 테넌트 기본 폴더 자동 생성 로직 추가
- 5개 기본 폴더 (생산관리, 품질관리, 회계, 인사, 일반)
- 에러 처리 및 로깅
- 회원가입 시 자동 실행
2025-11-10 19:08:56 +09:00
// 공유 링크 다운로드 (인증 불필요 - auth.apikey 그룹 밖)
Route :: get ( '/files/share/{token}' , [ FileStorageController :: class , 'downloadShared' ]) -> name ( 'v1.files.share.download' );
2025-08-16 03:25:06 +09:00
});