'integer', 'invoice_id' => 'integer', 'approved_by' => 'integer', 'updated_by' => 'integer', 'refund_amount' => 'decimal:2', 'refund_paid_amount' => 'decimal:2', 'requested_at' => 'datetime', 'approved_at' => 'datetime', 'refunded_at' => 'datetime', 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; /* ============================================================ * Status (enum-like) * ============================================================ */ public const STATUS_PENDING = 'Pending'; public const STATUS_APPROVED = 'Approved'; public const STATUS_PARTIAL = 'Partial'; public const STATUS_PAID = 'Paid'; public const STATUS_REJECTED = 'Rejected'; public const STATUS_CANCELED = 'Canceled'; public static function paidOutStatuses(): array { // matches your legacy whereIn(['Partial','Paid']) return [self::STATUS_PARTIAL, self::STATUS_PAID]; } /* ============================================================ * Relationships (optional) * ============================================================ */ public function parent(): BelongsTo { return $this->belongsTo(ParentModel::class, 'parent_id'); // rename to your real model, e.g. ParentUser/Guardian/Customer } public function invoice(): BelongsTo { return $this->belongsTo(Invoice::class, 'invoice_id'); } /* ============================================================ * Scopes * ============================================================ */ public function scopeForParent(Builder $q, int $parentId): Builder { return $q->where('parent_id', $parentId); } public function scopeForSchoolYear(Builder $q, string $schoolYear): Builder { return $q->where('school_year', $schoolYear); } public function scopePaidOut(Builder $q): Builder { return $q->whereIn('status', self::paidOutStatuses()); } /* ============================================================ * Business: totals * ============================================================ */ /** * Total paid-out refunds for a parent in a specific school year (Partial + Paid). * Mirrors the legacy behavior and returns 0.00 if none. */ public static function totalApprovedPaidOutForParentAndYear(int $parentId, string $schoolYear): float { $sum = static::query() ->forParent($parentId) ->forSchoolYear($schoolYear) ->paidOut() ->sum('refund_paid_amount'); return (float) $sum; } /** * Instance-style method if you want to keep a similar calling style to legacy. */ public function getTotalApprovedRefundByParentIdAndSchoolYear(int $parentId, string $schoolYear): float { return static::totalApprovedPaidOutForParentAndYear($parentId, $schoolYear); } /* ============================================================ * Validation rules (optional helper for controllers/FormRequests) * ============================================================ */ public static function rules(?int $id = null): array { return [ 'parent_id' => ['required', 'integer', 'min:1', 'exists:parents,id'], // adjust table name 'school_year' => ['required', 'string', 'max:20'], 'invoice_id' => ['nullable', 'integer', 'exists:invoices,id'], 'refund_amount' => ['required', 'numeric', 'min:0'], 'refund_paid_amount' => ['nullable', 'numeric', 'min:0'], 'requested_at' => ['nullable', 'date'], 'approved_at' => ['nullable', 'date', 'after_or_equal:requested_at'], 'refunded_at' => ['nullable', 'date', 'after_or_equal:approved_at'], 'status' => ['required', 'string', 'in:' . implode(',', [ self::STATUS_PENDING, self::STATUS_APPROVED, self::STATUS_PARTIAL, self::STATUS_PAID, self::STATUS_REJECTED, self::STATUS_CANCELED, ])], 'reason' => ['nullable', 'string', 'max:2000'], 'request' => ['nullable', 'string'], 'note' => ['nullable', 'string', 'max:5000'], 'semester' => ['nullable', 'string', 'max:20'], 'approved_by' => ['nullable', 'integer'], 'updated_by' => ['nullable', 'integer'], 'refund_method' => ['nullable', 'string', 'max:50'], // cash/check/cc/etc. 'check_nbr' => ['nullable', 'string', 'max:80'], 'check_file' => ['nullable', 'string', 'max:255'], ]; } }