241 lines
8.3 KiB
PHP
241 lines
8.3 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Sales;
|
|
|
|
use App\Models\Sales\SalesTenantManagement;
|
|
use Illuminate\Pagination\LengthAwarePaginator;
|
|
|
|
/**
|
|
* 개발 승인 관리 서비스
|
|
* 영업/매니저 진행률이 100% 완료된 고객의 개발 진행 상태를 관리
|
|
*/
|
|
class SalesDevelopmentApprovalService
|
|
{
|
|
/**
|
|
* 통계 조회
|
|
*/
|
|
public function getStats(): array
|
|
{
|
|
// 승인 대기 (영업 100% + 매니저 100% + hq_status = pending)
|
|
$pendingCount = SalesTenantManagement::query()
|
|
->where('sales_progress', 100)
|
|
->where('manager_progress', 100)
|
|
->where('hq_status', SalesTenantManagement::HQ_STATUS_PENDING)
|
|
->count();
|
|
|
|
// 개발 진행 중 (review ~ int_test)
|
|
$progressStatuses = [
|
|
SalesTenantManagement::HQ_STATUS_REVIEW,
|
|
SalesTenantManagement::HQ_STATUS_PLANNING,
|
|
SalesTenantManagement::HQ_STATUS_CODING,
|
|
SalesTenantManagement::HQ_STATUS_DEV_TEST,
|
|
SalesTenantManagement::HQ_STATUS_DEV_DONE,
|
|
SalesTenantManagement::HQ_STATUS_INT_TEST,
|
|
];
|
|
$inProgressCount = SalesTenantManagement::query()
|
|
->where('sales_progress', 100)
|
|
->where('manager_progress', 100)
|
|
->whereIn('hq_status', $progressStatuses)
|
|
->count();
|
|
|
|
// 오늘 완료 (handover 상태이고 오늘 업데이트된)
|
|
$todayCompletedCount = SalesTenantManagement::query()
|
|
->where('hq_status', SalesTenantManagement::HQ_STATUS_HANDOVER)
|
|
->whereDate('updated_at', today())
|
|
->count();
|
|
|
|
// 총 완료
|
|
$totalCompletedCount = SalesTenantManagement::query()
|
|
->where('hq_status', SalesTenantManagement::HQ_STATUS_HANDOVER)
|
|
->count();
|
|
|
|
return [
|
|
'pending' => $pendingCount,
|
|
'in_progress' => $inProgressCount,
|
|
'today_completed' => $todayCompletedCount,
|
|
'total_completed' => $totalCompletedCount,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 승인 대기 목록 조회
|
|
*/
|
|
public function getPendingApprovals(?string $search = null, int $perPage = 10): LengthAwarePaginator
|
|
{
|
|
$query = SalesTenantManagement::query()
|
|
->with(['tenant', 'tenantProspect.registeredBy', 'salesPartner.user', 'manager'])
|
|
->where('sales_progress', 100)
|
|
->where('manager_progress', 100)
|
|
->where('hq_status', SalesTenantManagement::HQ_STATUS_PENDING);
|
|
|
|
// 검색
|
|
if ($search) {
|
|
$query->whereHas('tenant', function ($q) use ($search) {
|
|
$q->where('company_name', 'like', "%{$search}%")
|
|
->orWhere('business_number', 'like', "%{$search}%");
|
|
});
|
|
}
|
|
|
|
return $query->latest('updated_at')->paginate($perPage, ['*'], 'pending_page');
|
|
}
|
|
|
|
/**
|
|
* 개발 진행 중 목록 조회
|
|
*/
|
|
public function getInProgressItems(?string $search = null, int $perPage = 10): LengthAwarePaginator
|
|
{
|
|
$progressStatuses = [
|
|
SalesTenantManagement::HQ_STATUS_REVIEW,
|
|
SalesTenantManagement::HQ_STATUS_PLANNING,
|
|
SalesTenantManagement::HQ_STATUS_CODING,
|
|
SalesTenantManagement::HQ_STATUS_DEV_TEST,
|
|
SalesTenantManagement::HQ_STATUS_DEV_DONE,
|
|
SalesTenantManagement::HQ_STATUS_INT_TEST,
|
|
];
|
|
|
|
$query = SalesTenantManagement::query()
|
|
->with(['tenant', 'tenantProspect.registeredBy', 'salesPartner.user', 'manager'])
|
|
->where('sales_progress', 100)
|
|
->where('manager_progress', 100)
|
|
->whereIn('hq_status', $progressStatuses);
|
|
|
|
// 검색
|
|
if ($search) {
|
|
$query->whereHas('tenant', function ($q) use ($search) {
|
|
$q->where('company_name', 'like', "%{$search}%")
|
|
->orWhere('business_number', 'like', "%{$search}%");
|
|
});
|
|
}
|
|
|
|
return $query->latest('updated_at')->paginate($perPage, ['*'], 'progress_page');
|
|
}
|
|
|
|
/**
|
|
* 완료 목록 조회
|
|
*/
|
|
public function getCompletedItems(?string $search = null, int $perPage = 10): LengthAwarePaginator
|
|
{
|
|
$query = SalesTenantManagement::query()
|
|
->with(['tenant', 'tenantProspect.registeredBy', 'salesPartner.user', 'manager'])
|
|
->where('hq_status', SalesTenantManagement::HQ_STATUS_HANDOVER);
|
|
|
|
// 검색
|
|
if ($search) {
|
|
$query->whereHas('tenant', function ($q) use ($search) {
|
|
$q->where('company_name', 'like', "%{$search}%")
|
|
->orWhere('business_number', 'like', "%{$search}%");
|
|
});
|
|
}
|
|
|
|
return $query->latest('updated_at')->paginate($perPage, ['*'], 'completed_page');
|
|
}
|
|
|
|
/**
|
|
* 개발 승인 처리 (pending → review)
|
|
*/
|
|
public function approve(int $id): SalesTenantManagement
|
|
{
|
|
$management = SalesTenantManagement::findOrFail($id);
|
|
|
|
// 승인 조건 확인
|
|
if ($management->sales_progress < 100 || $management->manager_progress < 100) {
|
|
throw new \InvalidArgumentException('영업/매니저 진행률이 100%가 아닙니다.');
|
|
}
|
|
|
|
if ($management->hq_status !== SalesTenantManagement::HQ_STATUS_PENDING) {
|
|
throw new \InvalidArgumentException('이미 승인된 항목입니다.');
|
|
}
|
|
|
|
$management->update([
|
|
'hq_status' => SalesTenantManagement::HQ_STATUS_REVIEW,
|
|
]);
|
|
|
|
return $management->fresh();
|
|
}
|
|
|
|
/**
|
|
* 반려 처리
|
|
*/
|
|
public function reject(int $id, string $reason): SalesTenantManagement
|
|
{
|
|
$management = SalesTenantManagement::findOrFail($id);
|
|
|
|
if ($management->hq_status !== SalesTenantManagement::HQ_STATUS_PENDING) {
|
|
throw new \InvalidArgumentException('승인 대기 상태가 아닙니다.');
|
|
}
|
|
|
|
// notes 필드에 반려 사유 추가
|
|
$currentNotes = $management->notes ?? '';
|
|
$rejectionNote = '[반려 '.now()->format('Y-m-d H:i').'] '.$reason;
|
|
$newNotes = $currentNotes ? $currentNotes."\n".$rejectionNote : $rejectionNote;
|
|
|
|
$management->update([
|
|
'notes' => $newNotes,
|
|
]);
|
|
|
|
return $management->fresh();
|
|
}
|
|
|
|
/**
|
|
* 본사 진행 상태 업데이트
|
|
*/
|
|
public function updateHqStatus(int $id, string $status): SalesTenantManagement
|
|
{
|
|
$management = SalesTenantManagement::findOrFail($id);
|
|
|
|
// 유효한 상태인지 확인
|
|
if (! array_key_exists($status, SalesTenantManagement::$hqStatusLabels)) {
|
|
throw new \InvalidArgumentException('유효하지 않은 상태입니다.');
|
|
}
|
|
|
|
// pending 상태에서는 review로만 변경 가능 (승인 처리)
|
|
if ($management->hq_status === SalesTenantManagement::HQ_STATUS_PENDING && $status !== SalesTenantManagement::HQ_STATUS_REVIEW) {
|
|
throw new \InvalidArgumentException('승인 대기 상태에서는 검토 상태로만 변경 가능합니다. 먼저 승인 처리를 해주세요.');
|
|
}
|
|
|
|
$management->update([
|
|
'hq_status' => $status,
|
|
]);
|
|
|
|
return $management->fresh();
|
|
}
|
|
|
|
/**
|
|
* 승인대기로 되돌리기 (진행중 → pending)
|
|
*/
|
|
public function revertToPending(int $id): SalesTenantManagement
|
|
{
|
|
$management = SalesTenantManagement::findOrFail($id);
|
|
|
|
// 이미 pending 상태인 경우
|
|
if ($management->hq_status === SalesTenantManagement::HQ_STATUS_PENDING) {
|
|
throw new \InvalidArgumentException('이미 승인대기 상태입니다.');
|
|
}
|
|
|
|
// handover(인계) 상태에서는 되돌리기 불가
|
|
if ($management->hq_status === SalesTenantManagement::HQ_STATUS_HANDOVER) {
|
|
throw new \InvalidArgumentException('인계 완료된 항목은 되돌릴 수 없습니다.');
|
|
}
|
|
|
|
$management->update([
|
|
'hq_status' => SalesTenantManagement::HQ_STATUS_PENDING,
|
|
]);
|
|
|
|
return $management->fresh();
|
|
}
|
|
|
|
/**
|
|
* 상세 정보 조회
|
|
*/
|
|
public function getDetail(int $id): SalesTenantManagement
|
|
{
|
|
return SalesTenantManagement::with([
|
|
'tenant',
|
|
'tenantProspect',
|
|
'salesPartner.user',
|
|
'manager',
|
|
'contractProducts.product',
|
|
])->findOrFail($id);
|
|
}
|
|
}
|