Files
root e13df69885
API CI/CD / Validate (composer + pint) (push) Successful in 3m6s
API CI/CD / Test (PHPUnit) (push) Failing after 4m53s
API CI/CD / Build frontend assets (push) Successful in 1m2s
API CI/CD / Security audit (push) Failing after 59s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
fix unittests issues
2026-07-07 20:56:32 -04:00

161 lines
5.3 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Refund extends BaseModel
{
protected $table = 'refunds';
protected $fillable = ['parent_id', 'school_year', 'invoice_id', 'refund_amount', 'requested_at', 'approved_at', 'refunded_at', 'status', 'reason', 'refund_paid_amount', 'request', 'note', 'approved_by', 'updated_by', 'refund_method', 'check_nbr', 'check_file', 'created_at', 'updated_at', 'semester'];
public $timestamps = true;
protected $casts = [
'parent_id' => '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'],
];
}
}