'임시저장', self::STATUS_ISSUED => '발행완료', self::STATUS_SENT => '국세청 전송', self::STATUS_CANCELLED => '취소', self::STATUS_FAILED => '발행실패', ]; // ========================================================================= // 모델 설정 // ========================================================================= protected $fillable = [ 'tenant_id', 'nts_confirm_num', 'invoice_type', 'issue_type', 'direction', 'supplier_corp_num', 'supplier_corp_name', 'supplier_ceo_name', 'supplier_addr', 'supplier_biz_type', 'supplier_biz_class', 'supplier_contact_id', 'buyer_corp_num', 'buyer_corp_name', 'buyer_ceo_name', 'buyer_addr', 'buyer_biz_type', 'buyer_biz_class', 'buyer_contact_id', 'issue_date', 'supply_amount', 'tax_amount', 'total_amount', 'items', 'status', 'nts_send_status', 'issued_at', 'sent_at', 'cancelled_at', 'barobill_invoice_id', 'description', 'error_message', 'reference_type', 'reference_id', 'created_by', 'updated_by', 'deleted_by', ]; protected $casts = [ 'issue_date' => 'date', 'supply_amount' => 'decimal:2', 'tax_amount' => 'decimal:2', 'total_amount' => 'decimal:2', 'items' => 'array', 'issued_at' => 'datetime', 'sent_at' => 'datetime', 'cancelled_at' => 'datetime', ]; // ========================================================================= // 관계 정의 // ========================================================================= /** * 참조 관계 (Sale 또는 Purchase) */ public function reference(): MorphTo { return $this->morphTo(__FUNCTION__, 'reference_type', 'reference_id'); } /** * 생성자 관계 */ public function creator(): BelongsTo { return $this->belongsTo(\App\Models\Members\User::class, 'created_by'); } /** * 수정자 관계 */ public function updater(): BelongsTo { return $this->belongsTo(\App\Models\Members\User::class, 'updated_by'); } // ========================================================================= // 접근자 (Accessors) // ========================================================================= /** * 상태 라벨 */ public function getStatusLabelAttribute(): string { return self::STATUS_LABELS[$this->status] ?? $this->status; } /** * 세금계산서 유형 라벨 */ public function getInvoiceTypeLabelAttribute(): string { return match ($this->invoice_type) { self::TYPE_TAX_INVOICE => '세금계산서', self::TYPE_INVOICE => '계산서', self::TYPE_MODIFIED_TAX_INVOICE => '수정세금계산서', default => $this->invoice_type, }; } /** * 발행 유형 라벨 */ public function getIssueTypeLabelAttribute(): string { return match ($this->issue_type) { self::ISSUE_TYPE_NORMAL => '정발행', self::ISSUE_TYPE_REVERSE => '역발행', self::ISSUE_TYPE_TRUSTEE => '위수탁', default => $this->issue_type, }; } /** * 방향 라벨 */ public function getDirectionLabelAttribute(): string { return $this->direction === self::DIRECTION_SALES ? '매출' : '매입'; } /** * 공급자 사업자번호 포맷 */ public function getFormattedSupplierCorpNumAttribute(): string { return $this->formatCorpNum($this->supplier_corp_num); } /** * 공급받는자 사업자번호 포맷 */ public function getFormattedBuyerCorpNumAttribute(): string { return $this->formatCorpNum($this->buyer_corp_num); } // ========================================================================= // 상태 체크 메서드 // ========================================================================= /** * 취소 가능 여부 */ public function canCancel(): bool { return in_array($this->status, [self::STATUS_ISSUED, self::STATUS_SENT]); } /** * 수정 가능 여부 */ public function canEdit(): bool { return $this->status === self::STATUS_DRAFT; } /** * 발행 완료 여부 */ public function isIssued(): bool { return in_array($this->status, [self::STATUS_ISSUED, self::STATUS_SENT]); } // ========================================================================= // 헬퍼 메서드 // ========================================================================= /** * 사업자번호 포맷팅 */ private function formatCorpNum(?string $num): string { if (! $num || strlen($num) !== 10) { return $num ?? ''; } return substr($num, 0, 3).'-'.substr($num, 3, 2).'-'.substr($num, 5); } }