174 lines
7.1 KiB
PHP
174 lines
7.1 KiB
PHP
<?= $this->extend('layout/main_layout') ?>
|
|
<?= $this->section('content') ?>
|
|
|
|
<?php
|
|
// ---- Local helper: parse DB date/datetime safely and convert to display TZ ----
|
|
if (!function_exists('parseDbDateTime')) {
|
|
/**
|
|
* @param mixed $raw String "Y-m-d H:i:s" or "Y-m-d", DateTimeInterface, or null
|
|
* @param string $sourceTz Timezone of stored value (DB). Default: UTC
|
|
* @param string|null $displayTz Target timezone (defaults to user_timezone() or configured school timezone)
|
|
* @param string|null $defaultTime If $raw is date-only, set this default time ("HH:MM:SS") or null to keep 00:00:00
|
|
*/
|
|
function parseDbDateTime($raw, string $sourceTz = 'UTC', ?string $displayTz = null, ?string $defaultTime = null): ?DateTime
|
|
{
|
|
if (!$raw) return null;
|
|
|
|
// Resolve display timezone dynamically if not provided
|
|
if ($displayTz === null || $displayTz === '') {
|
|
try {
|
|
$displayTz = (string) (config('School')->attendance['timezone'] ?? user_timezone());
|
|
} catch (Throwable $e) {
|
|
$displayTz = user_timezone();
|
|
}
|
|
}
|
|
|
|
if ($raw instanceof DateTimeInterface) {
|
|
$dt = new DateTime('@' . $raw->getTimestamp());
|
|
return $dt->setTimezone(new DateTimeZone($displayTz));
|
|
}
|
|
|
|
$s = trim((string)$raw);
|
|
if ($s === '' || preg_match('/^0{4}-0{2}-0{2}/', $s)) return null;
|
|
|
|
$srcTz = new DateTimeZone($sourceTz);
|
|
$outTz = new DateTimeZone($displayTz);
|
|
|
|
// Try full datetime first
|
|
$dt = DateTime::createFromFormat('Y-m-d H:i:s', $s, $srcTz);
|
|
if ($dt !== false) {
|
|
return $dt->setTimezone($outTz);
|
|
}
|
|
|
|
// Try date-only
|
|
$d = DateTime::createFromFormat('Y-m-d', $s, $srcTz);
|
|
if ($d !== false) {
|
|
if ($defaultTime) {
|
|
[$H, $i, $sec] = array_map('intval', explode(':', $defaultTime));
|
|
$d->setTime($H, $i, $sec);
|
|
} // else leave 00:00:00
|
|
return $d->setTimezone($outTz);
|
|
}
|
|
|
|
// Fallback parser
|
|
try {
|
|
$fallback = new DateTime($s, $srcTz);
|
|
return $fallback->setTimezone($outTz);
|
|
} catch (Throwable $e) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
?>
|
|
|
|
<div class="container my-5">
|
|
<h3 class="text-center text-success" style="font-family: Arial, sans-serif;">Invoices and Payments</h3>
|
|
</div>
|
|
|
|
<!-- School Year Filter -->
|
|
<form action="<?= base_url('/parent/invoice_payment') ?>" method="get" class="form-inline mb-3">
|
|
<div class="d-flex align-items-center justify-content-center">
|
|
<select name="school_year" id="school_year" class="form-control me-3">
|
|
<?php foreach ($schoolYears as $year): ?>
|
|
<option value="<?= esc($year['school_year']) ?>"
|
|
<?= ($selectedYear === $year['school_year']) ? 'selected' : '' ?>
|
|
<?= ($currentSchoolYear === $year['school_year']) ? 'data-current="true"' : '' ?>>
|
|
<?= esc($year['school_year']) ?><?= ($currentSchoolYear === $year['school_year']) ? ' (Current)' : '' ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
<button type="submit" class="btn btn-success">Filter</button>
|
|
</div>
|
|
</form>
|
|
|
|
<!-- Display Payment Notice / Deadline -->
|
|
<?php
|
|
$displayTz = user_timezone();
|
|
$deadline = parseDbDateTime($dueDate, 'UTC', $displayTz); // format either DATE or DATETIME safely
|
|
?>
|
|
<div class="alert alert-info mb-3 d-inline-block" style="display: inline-block; width: auto; padding: 10px;">
|
|
<ul class="mb-0">
|
|
<li>
|
|
This website release version does not have an option to pay your invoice online.
|
|
All payments this school year will be made in person on first day of school
|
|
(<strong><?= esc($deadline ? $deadline->format('m-d-Y') : 'TBD') ?></strong>).
|
|
</li>
|
|
<li>
|
|
Cash, checks and debit/credit cards are all accepted forms of payment. However, if you elect to pay in
|
|
installments, only cash and checks will be accepted.
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<!-- Invoice Table -->
|
|
<?php if (!empty($invoices)): ?>
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-bordered">
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Invoice Number</th>
|
|
<th>Issue Date</th>
|
|
<th>Due Date</th>
|
|
<th>Balance</th>
|
|
<th>Last Payment Amount</th>
|
|
<th>Last Payment Date</th>
|
|
<th>Status</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($invoices as $index => $invoice): ?>
|
|
<?php
|
|
$issueDt = parseDbDateTime($invoice['issue_date'] ?? null, 'UTC', $displayTz); // assume stored UTC
|
|
$dueDt = parseDbDateTime($invoice['due_date'] ?? null, 'UTC', $displayTz, '12:00:00'); // if date-only, set noon
|
|
$lastPay = parseDbDateTime($invoice['last_payment_date'] ?? null, 'UTC', $displayTz);
|
|
?>
|
|
|
|
<tr>
|
|
<td><?= (int)$index + 1 ?></td>
|
|
<td><?= esc($invoice['invoice_number']) ?></td>
|
|
<td><?= $issueDt ? esc($issueDt->format('m-d-Y h:i A')) : '—' ?></td>
|
|
<td><?= $dueDt ? esc($dueDt->format('m-d-Y')) : '—' ?></td>
|
|
<td>$<?= number_format((float)($invoice['balance'] ?? 0), 2) ?></td>
|
|
<td>$<?= number_format((float)($invoice['last_paid_amount'] ?? 0), 2) ?></td>
|
|
<td><?= $lastPay ? esc($lastPay->format('m-d-Y h:i A')) : '—' ?></td>
|
|
<td><?= esc($invoice['status'] ?? '') ?></td>
|
|
<td>
|
|
<a href="<?= base_url('invoice/pdf/' . (int)($invoice['id'] ?? 0)) ?>" target="_blank"
|
|
class="btn btn-info btn-sm external-link">View/Print PDF</a>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="alert alert-info mb-3 d-inline-block">
|
|
No invoice found for the selected school year.
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?= $this->endSection() ?>
|
|
|
|
<?= $this->section('scripts') ?>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const dropdown = document.getElementById('school_year');
|
|
const options = dropdown.options;
|
|
for (let i = 0; i < options.length; i++) {
|
|
if (options[i].dataset.current === "true") {
|
|
options[i].style.fontWeight = 'bold';
|
|
if (!dropdown.value) dropdown.selectedIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
function showPaymentDevelopmentMessage(event) {
|
|
event.preventDefault();
|
|
alert('The payment method is currently under development. Please contact the school administration at alrahma.isgl@gmail.com');
|
|
}
|
|
</script>
|
|
<?= $this->endSection() ?>
|