refactor:고객사정산 탭 재설계 (실 테넌트 데이터 기반)

- customerTab() 메서드: SalesTenantManagement 기반 쿼리로 재작성
- getCustomerStats() private 메서드 추가 (총개발비/수금완료/미수금/개발진행/구독전환)
- customer-tab.blade.php: Alpine.js CRUD → 순수 Blade 테이블로 전체 교체
- index.blade.php: 미사용 customerSettlementManager() Alpine 함수 제거
- 필터: 검색/개발상태/수금상태/담당파트너 4종
- 테이블: 고객사/파트너/매니저/개발비/1차/2차/구독료/개발상태 8열

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-02-19 13:51:28 +09:00
parent de3e6c2ba0
commit a1aa6036cf
3 changed files with 310 additions and 150 deletions

View File

@@ -4,7 +4,9 @@
use App\Http\Controllers\Controller;
use App\Models\Sales\SalesCommission;
use App\Models\Sales\SalesContractProduct;
use App\Models\Sales\SalesPartner;
use App\Models\Sales\SalesTenantManagement;
use App\Services\SalesCommissionService;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
@@ -169,7 +171,91 @@ public function consultingTab(Request $request): View
*/
public function customerTab(Request $request): View
{
return view('finance.settlement.partials.customer-tab');
$query = SalesTenantManagement::with([
'tenant',
'tenantProspect',
'salesPartner.user',
'manager',
'commissions',
])->where('hq_status', '!=', SalesTenantManagement::HQ_STATUS_PENDING);
// 필터: 검색 (회사명)
if ($search = $request->input('search')) {
$query->where(function ($q) use ($search) {
$q->whereHas('tenant', fn ($tq) => $tq->where('company_name', 'like', "%{$search}%"))
->orWhereHas('tenantProspect', fn ($pq) => $pq->where('company_name', 'like', "%{$search}%"));
});
}
// 필터: 개발 상태
if ($hqStatus = $request->input('hq_status')) {
$query->where('hq_status', $hqStatus);
}
// 필터: 담당 파트너
if ($partnerId = $request->input('partner_id')) {
$query->where('sales_partner_id', $partnerId);
}
// 필터: 수금 상태
$paymentStatus = $request->input('payment_status');
$managements = $query->orderByDesc('id')->paginate(20)->withQueryString();
// 수금 상태 필터 (컬렉션 레벨)
if ($paymentStatus) {
$managements->setCollection(
$managements->getCollection()->filter(function ($mgmt) use ($paymentStatus) {
$depositPaid = $mgmt->deposit_status === 'paid';
$balancePaid = $mgmt->balance_status === 'paid';
return match ($paymentStatus) {
'fully_paid' => $depositPaid && $balancePaid,
'partial' => ($depositPaid || $balancePaid) && !($depositPaid && $balancePaid),
'unpaid' => !$depositPaid && !$balancePaid,
default => true,
};
})
);
}
// 구독료 일괄 조회 (N+1 방지)
$tenantIds = $managements->getCollection()
->pluck('tenant_id')
->filter()
->unique()
->values()
->toArray();
$subscriptionFees = [];
if (!empty($tenantIds)) {
$subscriptionFees = SalesContractProduct::whereIn('tenant_id', $tenantIds)
->selectRaw('tenant_id, SUM(subscription_fee) as total_subscription_fee')
->groupBy('tenant_id')
->pluck('total_subscription_fee', 'tenant_id')
->toArray();
}
// 파트너 목록 (필터용)
$partners = SalesPartner::with('user')
->active()
->orderBy('partner_code')
->get();
// hqStatusLabels에서 pending 제외
$hqStatusLabels = collect(SalesTenantManagement::$hqStatusLabels)
->except(SalesTenantManagement::HQ_STATUS_PENDING)
->toArray();
// 통계 카드
$customerStats = $this->getCustomerStats();
return view('finance.settlement.partials.customer-tab', compact(
'managements',
'subscriptionFees',
'partners',
'hqStatusLabels',
'customerStats',
));
}
/**
@@ -317,6 +403,48 @@ public function paymentStats(Request $request): View|Response
));
}
/**
* 고객사정산 통계 데이터
*/
private function getCustomerStats(): array
{
$baseQuery = SalesTenantManagement::where('hq_status', '!=', SalesTenantManagement::HQ_STATUS_PENDING);
// 총 개발비
$totalFee = (clone $baseQuery)->sum('total_registration_fee');
$totalCount = (clone $baseQuery)->count();
// 수금완료 (deposit + balance 모두 paid인 건의 합계)
$collectedAmount = (clone $baseQuery)
->where('deposit_status', 'paid')
->sum('deposit_amount')
+ (clone $baseQuery)
->where('balance_status', 'paid')
->sum('balance_amount');
// 미수금
$uncollectedAmount = $totalFee - $collectedAmount;
// 개발 진행 중 (handover 제외)
$inProgressCount = (clone $baseQuery)
->where('hq_status', '!=', SalesTenantManagement::HQ_STATUS_HANDOVER)
->count();
// 구독 전환 (handover + tenant active)
$subscriptionCount = SalesTenantManagement::where('hq_status', SalesTenantManagement::HQ_STATUS_HANDOVER)
->whereHas('tenant', fn ($q) => $q->whereNull('deleted_at'))
->count();
return [
'total_fee' => $totalFee,
'total_count' => $totalCount,
'collected_amount' => $collectedAmount,
'uncollected_amount' => max(0, $uncollectedAmount),
'in_progress_count' => $inProgressCount,
'subscription_count' => $subscriptionCount,
];
}
/**
* 통합 통계 데이터
*/