fix the event print and certificate qr

This commit is contained in:
root
2026-05-21 17:00:38 -04:00
parent 3333e71c94
commit 239d35f8ff
7 changed files with 502 additions and 276 deletions
@@ -308,11 +308,11 @@ class CertificateController extends BaseController
$pdf->Image($imgDir . 'background.png', 280, 176, 291, 340);
$pdf->Image($imgDir . 'signature.png', 140, 399, 90, 90);
// ── QR code — small, bottom-right, just above the certificate number
// ── QR code — bottom-right corner: 1.5 cm from bottom, 2.5 cm from right
$qrSize = 42; // pt
if ($qrFile !== null && file_exists($qrFile)) {
$qrX = $W - 10 - $qrSize; // right-aligned with the cert number text
$qrY = $H - 14 - 3 - $qrSize; // 3 pt gap above the cert number line
$qrX = $W - 70.87 - $qrSize; // 2.5 cm from right edge
$qrY = $H - 42.52 - $qrSize; // 1.5 cm from bottom edge
$pdf->Image($qrFile, $qrX, $qrY, $qrSize, $qrSize);
}
@@ -371,12 +371,12 @@ class CertificateController extends BaseController
$pdf->SetXY(106, 492);
$pdf->Cell(168, 20, 'Signature', 0, 0, 'C');
// ── Certificate number — bottom-right, small gray
// ── Certificate number — 1.4 cm from bottom, 2.5 cm from left
if ($certNumber !== '') {
$pdf->SetFont('helvetica', '', 8);
$pdf->SetTextColor(150, 150, 150);
$pdf->SetXY(0, $H - 14);
$pdf->Cell($W - 10, 12, 'Certificate No. ' . $certNumber, 0, 0, 'R');
$pdf->SetXY(70.87, $H - 39.68);
$pdf->Cell(200, 12, 'Certificate No. ' . $certNumber, 0, 0, 'L');
$pdf->SetTextColor(0, 0, 0);
}
}
+221 -91
View File
@@ -17,9 +17,11 @@ use App\Models\PaymentModel;
use App\Models\CalendarModel;
use App\Services\EmailService;
use Config\Database;
#use ???
use App\Controllers\View\InvoiceController;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\RESTful\ResourceController;
use Dompdf\Dompdf;
use Dompdf\Options;
class EventController extends ResourceController
{
@@ -121,6 +123,194 @@ class EventController extends ResourceController
return $fallback;
}
private function loadEventChargesListingData(bool $includeParents = true): array
{
$schoolYear = $this->request->getGet('school_year') ?? $this->schoolYear;
$semester = $this->request->getGet('semester') ?? $this->semester;
$filterEventId = (int) ($this->request->getGet('event_id') ?? 0);
$filterParentId = (int) ($this->request->getGet('parent_id') ?? 0);
$parents = [];
if ($includeParents) {
$parents = $this->userModel->getParents();
usort($parents, static function (array $a, array $b): int {
$aLabel = trim(preg_replace('/\s+/', ' ', (string) ($a['firstname'] ?? '') . ' ' . (string) ($a['lastname'] ?? '')));
$bLabel = trim(preg_replace('/\s+/', ' ', (string) ($b['firstname'] ?? '') . ' ' . (string) ($b['lastname'] ?? '')));
$nameCmp = strnatcasecmp($aLabel, $bLabel);
if ($nameCmp !== 0) {
return $nameCmp;
}
return strnatcasecmp((string) ($a['school_id'] ?? ''), (string) ($b['school_id'] ?? ''));
});
}
$events = $this->eventModel->getActiveEvents($schoolYear);
$chargesBuilder = $this->eventChargesModel
->select('event_charges.*,
users.firstname AS parent_firstname, users.lastname AS parent_lastname, users.cellphone AS parent_cellphone,
students.firstname AS student_firstname, students.lastname AS student_lastname,
events.event_name, events.description AS event_description, events.amount AS event_amount')
->join('users', 'users.id = event_charges.parent_id', 'left')
->join('students', 'students.id = event_charges.student_id', 'left')
->join('events', 'events.id = event_charges.event_id', 'left')
->where('event_charges.school_year', $schoolYear)
->where('event_charges.semester', $semester);
if ($filterEventId > 0) {
$chargesBuilder->where('event_charges.event_id', $filterEventId);
}
if ($filterParentId > 0) {
$chargesBuilder->where('event_charges.parent_id', $filterParentId);
}
$charges = $chargesBuilder
->orderBy('event_charges.created_at', 'DESC')
->findAll();
foreach ($charges as &$charge) {
if (empty($charge['class_section_id']) && !empty($charge['student_id'])) {
$sections = $this->studentClassModel->getClassSectionIdsByStudentId(
(int) $charge['student_id'],
$schoolYear
);
if (!empty($sections)) {
$charge['class_section_id'] = $sections[0];
}
}
}
unset($charge);
$parentBalances = [];
try {
$balanceRows = $this->invoiceModel
->select('parent_id, COALESCE(SUM(balance),0) AS total_balance')
->where('school_year', $schoolYear)
->where('semester', $semester)
->groupBy('parent_id')
->findAll();
foreach ($balanceRows as $row) {
$parentBalances[(int) ($row['parent_id'] ?? 0)] = (float) ($row['total_balance'] ?? 0.0);
}
} catch (\Throwable $e) {
log_message('error', 'Failed to load parent balances: ' . $e->getMessage());
}
$sectionIds = array_unique(array_filter(array_column($charges, 'class_section_id')));
$classSectionNames = [];
if (!empty($sectionIds)) {
$sections = $this->classSectionModel
->select('class_section_id, class_section_name')
->whereIn('class_section_id', $sectionIds)
->findAll();
foreach ($sections as $section) {
$classSectionNames[(int) ($section['class_section_id'] ?? 0)] = $section['class_section_name'] ?? '';
}
}
return [
'charges' => $charges,
'parents' => $parents,
'events' => $events,
'school_year' => $schoolYear,
'semester' => $semester,
'filterEventId' => $filterEventId,
'filterParentId' => $filterParentId,
'parentBalances' => $parentBalances,
'classSectionNames' => $classSectionNames,
];
}
private function buildGroupedEventCharges(array $charges): array
{
$grouped = [];
foreach ($charges as $charge) {
$eventId = (int) ($charge['event_id'] ?? 0);
if (!isset($grouped[$eventId])) {
$grouped[$eventId] = [
'label' => $charge['event_name'] ?? 'N/A',
'rows' => [],
];
}
$grouped[$eventId]['rows'][] = $charge;
}
return $grouped;
}
private function applyGroupedEventChargeRowOrder(array $groupedCharges): array
{
$rowOrder = $this->request->getGet('row_order');
if (!is_array($rowOrder) || empty($rowOrder)) {
return $groupedCharges;
}
foreach ($groupedCharges as $eventId => &$group) {
$rawOrder = $rowOrder[$eventId] ?? $rowOrder[(string) $eventId] ?? null;
if (!is_string($rawOrder) || trim($rawOrder) === '') {
continue;
}
$orderedIds = array_values(array_filter(array_map(
static fn ($value): int => (int) trim((string) $value),
explode(',', $rawOrder)
)));
if (empty($orderedIds) || empty($group['rows']) || !is_array($group['rows'])) {
continue;
}
$rowsById = [];
foreach ($group['rows'] as $row) {
$rowsById[(int) ($row['id'] ?? 0)] = $row;
}
$reorderedRows = [];
foreach ($orderedIds as $chargeId) {
if (isset($rowsById[$chargeId])) {
$reorderedRows[] = $rowsById[$chargeId];
unset($rowsById[$chargeId]);
}
}
foreach ($group['rows'] as $row) {
$chargeId = (int) ($row['id'] ?? 0);
if (isset($rowsById[$chargeId])) {
$reorderedRows[] = $row;
unset($rowsById[$chargeId]);
}
}
$group['rows'] = $reorderedRows;
}
unset($group);
return $groupedCharges;
}
private function buildEventChargesPdfFilename(array $data): string
{
$label = 'event_participant_lists';
if (!empty($data['filterEventId'])) {
foreach (($data['events'] ?? []) as $event) {
if ((int) ($event['id'] ?? 0) === (int) $data['filterEventId']) {
$label = (string) ($event['event_name'] ?? $label);
break;
}
}
}
$safeLabel = trim((string) preg_replace('/[^A-Za-z0-9]+/', '_', strtolower($label)), '_');
if ($safeLabel === '') {
$safeLabel = 'event_participant_lists';
}
return $safeLabel . '_' . date('Ymd_His') . '.pdf';
}
public function index()
{
$eventModel = new EventModel();
@@ -475,85 +665,7 @@ class EventController extends ResourceController
// Optionally keep your eventShow / eventUpdate for legacy administrator event charges
public function eventShow()
{
$schoolYear = $this->request->getGet('school_year') ?? $this->schoolYear;
$semester = $this->request->getGet('semester') ?? $this->semester;
$parents = $this->userModel->getParents();
usort($parents, static function (array $a, array $b): int {
$aLabel = trim(preg_replace('/\s+/', ' ', (string) ($a['firstname'] ?? '') . ' ' . (string) ($a['lastname'] ?? '')));
$bLabel = trim(preg_replace('/\s+/', ' ', (string) ($b['firstname'] ?? '') . ' ' . (string) ($b['lastname'] ?? '')));
$nameCmp = strnatcasecmp($aLabel, $bLabel);
if ($nameCmp !== 0) {
return $nameCmp;
}
return strnatcasecmp((string) ($a['school_id'] ?? ''), (string) ($b['school_id'] ?? ''));
});
$events = $this->eventModel->getActiveEvents($schoolYear);
$filterEventId = (int) ($this->request->getGet('event_id') ?? 0);
$filterParentId = (int) ($this->request->getGet('parent_id') ?? 0);
$chargesBuilder = $this->eventChargesModel
->select('event_charges.*,
users.firstname AS parent_firstname, users.lastname AS parent_lastname, users.cellphone AS parent_cellphone,
students.firstname AS student_firstname, students.lastname AS student_lastname,
events.event_name, events.description AS event_description, events.amount AS event_amount')
->join('users', 'users.id = event_charges.parent_id', 'left')
->join('students', 'students.id = event_charges.student_id', 'left')
->join('events', 'events.id = event_charges.event_id', 'left')
->where('event_charges.school_year', $schoolYear)
->where('event_charges.semester', $semester);
if ($filterEventId > 0) {
$chargesBuilder->where('event_charges.event_id', $filterEventId);
}
$charges = $chargesBuilder
->orderBy('event_charges.created_at', 'DESC')
->findAll();
foreach ($charges as &$charge) {
if (empty($charge['class_section_id']) && !empty($charge['student_id'])) {
$sections = $this->studentClassModel->getClassSectionIdsByStudentId(
(int)$charge['student_id'],
$schoolYear
);
if (!empty($sections)) {
$charge['class_section_id'] = $sections[0];
}
}
}
unset($charge);
$parentBalances = [];
try {
$balanceRows = $this->invoiceModel
->select('parent_id, COALESCE(SUM(balance),0) AS total_balance')
->where('school_year', $schoolYear)
->where('semester', $semester)
->groupBy('parent_id')
->findAll();
foreach ($balanceRows as $row) {
$parentBalances[(int)($row['parent_id'] ?? 0)] = (float)($row['total_balance'] ?? 0.0);
}
} catch (\Throwable $e) {
log_message('error', 'Failed to load parent balances: ' . $e->getMessage());
}
$sectionIds = array_unique(array_filter(array_column($charges, 'class_section_id')));
$classSectionNames = [];
if (!empty($sectionIds)) {
$sections = $this->classSectionModel
->select('class_section_id, class_section_name')
->whereIn('class_section_id', $sectionIds)
->findAll();
foreach ($sections as $section) {
$classSectionNames[(int)($section['class_section_id'] ?? 0)] = $section['class_section_name'] ?? '';
}
}
$data = $this->loadEventChargesListingData();
$semesterOptions = (new EventChargesModel())
->select('semester')
@@ -575,19 +687,37 @@ class EventController extends ResourceController
$schoolYearOptions = [$this->schoolYear];
}
return view('administrator/events/event_charges', [
'charges' => $charges,
'parents' => $parents,
'events' => $events,
'school_year' => $schoolYear,
'semester' => $semester,
'filterEventId' => $filterEventId,
'filterParentId' => $filterParentId,
'parentBalances' => $parentBalances,
'classSectionNames' => $classSectionNames,
return view('administrator/events/event_charges', array_merge($data, [
'semesterOptions' => $semesterOptions,
'schoolYearOptions' => $schoolYearOptions,
]);
]));
}
public function eventChargesPdf(): ResponseInterface
{
$data = $this->loadEventChargesListingData(false);
$groupedCharges = $this->buildGroupedEventCharges($data['charges']);
$groupedCharges = $this->applyGroupedEventChargeRowOrder($groupedCharges);
$generatedAt = date('m-d-Y h:i A');
$html = view('administrator/events/event_charges_pdf', array_merge($data, [
'groupedCharges' => $groupedCharges,
'generatedAt' => $generatedAt,
]));
$options = new Options();
$options->set('isRemoteEnabled', true);
$options->set('defaultFont', 'DejaVu Sans');
$dompdf = new Dompdf($options);
$dompdf->loadHtml($html, 'UTF-8');
$dompdf->setPaper('A4', 'landscape');
$dompdf->render();
return $this->response
->setHeader('Content-Type', 'application/pdf')
->setHeader('Content-Disposition', 'inline; filename="' . $this->buildEventChargesPdfFilename($data) . '"')
->setBody($dompdf->output());
}