'integer', 'invoice_id' => 'integer', 'number_of_installments' => 'integer', 'updated_by' => 'integer', 'total_amount' => 'decimal:2', 'paid_amount' => 'decimal:2', 'balance' => 'decimal:2', 'payment_date' => 'datetime', ]; /* Optional relationships */ public function invoice() { return $this->belongsTo(Invoice::class, 'invoice_id'); } public function parent() { return $this->belongsTo(User::class, 'parent_id'); } /* ========================= * legacy method equivalents * ========================= */ public static function getPaymentsByParentId(int $parentId): array { return static::query() ->where('parent_id', $parentId) ->orderByDesc('payment_date') ->get() ->toArray(); } public static function getTotalPaidByParentId(int $parentId, string $schoolYear): float { $total = DB::table('payments') ->where('parent_id', $parentId) ->where('school_year', $schoolYear) ->sum('paid_amount'); return (float) $total; } public static function updateBalance(int $paymentId, float $amountPaid): bool { /** @var self|null $payment */ $payment = static::query()->find($paymentId); if (! $payment) { return false; } $newBalance = (float) $payment->balance - (float) $amountPaid; // keep precision stable $paidAmount = (float) $payment->paid_amount + (float) $amountPaid; $affected = static::query() ->whereKey($paymentId) ->update([ 'paid_amount' => $paidAmount, 'balance' => $newBalance, ]); return $affected > 0; } public static function getPaymentsByYear(string $schoolYear): array { return static::query() ->where('school_year', $schoolYear) ->orderByDesc('payment_date') ->get() ->toArray(); } /** * Generate a new transaction ID: YYYY-000001 */ public static function generateNewTransactionId(): string { $currentYear = date('Y'); $prefix = $currentYear.'-'; $latest = static::query() ->where('transaction_id', 'like', $prefix.'%') ->orderByDesc('transaction_id') ->first(); if ($latest && ! empty($latest->transaction_id)) { $lastNumber = (int) str_replace($prefix, '', (string) $latest->transaction_id); $newNumber = $lastNumber + 1; } else { $newNumber = 1; } return $prefix.str_pad((string) $newNumber, 6, '0', STR_PAD_LEFT); } /** * Get latest payment (date/amount/status) per invoice for given invoice IDs. * Matches your legacy logic: statuses in ['Full','Paid','Partially Paid']. */ public static function getPaymentsByInvoice($invoiceIds): array { if (! is_array($invoiceIds)) { $invoiceIds = [$invoiceIds]; } $invoiceIds = array_values(array_unique(array_map('intval', array_filter($invoiceIds)))); if (empty($invoiceIds)) { return []; } // Subquery: latest payment_date per invoice (Full/Partial only) $latestSub = DB::table('payments') ->selectRaw('invoice_id, MAX(payment_date) AS max_date') ->whereIn('status', ['Full', 'Paid', 'Partially Paid']) ->groupBy('invoice_id'); // Main query: join payments to the latest per invoice $rows = DB::table('payments as p') ->joinSub($latestSub, 'latest', function ($join) { $join->on('latest.invoice_id', '=', 'p.invoice_id') ->on('latest.max_date', '=', 'p.payment_date'); }) ->whereIn('p.invoice_id', $invoiceIds) ->whereIn('p.status', ['Full', 'Paid', 'Partially Paid']) ->select([ 'p.invoice_id', DB::raw('p.paid_amount AS last_paid_amount'), DB::raw('p.payment_date AS last_payment_date'), DB::raw('p.status AS last_payment_status'), ]) ->get(); return $rows->map(fn ($r) => (array) $r)->all(); } }