diff --git a/app/Services/MenuBootstrapService.php b/app/Services/MenuBootstrapService.php index 6ecafa0..5348439 100644 --- a/app/Services/MenuBootstrapService.php +++ b/app/Services/MenuBootstrapService.php @@ -11,11 +11,58 @@ class MenuBootstrapService { /** - * 테넌트를 위한 기본 메뉴 구조 생성 + * 글로벌 메뉴 템플릿을 테넌트에 복제 * * @param int $tenantId 테넌트 ID * @return array 생성된 메뉴 ID 목록 */ + public static function cloneGlobalMenusForTenant(int $tenantId): array + { + return DB::transaction(function () use ($tenantId) { + // 1. 글로벌 템플릿 메뉴 조회 (parent_id 순서대로 정렬) + $templateMenus = Menu::withoutGlobalScopes() + ->whereNull('tenant_id') + ->orderByRaw('COALESCE(parent_id, 0)') + ->orderBy('sort_order') + ->get(); + + // 2. parent_id 매핑 테이블 생성 + $idMapping = []; + $menuIds = []; + + // 3. 계층 순서대로 복제 (parent → child) + foreach ($templateMenus as $template) { + $newMenu = Menu::create([ + 'tenant_id' => $tenantId, + 'parent_id' => $template->parent_id + ? ($idMapping[$template->parent_id] ?? null) + : null, + 'name' => $template->name, + 'url' => $template->url, + 'icon' => $template->icon, + 'sort_order' => $template->sort_order, + 'is_active' => $template->is_active, + 'hidden' => $template->hidden, + 'is_external' => $template->is_external, + 'external_url' => $template->external_url, + ]); + + // 4. ID 매핑 저장 + $idMapping[$template->id] = $newMenu->id; + $menuIds[] = $newMenu->id; + } + + return $menuIds; + }); + } + + /** + * 테넌트를 위한 기본 메뉴 구조 생성 (구버전 - 하위 호환성 유지) + * + * @deprecated Use cloneGlobalMenusForTenant() instead + * @param int $tenantId 테넌트 ID + * @return array 생성된 메뉴 ID 목록 + */ public static function createDefaultMenus(int $tenantId): array { return DB::transaction(function () use ($tenantId) { diff --git a/app/Services/RegisterService.php b/app/Services/RegisterService.php index 9c21e36..60516ad 100644 --- a/app/Services/RegisterService.php +++ b/app/Services/RegisterService.php @@ -51,8 +51,8 @@ public static function register(array $params): array ], ]); - // 3. Create default menus for tenant - $menuIds = MenuBootstrapService::createDefaultMenus($tenant->id); + // 3. Clone global menu template for tenant + $menuIds = MenuBootstrapService::cloneGlobalMenusForTenant($tenant->id); // 4. Create User with hashed password and options $user = User::create([ diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 9a05b4d..0a85d1a 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -20,5 +20,10 @@ public function run(): void 'name' => 'Test User', 'email' => 'test@example.com', ]); + + // 글로벌 메뉴 템플릿 시딩 + $this->call([ + GlobalMenuTemplateSeeder::class, + ]); } } diff --git a/database/seeders/GlobalMenuTemplateSeeder.php b/database/seeders/GlobalMenuTemplateSeeder.php new file mode 100644 index 0000000..fed8294 --- /dev/null +++ b/database/seeders/GlobalMenuTemplateSeeder.php @@ -0,0 +1,271 @@ + 'layout-dashboard', + 'Users' => 'users', + 'Sliders' => 'sliders', + 'Database' => 'database', + 'Box' => 'box', + 'Archive' => 'archive', + 'Wrench' => 'wrench', + 'Building' => 'building', + 'User' => 'user', + 'ClipboardCheck' => 'clipboard-check', + 'Code' => 'code', + 'Calculator' => 'calculator', + 'Palette' => 'palette', + 'TrendingUp' => 'trending-up', + 'Building2' => 'building-2', + 'BarChart3' => 'bar-chart-3', + 'Briefcase' => 'briefcase', + 'ShoppingCart' => 'shopping-cart', + 'MapPin' => 'map-pin', + 'DollarSign' => 'dollar-sign', + 'FileText' => 'file-text', + 'Factory' => 'factory', + 'Package' => 'package', + 'CheckSquare' => 'check-square', + 'CheckCircle' => 'check-circle', + 'Warehouse' => 'warehouse', + 'Layers' => 'layers', + 'Truck' => 'truck', + 'XCircle' => 'x-circle', + 'Car' => 'car', + 'Shield' => 'shield', + 'Settings' => 'settings', + 'Activity' => 'activity', + 'Server' => 'server', + ]; + + // SystemAdmin 메뉴 구조 정의 + $menus = [ + [ + 'name' => '시스템 대시보드', + 'url' => '/dashboard', + 'icon' => $iconMap['LayoutDashboard'], + 'sort_order' => 1, + ], + [ + 'name' => '사용자 관리', + 'url' => '/users', + 'icon' => $iconMap['Users'], + 'sort_order' => 2, + ], + [ + 'name' => '메뉴 커스터마이징', + 'url' => '/menu-customization', + 'icon' => $iconMap['Sliders'], + 'sort_order' => 3, + ], + [ + 'name' => '기준정보 관리', + 'url' => '/master-data', + 'icon' => $iconMap['Database'], + 'sort_order' => 4, + 'children' => [ + ['name' => '모델관리', 'url' => '/master-data/models-management', 'icon' => $iconMap['Box']], + ['name' => '자재관리', 'url' => '/master-data/items-management', 'icon' => $iconMap['Archive']], + ['name' => '품목기준관리', 'url' => '/master-data/item-master-data-management', 'icon' => $iconMap['Database']], + ['name' => '공정관리', 'url' => '/master-data/process-management', 'icon' => $iconMap['Wrench']], + ['name' => '거래처관리', 'url' => '/master-data/customer-management', 'icon' => $iconMap['Building']], + ['name' => '사원관리', 'url' => '/master-data/employee-management', 'icon' => $iconMap['User']], + ['name' => '부서관리', 'url' => '/master-data/department-management', 'icon' => $iconMap['Building']], + ['name' => '검사기준관리', 'url' => '/master-data/inspection-standard', 'icon' => $iconMap['ClipboardCheck']], + ['name' => '코드관리', 'url' => '/master-data/code-management', 'icon' => $iconMap['Code']], + ['name' => '견적수식관리', 'url' => '/master-data/formula-management', 'icon' => $iconMap['Calculator']], + ['name' => '디자인시스템', 'url' => '/master-data/design-system', 'icon' => $iconMap['Palette']], + ], + ], + [ + 'name' => '영업관리', + 'url' => '/sales-dept', + 'icon' => $iconMap['TrendingUp'], + 'sort_order' => 5, + 'children' => [ + ['name' => '리드관리', 'url' => '/sales-dept/sales-leads', 'icon' => $iconMap['Users']], + ['name' => '매출처관리', 'url' => '/sales-dept/customer-account-management', 'icon' => $iconMap['Building2']], + ['name' => '영업실적', 'url' => '/sales-dept/sales-performance', 'icon' => $iconMap['BarChart3']], + ], + ], + [ + 'name' => '판매관리', + 'url' => '/sales', + 'icon' => $iconMap['Briefcase'], + 'sort_order' => 6, + 'children' => [ + ['name' => '거래처관리', 'url' => '/sales/client-management-sales-admin', 'icon' => $iconMap['Building2']], + ['name' => '견적관리', 'url' => '/sales/quote-management', 'icon' => $iconMap['Calculator']], + ['name' => '수주관리', 'url' => '/sales/order-management-sales', 'icon' => $iconMap['ShoppingCart']], + ['name' => '현장관리', 'url' => '/sales/site-management', 'icon' => $iconMap['MapPin']], + ['name' => '단가관리', 'url' => '/sales/pricing-management', 'icon' => $iconMap['DollarSign']], + ], + ], + [ + 'name' => '구매관리', + 'url' => '/purchase', + 'icon' => $iconMap['ShoppingCart'], + 'sort_order' => 7, + 'children' => [ + ['name' => '거래처관리', 'url' => '/purchase/supplier-management', 'icon' => $iconMap['Building2']], + ['name' => '발주관리', 'url' => '/purchase/purchase-order', 'icon' => $iconMap['FileText']], + ['name' => '구매현황', 'url' => '/purchase/purchase-status', 'icon' => $iconMap['BarChart3']], + ], + ], + [ + 'name' => '생산관리', + 'url' => '/production', + 'icon' => $iconMap['Factory'], + 'sort_order' => 8, + 'children' => [ + ['name' => '스크린 생산', 'url' => '/production/screen-production', 'icon' => $iconMap['Package']], + ['name' => '슬랫 생산', 'url' => '/production/slat-production', 'icon' => $iconMap['Package']], + ['name' => '절곡 생산', 'url' => '/production/bending-production', 'icon' => $iconMap['Package']], + ['name' => '재고 생산', 'url' => '/production/stock-production', 'icon' => $iconMap['Package']], + ], + ], + [ + 'name' => '품질관리', + 'url' => '/quality', + 'icon' => $iconMap['ClipboardCheck'], + 'sort_order' => 9, + 'children' => [ + ['name' => '수입검사', 'url' => '/quality/incoming-inspection', 'icon' => $iconMap['CheckSquare']], + ['name' => '중간검사', 'url' => '/quality/process-inspection', 'icon' => $iconMap['ClipboardCheck']], + ['name' => '제품검사', 'url' => '/quality/final-inspection', 'icon' => $iconMap['CheckCircle']], + ['name' => '품목별 검사', 'url' => '/quality/quality-item-inspection', 'icon' => $iconMap['Package']], + ['name' => '품목별 검사 (고급)', 'url' => '/quality/quality-item-inspection-enhanced', 'icon' => $iconMap['BarChart3']], + ], + ], + [ + 'name' => '자재관리', + 'url' => '/material', + 'icon' => $iconMap['Warehouse'], + 'sort_order' => 10, + 'children' => [ + ['name' => '재고현황', 'url' => '/material/stock-status', 'icon' => $iconMap['Layers']], + ['name' => '입고관리', 'url' => '/material/receiving-management', 'icon' => $iconMap['Package']], + ['name' => '출고관리', 'url' => '/material/shipping-management', 'icon' => $iconMap['Truck']], + ['name' => '부적합품관리', 'url' => '/material/nonconforming-management', 'icon' => $iconMap['XCircle']], + ], + ], + [ + 'name' => '장비관리', + 'url' => '/equipment', + 'icon' => $iconMap['Wrench'], + 'sort_order' => 11, + 'children' => [ + ['name' => '설비관리', 'url' => '/equipment/equipment-management', 'icon' => $iconMap['Wrench']], + ], + ], + [ + 'name' => '차량관리', + 'url' => '/vehicle', + 'icon' => $iconMap['Truck'], + 'sort_order' => 12, + 'children' => [ + ['name' => '차량관리', 'url' => '/vehicle/vehicle-management', 'icon' => $iconMap['Car']], + ], + ], + [ + 'name' => '회계관리', + 'url' => '/accounting', + 'icon' => $iconMap['Calculator'], + 'sort_order' => 13, + 'children' => [ + ['name' => '거래처관리', 'url' => '/accounting/client-management', 'icon' => $iconMap['Building2']], + ['name' => '매출관리', 'url' => '/accounting/sales-accounting', 'icon' => $iconMap['TrendingUp']], + ['name' => '매입관리', 'url' => '/accounting/purchase-accounting', 'icon' => $iconMap['TrendingUp']], + ['name' => '원가관리', 'url' => '/accounting/cost-management', 'icon' => $iconMap['DollarSign']], + ['name' => '재무제표', 'url' => '/accounting/financial-statements', 'icon' => $iconMap['FileText']], + ], + ], + [ + 'name' => '권한 관리', + 'url' => '/permissions', + 'icon' => $iconMap['Shield'], + 'sort_order' => 14, + ], + [ + 'name' => '시스템 설정', + 'url' => '/system', + 'icon' => $iconMap['Settings'], + 'sort_order' => 15, + ], + [ + 'name' => '데이터베이스', + 'url' => '/database', + 'icon' => $iconMap['Database'], + 'sort_order' => 16, + ], + [ + 'name' => '시스템 모니터링', + 'url' => '/monitoring', + 'icon' => $iconMap['Activity'], + 'sort_order' => 17, + ], + [ + 'name' => '보안 관리', + 'url' => '/security', + 'icon' => $iconMap['Server'], + 'sort_order' => 18, + ], + ]; + + // 메뉴 생성 + foreach ($menus as $menuData) { + $children = $menuData['children'] ?? null; + unset($menuData['children']); + + // 최상위 메뉴 생성 (tenant_id = null) + $parentMenu = Menu::create([ + 'tenant_id' => null, + 'parent_id' => null, + 'name' => $menuData['name'], + 'url' => $menuData['url'], + 'icon' => $menuData['icon'], + 'sort_order' => $menuData['sort_order'], + 'is_active' => 1, + 'hidden' => 0, + 'is_external' => 0, + 'external_url' => null, + ]); + + // 하위 메뉴 생성 + if ($children) { + foreach ($children as $index => $childData) { + Menu::create([ + 'tenant_id' => null, + 'parent_id' => $parentMenu->id, + 'name' => $childData['name'], + 'url' => $childData['url'], + 'icon' => $childData['icon'], + 'sort_order' => $index + 1, + 'is_active' => 1, + 'hidden' => 0, + 'is_external' => 0, + 'external_url' => null, + ]); + } + } + } + }); + + $this->command->info('✅ 글로벌 메뉴 템플릿 생성 완료 (약 60개 메뉴 항목)'); + } +} \ No newline at end of file