header('HX-Request') && ! $request->header('HX-Boosted')) { return response('', 200)->header('HX-Redirect', route('finance.sales-commissions.index')); } // 필터 파라미터 $year = $request->input('year', now()->year); $month = $request->input('month', now()->month); $filters = [ 'scheduled_year' => $year, 'scheduled_month' => $month, 'status' => $request->input('status'), 'payment_type' => $request->input('payment_type'), 'partner_id' => $request->input('partner_id'), 'search' => $request->input('search'), ]; // 정산 목록 $commissions = $this->service->getCommissions($filters); // 통계 $stats = $this->service->getSettlementStats($year, $month); // 영업파트너 목록 (필터용) $partners = SalesPartner::with('user') ->active() ->orderBy('partner_code') ->get(); // 입금 대기 테넌트 목록 $pendingTenants = $this->service->getPendingPaymentTenants(); return view('finance.sales-commission.index', compact( 'commissions', 'stats', 'partners', 'pendingTenants', 'year', 'month', 'filters' )); } /** * 정산 상세 조회 */ public function show(int $id): JsonResponse { $commission = $this->service->getCommissionById($id); if (! $commission) { return response()->json([ 'success' => false, 'message' => '정산 정보를 찾을 수 없습니다.', ], 404); } return response()->json([ 'success' => true, 'data' => $commission, ]); } /** * 정산 상세 모달 (HTMX) */ public function detail(int $id): View { $commission = $this->service->getCommissionById($id); return view('finance.sales-commission.partials.detail-modal', compact('commission')); } /** * 입금 등록 (수당 생성) */ public function registerPayment(Request $request): JsonResponse { $validated = $request->validate([ 'management_id' => 'required|integer|exists:sales_tenant_managements,id', 'payment_type' => 'required|in:deposit,balance', 'payment_amount' => 'required|numeric|min:0', 'payment_date' => 'required|date', ]); try { $commission = $this->service->createCommission( $validated['management_id'], $validated['payment_type'], $validated['payment_amount'], $validated['payment_date'] ); return response()->json([ 'success' => true, 'message' => '입금이 등록되었습니다.', 'data' => $commission, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } /** * 승인 처리 */ public function approve(int $id): JsonResponse { try { $commission = $this->service->approve($id, auth()->id()); return response()->json([ 'success' => true, 'message' => '승인되었습니다.', 'data' => $commission, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } /** * 일괄 승인 */ public function bulkApprove(Request $request): JsonResponse { $validated = $request->validate([ 'ids' => 'required|array|min:1', 'ids.*' => 'integer|exists:sales_commissions,id', ]); try { $count = $this->service->bulkApprove($validated['ids'], auth()->id()); return response()->json([ 'success' => true, 'message' => "{$count}건이 승인되었습니다.", 'count' => $count, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } /** * 지급완료 처리 */ public function markPaid(int $id, Request $request): JsonResponse { $validated = $request->validate([ 'bank_reference' => 'nullable|string|max:100', ]); try { $commission = $this->service->markAsPaid($id, $validated['bank_reference'] ?? null); return response()->json([ 'success' => true, 'message' => '지급완료 처리되었습니다.', 'data' => $commission, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } /** * 일괄 지급완료 */ public function bulkMarkPaid(Request $request): JsonResponse { $validated = $request->validate([ 'ids' => 'required|array|min:1', 'ids.*' => 'integer|exists:sales_commissions,id', 'bank_reference' => 'nullable|string|max:100', ]); try { $count = $this->service->bulkMarkAsPaid($validated['ids'], $validated['bank_reference'] ?? null); return response()->json([ 'success' => true, 'message' => "{$count}건이 지급완료 처리되었습니다.", 'count' => $count, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } /** * 승인취소 처리 */ public function unapprove(int $id): JsonResponse { try { $commission = $this->service->unapprove($id); return response()->json([ 'success' => true, 'message' => '승인이 취소되었습니다.', 'data' => $commission, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } /** * 취소 처리 */ public function cancel(int $id): JsonResponse { try { $commission = $this->service->cancel($id); return response()->json([ 'success' => true, 'message' => '취소되었습니다.', 'data' => $commission, ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => $e->getMessage(), ], 400); } } /** * 수당지급일/협업지원금 인라인 수정 */ public function updateCommissionDate(int $id, Request $request): JsonResponse { $validated = $request->validate([ 'field' => 'required|in:first_partner_paid_at,second_partner_paid_at,manager_paid_at,referrer_commission', 'value' => 'nullable', ]); $commission = SalesCommission::findOrFail($id); // 수당지급일은 인계 상태일 때만 변경 가능 $paidFields = ['first_partner_paid_at', 'second_partner_paid_at', 'manager_paid_at']; if (in_array($validated['field'], $paidFields) && $commission->management?->hq_status !== 'handover') { return response()->json(['success' => false, 'message' => '인계 상태일 때만 수당지급일 설정 가능'], 422); } $value = $validated['value']; if ($validated['field'] === 'referrer_commission') { $value = (int) ($value ?: 0); } else { $value = $value ?: null; } $commission->update([$validated['field'] => $value]); return response()->json(['success' => true]); } /** * 정산 테이블 부분 새로고침 (HTMX) */ public function table(Request $request): View { $year = $request->input('year', now()->year); $month = $request->input('month', now()->month); $filters = [ 'scheduled_year' => $year, 'scheduled_month' => $month, 'status' => $request->input('status'), 'payment_type' => $request->input('payment_type'), 'partner_id' => $request->input('partner_id'), 'search' => $request->input('search'), ]; $commissions = $this->service->getCommissions($filters); return view('finance.sales-commission.partials.commission-table', compact('commissions')); } /** * 통계 카드 부분 새로고침 (HTMX) */ public function stats(Request $request): View { $year = $request->input('year', now()->year); $month = $request->input('month', now()->month); $stats = $this->service->getSettlementStats($year, $month); return view('finance.sales-commission.partials.stats-cards', compact('stats', 'year', 'month')); } /** * 입금 등록 폼 (HTMX 모달) */ public function paymentForm(Request $request): View { $managementId = $request->input('management_id'); $management = null; if ($managementId) { $management = SalesTenantManagement::with(['tenant', 'salesPartner.user', 'contractProducts.product']) ->find($managementId); } // 입금 대기 테넌트 목록 $pendingTenants = $this->service->getPendingPaymentTenants(); return view('finance.sales-commission.partials.payment-form', compact('management', 'pendingTenants')); } /** * 엑셀 다운로드 */ public function export(Request $request) { $year = $request->input('year', now()->year); $month = $request->input('month', now()->month); $filters = [ 'scheduled_year' => $year, 'scheduled_month' => $month, 'status' => $request->input('status'), 'payment_type' => $request->input('payment_type'), 'partner_id' => $request->input('partner_id'), ]; // 전체 데이터 조회 (페이지네이션 없이) $commissions = SalesCommission::query() ->with(['tenant', 'partner.user', 'manager']) ->when(! empty($filters['status']), fn ($q) => $q->where('status', $filters['status'])) ->when(! empty($filters['payment_type']), fn ($q) => $q->where('payment_type', $filters['payment_type'])) ->when(! empty($filters['partner_id']), fn ($q) => $q->where('partner_id', $filters['partner_id'])) ->forScheduledMonth($year, $month) ->orderBy('scheduled_payment_date') ->get(); // CSV 생성 $filename = "sales_commission_{$year}_{$month}.csv"; $headers = [ 'Content-Type' => 'text/csv; charset=UTF-8', 'Content-Disposition' => "attachment; filename=\"{$filename}\"", ]; $callback = function () use ($commissions) { $file = fopen('php://output', 'w'); // BOM for UTF-8 fprintf($file, chr(0xEF).chr(0xBB).chr(0xBF)); // 헤더 fputcsv($file, [ '번호', '테넌트', '입금구분', '입금액', '입금일', '기준액', '영업파트너', '파트너수당', '매니저', '매니저수당', '지급예정일', '상태', '실제지급일', ]); // 데이터 foreach ($commissions as $commission) { fputcsv($file, [ $commission->id, $commission->tenant->name ?? $commission->tenant->company_name, $commission->payment_type_label, number_format($commission->payment_amount), $commission->payment_date->format('Y-m-d'), number_format($commission->base_amount), $commission->partner?->user?->name ?? '-', number_format($commission->partner_commission), $commission->manager?->name ?? '-', number_format($commission->manager_commission), $commission->scheduled_payment_date->format('Y-m-d'), $commission->status_label, $commission->actual_payment_date?->format('Y-m-d') ?? '-', ]); } fclose($file); }; return response()->stream($callback, 200, $headers); } }