'date:Y-m-d', 'bid_date' => 'date:Y-m-d', 'construction_start_date' => 'date:Y-m-d', 'construction_end_date' => 'date:Y-m-d', 'attendees' => 'array', 'attendee_count' => 'integer', 'site_count' => 'integer', ]; protected $attributes = [ 'briefing_type' => self::TYPE_OFFLINE, 'status' => self::STATUS_SCHEDULED, 'bid_status' => self::BID_STATUS_PENDING, 'attendance_status' => self::ATTENDANCE_SCHEDULED, 'vat_type' => self::VAT_EXCLUDED, 'attendee_count' => 0, 'site_count' => 0, ]; // ========================================================================= // 관계 정의 // ========================================================================= /** * 거래처 */ public function partner(): BelongsTo { return $this->belongsTo(Client::class, 'partner_id'); } /** * 현장 */ public function site(): BelongsTo { return $this->belongsTo(Site::class); } /** * 생성자 */ public function creator(): BelongsTo { return $this->belongsTo(User::class, 'created_by'); } /** * 수정자 */ public function updater(): BelongsTo { return $this->belongsTo(User::class, 'updated_by'); } /** * 견적들 (현장설명회에서 자동생성된 견적) */ public function quotes(): HasMany { return $this->hasMany(Quote::class); } // ========================================================================= // Accessors // ========================================================================= /** * 거래처명 (partner.name) */ public function getPartnerNameAttribute(): ?string { return $this->partner?->name; } /** * 현장명 (site.name) */ public function getSiteNameAttribute(): ?string { return $this->site?->name; } /** * API 응답에 자동 추가할 속성 */ protected $appends = ['partner_name', 'site_name']; // ========================================================================= // 스코프 // ========================================================================= /** * 상태별 필터 */ public function scopeStatus($query, string $status) { return $query->where('status', $status); } /** * 입찰상태별 필터 */ public function scopeBidStatus($query, string $bidStatus) { return $query->where('bid_status', $bidStatus); } /** * 날짜 범위 필터 */ public function scopeDateRange($query, ?string $startDate, ?string $endDate) { if ($startDate) { $query->where('briefing_date', '>=', $startDate); } if ($endDate) { $query->where('briefing_date', '<=', $endDate); } return $query; } // ========================================================================= // 헬퍼 메서드 // ========================================================================= /** * 현설번호 생성 * 형식: SB-YYYYMM-XXXX (예: SB-202601-0001) */ public static function generateBriefingCode(int $tenantId): string { $prefix = 'SB'; $yearMonth = now()->format('Ym'); // 해당 월의 마지막 번호 조회 $lastCode = self::withoutGlobalScopes() ->where('tenant_id', $tenantId) ->where('briefing_code', 'like', "{$prefix}-{$yearMonth}-%") ->orderBy('briefing_code', 'desc') ->value('briefing_code'); if ($lastCode) { // 기존 번호에서 시퀀스 추출 $lastSequence = (int) substr($lastCode, -4); $newSequence = $lastSequence + 1; } else { $newSequence = 1; } return sprintf('%s-%s-%04d', $prefix, $yearMonth, $newSequence); } }