Files
sam-manage/app/Http/Controllers/Sales/AdminProspectController.php
김보곤 1519673b34 feat:영업파트너 고객관리 진행완료 필터 및 날짜입력 비활성화
- 진행완료(두 시나리오 모두 100%) 필터 버튼 추가 (보라색)
- 진행률 미달 시 수당 날짜 input 5개 disabled 처리
- 통계에 progress_complete 건수 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 16:24:00 +09:00

338 lines
12 KiB
PHP

<?php
namespace App\Http\Controllers\Sales;
use App\Http\Controllers\Controller;
use App\Models\Sales\SalesCommission;
use App\Models\Sales\SalesScenarioChecklist;
use App\Models\Sales\SalesTenantManagement;
use App\Models\Sales\TenantProspect;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\View\View;
/**
* 관리자용 전체 영업파트너 고객 관리 컨트롤러
* 관리자/슈퍼관리자만 접근 가능
*/
class AdminProspectController extends Controller
{
/**
* 관리자 권한 체크
*/
private function checkAdminAccess(): void
{
if (!auth()->user()->isAdmin() && !auth()->user()->isSuperAdmin()) {
abort(403, '관리자만 접근할 수 있습니다.');
}
}
/**
* 전체 고객 목록 페이지
*/
public function index(Request $request): View|Response
{
$this->checkAdminAccess();
if ($request->header('HX-Request')) {
return response('', 200)->header('HX-Redirect', route('sales.admin-prospects.index'));
}
$data = $this->getIndexData($request);
return view('sales.admin-prospects.index', $data);
}
/**
* 고객 상세 모달
*/
public function modalShow(int $id): View
{
$this->checkAdminAccess();
$prospect = TenantProspect::with(['registeredBy', 'tenant'])->findOrFail($id);
// 진행률
$progress = SalesScenarioChecklist::getProspectProgress($prospect->id);
$prospect->sales_progress = $progress['sales']['percentage'];
$prospect->manager_progress = $progress['manager']['percentage'];
// management 정보
$management = SalesTenantManagement::findOrCreateByProspect($prospect->id);
return view('sales.admin-prospects.partials.show-modal', compact('prospect', 'management', 'progress'));
}
/**
* 콘텐츠 새로고침 (HTMX)
*/
public function refresh(Request $request): View
{
$this->checkAdminAccess();
$data = $this->getIndexData($request);
return view('sales.admin-prospects.partials.content', $data);
}
/**
* index 데이터 조회 (공통)
*/
private function getIndexData(Request $request): array
{
// 영업 역할을 가진 사용자 목록 (영업파트너)
$salesPartners = User::whereHas('userRoles', function ($q) {
$q->whereHas('role', function ($rq) {
$rq->whereIn('name', ['sales', 'manager']);
});
})->orderBy('name')->get();
// 필터
$filters = [
'search' => $request->get('search'),
'status' => $request->get('status'),
'registered_by' => $request->get('registered_by'),
];
// 쿼리 빌드
$query = TenantProspect::with(['registeredBy', 'tenant']);
// 검색
if (!empty($filters['search'])) {
$search = $filters['search'];
$query->where(function ($q) use ($search) {
$q->where('company_name', 'like', "%{$search}%")
->orWhere('business_number', 'like', "%{$search}%")
->orWhere('ceo_name', 'like', "%{$search}%")
->orWhere('contact_phone', 'like', "%{$search}%");
});
}
// 상태 필터 (progress_complete는 계산값 기반이므로 별도 처리)
$isProgressCompleteFilter = ($filters['status'] === 'progress_complete');
if (!empty($filters['status']) && !$isProgressCompleteFilter) {
$query->where('status', $filters['status']);
}
// 영업파트너 필터
if (!empty($filters['registered_by'])) {
$query->where('registered_by', $filters['registered_by']);
}
// progress_complete 필터: 전체 조회 후 PHP에서 필터링
if ($isProgressCompleteFilter) {
$allProspects = $query->orderByDesc('created_at')->get();
// 진행률 계산 및 부가정보 세팅
foreach ($allProspects as $prospect) {
$progress = SalesScenarioChecklist::getProspectProgress($prospect->id);
$prospect->sales_progress = $progress['sales']['percentage'];
$prospect->manager_progress = $progress['manager']['percentage'];
if ($progress['sales']['percentage'] === 100 && $progress['manager']['percentage'] === 100) {
SalesScenarioChecklist::checkAndConvertProspectStatus($prospect->id);
$prospect->refresh();
}
$management = SalesTenantManagement::where('tenant_prospect_id', $prospect->id)->first();
$prospect->hq_status = $management?->hq_status ?? 'pending';
$prospect->hq_status_label = $management?->hq_status_label ?? '대기';
$prospect->manager_user = $management?->manager;
if ($management) {
$commission = SalesCommission::where('management_id', $management->id)->first();
$prospect->commission = $commission;
} else {
$prospect->commission = null;
}
}
// 두 시나리오 모두 100%인 것만 필터링
$filtered = $allProspects->filter(function ($prospect) {
return $prospect->sales_progress === 100 && $prospect->manager_progress === 100;
});
// 수동 페이지네이션
$page = request()->get('page', 1);
$perPage = 20;
$prospects = new \Illuminate\Pagination\LengthAwarePaginator(
$filtered->forPage($page, $perPage)->values(),
$filtered->count(),
$perPage,
$page,
['path' => request()->url(), 'query' => request()->query()]
);
} else {
$prospects = $query->orderByDesc('created_at')->paginate(20);
// 각 가망고객의 진행률 계산 및 상태 자동 전환
foreach ($prospects as $prospect) {
$progress = SalesScenarioChecklist::getProspectProgress($prospect->id);
$prospect->sales_progress = $progress['sales']['percentage'];
$prospect->manager_progress = $progress['manager']['percentage'];
// 진행률 100% 시 상태 자동 전환 체크
if ($progress['sales']['percentage'] === 100 && $progress['manager']['percentage'] === 100) {
SalesScenarioChecklist::checkAndConvertProspectStatus($prospect->id);
$prospect->refresh();
}
// management 정보
$management = SalesTenantManagement::where('tenant_prospect_id', $prospect->id)->first();
$prospect->hq_status = $management?->hq_status ?? 'pending';
$prospect->hq_status_label = $management?->hq_status_label ?? '대기';
$prospect->manager_user = $management?->manager;
// 수당 정보 (management가 있는 경우)
if ($management) {
$commission = SalesCommission::where('management_id', $management->id)->first();
$prospect->commission = $commission;
} else {
$prospect->commission = null;
}
}
}
// 진행완료 건수 계산 (전체 prospect 중 두 시나리오 모두 100%인 건수)
$progressCompleteCount = 0;
$allForStats = TenantProspect::all();
foreach ($allForStats as $p) {
$prog = SalesScenarioChecklist::getProspectProgress($p->id);
if ($prog['sales']['percentage'] === 100 && $prog['manager']['percentage'] === 100) {
$progressCompleteCount++;
}
}
// 전체 통계
$stats = [
'total' => TenantProspect::count(),
'active' => TenantProspect::where('status', TenantProspect::STATUS_ACTIVE)->count(),
'expired' => TenantProspect::where('status', TenantProspect::STATUS_EXPIRED)->count(),
'converted' => TenantProspect::where('status', TenantProspect::STATUS_CONVERTED)->count(),
'progress_complete' => $progressCompleteCount,
];
// 영업파트너별 통계
$partnerStats = TenantProspect::selectRaw('registered_by, COUNT(*) as total')
->groupBy('registered_by')
->with('registeredBy')
->get()
->map(function ($item) {
return [
'user' => $item->registeredBy,
'total' => $item->total,
];
});
return compact('prospects', 'stats', 'salesPartners', 'partnerStats', 'filters');
}
/**
* 개발 진행 상태 변경
*/
public function updateHqStatus(int $id, Request $request)
{
$this->checkAdminAccess();
$request->validate([
'hq_status' => 'required|in:' . implode(',', array_keys(SalesTenantManagement::$hqStatusLabels)),
]);
$prospect = TenantProspect::findOrFail($id);
$management = SalesTenantManagement::findOrCreateByProspect($prospect->id);
$management->update([
'hq_status' => $request->input('hq_status'),
]);
return response()->json([
'success' => true,
'hq_status' => $management->hq_status,
'hq_status_label' => $management->hq_status_label,
]);
}
/**
* 수당 날짜 기록/수정
*/
public function updateCommissionDate(int $id, Request $request)
{
$this->checkAdminAccess();
$request->validate([
'field' => 'required|in:first_payment_at,first_partner_paid_at,second_payment_at,second_partner_paid_at,first_subscription_at,manager_paid_at',
'date' => 'nullable|date',
]);
$prospect = TenantProspect::findOrFail($id);
$management = SalesTenantManagement::findOrCreateByProspect($prospect->id);
// Commission 레코드 조회 또는 생성
$commission = SalesCommission::firstOrCreate(
['management_id' => $management->id],
[
'tenant_id' => $prospect->tenant_id ?? 1,
'payment_type' => 'deposit',
'payment_amount' => 0,
'payment_date' => now(),
'base_amount' => 0,
'partner_rate' => 0,
'manager_rate' => 0,
'partner_commission' => 0,
'manager_commission' => 0,
'scheduled_payment_date' => now()->addMonth()->day(10),
'status' => SalesCommission::STATUS_PENDING,
'partner_id' => $management->sales_partner_id ?? 0,
'manager_user_id' => $management->manager_user_id,
]
);
$field = $request->input('field');
$date = $request->input('date') ?: now()->format('Y-m-d');
$commission->update([
$field => $date,
]);
return response()->json([
'success' => true,
'field' => $field,
'date' => $commission->$field?->format('Y-m-d'),
'date_display' => $commission->$field?->format('m/d'),
]);
}
/**
* 수당 날짜 삭제 (초기화)
*/
public function clearCommissionDate(int $id, Request $request)
{
$this->checkAdminAccess();
$request->validate([
'field' => 'required|in:first_payment_at,first_partner_paid_at,second_payment_at,second_partner_paid_at,first_subscription_at,manager_paid_at',
]);
$prospect = TenantProspect::findOrFail($id);
$management = SalesTenantManagement::where('tenant_prospect_id', $prospect->id)->first();
if (!$management) {
return response()->json(['success' => false, 'message' => '관리 정보가 없습니다.']);
}
$commission = SalesCommission::where('management_id', $management->id)->first();
if (!$commission) {
return response()->json(['success' => false, 'message' => '수당 정보가 없습니다.']);
}
$field = $request->input('field');
$commission->update([$field => null]);
return response()->json([
'success' => true,
'field' => $field,
]);
}
}