header('HX-Request')) { return response('', 200)->header('HX-Redirect', route('sales.managers.index')); } $filters = [ 'search' => $request->get('search'), 'role' => $request->get('role'), 'approval_status' => $request->get('approval_status'), 'parent_id' => auth()->id(), // 현재 로그인한 사용자가 유치한 파트너만 조회 ]; $partners = $this->service->getSalesPartners($filters)->paginate(20); $stats = $this->service->getStats(auth()->id()); // 통계도 현재 사용자 기준 return view('sales.managers.index', compact('partners', 'stats')); } /** * 등록 폼 */ public function create(): View { // 영업 역할 목록 $roles = $this->service->getSalesRoles(); // 문서 타입 목록 $documentTypes = SalesManagerDocument::DOCUMENT_TYPES; return view('sales.managers.create', compact('roles', 'documentTypes')); } /** * 등록 처리 */ public function store(Request $request) { $validated = $request->validate([ 'user_id' => 'nullable|string|max:50|unique:users,user_id', 'name' => 'required|string|max:100', 'email' => 'required|email|max:255|unique:users,email', 'phone' => 'nullable|string|max:20', 'password' => 'required|string|min:4|confirmed', 'role_ids' => 'required|array|min:1', 'role_ids.*' => 'exists:roles,id', 'documents' => 'nullable|array', 'documents.*.file' => 'nullable|file|max:10240', 'documents.*.document_type' => 'nullable|string', 'documents.*.description' => 'nullable|string|max:500', ]); // 등록자를 추천인(parent)으로 자동 설정 // 본사 관리자가 등록해도 해당 관리자가 추천인이 됨 $validated['parent_id'] = auth()->id(); // 문서 배열 구성 $documents = []; if ($request->hasFile('documents')) { foreach ($request->file('documents') as $index => $file) { $documents[] = [ 'file' => $file, 'document_type' => $request->input("documents.{$index}.document_type", 'other'), 'description' => $request->input("documents.{$index}.description"), ]; } } $this->service->createSalesPartner($validated, $documents); return redirect()->route('sales.managers.index') ->with('success', '영업파트너 등록 신청이 완료되었습니다. 본사 승인 후 활성화됩니다.'); } /** * 상세 페이지 */ public function show(int $id): View { $partner = User::with(['parent', 'children', 'userRoles.role', 'salesDocuments', 'approver']) ->findOrFail($id); // 계층 레벨 $level = $this->service->getPartnerLevel($partner); // 하위 파트너 $children = User::where('parent_id', $partner->id) ->with('userRoles.role') ->get(); // 역할 위임 가능한 하위 파트너 $delegationCandidates = $this->service->getDelegationCandidates($partner); return view('sales.managers.show', compact('partner', 'level', 'children', 'delegationCandidates')); } /** * 상세 모달용 */ public function modalShow(int $id): View { $partner = User::with(['parent', 'children', 'userRoles.role', 'salesDocuments', 'approver']) ->findOrFail($id); $level = $this->service->getPartnerLevel($partner); $children = User::where('parent_id', $partner->id) ->with('userRoles.role') ->get(); return view('sales.managers.partials.show-modal', compact('partner', 'level', 'children')); } /** * 수정 모달용 */ public function modalEdit(int $id): View { $partner = User::with(['userRoles.role', 'salesDocuments', 'parent'])->findOrFail($id); $roles = $this->service->getSalesRoles(); $currentRoleIds = $partner->userRoles->pluck('role_id')->toArray(); $documentTypes = SalesManagerDocument::DOCUMENT_TYPES; return view('sales.managers.partials.edit-modal', compact( 'partner', 'roles', 'currentRoleIds', 'documentTypes' )); } /** * 수정 폼 */ public function edit(int $id): View { $partner = User::with(['userRoles.role', 'salesDocuments', 'parent'])->findOrFail($id); // 영업 역할 목록 $roles = $this->service->getSalesRoles(); // 현재 역할 ID 목록 $currentRoleIds = $partner->userRoles->pluck('role_id')->toArray(); // 문서 타입 목록 $documentTypes = SalesManagerDocument::DOCUMENT_TYPES; return view('sales.managers.edit', compact( 'partner', 'roles', 'currentRoleIds', 'documentTypes' )); } /** * 수정 처리 */ public function update(Request $request, int $id) { $partner = User::findOrFail($id); $validated = $request->validate([ 'name' => 'required|string|max:100', 'email' => 'required|email|max:255|unique:users,email,' . $id, 'phone' => 'nullable|string|max:20', 'password' => 'nullable|string|min:4|confirmed', 'role_ids' => 'required|array|min:1', 'role_ids.*' => 'exists:roles,id', 'documents' => 'nullable|array', 'documents.*.file' => 'nullable|file|max:10240', 'documents.*.document_type' => 'nullable|string', 'documents.*.description' => 'nullable|string|max:500', ]); // 문서 배열 구성 $documents = []; if ($request->hasFile('documents')) { foreach ($request->file('documents') as $index => $file) { $documents[] = [ 'file' => $file, 'document_type' => $request->input("documents.{$index}.document_type", 'other'), 'description' => $request->input("documents.{$index}.description"), ]; } } $this->service->updateSalesPartner($partner, $validated, $documents); return redirect()->route('sales.managers.index') ->with('success', '영업파트너 정보가 수정되었습니다.'); } /** * 삭제 처리 (비활성화) - 최고관리자만 가능 */ public function destroy(int $id) { // 권한 체크: admin 역할만 삭제 가능 if (!auth()->user()->isAdmin()) { abort(403, '삭제 권한이 없습니다.'); } $partner = User::findOrFail($id); $partner->update(['is_active' => false]); return redirect()->route('sales.managers.index') ->with('success', '영업파트너가 비활성화되었습니다.'); } /** * 승인 처리 */ public function approve(int $id) { $partner = User::findOrFail($id); $this->service->approve($partner, auth()->id()); return redirect()->back() ->with('success', '영업파트너가 승인되었습니다.'); } /** * 반려 처리 */ public function reject(Request $request, int $id) { $validated = $request->validate([ 'rejection_reason' => 'required|string|max:1000', ]); $partner = User::findOrFail($id); $this->service->reject($partner, auth()->id(), $validated['rejection_reason']); return redirect()->back() ->with('success', '영업파트너가 반려되었습니다.'); } /** * 역할 위임 처리 */ public function delegateRole(Request $request, int $id) { $validated = $request->validate([ 'to_user_id' => 'required|exists:users,id', 'role_name' => 'required|string|in:manager', ]); $fromUser = User::findOrFail($id); $toUser = User::findOrFail($validated['to_user_id']); try { $this->service->delegateRole($fromUser, $toUser, $validated['role_name']); $roleLabel = '상담매니저'; return redirect()->back() ->with('success', "{$roleLabel} 역할이 {$toUser->name}님에게 위임되었습니다."); } catch (\InvalidArgumentException $e) { return redirect()->back() ->with('error', $e->getMessage()); } } /** * 역할 부여 */ public function assignRole(Request $request, int $id) { $validated = $request->validate([ 'role_name' => 'required|string|in:sales,manager', ]); $partner = User::findOrFail($id); $this->service->assignRole($partner, $validated['role_name']); $roleLabels = ['sales' => '영업파트너', 'manager' => '상담매니저']; return redirect()->back() ->with('success', "{$roleLabels[$validated['role_name']]} 역할이 부여되었습니다."); } /** * 역할 제거 */ public function removeRole(Request $request, int $id) { $validated = $request->validate([ 'role_name' => 'required|string|in:sales,manager', ]); $partner = User::findOrFail($id); $this->service->removeRole($partner, $validated['role_name']); $roleLabels = ['sales' => '영업파트너', 'manager' => '상담매니저']; return redirect()->back() ->with('success', "{$roleLabels[$validated['role_name']]} 역할이 제거되었습니다."); } /** * 서류 다운로드 */ public function downloadDocument(int $id, int $documentId) { $document = SalesManagerDocument::where('user_id', $id) ->findOrFail($documentId); return $document->download(); } /** * 서류 삭제 */ public function deleteDocument(int $id, int $documentId) { $document = SalesManagerDocument::where('user_id', $id) ->findOrFail($documentId); $this->service->deleteDocument($document); return redirect()->back() ->with('success', '서류가 삭제되었습니다.'); } /** * 영업파트너 승인 목록 (본사 관리자 전용) */ public function approvals(Request $request): View|Response { // 권한 체크: admin 역할만 접근 가능 if (!auth()->user()->isAdmin()) { abort(403, '접근 권한이 없습니다.'); } if ($request->header('HX-Request')) { return response('', 200)->header('HX-Redirect', route('sales.managers.approvals')); } $search = $request->get('search'); // 승인 대기자 목록 $pendingPartners = $this->service->getSalesPartners([ 'search' => $search, 'approval_status' => 'pending', ])->paginate(10, ['*'], 'pending_page'); // 승인된 파트너 목록 (최근 승인 순) $approvedPartners = $this->service->getSalesPartners([ 'search' => $search, 'approval_status' => 'approved', ])->reorder()->latest('approved_at')->paginate(10, ['*'], 'approved_page'); $stats = $this->service->getApprovalStats(); return view('sales.managers.approvals', compact('pendingPartners', 'approvedPartners', 'stats')); } /** * 승인 목록에서 승인 처리 */ public function approveFromList(Request $request, int $id) { // 권한 체크 if (!auth()->user()->isAdmin()) { abort(403, '접근 권한이 없습니다.'); } $partner = User::findOrFail($id); $this->service->approve($partner, auth()->id()); if ($request->header('HX-Request')) { return response()->json([ 'success' => true, 'message' => "{$partner->name}님이 승인되었습니다.", ]); } return redirect()->route('sales.managers.approvals') ->with('success', "{$partner->name}님이 승인되었습니다."); } /** * 승인 목록에서 반려 처리 */ public function rejectFromList(Request $request, int $id) { // 권한 체크 if (!auth()->user()->isAdmin()) { abort(403, '접근 권한이 없습니다.'); } $validated = $request->validate([ 'rejection_reason' => 'required|string|max:1000', ]); $partner = User::findOrFail($id); $this->service->reject($partner, auth()->id(), $validated['rejection_reason']); if ($request->header('HX-Request')) { return response()->json([ 'success' => true, 'message' => "{$partner->name}님이 반려되었습니다.", ]); } return redirect()->route('sales.managers.approvals') ->with('success', "{$partner->name}님이 반려되었습니다."); } }