reconstruction of the project
This commit is contained in:
+107
-143
@@ -3,11 +3,18 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class Payment extends BaseModel
|
||||
{
|
||||
protected $table = 'payments';
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
/**
|
||||
* CI: useTimestamps = false because DB handles created_at/updated_at (defaults/triggers).
|
||||
* Keep OFF in Laravel too.
|
||||
*/
|
||||
public $timestamps = true;
|
||||
|
||||
protected $fillable = [
|
||||
'parent_id',
|
||||
'invoice_id',
|
||||
@@ -27,184 +34,141 @@ class Payment extends BaseModel
|
||||
'created_at',
|
||||
'updated_at',
|
||||
];
|
||||
public $timestamps = true;
|
||||
// ❗ Your DB handles created_at / updated_at with default CURRENT_TIMESTAMP
|
||||
// Remove CI4 auto timestamp management unless you modify your DB schema
|
||||
|
||||
protected $validationRules = [
|
||||
'parent_id' => 'required|integer',
|
||||
'invoice_id' => 'required|integer',
|
||||
'total_amount' => 'required|decimal',
|
||||
'paid_amount' => 'required|decimal',
|
||||
'balance' => 'required|decimal',
|
||||
'number_of_installments' => 'required|integer',
|
||||
'payment_method' => 'required|max_length[50]', // ✅ Removed 'string'
|
||||
'payment_date' => 'required|valid_date[Y-m-d H:i:s]',
|
||||
'status' => 'required|max_length[50]', // ✅ Removed 'string'
|
||||
'check_number' => 'permit_empty|max_length[100]',
|
||||
protected $casts = [
|
||||
'parent_id' => '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',
|
||||
];
|
||||
|
||||
protected $validationMessages = [
|
||||
'parent_id' => [
|
||||
'required' => 'Parent ID is required.',
|
||||
'integer' => 'Parent ID must be an integer.',
|
||||
],
|
||||
'invoice_id' => [
|
||||
'required' => 'Invoice ID is required.',
|
||||
'integer' => 'Invoice ID must be an integer.',
|
||||
],
|
||||
'total_amount' => [
|
||||
'required' => 'Total amount is required.',
|
||||
'decimal' => 'Total amount must be a valid decimal.',
|
||||
],
|
||||
'paid_amount' => [
|
||||
'required' => 'Paid amount is required.',
|
||||
'decimal' => 'Paid amount must be a valid decimal.',
|
||||
],
|
||||
'balance' => [
|
||||
'required' => 'Balance is required.',
|
||||
'decimal' => 'Balance must be a valid decimal.',
|
||||
],
|
||||
'payment_date' => [
|
||||
'required' => 'Payment date is required.',
|
||||
'valid_date' => 'Payment date must be a valid date (Y-m-d H:i:s).',
|
||||
],
|
||||
'payment_method' => [
|
||||
'required' => 'Payment method is required.',
|
||||
'max_length' => 'Payment method must not exceed 50 characters.',
|
||||
],
|
||||
'status' => [
|
||||
'required' => 'Status is required.',
|
||||
'max_length' => 'Status must not exceed 50 characters.',
|
||||
],
|
||||
'number_of_installments' => [
|
||||
'required' => 'Number of installments is required.',
|
||||
'integer' => 'Number of installments must be an integer.',
|
||||
],
|
||||
'check_number' => [
|
||||
'max_length' => 'Check number must not exceed 100 characters.',
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* Get payments by parent ID.
|
||||
*
|
||||
* @param int $parentId
|
||||
* @return array
|
||||
*/
|
||||
public function getPaymentsByParentId(int $parentId): array
|
||||
/* Optional relationships */
|
||||
public function invoice()
|
||||
{
|
||||
return $this->where('parent_id', $parentId)
|
||||
->orderBy('payment_date', 'DESC')
|
||||
->findAll();
|
||||
return $this->belongsTo(Invoice::class, 'invoice_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the total paid amount for a specific parent.
|
||||
*
|
||||
* @param int $parentId
|
||||
* @return float
|
||||
*/
|
||||
public function getTotalPaidByParentId(int $parentId, string $schoolYear): float
|
||||
public function parent()
|
||||
{
|
||||
$db = \Config\Database::connect();
|
||||
return $this->belongsTo(User::class, 'parent_id');
|
||||
}
|
||||
|
||||
$result = $db->table('payments')
|
||||
->selectSum('paid_amount', 'total_paid')
|
||||
/* =========================
|
||||
* CI 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()
|
||||
->getRowArray();
|
||||
|
||||
return $result && isset($result['total_paid']) ? (float) $result['total_paid'] : 0.00;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update balance for a specific payment.
|
||||
*
|
||||
* @param int $paymentId
|
||||
* @param float $amountPaid
|
||||
* @return bool
|
||||
*/
|
||||
public function updateBalance(int $paymentId, float $amountPaid): bool
|
||||
{
|
||||
$payment = $this->find($paymentId);
|
||||
if (!$payment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$newBalance = $payment['balance'] - $amountPaid;
|
||||
|
||||
return $this->update($paymentId, [
|
||||
'paid_amount' => $payment['paid_amount'] + $amountPaid,
|
||||
'balance' => $newBalance
|
||||
]);
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get payments by school year.
|
||||
*
|
||||
* @param string $schoolYear
|
||||
* @return array
|
||||
* Generate a new transaction ID: YYYY-000001
|
||||
*/
|
||||
public function getPaymentsByYear(string $schoolYear): array
|
||||
{
|
||||
return $this->where('school_year', $schoolYear)
|
||||
->orderBy('payment_date', 'DESC')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new transaction ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateNewTransactionId(): string
|
||||
public static function generateNewTransactionId(): string
|
||||
{
|
||||
$currentYear = date('Y');
|
||||
$prefix = $currentYear . '-';
|
||||
|
||||
$latest = $this->like('transaction_id', $prefix, 'after')
|
||||
->orderBy('transaction_id', 'DESC')
|
||||
$latest = static::query()
|
||||
->where('transaction_id', 'like', $prefix . '%')
|
||||
->orderByDesc('transaction_id')
|
||||
->first();
|
||||
|
||||
if ($latest && isset($latest['transaction_id'])) {
|
||||
$lastNumber = (int) str_replace($prefix, '', $latest['transaction_id']);
|
||||
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($newNumber, 6, '0', STR_PAD_LEFT);
|
||||
return $prefix . str_pad((string)$newNumber, 6, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
public function getPaymentsByInvoice($invoiceIds): array
|
||||
/**
|
||||
* Get latest payment (date/amount/status) per invoice for given invoice IDs.
|
||||
* Matches your CI logic: statuses in ['Full','Paid','Partially Paid'].
|
||||
*/
|
||||
public static function getPaymentsByInvoice($invoiceIds): array
|
||||
{
|
||||
// Normalize input
|
||||
if (!is_array($invoiceIds)) {
|
||||
$invoiceIds = [$invoiceIds];
|
||||
}
|
||||
if (empty($invoiceIds)) {
|
||||
return [];
|
||||
}
|
||||
$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 = $this->db->table('payments')
|
||||
->select('invoice_id, MAX(payment_date) AS max_date')
|
||||
->whereIn('status', ['Full', 'Partially Paid'])
|
||||
->groupBy('invoice_id')
|
||||
->getCompiledSelect();
|
||||
$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 = $this->db->table('payments p')
|
||||
->select('p.invoice_id, p.paid_amount AS last_paid_amount, p.payment_date AS last_payment_date, p.status AS last_payment_status')
|
||||
->join("($latestSub) latest", 'latest.invoice_id = p.invoice_id AND latest.max_date = p.payment_date', 'inner', false) // <-- escape=false
|
||||
$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', 'Partially Paid'])
|
||||
->get()
|
||||
->getResultArray();
|
||||
->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;
|
||||
return $rows->map(fn ($r) => (array) $r)->all();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user