From f1f31d5f70688d9cb5dbc7e1bc2430f4bfb20c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Sat, 14 Feb 2026 19:42:51 +0900 Subject: [PATCH] =?UTF-8?q?feat:=EB=8B=A8=EC=B2=B4(=EA=B7=B8=EB=A3=B9)=20?= =?UTF-8?q?=EC=88=98=EB=8B=B9=20=EC=B2=B4=EA=B3=84=20=ED=86=B5=ED=95=A9=20?= =?UTF-8?q?(=EB=8B=A8=EC=B2=B4=2030%,=20=EC=9C=A0=EC=B9=98=203%,=20?= =?UTF-8?q?=EB=A7=A4=EB=8B=88=EC=A0=80=200%)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SalesPartner: referrer_partner_id, referrer/referredGroups 관계, isGroup() 헬퍼 - SalesCommission: 유치수당 필드, referrerPartner 관계, 지급 추적 메서드 - SalesCommissionService: 단체/개인 분기 수당 계산 로직 - SalesManagerService: 단체 등록 시 수당률 자동 설정 - SalesManagerController: 유형 선택 및 유치 파트너 지정 기능 - 대시보드: 유치수당 카드 표시 및 합산 - UI: purple 색상 처리 추가 Co-Authored-By: Claude Opus 4.6 --- .../Sales/SalesDashboardController.php | 22 ++++-- .../Sales/SalesManagerController.php | 11 ++- app/Models/Sales/SalesCommission.php | 73 ++++++++++++++++++- app/Models/Sales/SalesPartner.php | 25 +++++++ app/Services/Sales/SalesManagerService.php | 26 +++++-- app/Services/SalesCommissionService.php | 26 ++++++- .../partials/commission-by-role.blade.php | 5 ++ .../views/sales/managers/create.blade.php | 40 ++++++++++ 8 files changed, 207 insertions(+), 21 deletions(-) diff --git a/app/Http/Controllers/Sales/SalesDashboardController.php b/app/Http/Controllers/Sales/SalesDashboardController.php index 20dbe357..30f42780 100644 --- a/app/Http/Controllers/Sales/SalesDashboardController.php +++ b/app/Http/Controllers/Sales/SalesDashboardController.php @@ -84,6 +84,15 @@ private function getDashboardData(Request $request): array : collect(); $myCommissionsAsManager = SalesCommission::forManager($currentUserId)->get(); + // 유치수당 계산 (내가 유치 파트너인 건) + $myCommissionsAsReferrer = $partnerId + ? SalesCommission::where('referrer_partner_id', $partnerId)->get() + : collect(); + $referrerCommissionTotal = $myCommissionsAsReferrer->sum('referrer_commission'); + $referrerCommissionPaid = $myCommissionsAsReferrer->where('status', SalesCommission::STATUS_PAID)->sum('referrer_commission'); + $referrerCommissionPending = $myCommissionsAsReferrer->where('status', SalesCommission::STATUS_PENDING)->sum('referrer_commission'); + $referrerCommissionApproved = $myCommissionsAsReferrer->where('status', SalesCommission::STATUS_APPROVED)->sum('referrer_commission'); + // 판매자(영업파트너) 수당 계산 $partnerCommissionTotal = $myCommissionsAsPartner->sum('partner_commission'); $partnerCommissionPaid = $myCommissionsAsPartner->where('status', SalesCommission::STATUS_PAID)->sum('partner_commission'); @@ -102,8 +111,8 @@ private function getDashboardData(Request $request): array // 통계 데이터 (실제 데이터) $totalMembershipFee = $myCommissionsAsPartner->sum('payment_amount') + $myCommissionsAsManager->sum('payment_amount'); - $totalCommission = $partnerCommissionTotal + $managerCommissionTotal; - $paidCommission = $partnerCommissionPaid + $managerCommissionPaid; + $totalCommission = $partnerCommissionTotal + $managerCommissionTotal + $referrerCommissionTotal; + $paidCommission = $partnerCommissionPaid + $managerCommissionPaid + $referrerCommissionPaid; $commissionRate = $totalCommission > 0 ? round(($paidCommission / $totalCommission) * 100, 1) : 0; $stats = [ @@ -139,9 +148,12 @@ private function getDashboardData(Request $request): array 'color' => 'blue', ], [ - 'name' => '협업지원금', - 'rate' => null, // 메뉴당 2,000원 - 'amount' => null, // 개발비 완납 시 계산 + 'name' => '유치수당', + 'rate' => 3, + 'amount' => $referrerCommissionTotal, + 'paid' => $referrerCommissionPaid, + 'pending' => $referrerCommissionPending, + 'approved' => $referrerCommissionApproved, 'color' => 'purple', ], ]; diff --git a/app/Http/Controllers/Sales/SalesManagerController.php b/app/Http/Controllers/Sales/SalesManagerController.php index c936c9e6..937be760 100644 --- a/app/Http/Controllers/Sales/SalesManagerController.php +++ b/app/Http/Controllers/Sales/SalesManagerController.php @@ -4,6 +4,7 @@ use App\Http\Controllers\Controller; use App\Models\Sales\SalesManagerDocument; +use App\Models\Sales\SalesPartner; use App\Models\User; use App\Services\Sales\SalesManagerService; use Illuminate\Http\Request; @@ -52,7 +53,13 @@ public function create(): View // 문서 타입 목록 $documentTypes = SalesManagerDocument::DOCUMENT_TYPES; - return view('sales.managers.create', compact('roles', 'documentTypes')); + // 활성 개인 파트너 목록 (유치 파트너 선택용) + $activePartners = SalesPartner::active() + ->with('user') + ->where('partner_type', 'individual') + ->get(); + + return view('sales.managers.create', compact('roles', 'documentTypes', 'activePartners')); } /** @@ -71,6 +78,8 @@ public function store(Request $request) 'company_name' => 'nullable|string|max:100', 'biz_no' => 'nullable|string|max:20', 'address' => 'nullable|string|max:255', + 'partner_type' => 'nullable|in:individual,corporate', + 'referrer_partner_id' => 'nullable|exists:sales_partners,id', 'documents' => 'nullable|array', 'documents.*.file' => 'nullable|file|max:10240', 'documents.*.document_type' => 'nullable|string', diff --git a/app/Models/Sales/SalesCommission.php b/app/Models/Sales/SalesCommission.php index 3f56279d..3db745f3 100644 --- a/app/Models/Sales/SalesCommission.php +++ b/app/Models/Sales/SalesCommission.php @@ -105,6 +105,11 @@ class SalesCommission extends Model 'bank_reference', 'approved_by', 'approved_at', + 'referrer_partner_id', + 'referrer_rate', + 'referrer_commission', + 'first_referrer_paid_at', + 'second_referrer_paid_at', ]; protected $casts = [ @@ -124,6 +129,10 @@ class SalesCommission extends Model 'scheduled_payment_date' => 'date', 'actual_payment_date' => 'date', 'approved_at' => 'datetime', + 'referrer_rate' => 'decimal:2', + 'referrer_commission' => 'decimal:2', + 'first_referrer_paid_at' => 'date', + 'second_referrer_paid_at' => 'date', ]; /** @@ -166,6 +175,14 @@ public function details(): HasMany return $this->hasMany(SalesCommissionDetail::class, 'commission_id'); } + /** + * 유치 영업파트너 관계 + */ + public function referrerPartner(): BelongsTo + { + return $this->belongsTo(SalesPartner::class, 'referrer_partner_id'); + } + /** * 승인자 관계 */ @@ -195,7 +212,7 @@ public function getPaymentTypeLabelAttribute(): string */ public function getTotalCommissionAttribute(): float { - return $this->partner_commission + $this->manager_commission; + return $this->partner_commission + $this->manager_commission + $this->referrer_commission; } /** @@ -443,12 +460,62 @@ public function isManagerPaid(): bool } /** - * 전체 수당 지급 완료 여부 (파트너 1차/2차 + 매니저) + * 1차 유치수당 지급 처리 + */ + public function recordFirstReferrerPaid(?Carbon $paidDate = null): bool + { + if (!$this->referrer_partner_id) { + return false; + } + + return $this->update([ + 'first_referrer_paid_at' => $paidDate ?? now(), + ]); + } + + /** + * 2차 유치수당 지급 처리 + */ + public function recordSecondReferrerPaid(?Carbon $paidDate = null): bool + { + if (!$this->referrer_partner_id) { + return false; + } + + return $this->update([ + 'second_referrer_paid_at' => $paidDate ?? now(), + ]); + } + + /** + * 1차 유치수당 지급 완료 여부 + */ + public function isFirstReferrerPaid(): bool + { + return $this->first_referrer_paid_at !== null; + } + + /** + * 2차 유치수당 지급 완료 여부 + */ + public function isSecondReferrerPaid(): bool + { + return $this->second_referrer_paid_at !== null; + } + + /** + * 전체 수당 지급 완료 여부 (파트너 1차/2차 + 매니저 + 유치수당) */ public function isFullyPaid(): bool { - return $this->isFirstPartnerPaid() + $basePaid = $this->isFirstPartnerPaid() && $this->isSecondPartnerPaid() && $this->isManagerPaid(); + + if ($this->referrer_partner_id) { + return $basePaid && $this->isFirstReferrerPaid() && $this->isSecondReferrerPaid(); + } + + return $basePaid; } } diff --git a/app/Models/Sales/SalesPartner.php b/app/Models/Sales/SalesPartner.php index 56f27e02..cc06177c 100644 --- a/app/Models/Sales/SalesPartner.php +++ b/app/Models/Sales/SalesPartner.php @@ -51,6 +51,7 @@ class SalesPartner extends Model 'company_name', 'biz_no', 'address', + 'referrer_partner_id', ]; protected $casts = [ @@ -85,6 +86,30 @@ public function tenantManagements(): HasMany return $this->hasMany(SalesTenantManagement::class, 'sales_partner_id'); } + /** + * 이 단체를 유치한 영업파트너 + */ + public function referrer(): BelongsTo + { + return $this->belongsTo(SalesPartner::class, 'referrer_partner_id'); + } + + /** + * 이 영업파트너가 유치한 단체 목록 + */ + public function referredGroups(): HasMany + { + return $this->hasMany(SalesPartner::class, 'referrer_partner_id'); + } + + /** + * 단체 여부 확인 + */ + public function isGroup(): bool + { + return $this->referrer_partner_id !== null; + } + /** * 파트너 코드 자동 생성 */ diff --git a/app/Services/Sales/SalesManagerService.php b/app/Services/Sales/SalesManagerService.php index 80a90e3a..57d2cdcb 100644 --- a/app/Services/Sales/SalesManagerService.php +++ b/app/Services/Sales/SalesManagerService.php @@ -60,16 +60,26 @@ public function createSalesPartner(array $data, array $documents = []): User // 4-1. 사업자 정보 저장 (선택) if (!empty($data['company_name']) || !empty($data['biz_no']) || !empty($data['address'])) { + $partnerType = $data['partner_type'] ?? 'individual'; + $spData = [ + 'partner_code' => SalesPartner::generatePartnerCode(), + 'partner_type' => $partnerType, + 'status' => 'active', + 'company_name' => $data['company_name'] ?? null, + 'biz_no' => $data['biz_no'] ?? null, + 'address' => $data['address'] ?? null, + 'referrer_partner_id' => $data['referrer_partner_id'] ?? null, + ]; + + // 단체(corporate)이면 수당률 설정 + if ($partnerType === 'corporate') { + $spData['commission_rate'] = 30.00; + $spData['manager_commission_rate'] = 0; + } + SalesPartner::updateOrCreate( ['user_id' => $user->id], - [ - 'partner_code' => SalesPartner::generatePartnerCode(), - 'partner_type' => 'individual', - 'status' => 'active', - 'company_name' => $data['company_name'] ?? null, - 'biz_no' => $data['biz_no'] ?? null, - 'address' => $data['address'] ?? null, - ] + $spData ); } diff --git a/app/Services/SalesCommissionService.php b/app/Services/SalesCommissionService.php index 384bb07c..e840ecba 100644 --- a/app/Services/SalesCommissionService.php +++ b/app/Services/SalesCommissionService.php @@ -19,6 +19,8 @@ class SalesCommissionService */ const DEFAULT_PARTNER_RATE = 20.00; const DEFAULT_MANAGER_RATE = 5.00; + const DEFAULT_GROUP_RATE = 30.00; // 단체 수당률 + const DEFAULT_REFERRER_RATE = 3.00; // 유치 수당률 // ========================================================================= // 정산 목록 조회 @@ -118,15 +120,28 @@ public function createCommission(int $managementId, string $paymentType, float $ $totalRegistrationFee = $contractProducts->sum('registration_fee') ?: $paymentAmount * 2; $baseAmount = $totalRegistrationFee / 2; // 개발비의 50% - // 수당률 (영업파트너 설정 또는 기본값) - $partnerRate = $partner->commission_rate ?? self::DEFAULT_PARTNER_RATE; - $managerRate = $partner->manager_commission_rate ?? self::DEFAULT_MANAGER_RATE; + // 수당률 (단체/개인 분기 처리) + $isGroup = $partner->isGroup(); + if ($isGroup) { + $partnerRate = $partner->commission_rate ?? self::DEFAULT_GROUP_RATE; + $managerRate = 0; + $referrerId = $partner->referrer_partner_id; + $referrerRate = self::DEFAULT_REFERRER_RATE; + } else { + $partnerRate = $partner->commission_rate ?? self::DEFAULT_PARTNER_RATE; + $managerRate = $partner->manager_commission_rate ?? self::DEFAULT_MANAGER_RATE; + $referrerId = null; + $referrerRate = 0; + } // 수당 계산 $partnerCommission = $baseAmount * ($partnerRate / 100); - $managerCommission = $management->manager_user_id + $managerCommission = ($management->manager_user_id && $managerRate > 0) ? $baseAmount * ($managerRate / 100) : 0; + $referrerCommission = ($referrerId && $referrerRate > 0) + ? $baseAmount * ($referrerRate / 100) + : 0; // 지급예정일 (익월 10일) $scheduledPaymentDate = SalesCommission::calculateScheduledPaymentDate($paymentDateCarbon); @@ -147,6 +162,9 @@ public function createCommission(int $managementId, string $paymentType, float $ 'status' => SalesCommission::STATUS_PENDING, 'partner_id' => $partner->id, 'manager_user_id' => $management->manager_user_id, + 'referrer_partner_id' => $referrerId, + 'referrer_rate' => $referrerRate, + 'referrer_commission' => $referrerCommission, ]); // 상품별 상세 내역 생성 diff --git a/resources/views/sales/dashboard/partials/commission-by-role.blade.php b/resources/views/sales/dashboard/partials/commission-by-role.blade.php index 53e907f0..85fa6160 100644 --- a/resources/views/sales/dashboard/partials/commission-by-role.blade.php +++ b/resources/views/sales/dashboard/partials/commission-by-role.blade.php @@ -12,6 +12,7 @@
@@ -20,6 +21,7 @@ @@ -28,6 +30,7 @@ {{ $role['name'] }} @@ -36,6 +39,7 @@ {{ $role['rate'] }}% @elseif(!empty($role['rate_label'])) @@ -48,6 +52,7 @@

₩{{ number_format($role['amount']) }}

@else diff --git a/resources/views/sales/managers/create.blade.php b/resources/views/sales/managers/create.blade.php index 50916ca8..e507f16c 100644 --- a/resources/views/sales/managers/create.blade.php +++ b/resources/views/sales/managers/create.blade.php @@ -198,6 +198,46 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc
+ +
+

파트너 유형

+ +
+
+ +
+ + +
+
+ +
+ + +

단체 가입 시: 단체 30%, 유치 파트너 3%, 매니저 0%

+ @error('referrer_partner_id') +

{{ $message }}

+ @enderror +
+
+
+

역할 및 조직