diff --git a/app/Http/Controllers/Finance/SettlementController.php b/app/Http/Controllers/Finance/SettlementController.php index ff6fac9b..90fb14ac 100644 --- a/app/Http/Controllers/Finance/SettlementController.php +++ b/app/Http/Controllers/Finance/SettlementController.php @@ -7,9 +7,12 @@ use App\Models\Sales\SalesContractProduct; use App\Models\Sales\SalesPartner; use App\Models\Sales\SalesTenantManagement; +use App\Models\User; use App\Services\SalesCommissionService; +use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Http\Response; +use Illuminate\Support\Facades\DB; use Illuminate\View\View; class SettlementController extends Controller @@ -365,73 +368,259 @@ public function paymentStats(Request $request): View|Response return response('', 200)->header('HX-Redirect', route('finance.settlement.payment-stats')); } - $year = (int) $request->input('year', now()->year); + // 필터 파라미터 + $now = now(); + $startYear = (int) $request->input('start_year', $now->year); + $startMonth = (int) $request->input('start_month', 1); + $endYear = (int) $request->input('end_year', $now->year); + $endMonth = (int) $request->input('end_month', $now->month); + $status = $request->input('status'); + $paymentType = $request->input('payment_type'); + $partnerId = $request->input('partner_id'); + $managerUserId = $request->input('manager_user_id'); + $search = $request->input('search'); - // 통계 카드 - $totalPaidAmount = SalesCommission::where('status', SalesCommission::STATUS_PAID) - ->whereYear('actual_payment_date', $year) - ->selectRaw('SUM(partner_commission + manager_commission + COALESCE(referrer_commission, 0)) as total') - ->value('total') ?? 0; + $startDate = Carbon::create($startYear, $startMonth, 1)->startOfMonth(); + $endDate = Carbon::create($endYear, $endMonth, 1)->endOfMonth(); - $totalPaidCount = SalesCommission::where('status', SalesCommission::STATUS_PAID) - ->whereYear('actual_payment_date', $year) - ->count(); + // 공통 baseQuery 클로저 + $baseQuery = function () use ($startDate, $endDate, $status, $paymentType, $partnerId, $managerUserId, $search) { + $query = SalesCommission::query() + ->whereBetween('scheduled_payment_date', [$startDate, $endDate]); - $activePartners = SalesCommission::where('status', SalesCommission::STATUS_PAID) - ->whereYear('actual_payment_date', $year) - ->distinct('partner_id') - ->count('partner_id'); + if ($status) { + $query->where('status', $status); + } + if ($paymentType) { + $query->where('payment_type', $paymentType); + } + if ($partnerId) { + $query->where('partner_id', $partnerId); + } + if ($managerUserId) { + $query->where('manager_user_id', $managerUserId); + } + if ($search) { + $query->where(function ($q) use ($search) { + $q->whereHas('partner.user', fn ($uq) => $uq->where('name', 'like', "%{$search}%")) + ->orWhereHas('partner', fn ($pq) => $pq->where('partner_code', 'like', "%{$search}%")) + ->orWhereHas('management.tenant', fn ($tq) => $tq->where('company_name', 'like', "%{$search}%")); + }); + } - $avgCommission = $totalPaidCount > 0 ? round($totalPaidAmount / $totalPaidCount) : 0; + return $query; + }; - $statsCards = [ - 'total_paid_amount' => $totalPaidAmount, - 'total_paid_count' => $totalPaidCount, + $filters = compact('startYear', 'startMonth', 'endYear', 'endMonth', 'status', 'paymentType', 'partnerId', 'managerUserId', 'search'); + + // 통계 카드 8개 + $statsCards = $this->calculateStatsCards($baseQuery); + + // 차트 데이터 + $monthlyTrend = $this->getMonthlyTrend($baseQuery, $startDate, $endDate); + $typeRatio = $this->getTypeRatio($baseQuery); + $topPartners = $this->getTopPartners($baseQuery); + $statusDistribution = $this->getStatusDistribution($baseQuery); + $monthlyComparison = $this->getMonthlyComparison($baseQuery, $startDate, $endDate); + + // 테이블 데이터 + $partnerSettlement = $this->getPartnerSettlement($baseQuery); + $managerSettlement = $this->getManagerSettlement($baseQuery); + + // 필터 옵션 + $partners = SalesPartner::with('user')->active()->orderBy('partner_code')->get(); + $managers = User::whereIn('id', SalesCommission::whereNotNull('manager_user_id')->distinct()->pluck('manager_user_id'))->orderBy('name')->get(); + + return view('finance.settlement.payment-stats', compact( + 'filters', 'statsCards', + 'monthlyTrend', 'typeRatio', 'topPartners', 'statusDistribution', 'monthlyComparison', + 'partnerSettlement', 'managerSettlement', + 'partners', 'managers' + )); + } + + /** + * 통계 카드 8개 계산 + */ + private function calculateStatsCards(\Closure $baseQuery): array + { + $commissionSum = 'partner_commission + manager_commission + COALESCE(referrer_commission, 0)'; + + $totalAmount = (clone $baseQuery())->selectRaw("SUM({$commissionSum}) as total")->value('total') ?? 0; + $totalCount = (clone $baseQuery())->count(); + $paidAmount = (clone $baseQuery())->where('status', SalesCommission::STATUS_PAID) + ->selectRaw("SUM({$commissionSum}) as total")->value('total') ?? 0; + $unpaidAmount = (clone $baseQuery())->whereIn('status', [SalesCommission::STATUS_PENDING, SalesCommission::STATUS_APPROVED]) + ->selectRaw("SUM({$commissionSum}) as total")->value('total') ?? 0; + $activePartners = (clone $baseQuery())->distinct('partner_id')->count('partner_id'); + $partnerSum = (clone $baseQuery())->sum('partner_commission'); + $managerSum = (clone $baseQuery())->sum('manager_commission'); + $referrerSum = (clone $baseQuery())->selectRaw('SUM(COALESCE(referrer_commission, 0)) as total')->value('total') ?? 0; + $avgCommission = $totalCount > 0 ? round($totalAmount / $totalCount) : 0; + + return [ + 'total_amount' => $totalAmount, + 'paid_amount' => $paidAmount, + 'unpaid_amount' => $unpaidAmount, 'active_partners' => $activePartners, + 'partner_sum' => $partnerSum, + 'manager_sum' => $managerSum, + 'referrer_sum' => $referrerSum, 'avg_commission' => $avgCommission, ]; + } - // 차트 1 & 4: 월별 지급 추이 (해당 연도) - $monthlyTrend = SalesCommission::where('status', SalesCommission::STATUS_PAID) - ->whereYear('actual_payment_date', $year) + /** + * 월별 지급 추이 (stacked bar) + */ + private function getMonthlyTrend(\Closure $baseQuery, Carbon $startDate, Carbon $endDate): \Illuminate\Support\Collection + { + return $baseQuery() ->selectRaw(" - DATE_FORMAT(actual_payment_date, '%Y-%m') as month, + DATE_FORMAT(scheduled_payment_date, '%Y-%m') as month, SUM(partner_commission) as partner_total, SUM(manager_commission) as manager_total, SUM(COALESCE(referrer_commission, 0)) as referrer_total, + SUM(CASE WHEN status = 'paid' THEN partner_commission + manager_commission + COALESCE(referrer_commission, 0) ELSE 0 END) as paid_total, + SUM(CASE WHEN status IN ('pending','approved') THEN partner_commission + manager_commission + COALESCE(referrer_commission, 0) ELSE 0 END) as unpaid_total, COUNT(*) as count ") - ->groupByRaw("DATE_FORMAT(actual_payment_date, '%Y-%m')") + ->groupByRaw("DATE_FORMAT(scheduled_payment_date, '%Y-%m')") ->orderBy('month') ->get(); + } - // 차트 2: 수당 유형별 비율 - $typeRatio = SalesCommission::where('status', SalesCommission::STATUS_PAID) - ->whereYear('actual_payment_date', $year) + /** + * 수당 유형별 비율 (doughnut) + */ + private function getTypeRatio(\Closure $baseQuery): ?object + { + return $baseQuery() ->selectRaw(" SUM(partner_commission) as partner_total, SUM(manager_commission) as manager_total, SUM(COALESCE(referrer_commission, 0)) as referrer_total ") ->first(); + } - // 차트 3: 파트너별 수당 Top 10 - $topPartners = SalesCommission::where('status', SalesCommission::STATUS_PAID) - ->whereYear('actual_payment_date', $year) + /** + * 파트너별 Top 10 (horizontal bar) + */ + private function getTopPartners(\Closure $baseQuery): array + { + $topPartners = $baseQuery() ->selectRaw('partner_id, SUM(partner_commission + manager_commission + COALESCE(referrer_commission, 0)) as total') ->groupBy('partner_id') ->orderByDesc('total') ->limit(10) ->get(); - $topPartnerNames = SalesPartner::with('user') + $partnerNames = SalesPartner::with('user') ->whereIn('id', $topPartners->pluck('partner_id')) ->get() ->keyBy('id'); - return view('finance.settlement.payment-stats', compact( - 'year', 'statsCards', 'monthlyTrend', 'typeRatio', 'topPartners', 'topPartnerNames' - )); + return [ + 'data' => $topPartners, + 'names' => $partnerNames->map(fn ($p) => $p->user?->name ?? $p->partner_code), + ]; + } + + /** + * 상태별 건수/금액 분포 (doughnut + bar) + */ + private function getStatusDistribution(\Closure $baseQuery): \Illuminate\Support\Collection + { + return $baseQuery() + ->selectRaw(" + status, + COUNT(*) as count, + SUM(partner_commission + manager_commission + COALESCE(referrer_commission, 0)) as amount + ") + ->groupBy('status') + ->get(); + } + + /** + * 파트너 vs 매니저 월별 추이 (line) - paid만 + */ + private function getMonthlyComparison(\Closure $baseQuery, Carbon $startDate, Carbon $endDate): \Illuminate\Support\Collection + { + return $baseQuery() + ->where('status', SalesCommission::STATUS_PAID) + ->selectRaw(" + DATE_FORMAT(actual_payment_date, '%Y-%m') as month, + SUM(partner_commission) as partner_total, + SUM(manager_commission) as manager_total + ") + ->groupByRaw("DATE_FORMAT(actual_payment_date, '%Y-%m')") + ->orderBy('month') + ->get(); + } + + /** + * 파트너별 결산 테이블 + */ + private function getPartnerSettlement(\Closure $baseQuery): \Illuminate\Support\Collection + { + $data = $baseQuery() + ->selectRaw(" + partner_id, + COUNT(*) as contract_count, + SUM(CASE WHEN payment_type = 'deposit' THEN partner_commission ELSE 0 END) as first_commission, + SUM(CASE WHEN payment_type = 'balance' THEN partner_commission ELSE 0 END) as second_commission, + SUM(partner_commission) as total_partner, + SUM(CASE WHEN status = 'paid' THEN partner_commission + manager_commission + COALESCE(referrer_commission, 0) ELSE 0 END) as paid_amount, + SUM(CASE WHEN status IN ('pending','approved') THEN partner_commission + manager_commission + COALESCE(referrer_commission, 0) ELSE 0 END) as unpaid_amount, + SUM(partner_commission + manager_commission + COALESCE(referrer_commission, 0)) as total_amount + ") + ->groupBy('partner_id') + ->orderByDesc('total_amount') + ->get(); + + $partnerInfo = SalesPartner::with('user') + ->whereIn('id', $data->pluck('partner_id')) + ->get() + ->keyBy('id'); + + return $data->map(function ($row) use ($partnerInfo) { + $partner = $partnerInfo->get($row->partner_id); + $row->partner_name = $partner?->user?->name ?? 'N/A'; + $row->partner_type = $partner?->partner_type ?? ''; + $row->completion_rate = $row->total_amount > 0 ? round(($row->paid_amount / $row->total_amount) * 100, 1) : 0; + return $row; + }); + } + + /** + * 매니저별 결산 테이블 + */ + private function getManagerSettlement(\Closure $baseQuery): \Illuminate\Support\Collection + { + $data = $baseQuery() + ->whereNotNull('manager_user_id') + ->selectRaw(" + manager_user_id, + COUNT(*) as contract_count, + SUM(manager_commission) as total_manager, + SUM(CASE WHEN status = 'paid' THEN manager_commission ELSE 0 END) as paid_amount, + SUM(CASE WHEN status IN ('pending','approved') THEN manager_commission ELSE 0 END) as unpaid_amount + ") + ->groupBy('manager_user_id') + ->orderByDesc('total_manager') + ->get(); + + $managerInfo = User::whereIn('id', $data->pluck('manager_user_id')) + ->get() + ->keyBy('id'); + + return $data->map(function ($row) use ($managerInfo) { + $manager = $managerInfo->get($row->manager_user_id); + $row->manager_name = $manager?->name ?? 'N/A'; + $row->completion_rate = $row->total_manager > 0 ? round(($row->paid_amount / $row->total_manager) * 100, 1) : 0; + return $row; + }); } /** diff --git a/resources/views/finance/settlement/payment-stats.blade.php b/resources/views/finance/settlement/payment-stats.blade.php index 5ebb04ad..fb13a3f7 100644 --- a/resources/views/finance/settlement/payment-stats.blade.php +++ b/resources/views/finance/settlement/payment-stats.blade.php @@ -3,12 +3,14 @@ @section('title', '수당지급현황통계') @section('content') -
{{ $year }}년 수당 지급 현황 종합 통계
++ {{ $filters['startYear'] }}년 {{ $filters['startMonth'] }}월 ~ {{ $filters['endYear'] }}년 {{ $filters['endMonth'] }}월 수당 종합 통계 +
총 지급 건수
-{{ number_format($statsCards['total_paid_count']) }}건
+총 수당 발생액
+{{ number_format($statsCards['total_amount']) }}원
{{ $year }}년 총 건수
지급 완료액
+{{ number_format($statsCards['paid_amount']) }}원
+미지급액
+{{ number_format($statsCards['unpaid_amount']) }}원
+지급 파트너 수
-{{ $statsCards['active_partners'] }}명
+활성 파트너 수
+{{ $statsCards['active_partners'] }}명
{{ $year }}년 지급 대상
건당 평균 수당
-{{ number_format($statsCards['avg_commission']) }}원
+파트너 수당
+{{ number_format($statsCards['partner_sum']) }}원
매니저 수당
+{{ number_format($statsCards['manager_sum']) }}원
+유치 수당
+{{ number_format($statsCards['referrer_sum']) }}원
+건당 평균 수당
+{{ number_format($statsCards['avg_commission']) }}원
+전체 평균
| 파트너수당 | 매니저수당 | 유치수당 | -합계 | +지급완료 | +미지급 | 건수 | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| {{ \Carbon\Carbon::parse($row->month . '-01')->format('n월') }} | +{{ \Carbon\Carbon::parse($row->month . '-01')->format('Y년 n월') }} | {{ number_format($row->partner_total) }}원 | {{ number_format($row->manager_total) }}원 | {{ number_format($row->referrer_total) }}원 | -{{ number_format($rowTotal) }}원 | +{{ number_format($row->paid_total) }}원 | +{{ number_format($row->unpaid_total) }}원 | {{ $row->count }}건 | ||||
| {{ $year }}년 지급 데이터가 없습니다. | +해당 기간의 데이터가 없습니다. | |||||||||||
| 합계 | -{{ number_format($grandPartner) }}원 | -{{ number_format($grandManager) }}원 | -{{ number_format($grandReferrer) }}원 | -{{ number_format($grandTotal) }}원 | -{{ $grandCount }}건 | +{{ number_format($gPartner) }}원 | +{{ number_format($gManager) }}원 | +{{ number_format($gReferrer) }}원 | +{{ number_format($gPaid) }}원 | +{{ number_format($gUnpaid) }}원 | +{{ $gCount }}건 | +|
| 파트너 | +유형 | +건수 | +1차수당 | +2차수당 | +총수당 | +지급완료 | +미지급 | +완료율 | +
|---|---|---|---|---|---|---|---|---|
| {{ $row->partner_name }} | ++ + {{ $row->partner_type === 'corporate' ? '법인' : '개인' }} + + | +{{ $row->contract_count }}건 | +{{ number_format($row->first_commission) }}원 | +{{ number_format($row->second_commission) }}원 | +{{ number_format($row->total_amount) }}원 | +{{ number_format($row->paid_amount) }}원 | +{{ number_format($row->unpaid_amount) }}원 | +
+
+
+
+
+
+ {{ $row->completion_rate }}%
+ |
+
| 해당 기간의 데이터가 없습니다. | +||||||||
| 합계 | +- | +{{ $pCount }}건 | +{{ number_format($pFirst) }}원 | +{{ number_format($pSecond) }}원 | +{{ number_format($pTotal) }}원 | +{{ number_format($pPaid) }}원 | +{{ number_format($pUnpaid) }}원 | +
+
+
+
+
+
+ {{ $overallRate }}%
+ |
+
| 매니저 | +담당건수 | +매니저수당 | +지급완료 | +미지급 | +완료율 | +
|---|---|---|---|---|---|
| {{ $row->manager_name }} | +{{ $row->contract_count }}건 | +{{ number_format($row->total_manager) }}원 | +{{ number_format($row->paid_amount) }}원 | +{{ number_format($row->unpaid_amount) }}원 | +
+
+
+
+
+
+ {{ $row->completion_rate }}%
+ |
+
| 해당 기간의 데이터가 없습니다. | +|||||
| 합계 | +{{ $mCount }}건 | +{{ number_format($mTotal) }}원 | +{{ number_format($mPaid) }}원 | +{{ number_format($mUnpaid) }}원 | +
+
+
+
+
+
+ {{ $mOverallRate }}%
+ |