feat: 예상 지출 자동 동기화 Observer 구현

- expected_expenses 테이블에 source_type, source_id 컬럼 추가
- PurchaseExpenseSyncObserver: 매입 → 예상 지출 동기화
- WithdrawalExpenseSyncObserver: 카드결제만 → 예상 지출 동기화
- BillExpenseSyncObserver: 발행어음만 → 예상 지출 동기화
- 생성/수정/삭제/복원/강제삭제 이벤트 모두 처리
- 조건 변경 시 자동 동기화 해제 (카드→현금, 발행→수취)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-01-23 11:06:06 +09:00
parent 73e49f2736
commit fa210b91c2
6 changed files with 515 additions and 0 deletions

View File

@@ -3,8 +3,10 @@
namespace App\Models\Tenants;
use App\Traits\BelongsToTenant;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\SoftDeletes;
class ExpectedExpense extends Model
@@ -24,6 +26,8 @@ class ExpectedExpense extends Model
'payment_status',
'approval_status',
'description',
'source_type',
'source_id',
'created_by',
'updated_by',
'deleted_by',
@@ -42,6 +46,8 @@ class ExpectedExpense extends Model
*/
public const TRANSACTION_TYPES = [
'purchase' => '매입',
'card' => '카드결제',
'bill' => '발행어음',
'advance' => '선급금',
'suspense' => '가지급금',
'rent' => '임대료',
@@ -131,4 +137,29 @@ public function getApprovalStatusLabelAttribute(): string
{
return self::APPROVAL_STATUSES[$this->approval_status] ?? $this->approval_status;
}
/**
* 원본 소스 관계 (Polymorphic)
*/
public function source(): MorphTo
{
return $this->morphTo();
}
/**
* 원본 소스로 검색
*/
public function scopeBySource(Builder $query, string $sourceType, int $sourceId): Builder
{
return $query->where('source_type', $sourceType)
->where('source_id', $sourceId);
}
/**
* 동기화된 레코드 여부
*/
public function isSynced(): bool
{
return ! is_null($this->source_type) && ! is_null($this->source_id);
}
}