refactor:영업파트너 고객관리 테이블 정리 + 정산관리에 수당지급일 관리 이동

- 고객관리 테이블: 1차수당/2차수당/매니저수당/협업지원금/등록일 열 제거, 계약일 열 추가 (16열→12열)
- 정산관리 수당정산 테이블: 수당지급일/매니저지급일/협업지원금 인라인 편집 컬럼 추가
- SalesCommissionController에 updateCommissionDate 메서드 추가
- 불필요한 JS 함수 제거 (saveReferrerCommission, checkHandoverStatus)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-02-19 16:20:42 +09:00
parent e7ab389023
commit 4ddc1efa34
7 changed files with 96 additions and 133 deletions

View File

@@ -253,6 +253,29 @@ public function cancel(int $id): JsonResponse
}
}
/**
* 수당지급일/협업지원금 인라인 수정
*/
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);
}
$commission->update([$validated['field'] => $validated['value'] ?: null]);
return response()->json(['success' => true]);
}
/**
* 정산 테이블 부분 새로고침 (HTMX)
*/

View File

@@ -191,6 +191,7 @@ private function getIndexData(Request $request): array
$prospect->hq_status = $management?->hq_status ?? 'pending';
$prospect->hq_status_label = $management?->hq_status_label ?? '대기';
$prospect->manager_user = $management?->manager;
$prospect->contracted_at = $management?->contracted_at;
$prospect->commission = $this->loadMergedCommission($management);
@@ -237,6 +238,7 @@ private function getIndexData(Request $request): array
$prospect->hq_status = $management?->hq_status ?? 'pending';
$prospect->hq_status_label = $management?->hq_status_label ?? '대기';
$prospect->manager_user = $management?->manager;
$prospect->contracted_at = $management?->contracted_at;
// 수당 정보 (management가 있는 경우)
$prospect->commission = $this->loadMergedCommission($management);

View File

@@ -362,6 +362,28 @@ function cancelCommission(id) {
});
}
function saveSettlementDate(commissionId, field, value) {
fetch(`{{ url('finance/sales-commissions') }}/${commissionId}/update-date`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Accept': 'application/json'
},
body: JSON.stringify({ field: field, value: value })
})
.then(r => r.json())
.then(data => {
if (!data.success) {
alert(data.message || '저장에 실패했습니다.');
}
})
.catch(error => {
console.error('Error:', error);
alert('저장 중 오류가 발생했습니다.');
});
}
function onTenantSelect(managementId) {
if (!managementId) return;
htmx.ajax('GET', '{{ route("finance.sales-commissions.payment-form") }}?management_id=' + managementId, {

View File

@@ -19,6 +19,9 @@
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">유치파트너</th>
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">유치수당</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">지급예정일</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">수당지급일</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">매니저지급일</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">협업지원금</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">상태</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">액션</th>
</tr>
@@ -179,6 +182,41 @@ class="commission-checkbox rounded border-gray-300 text-emerald-600 focus:ring-e
<td class="px-4 py-3 text-center text-sm text-gray-500">
{{ $commission->scheduled_payment_date->format('Y-m-d') }}
</td>
{{-- 수당지급일 (deposit→first_partner_paid_at, balance→second_partner_paid_at) --}}
@php
$paidField = $commission->payment_type === 'deposit' ? 'first_partner_paid_at' : 'second_partner_paid_at';
$paidValue = $commission->$paidField?->format('Y-m-d');
@endphp
<td class="px-1 py-2 whitespace-nowrap text-center">
<input type="date"
class="w-28 h-7 text-xs px-1 border border-gray-300 rounded cursor-pointer hover:border-blue-400 focus:outline-none focus:border-blue-500 {{ $paidValue ? 'text-blue-600 font-medium bg-blue-50 border-blue-400' : 'text-gray-500 bg-gray-50' }}"
value="{{ $paidValue }}"
onchange="saveSettlementDate({{ $commission->id }}, '{{ $paidField }}', this.value)">
</td>
{{-- 매니저지급일 --}}
<td class="px-1 py-2 whitespace-nowrap text-center">
@if($isGroup)
<span class="text-xs text-gray-400">-</span>
@else
<input type="date"
class="w-28 h-7 text-xs px-1 border border-gray-300 rounded cursor-pointer hover:border-purple-400 focus:outline-none focus:border-purple-500 {{ $commission->manager_paid_at ? 'text-purple-600 font-medium bg-purple-50 border-purple-400' : 'text-gray-500 bg-gray-50' }}"
value="{{ $commission->manager_paid_at?->format('Y-m-d') }}"
onchange="saveSettlementDate({{ $commission->id }}, 'manager_paid_at', this.value)">
@endif
</td>
{{-- 협업지원금 --}}
<td class="px-1 py-2 whitespace-nowrap text-center">
@if(!$isGroup)
<input type="number"
class="w-20 h-7 text-xs px-1 border border-gray-300 rounded cursor-pointer hover:border-orange-400 focus:outline-none focus:border-orange-500 {{ ($commission->referrer_commission ?? 0) > 0 ? 'text-orange-600 font-medium bg-orange-50 border-orange-400' : 'text-gray-500 bg-gray-50' }}"
value="{{ ($commission->referrer_commission ?? 0) > 0 ? intval($commission->referrer_commission) : '' }}"
placeholder="0"
min="0"
onchange="saveSettlementDate({{ $commission->id }}, 'referrer_commission', this.value)">
@else
<span class="text-xs text-gray-400">-</span>
@endif
</td>
<td class="px-4 py-3 text-center">
@php
$statusColors = [
@@ -235,7 +273,7 @@ class="p-1 text-green-400 hover:text-green-600"
</tr>
@empty
<tr>
<td colspan="15" class="px-4 py-8 text-center text-gray-500">
<td colspan="18" class="px-4 py-8 text-center text-gray-500">
등록된 정산 내역이 없습니다.
</td>
</tr>

View File

@@ -336,57 +336,7 @@ function confirmDelete() {
});
}
// 협업지원금 저장
function saveReferrerCommission(prospectId, amount) {
const input = document.querySelector(`input[data-prospect-id="${prospectId}"][data-field="referrer_commission"]`);
const numAmount = parseInt(amount) || 0;
fetch(`/sales/admin-prospects/${prospectId}/referrer-commission`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
'Accept': 'application/json'
},
body: JSON.stringify({ amount: numAmount })
})
.then(response => response.json())
.then(result => {
if (result.success && input) {
if (numAmount > 0) {
input.value = result.amount;
input.className = input.className.replace(/text-gray-500 bg-gray-50/g, '').replace(/text-orange-600 font-medium bg-orange-50 border-orange-400/g, '');
input.classList.add('text-orange-600', 'font-medium', 'bg-orange-50', 'border-orange-400');
} else {
input.value = '';
input.className = input.className.replace(/text-orange-600 font-medium bg-orange-50 border-orange-400/g, '');
input.classList.add('text-gray-500', 'bg-gray-50');
}
} else if (!result.success) {
alert(result.message || '협업지원금 저장에 실패했습니다.');
}
})
.catch(error => {
console.error('Error:', error);
alert('협업지원금 저장 중 오류가 발생했습니다.');
});
}
// 인계 상태 체크 (수당지급 필드만)
const commissionPaidFields = ['first_partner_paid_at', 'second_partner_paid_at', 'manager_paid_at'];
function checkHandoverStatus(prospectId, field) {
if (!commissionPaidFields.includes(field)) return true;
const hqSelect = document.querySelector(`select[data-hq-status="${prospectId}"]`);
if (hqSelect && hqSelect.value !== 'handover') {
alert('개발상태가 인계일 때만 수당이 지급됩니다.');
return false;
}
return true;
}
// 수당 날짜 저장 (date input에서 호출)
// 납입 날짜 저장 (date input에서 호출)
function saveCommissionDate(prospectId, field, date) {
const input = document.querySelector(`input[data-prospect-id="${prospectId}"][data-field="${field}"]`);
@@ -396,12 +346,6 @@ function saveCommissionDate(prospectId, field, date) {
return;
}
// 수당지급일 입력 시 인계 상태 체크
if (!checkHandoverStatus(prospectId, field)) {
input.value = '';
return;
}
fetch(`/sales/admin-prospects/${prospectId}/commission-date`, {
method: 'POST',
headers: {
@@ -474,22 +418,9 @@ function updateInputStyle(input, field, hasValue) {
input.className = 'commission-date-input w-24 text-xs px-1 py-1 border border-gray-200 rounded text-center focus:outline-none focus:ring-1';
if (hasValue) {
if (field === 'first_payment_at' || field === 'second_payment_at') {
input.className += ' text-emerald-600 font-medium bg-emerald-50 focus:ring-emerald-500 focus:border-emerald-500';
} else if (field === 'first_partner_paid_at' || field === 'second_partner_paid_at') {
input.className += ' text-blue-600 font-medium bg-blue-50 focus:ring-blue-500 focus:border-blue-500';
} else if (field === 'manager_paid_at') {
input.className += ' text-purple-600 font-medium bg-purple-50 focus:ring-purple-500 focus:border-purple-500';
}
input.className += ' text-emerald-600 font-medium bg-emerald-50 focus:ring-emerald-500 focus:border-emerald-500';
} else {
input.className += ' text-gray-400 bg-white';
if (field === 'first_payment_at' || field === 'second_payment_at') {
input.className += ' focus:ring-emerald-500 focus:border-emerald-500';
} else if (field === 'first_partner_paid_at' || field === 'second_partner_paid_at') {
input.className += ' focus:ring-blue-500 focus:border-blue-500';
} else if (field === 'manager_paid_at') {
input.className += ' focus:ring-purple-500 focus:border-purple-500';
}
input.className += ' text-gray-400 bg-white focus:ring-emerald-500 focus:border-emerald-500';
}
}
</script>

View File

@@ -89,15 +89,11 @@ class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none foc
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">담당 매니저</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">영업 진행률</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">매니저 진행률</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">계약일</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">1 납입</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">1 수당</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">2 납입</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">2 수당</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">매니저 수당</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">협업지원금</th>
<th class="px-4 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">개발 상태</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">상태</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">등록일</th>
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">관리</th>
</tr>
</thead>
@@ -150,6 +146,10 @@ class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none foc
$isCorporate = ($prospect->partner_type ?? 'individual') === 'corporate';
$disabledClass = $commissionDisabled ? 'opacity-40 cursor-not-allowed bg-gray-100' : '';
@endphp
{{-- 계약일 --}}
<td class="px-4 py-3 whitespace-nowrap text-center text-sm text-gray-500">
{{ $prospect->contracted_at ? \Carbon\Carbon::parse($prospect->contracted_at)->format('Y-m-d') : '-' }}
</td>
{{-- 1 납입완료 --}}
<td class="px-1 py-2 whitespace-nowrap text-center">
<input type="date"
@@ -160,16 +160,6 @@ class="w-28 h-7 text-xs px-1 border-2 border-gray-300 rounded {{ $commissionDisa
{{ $commissionDisabled ? 'disabled' : '' }}
onchange="saveCommissionDate({{ $prospect->id }}, 'first_payment_at', this.value)">
</td>
{{-- 1 파트너 수당지급 --}}
<td class="px-1 py-2 whitespace-nowrap text-center">
<input type="date"
class="w-28 h-7 text-xs px-1 border-2 border-gray-300 rounded {{ $commissionDisabled ? $disabledClass : 'cursor-pointer hover:border-blue-400' }} focus:outline-none focus:border-blue-500 {{ !$commissionDisabled && $prospect->commission?->first_partner_paid_at ? 'text-blue-600 font-medium bg-blue-50 border-blue-400' : (!$commissionDisabled ? 'text-gray-500 bg-gray-50' : '') }}"
value="{{ $prospect->commission?->first_partner_paid_at?->format('Y-m-d') }}"
data-prospect-id="{{ $prospect->id }}"
data-field="first_partner_paid_at"
{{ $commissionDisabled ? 'disabled' : '' }}
onchange="saveCommissionDate({{ $prospect->id }}, 'first_partner_paid_at', this.value)">
</td>
{{-- 2 납입완료 --}}
<td class="px-1 py-2 whitespace-nowrap text-center">
<input type="date"
@@ -180,47 +170,6 @@ class="w-28 h-7 text-xs px-1 border-2 border-gray-300 rounded {{ $commissionDisa
{{ $commissionDisabled ? 'disabled' : '' }}
onchange="saveCommissionDate({{ $prospect->id }}, 'second_payment_at', this.value)">
</td>
{{-- 2 파트너 수당지급 --}}
<td class="px-1 py-2 whitespace-nowrap text-center">
<input type="date"
class="w-28 h-7 text-xs px-1 border-2 border-gray-300 rounded {{ $commissionDisabled ? $disabledClass : 'cursor-pointer hover:border-blue-400' }} focus:outline-none focus:border-blue-500 {{ !$commissionDisabled && $prospect->commission?->second_partner_paid_at ? 'text-blue-600 font-medium bg-blue-50 border-blue-400' : (!$commissionDisabled ? 'text-gray-500 bg-gray-50' : '') }}"
value="{{ $prospect->commission?->second_partner_paid_at?->format('Y-m-d') }}"
data-prospect-id="{{ $prospect->id }}"
data-field="second_partner_paid_at"
{{ $commissionDisabled ? 'disabled' : '' }}
onchange="saveCommissionDate({{ $prospect->id }}, 'second_partner_paid_at', this.value)">
</td>
{{-- 매니저 수당지급 --}}
{{-- 매니저 수당지급 (단체는 해당없음) --}}
<td class="px-1 py-2 whitespace-nowrap text-center">
@if($isCorporate)
<span class="text-xs text-gray-400">-</span>
@else
<input type="date"
class="w-28 h-7 text-xs px-1 border-2 border-gray-300 rounded {{ $commissionDisabled ? $disabledClass : 'cursor-pointer hover:border-purple-400' }} focus:outline-none focus:border-purple-500 {{ !$commissionDisabled && $prospect->commission?->manager_paid_at ? 'text-purple-600 font-medium bg-purple-50 border-purple-400' : (!$commissionDisabled ? 'text-gray-500 bg-gray-50' : '') }}"
value="{{ $prospect->commission?->manager_paid_at?->format('Y-m-d') }}"
data-prospect-id="{{ $prospect->id }}"
data-field="manager_paid_at"
{{ $commissionDisabled ? 'disabled' : '' }}
onchange="saveCommissionDate({{ $prospect->id }}, 'manager_paid_at', this.value)">
@endif
</td>
{{-- 협업지원금 --}}
<td class="px-1 py-2 whitespace-nowrap text-center">
@if(($prospect->partner_type ?? 'individual') !== 'corporate')
<input type="number"
class="w-20 h-7 text-xs px-1 border-2 border-gray-300 rounded {{ $commissionDisabled ? $disabledClass : 'cursor-pointer hover:border-orange-400' }} focus:outline-none focus:border-orange-500 {{ !$commissionDisabled && ($prospect->commission?->referrer_commission ?? 0) > 0 ? 'text-orange-600 font-medium bg-orange-50 border-orange-400' : (!$commissionDisabled ? 'text-gray-500 bg-gray-50' : '') }}"
value="{{ ($prospect->commission?->referrer_commission ?? 0) > 0 ? intval($prospect->commission->referrer_commission) : '' }}"
placeholder="0"
min="0"
data-prospect-id="{{ $prospect->id }}"
data-field="referrer_commission"
{{ $commissionDisabled ? 'disabled' : '' }}
onchange="saveReferrerCommission({{ $prospect->id }}, this.value)">
@else
<span class="text-xs text-gray-400">-</span>
@endif
</td>
<td class="px-4 py-3 whitespace-nowrap text-center">
<select
data-hq-status="{{ $prospect->id }}"
@@ -250,9 +199,6 @@ class="px-2 py-1 text-xs font-medium rounded-full cursor-pointer hover:opacity-8
</span>
@endif
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
{{ $prospect->created_at->format('Y-m-d') }}
</td>
<td class="px-4 py-3 whitespace-nowrap text-right text-sm font-medium">
<div class="flex items-center justify-end gap-2">
<button type="button" onclick="openDetailModal({{ $prospect->id }})" class="text-blue-600 hover:text-blue-900">상세</button>
@@ -271,7 +217,7 @@ class="text-red-500 hover:text-red-700"
</tr>
@empty
<tr>
<td colspan="16" class="px-6 py-12 text-center text-gray-500">
<td colspan="12" class="px-6 py-12 text-center text-gray-500">
등록된 고객이 없습니다.
</td>
</tr>

View File

@@ -1025,6 +1025,7 @@
Route::post('/{id}/approve', [\App\Http\Controllers\Finance\SalesCommissionController::class, 'approve'])->name('approve');
Route::post('/{id}/mark-paid', [\App\Http\Controllers\Finance\SalesCommissionController::class, 'markPaid'])->name('mark-paid');
Route::post('/{id}/cancel', [\App\Http\Controllers\Finance\SalesCommissionController::class, 'cancel'])->name('cancel');
Route::put('/{id}/update-date', [\App\Http\Controllers\Finance\SalesCommissionController::class, 'updateCommissionDate'])->name('update-date');
});
// 기존 URL 리다이렉트 → 통합 정산관리