- API 사용 테이블 22개(23개 모델) 제외하고 55개 모델만 $connection = 'codebridge' 적용 - config/database.php에 codebridge connection 재추가 - 제외 대상: Barobill 12개, ESign 4개, Audit 2개, DevTools 1개, System 2개, HR 1개
290 lines
6.4 KiB
PHP
290 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace App\Models\Sales;
|
|
|
|
use App\Models\Tenants\Tenant;
|
|
use App\Models\User;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
/**
|
|
* 영업파트너 영업권(명함등록) 모델
|
|
*/
|
|
class TenantProspect extends Model
|
|
{
|
|
use SoftDeletes;
|
|
|
|
protected $connection = 'codebridge';
|
|
protected $table = 'tenant_prospects';
|
|
|
|
public const STATUS_ACTIVE = 'active'; // 영업권 유효
|
|
|
|
public const STATUS_EXPIRED = 'expired'; // 영업권 만료
|
|
|
|
public const STATUS_CONVERTED = 'converted'; // 테넌트 전환 완료
|
|
|
|
public const STATUS_COMPLETED = 'completed'; // 영업 완료
|
|
|
|
public const VALIDITY_MONTHS = 2; // 영업권 유효기간 (개월)
|
|
|
|
public const COOLDOWN_MONTHS = 1; // 재등록 대기 기간 (개월)
|
|
|
|
protected $fillable = [
|
|
'business_number',
|
|
'company_name',
|
|
'ceo_name',
|
|
'contact_phone',
|
|
'contact_email',
|
|
'address',
|
|
'registered_by',
|
|
'business_card_path',
|
|
'id_card_path',
|
|
'bankbook_path',
|
|
'status',
|
|
'registered_at',
|
|
'expires_at',
|
|
'cooldown_ends_at',
|
|
'tenant_id',
|
|
'converted_at',
|
|
'converted_by',
|
|
'memo',
|
|
];
|
|
|
|
protected $casts = [
|
|
'registered_at' => 'datetime',
|
|
'expires_at' => 'datetime',
|
|
'cooldown_ends_at' => 'datetime',
|
|
'converted_at' => 'datetime',
|
|
];
|
|
|
|
/**
|
|
* 등록한 영업파트너
|
|
*/
|
|
public function registeredBy(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'registered_by');
|
|
}
|
|
|
|
/**
|
|
* 전환된 테넌트
|
|
*/
|
|
public function tenant(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Tenant::class, 'tenant_id');
|
|
}
|
|
|
|
/**
|
|
* 전환 처리자
|
|
*/
|
|
public function convertedBy(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'converted_by');
|
|
}
|
|
|
|
/**
|
|
* 영업권 유효 여부
|
|
*/
|
|
public function isActive(): bool
|
|
{
|
|
return $this->status === self::STATUS_ACTIVE && now()->lt($this->expires_at);
|
|
}
|
|
|
|
/**
|
|
* 영업권 만료 여부
|
|
*/
|
|
public function isExpired(): bool
|
|
{
|
|
return $this->status === self::STATUS_EXPIRED || now()->gte($this->expires_at);
|
|
}
|
|
|
|
/**
|
|
* 테넌트 전환 완료 여부
|
|
*/
|
|
public function isConverted(): bool
|
|
{
|
|
return $this->status === self::STATUS_CONVERTED;
|
|
}
|
|
|
|
/**
|
|
* 영업 완료 여부
|
|
*/
|
|
public function isCompleted(): bool
|
|
{
|
|
return $this->status === self::STATUS_COMPLETED;
|
|
}
|
|
|
|
/**
|
|
* 재등록 대기 중 여부
|
|
*/
|
|
public function isInCooldown(): bool
|
|
{
|
|
return $this->isExpired() && now()->lt($this->cooldown_ends_at);
|
|
}
|
|
|
|
/**
|
|
* 재등록 가능 여부
|
|
*/
|
|
public function canReRegister(): bool
|
|
{
|
|
return $this->isExpired() && now()->gte($this->cooldown_ends_at);
|
|
}
|
|
|
|
/**
|
|
* 상태 라벨
|
|
*/
|
|
public function getStatusLabelAttribute(): string
|
|
{
|
|
if ($this->isConverted()) {
|
|
return '계약완료';
|
|
}
|
|
|
|
if ($this->isCompleted()) {
|
|
return '완료';
|
|
}
|
|
|
|
if ($this->isActive()) {
|
|
return '영업중';
|
|
}
|
|
|
|
if ($this->isInCooldown()) {
|
|
return '대기중';
|
|
}
|
|
|
|
return '만료';
|
|
}
|
|
|
|
/**
|
|
* 상태 색상 (Tailwind CSS)
|
|
*/
|
|
public function getStatusColorAttribute(): string
|
|
{
|
|
if ($this->isConverted()) {
|
|
return 'bg-green-100 text-green-800';
|
|
}
|
|
|
|
if ($this->isCompleted()) {
|
|
return 'bg-emerald-100 text-emerald-800';
|
|
}
|
|
|
|
if ($this->isActive()) {
|
|
return 'bg-blue-100 text-blue-800';
|
|
}
|
|
|
|
if ($this->isInCooldown()) {
|
|
return 'bg-yellow-100 text-yellow-800';
|
|
}
|
|
|
|
return 'bg-gray-100 text-gray-800';
|
|
}
|
|
|
|
/**
|
|
* 남은 일수
|
|
*/
|
|
public function getRemainingDaysAttribute(): int
|
|
{
|
|
if (! $this->isActive()) {
|
|
return 0;
|
|
}
|
|
|
|
return max(0, now()->diffInDays($this->expires_at, false));
|
|
}
|
|
|
|
/**
|
|
* 명함 이미지 URL
|
|
*/
|
|
public function getBusinessCardUrlAttribute(): ?string
|
|
{
|
|
if (! $this->business_card_path) {
|
|
return null;
|
|
}
|
|
|
|
return Storage::disk('tenant')->url($this->business_card_path);
|
|
}
|
|
|
|
/**
|
|
* 명함 이미지 존재 여부
|
|
*/
|
|
public function hasBusinessCard(): bool
|
|
{
|
|
return $this->business_card_path && Storage::disk('tenant')->exists($this->business_card_path);
|
|
}
|
|
|
|
/**
|
|
* 신분증 이미지 존재 여부
|
|
*/
|
|
public function hasIdCard(): bool
|
|
{
|
|
return $this->id_card_path && Storage::disk('tenant')->exists($this->id_card_path);
|
|
}
|
|
|
|
/**
|
|
* 신분증 이미지 URL
|
|
*/
|
|
public function getIdCardUrlAttribute(): ?string
|
|
{
|
|
if (! $this->id_card_path) {
|
|
return null;
|
|
}
|
|
|
|
return Storage::disk('tenant')->url($this->id_card_path);
|
|
}
|
|
|
|
/**
|
|
* 통장사본 이미지 존재 여부
|
|
*/
|
|
public function hasBankbook(): bool
|
|
{
|
|
return $this->bankbook_path && Storage::disk('tenant')->exists($this->bankbook_path);
|
|
}
|
|
|
|
/**
|
|
* 통장사본 이미지 URL
|
|
*/
|
|
public function getBankbookUrlAttribute(): ?string
|
|
{
|
|
if (! $this->bankbook_path) {
|
|
return null;
|
|
}
|
|
|
|
return Storage::disk('tenant')->url($this->bankbook_path);
|
|
}
|
|
|
|
/**
|
|
* 스코프: 유효한 영업권
|
|
*/
|
|
public function scopeActive($query)
|
|
{
|
|
return $query->where('status', self::STATUS_ACTIVE)
|
|
->where('expires_at', '>', now());
|
|
}
|
|
|
|
/**
|
|
* 스코프: 만료된 영업권
|
|
*/
|
|
public function scopeExpired($query)
|
|
{
|
|
return $query->where(function ($q) {
|
|
$q->where('status', self::STATUS_EXPIRED)
|
|
->orWhere('expires_at', '<=', now());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 스코프: 전환 완료
|
|
*/
|
|
public function scopeConverted($query)
|
|
{
|
|
return $query->where('status', self::STATUS_CONVERTED);
|
|
}
|
|
|
|
/**
|
|
* 스코프: 특정 영업파트너의 영업권
|
|
*/
|
|
public function scopeByPartner($query, int $userId)
|
|
{
|
|
return $query->where('registered_by', $userId);
|
|
}
|
|
}
|