Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
155
app/Http/Controllers/Sales/SalesManagerController.php
Normal file
155
app/Http/Controllers/Sales/SalesManagerController.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Sales;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Sales\SalesManager;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\View\View;
|
||||
|
||||
/**
|
||||
* 영업 담당자/매니저 관리 컨트롤러
|
||||
*/
|
||||
class SalesManagerController extends Controller
|
||||
{
|
||||
/**
|
||||
* 목록 페이지
|
||||
*/
|
||||
public function index(Request $request): View|Response
|
||||
{
|
||||
if ($request->header('HX-Request')) {
|
||||
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(),
|
||||
];
|
||||
|
||||
return view('sales.managers.index', compact('managers', 'stats'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 등록 폼
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
$parents = SalesManager::active()
|
||||
->whereIn('role', ['operator', 'sales_admin'])
|
||||
->orderBy('name')
|
||||
->get();
|
||||
|
||||
return view('sales.managers.create', compact('parents'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 등록 처리
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'member_id' => 'required|string|max:50|unique:sales_managers,member_id',
|
||||
'password' => 'required|string|min:4',
|
||||
'name' => 'required|string|max:100',
|
||||
'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',
|
||||
]);
|
||||
|
||||
SalesManager::create($validated);
|
||||
|
||||
return redirect()->route('sales.managers.index')
|
||||
->with('success', '담당자가 등록되었습니다.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 상세 페이지
|
||||
*/
|
||||
public function show(int $id): View
|
||||
{
|
||||
$manager = SalesManager::with(['parent', 'children', 'registeredProspects', 'records'])
|
||||
->findOrFail($id);
|
||||
|
||||
return view('sales.managers.show', compact('manager'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 수정 폼
|
||||
*/
|
||||
public function edit(int $id): View
|
||||
{
|
||||
$manager = SalesManager::findOrFail($id);
|
||||
$parents = SalesManager::active()
|
||||
->whereIn('role', ['operator', 'sales_admin'])
|
||||
->where('id', '!=', $id)
|
||||
->orderBy('name')
|
||||
->get();
|
||||
|
||||
return view('sales.managers.edit', compact('manager', 'parents'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 수정 처리
|
||||
*/
|
||||
public function update(Request $request, int $id)
|
||||
{
|
||||
$manager = SalesManager::findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:100',
|
||||
'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',
|
||||
]);
|
||||
|
||||
// 비밀번호가 비어있으면 제외
|
||||
if (empty($validated['password'])) {
|
||||
unset($validated['password']);
|
||||
}
|
||||
|
||||
$manager->update($validated);
|
||||
|
||||
return redirect()->route('sales.managers.index')
|
||||
->with('success', '담당자 정보가 수정되었습니다.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 삭제 처리 (비활성화)
|
||||
*/
|
||||
public function destroy(int $id)
|
||||
{
|
||||
$manager = SalesManager::findOrFail($id);
|
||||
$manager->update(['is_active' => false]);
|
||||
|
||||
return redirect()->route('sales.managers.index')
|
||||
->with('success', '담당자가 비활성화되었습니다.');
|
||||
}
|
||||
}
|
||||
170
app/Http/Controllers/Sales/SalesProspectController.php
Normal file
170
app/Http/Controllers/Sales/SalesProspectController.php
Normal file
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Sales;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Sales\SalesManager;
|
||||
use App\Models\Sales\SalesProspect;
|
||||
use App\Models\Sales\SalesProspectProduct;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\View\View;
|
||||
|
||||
/**
|
||||
* 가망고객 관리 컨트롤러
|
||||
*/
|
||||
class SalesProspectController extends Controller
|
||||
{
|
||||
/**
|
||||
* 목록 페이지
|
||||
*/
|
||||
public function index(Request $request): View|Response
|
||||
{
|
||||
if ($request->header('HX-Request')) {
|
||||
return response('', 200)->header('HX-Redirect', route('sales.prospects.index'));
|
||||
}
|
||||
|
||||
$query = SalesProspect::with(['manager', 'salesManager', 'products']);
|
||||
|
||||
// 검색
|
||||
if ($search = $request->get('search')) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('company_name', 'like', "%{$search}%")
|
||||
->orWhere('business_no', 'like', "%{$search}%")
|
||||
->orWhere('representative', 'like', "%{$search}%")
|
||||
->orWhere('contact_phone', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
// 상태 필터
|
||||
if ($status = $request->get('status')) {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
|
||||
// 담당자 필터
|
||||
if ($managerId = $request->get('manager_id')) {
|
||||
$query->where(function ($q) use ($managerId) {
|
||||
$q->where('manager_id', $managerId)
|
||||
->orWhere('sales_manager_id', $managerId);
|
||||
});
|
||||
}
|
||||
|
||||
$prospects = $query->orderByDesc('created_at')->paginate(20);
|
||||
|
||||
// 통계
|
||||
$stats = [
|
||||
'total' => SalesProspect::count(),
|
||||
'lead' => SalesProspect::where('status', 'lead')->count(),
|
||||
'prospect' => SalesProspect::where('status', 'prospect')->count(),
|
||||
'negotiation' => SalesProspect::where('status', 'negotiation')->count(),
|
||||
'contracted' => SalesProspect::where('status', 'contracted')->count(),
|
||||
'total_contract' => SalesProspectProduct::sum('contract_amount'),
|
||||
'total_commission' => SalesProspectProduct::sum('commission_amount'),
|
||||
];
|
||||
|
||||
$managers = SalesManager::active()->orderBy('name')->get();
|
||||
|
||||
return view('sales.prospects.index', compact('prospects', 'stats', 'managers'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 등록 폼
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
$managers = SalesManager::active()->orderBy('name')->get();
|
||||
|
||||
return view('sales.prospects.create', compact('managers'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 등록 처리
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'manager_id' => 'required|exists:sales_managers,id',
|
||||
'sales_manager_id' => 'nullable|exists:sales_managers,id',
|
||||
'company_name' => 'required|string|max:200',
|
||||
'representative' => 'nullable|string|max:100',
|
||||
'business_no' => 'nullable|string|max:20',
|
||||
'contact_phone' => 'nullable|string|max:20',
|
||||
'email' => 'nullable|email|max:100',
|
||||
'address' => 'nullable|string|max:500',
|
||||
'status' => 'required|in:lead,prospect,negotiation,contracted,lost',
|
||||
]);
|
||||
|
||||
$prospect = SalesProspect::create($validated);
|
||||
|
||||
return redirect()->route('sales.prospects.show', $prospect->id)
|
||||
->with('success', '가망고객이 등록되었습니다.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 상세 페이지
|
||||
*/
|
||||
public function show(int $id): View
|
||||
{
|
||||
$prospect = SalesProspect::with([
|
||||
'manager',
|
||||
'salesManager',
|
||||
'products',
|
||||
'scenarios',
|
||||
'consultations.manager'
|
||||
])->findOrFail($id);
|
||||
|
||||
$managers = SalesManager::active()
|
||||
->whereIn('role', ['sales_admin', 'manager'])
|
||||
->orderBy('name')
|
||||
->get();
|
||||
|
||||
return view('sales.prospects.show', compact('prospect', 'managers'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 수정 폼
|
||||
*/
|
||||
public function edit(int $id): View
|
||||
{
|
||||
$prospect = SalesProspect::findOrFail($id);
|
||||
$managers = SalesManager::active()->orderBy('name')->get();
|
||||
|
||||
return view('sales.prospects.edit', compact('prospect', 'managers'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 수정 처리
|
||||
*/
|
||||
public function update(Request $request, int $id)
|
||||
{
|
||||
$prospect = SalesProspect::findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'sales_manager_id' => 'nullable|exists:sales_managers,id',
|
||||
'company_name' => 'required|string|max:200',
|
||||
'representative' => 'nullable|string|max:100',
|
||||
'business_no' => 'nullable|string|max:20',
|
||||
'contact_phone' => 'nullable|string|max:20',
|
||||
'email' => 'nullable|email|max:100',
|
||||
'address' => 'nullable|string|max:500',
|
||||
'status' => 'required|in:lead,prospect,negotiation,contracted,lost',
|
||||
]);
|
||||
|
||||
$prospect->update($validated);
|
||||
|
||||
return redirect()->route('sales.prospects.show', $prospect->id)
|
||||
->with('success', '가망고객 정보가 수정되었습니다.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 삭제 처리
|
||||
*/
|
||||
public function destroy(int $id)
|
||||
{
|
||||
$prospect = SalesProspect::findOrFail($id);
|
||||
$prospect->delete();
|
||||
|
||||
return redirect()->route('sales.prospects.index')
|
||||
->with('success', '가망고객이 삭제되었습니다.');
|
||||
}
|
||||
}
|
||||
166
app/Http/Controllers/Sales/SalesRecordController.php
Normal file
166
app/Http/Controllers/Sales/SalesRecordController.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Sales;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Sales\SalesManager;
|
||||
use App\Models\Sales\SalesProspect;
|
||||
use App\Models\Sales\SalesRecord;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\View\View;
|
||||
|
||||
/**
|
||||
* 영업 실적 관리 컨트롤러
|
||||
*/
|
||||
class SalesRecordController extends Controller
|
||||
{
|
||||
/**
|
||||
* 목록 페이지
|
||||
*/
|
||||
public function index(Request $request): View|Response
|
||||
{
|
||||
if ($request->header('HX-Request')) {
|
||||
return response('', 200)->header('HX-Redirect', route('sales.records.index'));
|
||||
}
|
||||
|
||||
$query = SalesRecord::with(['manager', 'prospect']);
|
||||
|
||||
// 검색
|
||||
if ($search = $request->get('search')) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('description', 'like', "%{$search}%")
|
||||
->orWhere('record_type', 'like', "%{$search}%")
|
||||
->orWhereHas('prospect', function ($q2) use ($search) {
|
||||
$q2->where('company_name', 'like', "%{$search}%");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 상태 필터
|
||||
if ($status = $request->get('status')) {
|
||||
$query->where('status', $status);
|
||||
}
|
||||
|
||||
// 담당자 필터
|
||||
if ($managerId = $request->get('manager_id')) {
|
||||
$query->where('manager_id', $managerId);
|
||||
}
|
||||
|
||||
// 기간 필터
|
||||
if ($startDate = $request->get('start_date')) {
|
||||
$query->where('record_date', '>=', $startDate);
|
||||
}
|
||||
if ($endDate = $request->get('end_date')) {
|
||||
$query->where('record_date', '<=', $endDate);
|
||||
}
|
||||
|
||||
$records = $query->orderByDesc('record_date')->paginate(20);
|
||||
|
||||
// 통계
|
||||
$stats = [
|
||||
'total_count' => SalesRecord::count(),
|
||||
'pending_count' => SalesRecord::pending()->count(),
|
||||
'approved_count' => SalesRecord::approved()->count(),
|
||||
'total_amount' => SalesRecord::sum('amount'),
|
||||
'total_commission' => SalesRecord::sum('commission'),
|
||||
'pending_amount' => SalesRecord::pending()->sum('amount'),
|
||||
'approved_amount' => SalesRecord::approved()->sum('amount'),
|
||||
];
|
||||
|
||||
$managers = SalesManager::active()->orderBy('name')->get();
|
||||
|
||||
return view('sales.records.index', compact('records', 'stats', 'managers'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 등록 폼
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
$managers = SalesManager::active()->orderBy('name')->get();
|
||||
$prospects = SalesProspect::orderBy('company_name')->get();
|
||||
|
||||
return view('sales.records.create', compact('managers', 'prospects'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 등록 처리
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'manager_id' => 'required|exists:sales_managers,id',
|
||||
'prospect_id' => 'nullable|exists:sales_prospects,id',
|
||||
'record_date' => 'required|date',
|
||||
'record_type' => 'required|string|max:50',
|
||||
'amount' => 'required|numeric|min:0',
|
||||
'commission' => 'nullable|numeric|min:0',
|
||||
'description' => 'nullable|string',
|
||||
'status' => 'required|in:pending,approved,rejected,paid',
|
||||
]);
|
||||
|
||||
SalesRecord::create($validated);
|
||||
|
||||
return redirect()->route('sales.records.index')
|
||||
->with('success', '실적이 등록되었습니다.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 상세 페이지
|
||||
*/
|
||||
public function show(int $id): View
|
||||
{
|
||||
$record = SalesRecord::with(['manager', 'prospect'])->findOrFail($id);
|
||||
|
||||
return view('sales.records.show', compact('record'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 수정 폼
|
||||
*/
|
||||
public function edit(int $id): View
|
||||
{
|
||||
$record = SalesRecord::findOrFail($id);
|
||||
$managers = SalesManager::active()->orderBy('name')->get();
|
||||
$prospects = SalesProspect::orderBy('company_name')->get();
|
||||
|
||||
return view('sales.records.edit', compact('record', 'managers', 'prospects'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 수정 처리
|
||||
*/
|
||||
public function update(Request $request, int $id)
|
||||
{
|
||||
$record = SalesRecord::findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'manager_id' => 'required|exists:sales_managers,id',
|
||||
'prospect_id' => 'nullable|exists:sales_prospects,id',
|
||||
'record_date' => 'required|date',
|
||||
'record_type' => 'required|string|max:50',
|
||||
'amount' => 'required|numeric|min:0',
|
||||
'commission' => 'nullable|numeric|min:0',
|
||||
'description' => 'nullable|string',
|
||||
'status' => 'required|in:pending,approved,rejected,paid',
|
||||
]);
|
||||
|
||||
$record->update($validated);
|
||||
|
||||
return redirect()->route('sales.records.index')
|
||||
->with('success', '실적이 수정되었습니다.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 삭제 처리
|
||||
*/
|
||||
public function destroy(int $id)
|
||||
{
|
||||
$record = SalesRecord::findOrFail($id);
|
||||
$record->delete();
|
||||
|
||||
return redirect()->route('sales.records.index')
|
||||
->with('success', '실적이 삭제되었습니다.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user