'integer', 'write_date' => 'date', 'issue_date' => 'date', 'send_date' => 'date', 'supply_amount' => 'integer', 'tax_amount' => 'integer', 'total_amount' => 'integer', 'tax_type' => 'integer', 'purpose_type' => 'integer', 'issue_type' => 'integer', 'is_checked' => 'boolean', 'synced_at' => 'datetime', ]; // 과세유형 상수 public const TAX_TYPE_TAXABLE = 1; // 과세 public const TAX_TYPE_ZERO_RATE = 2; // 영세 public const TAX_TYPE_EXEMPT = 3; // 면세 // 영수/청구 상수 public const PURPOSE_TYPE_RECEIPT = 1; // 영수 public const PURPOSE_TYPE_CLAIM = 2; // 청구 // 발급유형 상수 public const ISSUE_TYPE_NORMAL = 1; // 정발행 public const ISSUE_TYPE_REVERSE = 2; // 역발행 /** * 테넌트 관계 */ public function tenant(): BelongsTo { return $this->belongsTo(Tenant::class); } /** * 분개 관계 */ public function journals(): HasMany { return $this->hasMany(HometaxInvoiceJournal::class); } /** * 매출 스코프 */ public function scopeSales($query) { return $query->where('invoice_type', 'sales'); } /** * 매입 스코프 */ public function scopePurchase($query) { return $query->where('invoice_type', 'purchase'); } /** * 기간 스코프 */ public function scopePeriod($query, string $startDate, string $endDate, string $dateType = 'write') { $column = match ($dateType) { 'issue' => 'issue_date', 'send' => 'send_date', default => 'write_date', }; return $query->whereBetween($column, [$startDate, $endDate]); } /** * 거래처 검색 스코프 */ public function scopeSearchCorp($query, string $keyword, string $invoiceType = 'sales') { if (empty($keyword)) { return $query; } // 매출이면 공급받는자, 매입이면 공급자 검색 if ($invoiceType === 'sales') { return $query->where(function ($q) use ($keyword) { $q->where('invoicee_corp_name', 'like', "%{$keyword}%") ->orWhere('invoicee_corp_num', 'like', "%{$keyword}%"); }); } else { return $query->where(function ($q) use ($keyword) { $q->where('invoicer_corp_name', 'like', "%{$keyword}%") ->orWhere('invoicer_corp_num', 'like', "%{$keyword}%"); }); } } /** * 과세유형 라벨 */ public function getTaxTypeNameAttribute(): string { return match ($this->tax_type) { self::TAX_TYPE_TAXABLE => '과세', self::TAX_TYPE_ZERO_RATE => '영세', self::TAX_TYPE_EXEMPT => '면세', default => '-', }; } /** * 영수/청구 라벨 */ public function getPurposeTypeNameAttribute(): string { return match ($this->purpose_type) { self::PURPOSE_TYPE_RECEIPT => '영수', self::PURPOSE_TYPE_CLAIM => '청구', default => '-', }; } /** * 발급유형 라벨 */ public function getIssueTypeNameAttribute(): string { return match ($this->issue_type) { self::ISSUE_TYPE_NORMAL => '정발급', self::ISSUE_TYPE_REVERSE => '역발급', default => '-', }; } /** * 포맷된 공급가액 */ public function getFormattedSupplyAmountAttribute(): string { return number_format($this->supply_amount); } /** * 포맷된 세액 */ public function getFormattedTaxAmountAttribute(): string { return number_format($this->tax_amount); } /** * 포맷된 합계 */ public function getFormattedTotalAmountAttribute(): string { return number_format($this->total_amount); } /** * API 응답 데이터를 모델 데이터로 변환 */ public static function fromApiData(array $apiData, int $tenantId, string $invoiceType): array { // 작성일자 파싱 $writeDate = null; if (! empty($apiData['writeDate']) && strlen($apiData['writeDate']) >= 8) { $writeDate = substr($apiData['writeDate'], 0, 4).'-'. substr($apiData['writeDate'], 4, 2).'-'. substr($apiData['writeDate'], 6, 2); } // 발급일자 파싱 $issueDate = null; if (! empty($apiData['issueDT']) && strlen($apiData['issueDT']) >= 8) { $issueDate = substr($apiData['issueDT'], 0, 4).'-'. substr($apiData['issueDT'], 4, 2).'-'. substr($apiData['issueDT'], 6, 2); } return [ 'tenant_id' => $tenantId, 'nts_confirm_num' => $apiData['ntsConfirmNum'] ?? '', 'invoice_type' => $invoiceType, 'write_date' => $writeDate, 'issue_date' => $issueDate, 'invoicer_corp_num' => $apiData['invoicerCorpNum'] ?? '', 'invoicer_corp_name' => $apiData['invoicerCorpName'] ?? '', 'invoicer_ceo_name' => $apiData['invoicerCEOName'] ?? null, 'invoicee_corp_num' => $apiData['invoiceeCorpNum'] ?? '', 'invoicee_corp_name' => $apiData['invoiceeCorpName'] ?? '', 'invoicee_ceo_name' => $apiData['invoiceeCEOName'] ?? null, 'supply_amount' => (int) ($apiData['supplyAmount'] ?? 0), 'tax_amount' => (int) ($apiData['taxAmount'] ?? 0), 'total_amount' => (int) ($apiData['totalAmount'] ?? 0), 'tax_type' => (int) ($apiData['taxType'] ?? 1), 'purpose_type' => (int) ($apiData['purposeType'] ?? 1), 'issue_type' => 1, // 기본값: 정발행 'item_name' => $apiData['itemName'] ?? null, 'remark' => $apiData['remark'] ?? null, 'synced_at' => now(), ]; } }