- 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)
546 lines
14 KiB
PHP
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;
|
|
}
|
|
}
|