Files
sam-manage/app/Models/Sales/SalesCommission.php
김보곤 8291cdc39b feat: [database] codebridge DB 분리 - 118개 MNG 전용 테이블 connection 설정
- config/database.php에 codebridge connection 추가
- 78개 MNG 전용 모델에 $connection = 'codebridge' 설정
  - Admin (15): PM, 로드맵, API Explorer
  - Sales (16): 영업파트너, 수수료, 가망고객
  - Finance (9): 법인카드, 자금관리, 홈택스
  - Barobill (12): 은행/카드 동기화 관리
  - Interview (1), ESign (6), Equipment (2)
  - AI (3), Audit (3), 기타 (11)
2026-03-07 11:31:27 +09:00

546 lines
14 KiB
PHP

<?php
namespace App\Models\Sales;
use App\Models\Tenants\Tenant;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* 영업수수료 정산 모델
*
* @property int $id
* @property int $tenant_id
* @property int $management_id
* @property string $payment_type
* @property float $payment_amount
* @property string $payment_date
* @property string|null $first_payment_at 1차 납입완료일
* @property string|null $first_partner_paid_at 1차 파트너 수당지급일
* @property string|null $second_payment_at 2차 납입완료일
* @property string|null $second_partner_paid_at 2차 파트너 수당지급일
* @property string|null $first_subscription_at 첫 구독료 입금일 (매니저 수당 기준)
* @property string|null $manager_paid_at 매니저 수당지급일
* @property float $base_amount
* @property float $partner_rate
* @property float $manager_rate
* @property float $partner_commission
* @property float $manager_commission
* @property string $scheduled_payment_date
* @property string $status
* @property string|null $actual_payment_date
* @property int $partner_id
* @property int|null $manager_user_id
* @property string|null $notes
* @property string|null $bank_reference
* @property int|null $approved_by
* @property \Carbon\Carbon|null $approved_at
*/
class SalesCommission extends Model
{
use SoftDeletes;
protected $connection = 'codebridge';
protected $table = 'sales_commissions';
/**
* 상태 상수
*/
const STATUS_PENDING = 'pending';
const STATUS_APPROVED = 'approved';
const STATUS_PAID = 'paid';
const STATUS_CANCELLED = 'cancelled';
/**
* 입금 구분 상수
*/
const PAYMENT_DEPOSIT = 'deposit';
const PAYMENT_BALANCE = 'balance';
/**
* 상태 라벨
*/
public static array $statusLabels = [
self::STATUS_PENDING => '대기',
self::STATUS_APPROVED => '승인',
self::STATUS_PAID => '지급완료',
self::STATUS_CANCELLED => '취소',
];
/**
* 입금 구분 라벨
*/
public static array $paymentTypeLabels = [
self::PAYMENT_DEPOSIT => '계약금',
self::PAYMENT_BALANCE => '잔금',
];
protected $fillable = [
'tenant_id',
'management_id',
'payment_type',
'payment_amount',
'payment_date',
'first_payment_at',
'first_partner_paid_at',
'second_payment_at',
'second_partner_paid_at',
'first_subscription_at',
'manager_paid_at',
'base_amount',
'partner_rate',
'manager_rate',
'partner_commission',
'manager_commission',
'scheduled_payment_date',
'status',
'actual_payment_date',
'partner_id',
'manager_user_id',
'notes',
'bank_reference',
'approved_by',
'approved_at',
'referrer_partner_id',
'referrer_rate',
'referrer_commission',
'first_referrer_paid_at',
'second_referrer_paid_at',
];
protected $casts = [
'payment_amount' => 'decimal:2',
'base_amount' => 'decimal:2',
'partner_rate' => 'decimal:2',
'manager_rate' => 'decimal:2',
'partner_commission' => 'decimal:2',
'manager_commission' => 'decimal:2',
'payment_date' => 'date',
'first_payment_at' => 'date',
'first_partner_paid_at' => 'date',
'second_payment_at' => 'date',
'second_partner_paid_at' => 'date',
'first_subscription_at' => 'date',
'manager_paid_at' => 'date',
'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',
];
/**
* 테넌트 관계
*/
public function tenant(): BelongsTo
{
return $this->belongsTo(Tenant::class);
}
/**
* 영업관리 관계
*/
public function management(): BelongsTo
{
return $this->belongsTo(SalesTenantManagement::class, 'management_id');
}
/**
* 영업파트너 관계
*/
public function partner(): BelongsTo
{
return $this->belongsTo(SalesPartner::class, 'partner_id');
}
/**
* 매니저(사용자) 관계
*/
public function manager(): BelongsTo
{
return $this->belongsTo(User::class, 'manager_user_id');
}
/**
* 상세 내역 관계
*/
public function details(): HasMany
{
return $this->hasMany(SalesCommissionDetail::class, 'commission_id');
}
/**
* 유치 영업파트너 관계
*/
public function referrerPartner(): BelongsTo
{
return $this->belongsTo(SalesPartner::class, 'referrer_partner_id');
}
/**
* 승인자 관계
*/
public function approver(): BelongsTo
{
return $this->belongsTo(User::class, 'approved_by');
}
/**
* 상태 라벨 Accessor
*/
public function getStatusLabelAttribute(): string
{
return self::$statusLabels[$this->status] ?? $this->status;
}
/**
* 입금 구분 라벨 Accessor
*/
public function getPaymentTypeLabelAttribute(): string
{
return self::$paymentTypeLabels[$this->payment_type] ?? $this->payment_type;
}
/**
* 총 수당액 Accessor
*/
public function getTotalCommissionAttribute(): float
{
return $this->partner_commission + $this->manager_commission + $this->referrer_commission;
}
/**
* 지급예정일 계산 (입금일 익월 10일)
*/
public static function calculateScheduledPaymentDate(Carbon $paymentDate): Carbon
{
return $paymentDate->copy()->addMonth()->day(10);
}
/**
* 승인 처리
*/
public function approve(int $approverId): bool
{
if ($this->status !== self::STATUS_PENDING) {
return false;
}
return $this->update([
'status' => self::STATUS_APPROVED,
'approved_by' => $approverId,
'approved_at' => now(),
]);
}
/**
* 지급완료 처리
*/
public function markAsPaid(?string $bankReference = null): bool
{
if ($this->status !== self::STATUS_APPROVED) {
return false;
}
return $this->update([
'status' => self::STATUS_PAID,
'actual_payment_date' => now()->format('Y-m-d'),
'bank_reference' => $bankReference,
]);
}
/**
* 승인취소 처리 (approved → pending)
*/
public function unapprove(): bool
{
if ($this->status !== self::STATUS_APPROVED) {
return false;
}
return $this->update([
'status' => self::STATUS_PENDING,
'approved_by' => null,
'approved_at' => null,
]);
}
/**
* 취소 처리
*/
public function cancel(): bool
{
if ($this->status === self::STATUS_PAID) {
return false;
}
return $this->update([
'status' => self::STATUS_CANCELLED,
]);
}
/**
* 대기 상태 스코프
*/
public function scopePending(Builder $query): Builder
{
return $query->where('status', self::STATUS_PENDING);
}
/**
* 승인완료 스코프
*/
public function scopeApproved(Builder $query): Builder
{
return $query->where('status', self::STATUS_APPROVED);
}
/**
* 지급완료 스코프
*/
public function scopePaid(Builder $query): Builder
{
return $query->where('status', self::STATUS_PAID);
}
/**
* 특정 영업파트너 스코프
*/
public function scopeForPartner(Builder $query, int $partnerId): Builder
{
return $query->where('partner_id', $partnerId);
}
/**
* 특정 매니저 스코프
*/
public function scopeForManager(Builder $query, int $managerUserId): Builder
{
return $query->where('manager_user_id', $managerUserId);
}
/**
* 특정 월 지급예정 스코프
*/
public function scopeForScheduledMonth(Builder $query, int $year, int $month): Builder
{
return $query->whereYear('scheduled_payment_date', $year)
->whereMonth('scheduled_payment_date', $month);
}
/**
* 특정 기간 입금 스코프
*/
public function scopePaymentDateBetween(Builder $query, string $startDate, string $endDate): Builder
{
return $query->whereBetween('payment_date', [$startDate, $endDate]);
}
// ========================================
// 2단계 수당 지급 관련 메서드
// ========================================
/**
* 1차 납입완료 처리
*/
public function recordFirstPayment(?Carbon $paymentDate = null): bool
{
return $this->update([
'first_payment_at' => $paymentDate ?? now(),
]);
}
/**
* 1차 파트너 수당 지급 처리
*/
public function recordFirstPartnerPaid(?Carbon $paidDate = null): bool
{
if (! $this->first_payment_at) {
return false; // 1차 납입이 먼저 완료되어야 함
}
return $this->update([
'first_partner_paid_at' => $paidDate ?? now(),
]);
}
/**
* 2차 납입완료 처리
*/
public function recordSecondPayment(?Carbon $paymentDate = null): bool
{
return $this->update([
'second_payment_at' => $paymentDate ?? now(),
]);
}
/**
* 2차 파트너 수당 지급 처리
*/
public function recordSecondPartnerPaid(?Carbon $paidDate = null): bool
{
if (! $this->second_payment_at) {
return false; // 2차 납입이 먼저 완료되어야 함
}
return $this->update([
'second_partner_paid_at' => $paidDate ?? now(),
]);
}
/**
* 첫 구독료 입금 기록 (매니저 수당 기준)
*/
public function recordFirstSubscription(?Carbon $subscriptionDate = null): bool
{
return $this->update([
'first_subscription_at' => $subscriptionDate ?? now(),
]);
}
/**
* 매니저 수당 지급 처리
*/
public function recordManagerPaid(?Carbon $paidDate = null): bool
{
if (! $this->first_subscription_at) {
return false; // 첫 구독료 입금이 먼저 완료되어야 함
}
return $this->update([
'manager_paid_at' => $paidDate ?? now(),
]);
}
/**
* 1차 파트너 수당 지급예정일 (1차 납입일 익월 10일)
*/
public function getFirstPartnerScheduledDateAttribute(): ?Carbon
{
if (! $this->first_payment_at) {
return null;
}
return Carbon::parse($this->first_payment_at)->addMonth()->day(10);
}
/**
* 2차 파트너 수당 지급예정일 (2차 납입일 익월 10일)
*/
public function getSecondPartnerScheduledDateAttribute(): ?Carbon
{
if (! $this->second_payment_at) {
return null;
}
return Carbon::parse($this->second_payment_at)->addMonth()->day(10);
}
/**
* 매니저 수당 지급예정일 (첫 구독료 입금일 익월 10일)
*/
public function getManagerScheduledDateAttribute(): ?Carbon
{
if (! $this->first_subscription_at) {
return null;
}
return Carbon::parse($this->first_subscription_at)->addMonth()->day(10);
}
/**
* 파트너 수당 1차 지급 완료 여부
*/
public function isFirstPartnerPaid(): bool
{
return $this->first_partner_paid_at !== null;
}
/**
* 파트너 수당 2차 지급 완료 여부
*/
public function isSecondPartnerPaid(): bool
{
return $this->second_partner_paid_at !== null;
}
/**
* 매니저 수당 지급 완료 여부
*/
public function isManagerPaid(): bool
{
return $this->manager_paid_at !== null;
}
/**
* 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
{
$basePaid = $this->isFirstPartnerPaid()
&& $this->isSecondPartnerPaid()
&& $this->isManagerPaid();
if ($this->referrer_partner_id) {
return $basePaid && $this->isFirstReferrerPaid() && $this->isSecondReferrerPaid();
}
return $basePaid;
}
}