feat:영업담당자 User 모듈 통합 및 승인 시스템 구현
- SalesManagerController: User 시스템 기반으로 재구현 - SalesManagerService: 영업담당자 CRUD, 승인/반려 로직 - SalesManagerDocument: 멀티파일 업로드 모델 - User 모델에 parent, approval 관계 및 메서드 추가 - SalesRoleSeeder: 영업 역할 시더 (sales_operator, sales_admin, sales_manager) - 뷰 파일 전면 수정 (역할 체크박스, 멀티파일 업로드, 승인/반려 UI) - 라우트 추가 (approve, reject, documents) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,10 @@
|
||||
namespace App\Http\Controllers\Sales;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Sales\SalesManager;
|
||||
use App\Models\Role;
|
||||
use App\Models\Sales\SalesManagerDocument;
|
||||
use App\Models\User;
|
||||
use App\Services\Sales\SalesManagerService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\View\View;
|
||||
@@ -13,6 +16,10 @@
|
||||
*/
|
||||
class SalesManagerController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private SalesManagerService $service
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 목록 페이지
|
||||
*/
|
||||
@@ -22,33 +29,15 @@ public function index(Request $request): View|Response
|
||||
return response('', 200)->header('HX-Redirect', route('sales.managers.index'));
|
||||
}
|
||||
|
||||
$query = SalesManager::query()->active();
|
||||
|
||||
// 검색
|
||||
if ($search = $request->get('search')) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('name', 'like', "%{$search}%")
|
||||
->orWhere('member_id', 'like', "%{$search}%")
|
||||
->orWhere('email', 'like', "%{$search}%")
|
||||
->orWhere('phone', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
// 역할 필터
|
||||
if ($role = $request->get('role')) {
|
||||
$query->where('role', $role);
|
||||
}
|
||||
|
||||
$managers = $query->orderBy('name')->paginate(20);
|
||||
|
||||
// 통계
|
||||
$stats = [
|
||||
'total' => SalesManager::active()->count(),
|
||||
'operators' => SalesManager::active()->where('role', 'operator')->count(),
|
||||
'sales_admins' => SalesManager::active()->where('role', 'sales_admin')->count(),
|
||||
'managers' => SalesManager::active()->where('role', 'manager')->count(),
|
||||
$filters = [
|
||||
'search' => $request->get('search'),
|
||||
'role' => $request->get('role'),
|
||||
'approval_status' => $request->get('approval_status'),
|
||||
];
|
||||
|
||||
$managers = $this->service->getSalesManagers($filters)->paginate(20);
|
||||
$stats = $this->service->getStats();
|
||||
|
||||
return view('sales.managers.index', compact('managers', 'stats'));
|
||||
}
|
||||
|
||||
@@ -57,12 +46,20 @@ public function index(Request $request): View|Response
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
$parents = SalesManager::active()
|
||||
->whereIn('role', ['operator', 'sales_admin'])
|
||||
->orderBy('name')
|
||||
$tenantId = session('selected_tenant_id', 1);
|
||||
|
||||
// 영업 관련 역할 목록
|
||||
$roles = Role::where('tenant_id', $tenantId)
|
||||
->whereIn('name', ['sales_operator', 'sales_admin', 'sales_manager'])
|
||||
->get();
|
||||
|
||||
return view('sales.managers.create', compact('parents'));
|
||||
// 상위 관리자 후보
|
||||
$parents = $this->service->getParentCandidates();
|
||||
|
||||
// 문서 타입 목록
|
||||
$documentTypes = SalesManagerDocument::DOCUMENT_TYPES;
|
||||
|
||||
return view('sales.managers.create', compact('roles', 'parents', 'documentTypes'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,20 +68,36 @@ public function create(): View
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'member_id' => 'required|string|max:50|unique:sales_managers,member_id',
|
||||
'password' => 'required|string|min:4',
|
||||
'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',
|
||||
'email' => 'nullable|email|max:100',
|
||||
'parent_id' => 'nullable|exists:sales_managers,id',
|
||||
'role' => 'required|in:operator,sales_admin,manager',
|
||||
'remarks' => 'nullable|string',
|
||||
'password' => 'required|string|min:4|confirmed',
|
||||
'parent_id' => 'nullable|exists:users,id',
|
||||
'role_ids' => 'required|array|min:1',
|
||||
'role_ids.*' => 'exists:roles,id',
|
||||
'documents' => 'nullable|array',
|
||||
'documents.*.file' => 'nullable|file|max:10240', // 10MB
|
||||
'documents.*.document_type' => 'nullable|string',
|
||||
'documents.*.description' => 'nullable|string|max:500',
|
||||
]);
|
||||
|
||||
SalesManager::create($validated);
|
||||
// 문서 배열 구성
|
||||
$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->createSalesManager($validated, $documents);
|
||||
|
||||
return redirect()->route('sales.managers.index')
|
||||
->with('success', '담당자가 등록되었습니다.');
|
||||
->with('success', '영업담당자 등록 신청이 완료되었습니다. 본사 승인 후 활성화됩니다.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +105,7 @@ public function store(Request $request)
|
||||
*/
|
||||
public function show(int $id): View
|
||||
{
|
||||
$manager = SalesManager::with(['parent', 'children', 'registeredProspects', 'records'])
|
||||
$manager = User::with(['parent', 'children', 'userRoles.role', 'salesDocuments', 'approver'])
|
||||
->findOrFail($id);
|
||||
|
||||
return view('sales.managers.show', compact('manager'));
|
||||
@@ -103,14 +116,26 @@ public function show(int $id): View
|
||||
*/
|
||||
public function edit(int $id): View
|
||||
{
|
||||
$manager = SalesManager::findOrFail($id);
|
||||
$parents = SalesManager::active()
|
||||
->whereIn('role', ['operator', 'sales_admin'])
|
||||
->where('id', '!=', $id)
|
||||
->orderBy('name')
|
||||
$manager = User::with(['userRoles.role', 'salesDocuments'])->findOrFail($id);
|
||||
$tenantId = session('selected_tenant_id', 1);
|
||||
|
||||
// 영업 관련 역할 목록
|
||||
$roles = Role::where('tenant_id', $tenantId)
|
||||
->whereIn('name', ['sales_operator', 'sales_admin', 'sales_manager'])
|
||||
->get();
|
||||
|
||||
return view('sales.managers.edit', compact('manager', 'parents'));
|
||||
// 상위 관리자 후보
|
||||
$parents = $this->service->getParentCandidates($id);
|
||||
|
||||
// 현재 역할 ID 목록
|
||||
$currentRoleIds = $manager->userRoles->pluck('role_id')->toArray();
|
||||
|
||||
// 문서 타입 목록
|
||||
$documentTypes = SalesManagerDocument::DOCUMENT_TYPES;
|
||||
|
||||
return view('sales.managers.edit', compact(
|
||||
'manager', 'roles', 'parents', 'currentRoleIds', 'documentTypes'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,24 +143,35 @@ public function edit(int $id): View
|
||||
*/
|
||||
public function update(Request $request, int $id)
|
||||
{
|
||||
$manager = SalesManager::findOrFail($id);
|
||||
$manager = 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',
|
||||
'email' => 'nullable|email|max:100',
|
||||
'parent_id' => 'nullable|exists:sales_managers,id',
|
||||
'role' => 'required|in:operator,sales_admin,manager',
|
||||
'remarks' => 'nullable|string',
|
||||
'password' => 'nullable|string|min:4',
|
||||
'password' => 'nullable|string|min:4|confirmed',
|
||||
'parent_id' => 'nullable|exists:users,id',
|
||||
'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',
|
||||
]);
|
||||
|
||||
// 비밀번호가 비어있으면 제외
|
||||
if (empty($validated['password'])) {
|
||||
unset($validated['password']);
|
||||
// 문서 배열 구성
|
||||
$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"),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$manager->update($validated);
|
||||
$this->service->updateSalesManager($manager, $validated, $documents);
|
||||
|
||||
return redirect()->route('sales.managers.index')
|
||||
->with('success', '담당자 정보가 수정되었습니다.');
|
||||
@@ -146,10 +182,63 @@ public function update(Request $request, int $id)
|
||||
*/
|
||||
public function destroy(int $id)
|
||||
{
|
||||
$manager = SalesManager::findOrFail($id);
|
||||
$manager = User::findOrFail($id);
|
||||
$manager->update(['is_active' => false]);
|
||||
|
||||
return redirect()->route('sales.managers.index')
|
||||
->with('success', '담당자가 비활성화되었습니다.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 승인 처리
|
||||
*/
|
||||
public function approve(int $id)
|
||||
{
|
||||
$manager = User::findOrFail($id);
|
||||
$this->service->approve($manager, auth()->id());
|
||||
|
||||
return redirect()->back()
|
||||
->with('success', '영업담당자가 승인되었습니다.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 반려 처리
|
||||
*/
|
||||
public function reject(Request $request, int $id)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'rejection_reason' => 'required|string|max:1000',
|
||||
]);
|
||||
|
||||
$manager = User::findOrFail($id);
|
||||
$this->service->reject($manager, auth()->id(), $validated['rejection_reason']);
|
||||
|
||||
return redirect()->back()
|
||||
->with('success', '영업담당자가 반려되었습니다.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 서류 다운로드
|
||||
*/
|
||||
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', '서류가 삭제되었습니다.');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user