Fix semester context, attendance rosters, and billing workflows
- load global semester helpers consistently and use date-based semester defaults - fix grading and daily attendance duplicate student/section rows - keep attendance violations scoped to the current semester by default - update invoice, refund, discount, payment, and financial aid flows - add configuration cleanup migrations for duplicate calendar/semester keys - refresh parent registration/report-card and print request handling - update related models, services, views, cron notes, and test coverage
This commit is contained in:
@@ -76,7 +76,7 @@ class AdministratorController extends BaseController
|
||||
$this->classSectionModel = new ClassSectionModel();
|
||||
$this->adminNotificationSubjectModel = new AdminNotificationSubjectModel();
|
||||
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->studentClassModel = new StudentClassModel();
|
||||
$this->staffAttendanceModel = new StaffAttendanceModel();
|
||||
@@ -701,7 +701,7 @@ class AdministratorController extends BaseController
|
||||
|
||||
public function teacherSubmissionsReport()
|
||||
{
|
||||
$semester = (string)($this->configModel->getConfig('semester') ?? $this->semester ?? '');
|
||||
$semester = (string)(getSemester() ?? $this->semester ?? '');
|
||||
$schoolYear = trim((string) ($this->request->getGet('school_year') ?? ''));
|
||||
if ($schoolYear === '') {
|
||||
$schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
|
||||
@@ -1106,7 +1106,7 @@ class AdministratorController extends BaseController
|
||||
|
||||
$semesterResolver = new SemesterRangeService($this->configModel);
|
||||
$schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
|
||||
$semester = (string)($this->configModel->getConfig('semester') ?? '');
|
||||
$semester = (string)(getSemester() ?? '');
|
||||
$schoolYearForRange = $schoolYear !== '' ? $schoolYear : $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
|
||||
[$rangeStart, $rangeEnd] = $semesterResolver->getSchoolYearRange($schoolYearForRange);
|
||||
$semesterNorm = $semesterResolver->normalizeSemester($semester);
|
||||
@@ -1224,7 +1224,7 @@ class AdministratorController extends BaseController
|
||||
if (!is_array($notify)) {
|
||||
return redirect()->back()->with('info', 'Select at least one teacher to notify.');
|
||||
}
|
||||
$semester = (string)($this->configModel->getConfig('semester') ?? $this->semester ?? '');
|
||||
$semester = (string)(getSemester() ?? $this->semester ?? '');
|
||||
$missingItemsPayload = $this->request->getPost('missing_items') ?? [];
|
||||
$homeworkNotifyAll = (bool) $this->request->getPost('homework_notify_all');
|
||||
$examTerm = $this->resolveExamTermLabel($semester);
|
||||
@@ -3208,7 +3208,7 @@ class AdministratorController extends BaseController
|
||||
'currency' => 'USD',
|
||||
'refund_paid_amount' => 0.0,
|
||||
'status' => 'Pending',
|
||||
'source_type' => 'invoice_overpayment',
|
||||
'source_type' => 'tuition_withdrawal',
|
||||
'source_id' => (int)$invoice['id'],
|
||||
'requested_at' => utc_now(),
|
||||
'updated_by' => session()->get('user_id') ?? null,
|
||||
|
||||
@@ -33,7 +33,7 @@ class AssignmentController extends BaseController
|
||||
$this->teacherClassModel = new TeacherClassModel();
|
||||
$this->studentClassModel = new StudentClassModel();
|
||||
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ class AttendanceController extends Controller
|
||||
$this->calendarModel = model(CalendarModel::class);
|
||||
$this->userRoleModel = new UserRoleModel();
|
||||
$this->semesterScoreService = service('semesterScoreService');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->currentSchoolYearName();
|
||||
$this->enableAttendance = $this->configModel->getConfig('enable_attendance');
|
||||
$this->semesterRangeService = new SemesterRangeService($this->configModel);
|
||||
@@ -534,19 +534,19 @@ public function showUpdateAttendanceForm()
|
||||
// If there are no assignments yet for the requested semester (e.g., Spring roster not populated),
|
||||
// fall back to the school year only so the roster still renders and can be used for the new term.
|
||||
$classSections = $this->classSectionModel
|
||||
->select('classSection.id, classSection.class_id, classSection.class_section_id, classSection.class_section_name')
|
||||
->select('MIN(classSection.id) AS id, classSection.class_id, classSection.class_section_id, classSection.class_section_name', false)
|
||||
->join('student_class sc', 'sc.class_section_id = classSection.class_section_id', 'inner')
|
||||
->where('sc.school_year', $termYear)
|
||||
->groupBy(['classSection.id', 'classSection.class_id', 'classSection.class_section_id', 'classSection.class_section_name'])
|
||||
->groupBy(['classSection.class_id', 'classSection.class_section_id', 'classSection.class_section_name'])
|
||||
->findAll();
|
||||
|
||||
$useRosterFallback = false;
|
||||
if (empty($classSections) && $termYear !== '') {
|
||||
$classSections = $this->classSectionModel
|
||||
->select('classSection.id, classSection.class_id, classSection.class_section_id, classSection.class_section_name')
|
||||
->select('MIN(classSection.id) AS id, classSection.class_id, classSection.class_section_id, classSection.class_section_name', false)
|
||||
->join('student_class sc', 'sc.class_section_id = classSection.class_section_id', 'inner')
|
||||
->where('sc.school_year', $termYear)
|
||||
->groupBy(['classSection.id', 'classSection.class_id', 'classSection.class_section_id', 'classSection.class_section_name'])
|
||||
->groupBy(['classSection.class_id', 'classSection.class_section_id', 'classSection.class_section_name'])
|
||||
->findAll();
|
||||
$useRosterFallback = !empty($classSections);
|
||||
}
|
||||
@@ -588,9 +588,15 @@ public function showUpdateAttendanceForm()
|
||||
}
|
||||
|
||||
$hasRoster = false;
|
||||
$seenStudentIds = [];
|
||||
|
||||
foreach ($students as $sc) {
|
||||
$studentId = (int)$sc['student_id'];
|
||||
if ($studentId <= 0 || isset($seenStudentIds[$studentId])) {
|
||||
continue;
|
||||
}
|
||||
$seenStudentIds[$studentId] = true;
|
||||
|
||||
$student = $this->studentModel
|
||||
->select('id, firstname, lastname, school_id')
|
||||
->where('id', $studentId)
|
||||
|
||||
@@ -40,7 +40,7 @@ class AttendanceTrackingController extends BaseController
|
||||
$this->db = \Config\Database::connect();
|
||||
$this->notificationModel = new ParentNotificationModel();
|
||||
$this->attendanceEmailTemplateModel = new AttendanceEmailTemplateModel();
|
||||
$this->semester = (string) $this->configModel->getConfig('semester');
|
||||
$this->semester = (string) getSemester();
|
||||
$this->schoolYear = (string) $this->configModel->getConfig('school_year');
|
||||
}
|
||||
|
||||
@@ -49,7 +49,8 @@ class AttendanceTrackingController extends BaseController
|
||||
$syParam = $this->request->getGet('school_year');
|
||||
$semParam = $this->request->getGet('semester');
|
||||
$schoolYear = (is_string($syParam) && $syParam !== '') ? (string)$syParam : (string)$this->schoolYear;
|
||||
$semester = (is_string($semParam) && $semParam !== '') ? (string)$semParam : null; // semester filter disabled
|
||||
$semester = (is_string($semParam) && $semParam !== '') ? (string)$semParam : (string)$this->semester;
|
||||
$semesterWasRequested = is_string($semParam) && $semParam !== '';
|
||||
|
||||
$debugInfo = [
|
||||
'school_year_param' => $schoolYear,
|
||||
@@ -325,7 +326,8 @@ class AttendanceTrackingController extends BaseController
|
||||
'semester' => $semester,
|
||||
'student_ids' => $studentIds,
|
||||
]);
|
||||
// Second chance: detect the latest attendance_data term for these students
|
||||
// Second chance: detect the latest attendance_data term only when the user has not
|
||||
// requested a semester and the date-based current semester is unavailable.
|
||||
$latestAttendanceTerm = $this->db->table('attendance_data')
|
||||
->select('school_year, semester, date')
|
||||
->whereIn('student_id', $studentIds)
|
||||
@@ -334,7 +336,7 @@ class AttendanceTrackingController extends BaseController
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
if ($latestAttendanceTerm) {
|
||||
if ($latestAttendanceTerm && !$semesterWasRequested && trim($semester) === '') {
|
||||
$schoolYear = !empty($latestAttendanceTerm['school_year'])
|
||||
? (string)$latestAttendanceTerm['school_year']
|
||||
: $schoolYear;
|
||||
@@ -2140,7 +2142,7 @@ class AttendanceTrackingController extends BaseController
|
||||
$wrapped = $this->renderWithEmailLayout($subject, $safeHtmlBody);
|
||||
|
||||
// Resolve term (prefer controller properties if set)
|
||||
$semester = $this->semester ?? (new \App\Models\ConfigurationModel())->getConfig('semester');
|
||||
$semester = $this->semester ?? getSemester();
|
||||
$schoolYear = $this->schoolYear ?? (new \App\Models\ConfigurationModel())->getConfig('school_year');
|
||||
|
||||
try {
|
||||
|
||||
@@ -37,6 +37,7 @@ class CertificateController extends BaseController
|
||||
->join('classSection cs', 'cs.class_section_id = sc.class_section_id')
|
||||
->where('s.is_active', 1)
|
||||
->where('sc.school_year', $schoolYear)
|
||||
->groupBy('s.id, s.firstname, s.lastname, sc.class_section_id, cs.class_section_name')
|
||||
->orderBy('s.firstname', 'ASC')
|
||||
->orderBy('s.lastname', 'ASC')
|
||||
->get()
|
||||
@@ -105,10 +106,18 @@ class CertificateController extends BaseController
|
||||
// ── Build per-class buckets and stats ──────────────────────────────────
|
||||
$studentsByClass = [];
|
||||
$statsPerClass = [];
|
||||
$seenEnrollments = [];
|
||||
|
||||
foreach ($allEnrolled as $row) {
|
||||
$sid = (int)$row['student_id'];
|
||||
$csid = (int)$row['class_section_id'];
|
||||
$enrollmentKey = $csid . ':' . $sid;
|
||||
|
||||
if (isset($seenEnrollments[$enrollmentKey])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$seenEnrollments[$enrollmentKey] = true;
|
||||
|
||||
if (!isset($statsPerClass[$csid])) {
|
||||
$statsPerClass[$csid] = [
|
||||
@@ -293,7 +302,7 @@ class CertificateController extends BaseController
|
||||
->with('error', 'Please select at least one student.');
|
||||
}
|
||||
|
||||
$studentIds = array_filter(array_map('intval', $studentIds));
|
||||
$studentIds = array_values(array_unique(array_filter(array_map('intval', $studentIds))));
|
||||
$certDate = preg_replace('/[^0-9\/\-]/', '', $certDate);
|
||||
|
||||
if (empty($studentIds)) {
|
||||
@@ -661,4 +670,4 @@ class CertificateController extends BaseController
|
||||
$pdf->SetTextColor(0, 0, 0);
|
||||
$pdf->Text($x, $y, $text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class ClassController extends BaseController
|
||||
$this->configModel = new ConfigurationModel();
|
||||
|
||||
// Get the semester from the configuration table
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class ClassPreparationController extends BaseController
|
||||
$this->db = \Config\Database::connect();
|
||||
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
}
|
||||
|
||||
public function index()
|
||||
|
||||
@@ -244,7 +244,7 @@ class CompetitionScoresController extends BaseController
|
||||
{
|
||||
$userId = (int) (session()->get('user_id') ?? 0);
|
||||
$schoolYear = (string) ($this->configModel->getConfig('school_year') ?? '');
|
||||
$semester = (string) ($this->configModel->getConfig('semester') ?? '');
|
||||
$semester = (string) (getSemester() ?? '');
|
||||
$assignments = $this->teacherClassModel->getClassAssignmentsByUserId(
|
||||
$userId,
|
||||
$schoolYear,
|
||||
|
||||
@@ -45,12 +45,14 @@ class DiscountController extends BaseController
|
||||
$this->classSectionModel = new ClassSectionModel();
|
||||
$this->invoiceLedgerService = new InvoiceLedgerService();
|
||||
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->schoolYear = $this->activeSchoolYearName();
|
||||
$this->semester = getSemester();
|
||||
}
|
||||
|
||||
public function applyVoucher()
|
||||
{
|
||||
$this->schoolYear = $this->activeSchoolYearName();
|
||||
|
||||
if (strtolower($this->request->getMethod()) === 'post') {
|
||||
$voucherId = $this->request->getPost('voucher_id');
|
||||
$parentIds = $this->request->getPost('parent_ids') ?? [];
|
||||
@@ -402,7 +404,10 @@ class DiscountController extends BaseController
|
||||
unset($parent);
|
||||
|
||||
return view('discounts/apply_voucher', [
|
||||
'vouchers' => $this->voucherModel->where('is_active', 1)->findAll(),
|
||||
'vouchers' => $this->voucherModel
|
||||
->where('is_active', 1)
|
||||
->where('school_year', $this->schoolYear)
|
||||
->findAll(),
|
||||
'parents' => $parents,
|
||||
]);
|
||||
}
|
||||
@@ -464,7 +469,7 @@ class DiscountController extends BaseController
|
||||
->orWhere('enrollment_status', 'Payment pending')
|
||||
->orWhere('enrollment_status', 'Payment Pending')
|
||||
->orWhere('enrollment_status', 'PAYMENT PENDING')
|
||||
->orWhere("LOWER(TRIM(REPLACE(enrollment_status, CHAR(160), ' '))) = 'payment pending'", null, false)
|
||||
->orWhere("LOWER(TRIM(REPLACE(enrollment_status, CONVERT(0xC2A0 USING utf8mb4), ' '))) = 'payment pending'", null, false)
|
||||
->groupEnd();
|
||||
|
||||
if (!empty($paidEnrollmentIds)) {
|
||||
@@ -524,13 +529,20 @@ class DiscountController extends BaseController
|
||||
|
||||
public function listVouchers()
|
||||
{
|
||||
$this->schoolYear = $this->activeSchoolYearName();
|
||||
|
||||
$vouchers = $this->voucherModel
|
||||
->where('school_year', $this->schoolYear)
|
||||
->orderBy('code', 'ASC')
|
||||
->findAll();
|
||||
|
||||
$vouchers = $this->voucherModel->findAll();
|
||||
return view('discounts/list', ['vouchers' => $vouchers]);
|
||||
}
|
||||
|
||||
public function createVoucher()
|
||||
{
|
||||
$this->schoolYear = $this->activeSchoolYearName();
|
||||
|
||||
if (strtolower($this->request->getMethod()) === 'post') {
|
||||
|
||||
// -------- Gather & normalize inputs --------
|
||||
@@ -606,6 +618,7 @@ class DiscountController extends BaseController
|
||||
'valid_until' => $validUntil,
|
||||
'is_active' => $isActive,
|
||||
'description' => $description, // <-- NEW
|
||||
'school_year' => $this->schoolYear,
|
||||
];
|
||||
|
||||
if ($this->voucherModel->save($data)) {
|
||||
@@ -624,7 +637,11 @@ class DiscountController extends BaseController
|
||||
|
||||
public function editVoucher($id)
|
||||
{
|
||||
$voucher = $this->voucherModel->find($id);
|
||||
$this->schoolYear = $this->activeSchoolYearName();
|
||||
|
||||
$voucher = $this->voucherModel
|
||||
->where('school_year', $this->schoolYear)
|
||||
->find($id);
|
||||
|
||||
if (!$voucher) {
|
||||
return redirect()->to('discounts/list')->with('error', 'Voucher not found');
|
||||
@@ -640,6 +657,7 @@ class DiscountController extends BaseController
|
||||
'valid_from' => $this->request->getPost('valid_from') ?: null,
|
||||
'valid_until' => $this->request->getPost('valid_until') ?: null,
|
||||
'is_active' => $this->request->getPost('is_active') ? 1 : 0,
|
||||
'school_year' => $this->schoolYear,
|
||||
];
|
||||
|
||||
$this->voucherModel->save($data);
|
||||
@@ -649,6 +667,15 @@ class DiscountController extends BaseController
|
||||
return view('discounts/edit', ['voucher' => $voucher]);
|
||||
}
|
||||
|
||||
private function activeSchoolYearName(): string
|
||||
{
|
||||
try {
|
||||
return service('schoolYearContext')->active()->yearName();
|
||||
} catch (\Throwable) {
|
||||
return (string) ($this->configModel->getConfig('school_year') ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔄 Helper: Current invoice balance (school-year scoped) = total - payments - discounts - refundsPaid
|
||||
*/
|
||||
|
||||
@@ -67,7 +67,7 @@ class EventController extends ResourceController
|
||||
$this->invoiceLedgerService = new InvoiceLedgerService();
|
||||
|
||||
$this->schoolYear = $this->currentSchoolYearName();
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->categories = [
|
||||
'workshops',
|
||||
'orientations',
|
||||
|
||||
@@ -65,7 +65,7 @@ class ExamDraftController extends BaseController
|
||||
$this->db = Database::connect();
|
||||
|
||||
$this->schoolYear = (string) ($this->configModel->getConfig('school_year') ?? '');
|
||||
$this->semester = (string) ($this->configModel->getConfig('semester') ?? '');
|
||||
$this->semester = (string) (getSemester() ?? '');
|
||||
$this->hasFinalPdfColumn = $this->schemaHasColumn('exam_drafts', 'final_pdf_file');
|
||||
$this->hasIsLegacyColumn = $this->schemaHasColumn('exam_drafts', 'is_legacy');
|
||||
$this->hasAcceptanceTypeColumn = $this->schemaHasColumn('exam_drafts', 'acceptance_type');
|
||||
@@ -882,7 +882,7 @@ class ExamDraftController extends BaseController
|
||||
private function syncAcademicContext(): void
|
||||
{
|
||||
$this->schoolYear = $this->currentSchoolYearName((string) ($this->configModel->getConfig('school_year') ?? ''));
|
||||
$this->semester = (string) ($this->configModel->getConfig('semester') ?? '');
|
||||
$this->semester = (string) (getSemester() ?? '');
|
||||
}
|
||||
|
||||
private function applyExamDraftYearScope($query): void
|
||||
|
||||
@@ -26,7 +26,7 @@ class ExpenseController extends BaseController
|
||||
$this->financialAttachmentService = new FinancialAttachmentService();
|
||||
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
|
||||
// Default list of common retailors; adjust as needed
|
||||
$this->retailors = [
|
||||
@@ -96,6 +96,8 @@ class ExpenseController extends BaseController
|
||||
|
||||
public function index()
|
||||
{
|
||||
$schoolYear = (string) ($this->configModel->getConfig('school_year') ?? '');
|
||||
|
||||
$expenses = $this->expenseModel
|
||||
->select("
|
||||
expenses.*,
|
||||
@@ -104,6 +106,7 @@ class ExpenseController extends BaseController
|
||||
")
|
||||
->join('users u', 'u.id = expenses.purchased_by', 'left')
|
||||
->join('users approver', 'approver.id = expenses.approved_by', 'left')
|
||||
->where('expenses.school_year', $schoolYear)
|
||||
->orderBy('expenses.created_at', 'DESC')
|
||||
->findAll();
|
||||
|
||||
@@ -115,7 +118,10 @@ class ExpenseController extends BaseController
|
||||
return $row;
|
||||
}, $expenses);
|
||||
|
||||
return view('expenses/index', ['expenses' => $expenses]);
|
||||
return view('expenses/index', [
|
||||
'expenses' => $expenses,
|
||||
'schoolYear' => $schoolYear,
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
|
||||
@@ -42,7 +42,7 @@ class ExtraChargesController extends BaseController
|
||||
$this->additionalChargeModel = new AdditionalChargeModel();
|
||||
$this->userModel = new UserModel();
|
||||
$this->invoiceModel = new InvoiceModel();
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->invoiceLedgerService = new InvoiceLedgerService();
|
||||
$this->invoiceAdjustmentService = new InvoiceAdjustmentService($this->db);
|
||||
|
||||
@@ -27,7 +27,7 @@ class FinalController extends BaseController
|
||||
$this->studentModel = new StudentModel();
|
||||
$this->teacherClassModel = new TeacherClassModel();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->gradingLockModel = new GradingLockModel();
|
||||
$this->missingScoreOverrideModel = new MissingScoreOverrideModel();
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ class FlagController extends Controller
|
||||
$userId = session()->get('user_id');
|
||||
|
||||
// Get the semester and school year from configuration
|
||||
$semester = $configModel->getConfig('semester');
|
||||
$semester = getSemester();
|
||||
$schoolYear = $configModel->getConfig('school_year');
|
||||
|
||||
// Set the current system date and time for flag_datetime
|
||||
|
||||
@@ -67,7 +67,7 @@ class GradingController extends BaseController
|
||||
$this->configModel = new ConfigurationModel();
|
||||
$this->db = \Config\Database::connect();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->classSection = new ClassSectionModel();
|
||||
$this->attendanceCalculator = new AttendanceCalculator(
|
||||
new AttendanceRecordModel(),
|
||||
@@ -93,7 +93,7 @@ class GradingController extends BaseController
|
||||
$configModel = new ConfigurationModel();
|
||||
|
||||
$schoolYear = $configModel->getConfig('school_year');
|
||||
$semester = $configModel->getConfig('semester');
|
||||
$semester = getSemester();
|
||||
|
||||
$student = $studentModel->find($studentId);
|
||||
$scores = $scoreModel->where([
|
||||
@@ -140,7 +140,7 @@ class GradingController extends BaseController
|
||||
$studentModel = new StudentModel();
|
||||
|
||||
$schoolYear = $configModel->getConfig('school_year');
|
||||
$semester = $configModel->getConfig('semester');
|
||||
$semester = getSemester();
|
||||
|
||||
$model = $this->getModelByType($type);
|
||||
$classSectionIdInt = (int) ($classSectionId ?? 0);
|
||||
@@ -433,6 +433,7 @@ class GradingController extends BaseController
|
||||
// Build structures keyed by BUSINESS section id
|
||||
$grades = []; // class_id => [ ['class_section_id','class_section_name'], ... ]
|
||||
$studentsBySection = []; // section_id => [ students... ]
|
||||
$seenStudentsBySection = [];
|
||||
|
||||
foreach ($rows as $r) {
|
||||
$sectionId = (int) ($r['section_id'] ?? 0); // BUSINESS id
|
||||
@@ -457,6 +458,8 @@ class GradingController extends BaseController
|
||||
|
||||
$sid = (int) ($r['student_id'] ?? 0);
|
||||
if ($sid <= 0) continue;
|
||||
if (isset($seenStudentsBySection[$sectionId][$sid])) continue;
|
||||
$seenStudentsBySection[$sectionId][$sid] = true;
|
||||
|
||||
$ptapScore = $r['ss_ptap_score'] ?? null;
|
||||
$semesterScore = $r['ss_semester_score'] ?? null;
|
||||
|
||||
@@ -42,7 +42,7 @@ class HomeworkController extends BaseController
|
||||
$this->configModel = new ConfigurationModel();
|
||||
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
|
||||
// Log the service initialization
|
||||
log_message('debug', 'Initializing SemesterScoreService');
|
||||
|
||||
@@ -26,7 +26,7 @@ class HomeworkTrackingController extends BaseController
|
||||
$this->homeworkModel = new HomeworkModel();
|
||||
$this->db = \Config\Database::connect();
|
||||
|
||||
$this->semester = (string) ($this->configModel->getConfig('semester') ?? '');
|
||||
$this->semester = (string) (getSemester() ?? '');
|
||||
$this->schoolYear = (string) ($this->configModel->getConfig('school_year') ?? '');
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class InventoryController extends BaseController
|
||||
$this->teacherClassModel = new TeacherClassModel();
|
||||
$this->studentClassModel = new StudentClassModel();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->classSectionModel = new ClassSectionModel();
|
||||
$this->db = \Config\Database::connect();
|
||||
$this->teacherModel = new TeacherModel();
|
||||
|
||||
@@ -80,7 +80,7 @@ class InvoiceController extends ResourceController
|
||||
|
||||
$this->gradeFee = $this->configModel->getConfig('grade_fee');
|
||||
$this->schoolYear = $this->currentSchoolYearName();
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->dueDate = $this->configModel->getConfig('first_day_of_school')
|
||||
?: $this->configModel->getConfig('due_date');
|
||||
$this->firstStudentFee = (float) ($this->configModel->getConfig('first_student_fee') ?? 380);
|
||||
@@ -136,16 +136,9 @@ class InvoiceController extends ResourceController
|
||||
: ($invoice['updated_at'] ?? null);
|
||||
$parentData['invoice_id'] = $invoice['id'];
|
||||
|
||||
// ✅ Fetch refund amount actually PAID this year (Partial/Paid)
|
||||
$refund = $this->db->table('refunds')
|
||||
->select('COALESCE(SUM(refund_paid_amount),0) AS refund_paid_amount')
|
||||
->where('parent_id', $parent['id'])
|
||||
->where('school_year', $schoolYear)
|
||||
->whereIn('status', ['Partial','Paid'])
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
$parentData['refund_amount'] = (float)($refund['refund_paid_amount'] ?? 0.0);
|
||||
$refundSummary = $this->paidRefundSummaryForParentYear((int) $parent['id'], (string) $schoolYear);
|
||||
$parentData['refund_amount'] = $refundSummary['amount'];
|
||||
$parentData['refund_details'] = $refundSummary['details'];
|
||||
|
||||
log_message('info', "Latest invoice for parent {$parent['firstname']} {$parent['lastname']} in school year $schoolYear: Amount = {$invoice['total_amount']}, Updated at = {$invoice['updated_at']}");
|
||||
} else {
|
||||
@@ -230,6 +223,120 @@ class InvoiceController extends ResourceController
|
||||
}
|
||||
}
|
||||
|
||||
private function paidRefundSummaryForParentYear(int $parentId, string $schoolYear): array
|
||||
{
|
||||
$refund = $this->db->table('refunds')
|
||||
->select('COALESCE(SUM(refund_paid_amount),0) AS refund_paid_amount')
|
||||
->where('parent_id', $parentId)
|
||||
->where('school_year', $schoolYear)
|
||||
->whereIn('status', ['Partial', 'Paid'])
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
$details = [];
|
||||
if ($this->db->tableExists('refund_payouts')) {
|
||||
$details = $this->db->table('refund_payouts rp')
|
||||
->select('rp.amount_cents, rp.payment_method, rp.check_number, rp.processed_at, rp.created_at')
|
||||
->join('refunds r', 'r.id = rp.refund_id', 'inner')
|
||||
->where('r.parent_id', $parentId)
|
||||
->where('r.school_year', $schoolYear)
|
||||
->where('rp.payout_type', 'cash_out')
|
||||
->whereIn('rp.status', ['completed', 'processing'])
|
||||
->orderBy('COALESCE(rp.processed_at, rp.created_at)', 'DESC', false)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
$details = array_map(static function (array $row): array {
|
||||
return [
|
||||
'amount' => ((int) ($row['amount_cents'] ?? 0)) / 100,
|
||||
'date' => $row['processed_at'] ?? $row['created_at'] ?? null,
|
||||
'method' => $row['payment_method'] ?? '',
|
||||
'check_number' => $row['check_number'] ?? '',
|
||||
];
|
||||
}, $details);
|
||||
}
|
||||
|
||||
if (empty($details)) {
|
||||
$legacyRows = $this->db->table('refunds')
|
||||
->select('refund_paid_amount, refund_method, check_nbr, refunded_at, updated_at')
|
||||
->where('parent_id', $parentId)
|
||||
->where('school_year', $schoolYear)
|
||||
->whereIn('status', ['Partial', 'Paid'])
|
||||
->where('refund_paid_amount >', 0)
|
||||
->orderBy('COALESCE(refunded_at, updated_at)', 'DESC', false)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
$details = array_map(static function (array $row): array {
|
||||
return [
|
||||
'amount' => (float) ($row['refund_paid_amount'] ?? 0),
|
||||
'date' => $row['refunded_at'] ?? $row['updated_at'] ?? null,
|
||||
'method' => $row['refund_method'] ?? '',
|
||||
'check_number' => $row['check_nbr'] ?? '',
|
||||
];
|
||||
}, $legacyRows);
|
||||
}
|
||||
|
||||
return [
|
||||
'amount' => (float) ($refund['refund_paid_amount'] ?? 0.0),
|
||||
'details' => $details,
|
||||
];
|
||||
}
|
||||
|
||||
private function paidRefundDetailsForInvoice(int $invoiceId): array
|
||||
{
|
||||
$details = [];
|
||||
if ($this->db->tableExists('refund_payouts')) {
|
||||
$rows = $this->db->table('refund_payouts rp')
|
||||
->select('rp.amount_cents, rp.payment_method, rp.check_number, rp.processed_at, rp.created_at, rp.payout_type')
|
||||
->join('refunds r', 'r.id = rp.refund_id', 'inner')
|
||||
->where('r.invoice_id', $invoiceId)
|
||||
->whereIn('rp.payout_type', ['cash_out', 'reversal'])
|
||||
->where('rp.status', 'completed')
|
||||
->orderBy('COALESCE(rp.processed_at, rp.created_at)', 'ASC', false)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$amount = ((int) ($row['amount_cents'] ?? 0)) / 100;
|
||||
if (($row['payout_type'] ?? '') === 'reversal') {
|
||||
$amount *= -1;
|
||||
}
|
||||
|
||||
$details[] = [
|
||||
'amount' => $amount,
|
||||
'date' => $row['processed_at'] ?? $row['created_at'] ?? null,
|
||||
'method' => $row['payment_method'] ?? '',
|
||||
'check_number' => $row['check_number'] ?? '',
|
||||
'type' => $row['payout_type'] ?? 'cash_out',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($details)) {
|
||||
$rows = $this->db->table('refunds')
|
||||
->select('refund_paid_amount, refund_method, check_nbr, refunded_at, updated_at')
|
||||
->where('invoice_id', $invoiceId)
|
||||
->whereIn('status', ['Partial', 'Paid'])
|
||||
->where('refund_paid_amount >', 0)
|
||||
->orderBy('COALESCE(refunded_at, updated_at)', 'ASC', false)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$details[] = [
|
||||
'amount' => (float) ($row['refund_paid_amount'] ?? 0),
|
||||
'date' => $row['refunded_at'] ?? $row['updated_at'] ?? null,
|
||||
'method' => $row['refund_method'] ?? '',
|
||||
'check_number' => $row['check_nbr'] ?? '',
|
||||
'type' => 'cash_out',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $details;
|
||||
}
|
||||
|
||||
private function assertSchoolYearNameWritable(string $schoolYear): void
|
||||
{
|
||||
service('schoolYearWriteGuard')->assertWritable(
|
||||
@@ -297,14 +404,9 @@ class InvoiceController extends ResourceController
|
||||
}
|
||||
$parentData['invoice_id'] = $invoice['id'] ?? null;
|
||||
|
||||
// Refund total paid for parent/year (Partial/Paid)
|
||||
$refund = $this->db->table('refunds')
|
||||
->select('COALESCE(SUM(refund_paid_amount),0) AS refund_paid_amount')
|
||||
->where('parent_id', $parent['id'])
|
||||
->where('school_year', $schoolYear)
|
||||
->whereIn('status', ['Partial','Paid'])
|
||||
->get()->getRowArray();
|
||||
$parentData['refund_amount'] = (float)($refund['refund_paid_amount'] ?? 0.0);
|
||||
$refundSummary = $this->paidRefundSummaryForParentYear((int) $parent['id'], (string) $schoolYear);
|
||||
$parentData['refund_amount'] = $refundSummary['amount'];
|
||||
$parentData['refund_details'] = $refundSummary['details'];
|
||||
break; // only most recent as before
|
||||
}
|
||||
}
|
||||
@@ -503,15 +605,6 @@ class InvoiceController extends ResourceController
|
||||
log_message('info', "Updated invoice ID {$invoice['id']} for parent ID {$parentId}.");
|
||||
$updated = true;
|
||||
} else {
|
||||
// Generate invoice number
|
||||
$schoolId = $this->userModel->getSchoolIdByUserId($parentId);
|
||||
if (!empty($schoolId)) {
|
||||
$invoiceNumber = 'INV-' . $schoolId . '-' . uniqid();
|
||||
} else {
|
||||
log_message('warning', "No school ID found for parent_id {$parentId}, generating fallback invoice number.");
|
||||
$invoiceNumber = uniqid('INV-');
|
||||
}
|
||||
|
||||
$issueUtc = (new DateTime('now', new DateTimeZone('UTC')))->format('Y-m-d H:i:s');
|
||||
|
||||
// Due date: interpret the date in configured/user local TZ,
|
||||
@@ -527,7 +620,7 @@ class InvoiceController extends ResourceController
|
||||
try {
|
||||
$issueResult = $this->invoiceIssuanceService->issueInvoice(new IssueInvoiceCommand([
|
||||
'parent_id' => $parentId,
|
||||
'invoice_number' => $invoiceNumber,
|
||||
'invoice_number' => $this->invoiceIssuanceService->generateInvoiceNumber($schoolYear, (int)$parentId),
|
||||
'total_amount' => $totalAmount,
|
||||
'paid_amount' => 0,
|
||||
'balance' => $totalAmount,
|
||||
@@ -894,6 +987,7 @@ class InvoiceController extends ResourceController
|
||||
->findAll();
|
||||
|
||||
$refundsPaidTotal = (float) ($ledger['refund_paid_total'] ?? 0.0);
|
||||
$refundDetails = $this->paidRefundDetailsForInvoice((int) $invoiceId);
|
||||
|
||||
/* ============================================================
|
||||
* ADDITIONAL CHARGES (itemized) for this invoice
|
||||
@@ -964,6 +1058,7 @@ class InvoiceController extends ResourceController
|
||||
'additionalChargeLines' => $additionalChargeLines,
|
||||
'invoiceLines' => $invoiceLines,
|
||||
'refundsPaidTotal' => $refundsPaidTotal,
|
||||
'refundDetails' => $refundDetails,
|
||||
'ledger' => $ledger,
|
||||
];
|
||||
}
|
||||
@@ -1265,6 +1360,28 @@ class InvoiceController extends ResourceController
|
||||
$push($dt, $desc, -1 * $amt, 'discount');
|
||||
}
|
||||
|
||||
// --- Refund payouts (positive) integrated into the timeline
|
||||
foreach (($refundDetails ?? []) as $refund) {
|
||||
$amount = (float)($refund['amount'] ?? 0.0);
|
||||
if (abs($amount) < 0.00001) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$dt = $toLocal($refund['date'] ?? null, true);
|
||||
$method = trim((string)($refund['method'] ?? ''));
|
||||
$checkNumber = trim((string)($refund['check_number'] ?? ''));
|
||||
$isReversal = (string)($refund['type'] ?? 'cash_out') === 'reversal';
|
||||
$desc = $isReversal ? 'Refund reversal' : 'Refund paid';
|
||||
if ($method !== '') {
|
||||
$desc .= ' (' . $method . ')';
|
||||
}
|
||||
if ($checkNumber !== '') {
|
||||
$desc .= ' - Check #' . $checkNumber;
|
||||
}
|
||||
|
||||
$push($dt, $desc, $amount, 'refund');
|
||||
}
|
||||
|
||||
// --- Sort by exact timestamp, then by insertion sequence for stability
|
||||
usort($transactions, function ($a, $b) {
|
||||
// Different days: keep chronological by timestamp
|
||||
|
||||
@@ -56,7 +56,7 @@ class LandingPageController extends BaseController
|
||||
|
||||
// Fetch Enrollment and Refund Deadlines from Configuration
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->selectedSchoolYearName((string) ($this->schoolYear ?? ''));
|
||||
$this->lastDayOfRegistration = $this->configModel->getConfig('enrollment_deadline') ?? 'Not set';
|
||||
$this->refundDeadline = $this->configModel->getConfig('refund_deadline') ?? 'Not set';
|
||||
|
||||
@@ -22,7 +22,7 @@ class LateSlipLogsController extends BaseController
|
||||
$req = $this->request;
|
||||
|
||||
$defaultYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
|
||||
$defaultSem = (string) ($this->configModel->getConfig('semester') ?? '');
|
||||
$defaultSem = (string) (getSemester() ?? '');
|
||||
$schoolYear = trim((string) ($req->getGet('school_year') ?? $defaultYear));
|
||||
$semester = trim((string) ($req->getGet('semester') ?? $defaultSem));
|
||||
$q = trim((string) ($req->getGet('q') ?? ''));
|
||||
|
||||
@@ -36,7 +36,7 @@ class MidtermController extends BaseController
|
||||
$this->teacherClassModel = new TeacherClassModel();
|
||||
$this->semesterScoreService = service('semesterScoreService');
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->gradingLockModel = new GradingLockModel();
|
||||
$this->missingScoreOverrideModel = new MissingScoreOverrideModel();
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ class ParentAttendanceReportController extends BaseController
|
||||
|
||||
// Upper bound already enforced by $validDates building
|
||||
|
||||
$semester = (string) $this->configModel->getConfig('semester');
|
||||
$semester = (string) getSemester();
|
||||
$semesterResolver = new SemesterRangeService($this->configModel);
|
||||
|
||||
$inserted = 0;
|
||||
@@ -996,7 +996,7 @@ class ParentAttendanceReportController extends BaseController
|
||||
: (string)$this->configModel->getConfig('school_year');
|
||||
$semester = (is_string($semesterParam) && $semesterParam !== '')
|
||||
? (string)$semesterParam
|
||||
: (string)$this->configModel->getConfig('semester');
|
||||
: (string) getSemester();
|
||||
|
||||
$rows = $this->reportModel->listForDateRange($start, $end ?: null, $schoolYear, $semester);
|
||||
|
||||
@@ -1306,7 +1306,7 @@ class ParentAttendanceReportController extends BaseController
|
||||
}
|
||||
|
||||
$schoolYear = (string)$this->configModel->getConfig('school_year');
|
||||
$semester = (string)$this->configModel->getConfig('semester');
|
||||
$semester = (string) getSemester();
|
||||
|
||||
// Fetch students with their current-year class section (if any)
|
||||
try {
|
||||
@@ -1362,7 +1362,7 @@ class ParentAttendanceReportController extends BaseController
|
||||
}
|
||||
|
||||
$schoolYear = (string)$this->configModel->getConfig('school_year');
|
||||
$semester = (string)$this->configModel->getConfig('semester');
|
||||
$semester = (string) getSemester();
|
||||
|
||||
// Resolve parent_id of the student
|
||||
$stu = $this->studentModel->select('id, parent_id, firstname, lastname')->find($studentId);
|
||||
|
||||
@@ -86,7 +86,7 @@ class ParentController extends BaseController
|
||||
$this->schoolStartDate = $this->configModel->getConfig('fall_semester_start');
|
||||
$this->withdrawalDeadline = $this->configModel->getConfig('refund_deadline');
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->dateAgeReference = $this->configModel->getConfig('date_age_reference');
|
||||
$this->maxChilds = (int) $this->configModel->getConfig('max_kids') ?? 0;
|
||||
$this->maxEmergency = (int) $this->configModel->getConfig('max_emergency') ?? 0;
|
||||
@@ -548,7 +548,7 @@ class ParentController extends BaseController
|
||||
|
||||
if ($existingEnrollment['is_withdrawn'] == 1) {
|
||||
// Reactivate the enrollment if the student was previously withdrawn
|
||||
$this->enrollmentModel->where('id', $existingEnrollment['id'])->update($update);
|
||||
$this->enrollmentModel->update((int) $existingEnrollment['id'], $update);
|
||||
log_message('info', "{$studentData[$studentId]['firstname']} {$studentData[$studentId]['lastname']} (ID $studentId) has been re-enrolled in enrollment ID {$existingEnrollment['id']}.");
|
||||
// Apply promotion-based class placement for the upcoming year
|
||||
$this->applyPromotionAssignment((int)$studentId, $selectedYear, $isReturningReEnrollment);
|
||||
@@ -557,7 +557,7 @@ class ParentController extends BaseController
|
||||
if ($currentStatus === 'enrolled') {
|
||||
$update['admission_status'] = 'accepted';
|
||||
}
|
||||
$this->enrollmentModel->where('id', $existingEnrollment['id'])->update($update);
|
||||
$this->enrollmentModel->update((int) $existingEnrollment['id'], $update);
|
||||
log_message('info', "{$studentData[$studentId]['firstname']} {$studentData[$studentId]['lastname']} (ID $studentId) is already actively enrolled.");
|
||||
$this->applyPromotionAssignment((int)$studentId, $selectedYear, $isReturningReEnrollment);
|
||||
}
|
||||
@@ -642,7 +642,7 @@ class ParentController extends BaseController
|
||||
|
||||
if ($enrollment !== null) {
|
||||
// Update enrollment as withdrawn
|
||||
$this->enrollmentModel->where('id', $enrollment['id'])->update([
|
||||
$this->enrollmentModel->update((int) $enrollment['id'], [
|
||||
'withdrawal_date' => local_date(utc_now(), 'Y-m-d'),
|
||||
'enrollment_status' => 'withdraw under review', // Withdrawal needs review
|
||||
'updated_at' => utc_now()
|
||||
@@ -660,6 +660,12 @@ class ParentController extends BaseController
|
||||
|
||||
if ($invoice !== null) {
|
||||
$invoiceId = $invoice['id'];
|
||||
$studentsForRefund = $this->enrollmentModel
|
||||
->where('parent_id', $parentId)
|
||||
->where('school_year', $this->schoolYear)
|
||||
->findAll();
|
||||
$refundAmount = $refundService->calculateRefund($studentsForRefund, (int) $parentId);
|
||||
$refundCents = max(0, (int) round($refundAmount * 100));
|
||||
|
||||
$refundTable = $this->db->table('refunds');
|
||||
|
||||
@@ -667,21 +673,29 @@ class ParentController extends BaseController
|
||||
->where('parent_id', $parentId)
|
||||
->where('invoice_id', $invoiceId)
|
||||
->where('school_year', $this->schoolYear)
|
||||
->whereIn('status', ['Pending', 'Approved', 'Partial', 'pending', 'requested', 'approved', 'partial', 'partially_paid'])
|
||||
->get()
|
||||
->getRow();
|
||||
|
||||
if ($existingRefund) {
|
||||
// Only update the fields that should change
|
||||
$updateData = [
|
||||
'refund_amount' => $refundAmount,
|
||||
'requested_amount_cents' => $refundCents,
|
||||
'currency' => 'USD',
|
||||
'reason' => 'Withdrawal under review for student ID ' . $studentId,
|
||||
'note' => null,
|
||||
'request' => 'tuition',
|
||||
'source_type' => 'tuition_withdrawal',
|
||||
'source_id' => (int) $invoiceId,
|
||||
'status' => 'Pending',
|
||||
'updated_by' => session()->get('user_id'), // optionally track updates
|
||||
// Add other fields if *and only if* they must be changed
|
||||
];
|
||||
|
||||
$refundTable
|
||||
->where('id', $existingRefund->id)
|
||||
->update($updateData);
|
||||
->update($this->filterPayloadByTableColumns($updateData, 'refunds'));
|
||||
|
||||
log_message('info', "Refund record updated for invoice ID {$invoiceId}, student ID {$studentId}.");
|
||||
} else {
|
||||
@@ -689,16 +703,22 @@ class ParentController extends BaseController
|
||||
$insertData = [
|
||||
'parent_id' => $parentId,
|
||||
'invoice_id' => $invoiceId,
|
||||
'refund_amount' => $refundAmount,
|
||||
'requested_amount_cents' => $refundCents,
|
||||
'approved_amount_cents' => null,
|
||||
'currency' => 'USD',
|
||||
'requested_at' => utc_now(),
|
||||
'school_year' => $this->schoolYear,
|
||||
'status' => 'Pending review',
|
||||
'status' => 'Pending',
|
||||
'reason' => 'Withdrawal under review for student ID ' . $studentId,
|
||||
'request' => 'new',
|
||||
'request' => 'tuition',
|
||||
'source_type' => 'tuition_withdrawal',
|
||||
'source_id' => (int) $invoiceId,
|
||||
'semester' => $this->semester,
|
||||
'refund_paid_amount' => 0.0,
|
||||
];
|
||||
|
||||
$refundTable->insert($insertData);
|
||||
$refundTable->insert($this->filterPayloadByTableColumns($insertData, 'refunds'));
|
||||
|
||||
log_message('info', "Refund record created for invoice ID {$invoiceId}, student ID {$studentId}.");
|
||||
}
|
||||
@@ -893,9 +913,14 @@ class ParentController extends BaseController
|
||||
}
|
||||
|
||||
private function filterEnrollmentPayloadByColumns(array $payload): array
|
||||
{
|
||||
return $this->filterPayloadByTableColumns($payload, 'enrollments');
|
||||
}
|
||||
|
||||
private function filterPayloadByTableColumns(array $payload, string $table): array
|
||||
{
|
||||
foreach (array_keys($payload) as $column) {
|
||||
if (! $this->db->fieldExists($column, 'enrollments')) {
|
||||
if (! $this->db->fieldExists($column, $table)) {
|
||||
unset($payload[$column]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class ParticipationController extends BaseController
|
||||
|
||||
$this->semesterScoreService = service('semesterScoreService');
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->gradingLockModel = new GradingLockModel();
|
||||
$this->missingScoreOverrideModel = new MissingScoreOverrideModel();
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ class PaymentController extends ResourceController
|
||||
$this->studentModel = new StudentModel();
|
||||
$this->studentClassModel = new StudentClassModel();
|
||||
$this->schoolYear = $this->currentSchoolYearName();
|
||||
$this->semester = $this->configModel->getConfig('semester'); //installment_date
|
||||
$this->semester = getSemester(); //installment_date
|
||||
$this->installmentDate = $this->configModel->getConfig('installment_date');
|
||||
$this->discountUsageModel = new DiscountUsageModel();
|
||||
$this->additionalChargeModel = new AdditionalChargeModel();
|
||||
@@ -151,6 +151,15 @@ class PaymentController extends ResourceController
|
||||
}
|
||||
}
|
||||
|
||||
private function activeSchoolYearName(): string
|
||||
{
|
||||
try {
|
||||
return service('schoolYearContext')->active()->yearName();
|
||||
} catch (\Throwable $e) {
|
||||
return (string) ($this->configModel->getConfig('school_year') ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
private function assertSchoolYearNameWritable(string $schoolYear): void
|
||||
{
|
||||
service('schoolYearWriteGuard')->assertWritable(
|
||||
@@ -323,6 +332,7 @@ class PaymentController extends ResourceController
|
||||
|
||||
// Read the search term (email or phone)
|
||||
$searchTerm = trim((string) $this->request->getGet('search_term'));
|
||||
$manualPaySchoolYear = $this->activeSchoolYearName();
|
||||
|
||||
// --- Installment end date comes ONLY from config ---
|
||||
$installmentDateRaw = (string) ($this->installmentDate ?? '');
|
||||
@@ -338,29 +348,36 @@ class PaymentController extends ResourceController
|
||||
$searchClean = preg_replace('/\D/', '', $searchTerm);
|
||||
$searchFormatted = $this->formatPhone($searchClean);
|
||||
|
||||
$builder = $this->db->table('users');
|
||||
$builder->select('*');
|
||||
$builder = $this->db->table('users u');
|
||||
$builder->distinct();
|
||||
$builder->select('u.*');
|
||||
$builder->join('user_roles ur', 'ur.user_id = u.id', 'inner');
|
||||
$builder->join('roles r', 'r.id = ur.role_id', 'inner');
|
||||
$builder->where('LOWER(r.name)', 'parent');
|
||||
|
||||
// Build search conditions
|
||||
$builder->groupStart();
|
||||
$builder->where('email', $searchTerm);
|
||||
$builder->where('u.email', $searchTerm);
|
||||
$builder->orLike("CONCAT_WS(' ', u.firstname, u.lastname)", $searchTerm, 'both', null, true);
|
||||
$builder->orLike('u.firstname', $searchTerm);
|
||||
$builder->orLike('u.lastname', $searchTerm);
|
||||
|
||||
if (!empty($searchClean)) {
|
||||
if (strlen($searchClean) === 10) {
|
||||
$formattedPhone = substr($searchClean, 0, 3) . '-' . substr($searchClean, 3, 3) . '-' . substr($searchClean, 6, 4);
|
||||
$builder->orWhere('cellphone', $formattedPhone);
|
||||
$builder->orWhere('u.cellphone', $formattedPhone);
|
||||
}
|
||||
|
||||
// compare stripped formatting
|
||||
$builder->orWhere(
|
||||
"REPLACE(REPLACE(REPLACE(REPLACE(cellphone, '(', ''), ')', ''), '-', ''), ' ', '') =",
|
||||
"REPLACE(REPLACE(REPLACE(REPLACE(u.cellphone, '(', ''), ')', ''), '-', ''), ' ', '') =",
|
||||
$this->db->escapeString($searchClean),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($searchFormatted)) {
|
||||
$builder->orWhere('cellphone', $searchFormatted);
|
||||
$builder->orWhere('u.cellphone', $searchFormatted);
|
||||
}
|
||||
$builder->groupEnd();
|
||||
|
||||
@@ -374,7 +391,7 @@ class PaymentController extends ResourceController
|
||||
if ($parent && !empty($parent['id'])) {
|
||||
$parentData = $parent;
|
||||
$parentId = (int) $parent['id'];
|
||||
$carryForwardPaymentRequired = $this->parentHasActiveCarryForwardBalance($parentId, $this->schoolYear);
|
||||
$carryForwardPaymentRequired = $this->parentHasActiveCarryForwardBalance($parentId, $manualPaySchoolYear);
|
||||
if ($carryForwardPaymentRequired) {
|
||||
$carryForwardPaymentMessage = 'This parent has a balance carried over from a previous school year. Manual payments must be paid in full; installments are not allowed.';
|
||||
}
|
||||
@@ -386,14 +403,14 @@ class PaymentController extends ResourceController
|
||||
|
||||
// Payments (paginated). Join invoices so history is filtered by invoice term
|
||||
// and displays current invoice state instead of stale payment snapshots.
|
||||
$selectedYear = $this->getSelectedPaymentHistoryYear();
|
||||
$paymentHistory = $this->paymentModel->parentPaymentHistoryQuery($parentId, $selectedYear);
|
||||
$paymentHistory = $this->paymentModel->parentPaymentHistoryQuery($parentId, $manualPaySchoolYear);
|
||||
$payments = $paymentHistory->paginate(10);
|
||||
$pager = $paymentHistory->pager;
|
||||
|
||||
// Invoices
|
||||
$rawInvoices = $this->invoiceModel
|
||||
->where('parent_id', $parentId) // <- fixed (removed "value:")
|
||||
->where('parent_id', $parentId)
|
||||
->where('school_year', $manualPaySchoolYear)
|
||||
->orderBy('issue_date', 'DESC')
|
||||
->findAll();
|
||||
|
||||
@@ -460,21 +477,23 @@ class PaymentController extends ResourceController
|
||||
$qs = '%' . $db->escapeLikeString($q) . '%';
|
||||
$digits = preg_replace('/\D/', '', $q);
|
||||
|
||||
$sql = "SELECT id, firstname, lastname, email, cellphone
|
||||
FROM users
|
||||
$sql = "SELECT DISTINCT u.id, u.firstname, u.lastname, u.email, u.cellphone
|
||||
FROM users u
|
||||
JOIN user_roles ur ON ur.user_id = u.id
|
||||
JOIN roles r ON r.id = ur.role_id AND LOWER(r.name) = 'parent'
|
||||
WHERE (
|
||||
CONCAT_WS(' ', firstname, lastname) LIKE ?
|
||||
OR email LIKE ?
|
||||
OR cellphone LIKE ?";
|
||||
CONCAT_WS(' ', u.firstname, u.lastname) LIKE ?
|
||||
OR u.email LIKE ?
|
||||
OR u.cellphone LIKE ?";
|
||||
$params = [$qs, $qs, $qs];
|
||||
|
||||
if ($digits !== '') {
|
||||
$sql .= " OR REPLACE(REPLACE(REPLACE(REPLACE(cellphone, '(', ''), ')', ''), '-', ''), ' ', '') LIKE ?";
|
||||
$sql .= " OR REPLACE(REPLACE(REPLACE(REPLACE(u.cellphone, '(', ''), ')', ''), '-', ''), ' ', '') LIKE ?";
|
||||
$params[] = '%' . $digits . '%';
|
||||
}
|
||||
|
||||
$sql .= ")
|
||||
ORDER BY lastname, firstname
|
||||
ORDER BY u.lastname, u.firstname
|
||||
LIMIT 20";
|
||||
|
||||
$rows = $db->query($sql, $params)->getResultArray();
|
||||
@@ -562,7 +581,7 @@ class PaymentController extends ResourceController
|
||||
->orWhere('enrollment_status', 'Payment pending')
|
||||
->orWhere('enrollment_status', 'Payment Pending')
|
||||
->orWhere('enrollment_status', 'PAYMENT PENDING')
|
||||
->orWhere("LOWER(TRIM(REPLACE(enrollment_status, CHAR(160), ' '))) = 'payment pending'", null, false)
|
||||
->orWhere("LOWER(TRIM(REPLACE(enrollment_status, CONVERT(0xC2A0 USING utf8mb4), ' '))) = 'payment pending'", null, false)
|
||||
->groupEnd();
|
||||
|
||||
if ($semester !== null) {
|
||||
@@ -1457,12 +1476,15 @@ class PaymentController extends ResourceController
|
||||
if ($amount > $preBalance + 0.00001) {
|
||||
return false;
|
||||
}
|
||||
$postBalance = max(0.0, round($preBalance - $amount, 2));
|
||||
$paymentData = [
|
||||
'parent_id' => (int) $invoice['parent_id'],
|
||||
'invoice_id' => $invoiceId,
|
||||
'total_amount' => $invoice['total_amount'],
|
||||
'paid_amount' => $amount,
|
||||
'balance' => null,
|
||||
'balance' => $postBalance,
|
||||
'balance_amount' => $postBalance,
|
||||
'balance_after_payment' => $postBalance,
|
||||
'number_of_installments' => $installmentSeq, // <-- installment sequence (1,2,3,...)
|
||||
'installment_seq' => $installmentSeq,
|
||||
'transaction_id' => $transactionId,
|
||||
@@ -1480,6 +1502,7 @@ class PaymentController extends ResourceController
|
||||
|
||||
$paymentId = $this->paymentModel->insert($paymentData);
|
||||
if (!$paymentId) {
|
||||
log_message('error', '[processPayment] Failed to insert payment: ' . json_encode($this->paymentModel->errors()));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ class PrintablesBaseController extends BaseController
|
||||
$this->attendanceRecordModel = new AttendanceRecordModel();
|
||||
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->stickerWidth = $this->configModel->getConfig('stickerWidth');
|
||||
$this->stickerHeight = $this->configModel->getConfig('stickerHeight');
|
||||
$this->pageW = $this->configModel->getConfig('pageWidth');
|
||||
|
||||
@@ -45,7 +45,7 @@ class QuizController extends BaseController
|
||||
$this->quizModel = new QuizModel();
|
||||
$this->semesterScoreService = service('semesterScoreService');
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->gradingLockModel = new GradingLockModel();
|
||||
$this->missingScoreOverrideModel = new MissingScoreOverrideModel();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ use App\Models\PaymentModel;
|
||||
use App\Models\ConfigurationModel;
|
||||
use App\Models\InvoiceModel;
|
||||
use App\Models\EnrollmentModel;
|
||||
use App\Services\FeeCalculationService;
|
||||
use CodeIgniter\Exceptions\PageNotFoundException;
|
||||
|
||||
class RefundController extends BaseController
|
||||
@@ -31,6 +32,7 @@ class RefundController extends BaseController
|
||||
protected ParentLedgerService $parentLedgerService;
|
||||
protected RefundEligibilityService $refundEligibilityService;
|
||||
protected FinancialAttachmentService $financialAttachmentService;
|
||||
protected FeeCalculationService $feeCalculationService;
|
||||
protected $db;
|
||||
|
||||
// Allowed request types (mapped to your `refunds.request` column)
|
||||
@@ -52,6 +54,7 @@ class RefundController extends BaseController
|
||||
$this->parentLedgerService = new ParentLedgerService();
|
||||
$this->refundEligibilityService = new RefundEligibilityService();
|
||||
$this->financialAttachmentService = new FinancialAttachmentService();
|
||||
$this->feeCalculationService = new FeeCalculationService();
|
||||
$this->db = \Config\Database::connect();
|
||||
}
|
||||
|
||||
@@ -70,6 +73,22 @@ class RefundController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
private function refundStatusForStorage(string $status): string
|
||||
{
|
||||
return match (FinancialStatus::normalizeRefundStatus($status)) {
|
||||
FinancialStatus::REFUND_APPROVED => 'Approved',
|
||||
FinancialStatus::REFUND_REJECTED => 'Rejected',
|
||||
FinancialStatus::REFUND_PARTIALLY_PAID => 'Partial',
|
||||
FinancialStatus::REFUND_PAID => 'Paid',
|
||||
default => 'Pending',
|
||||
};
|
||||
}
|
||||
|
||||
private function refundOpenStatuses(): array
|
||||
{
|
||||
return ['Pending', 'Approved', 'Partial', 'pending', 'requested', 'approved', 'partial', 'partially_paid'];
|
||||
}
|
||||
|
||||
private function refundFailureResponse(
|
||||
string $publicCode,
|
||||
string $publicMessage,
|
||||
@@ -89,12 +108,12 @@ class RefundController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
/** Get current term (school_year, semester) from configuration */
|
||||
/** Get current school year from configuration and derive the current semester from calendar dates. */
|
||||
private function getCurrentTerm(): array
|
||||
{
|
||||
$rows = $this->configModel
|
||||
->select('config_key, config_value')
|
||||
->whereIn('config_key', ['school_year','semester'])
|
||||
->whereIn('config_key', ['school_year'])
|
||||
->findAll();
|
||||
|
||||
$map = [];
|
||||
@@ -103,7 +122,7 @@ class RefundController extends BaseController
|
||||
}
|
||||
return [
|
||||
'school_year' => $map['school_year'] ?? date('Y') . '-' . (date('Y') + 1),
|
||||
'semester' => $map['semester'] ?? 'Fall',
|
||||
'semester' => getSemester(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -140,7 +159,7 @@ class RefundController extends BaseController
|
||||
->where('invoice_id', $iid)
|
||||
->where('source_type', 'invoice_overpayment')
|
||||
->where('source_id', $iid)
|
||||
->whereIn('status', ['Pending','Approved','Partial'])
|
||||
->whereIn('status', $this->refundOpenStatuses())
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
||||
if ($openRow) {
|
||||
@@ -161,7 +180,7 @@ class RefundController extends BaseController
|
||||
->where('invoice_id', $iid)
|
||||
->where('source_type', 'invoice_overpayment')
|
||||
->where('source_id', $iid)
|
||||
->whereIn('status', ['Pending','Approved','Partial','Paid'])
|
||||
->whereIn('status', array_merge($this->refundOpenStatuses(), ['Paid', 'paid']))
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
||||
|
||||
@@ -176,7 +195,7 @@ class RefundController extends BaseController
|
||||
'approved_amount_cents' => null,
|
||||
'currency' => 'USD',
|
||||
'refund_paid_amount' => 0.00,
|
||||
'status' => FinancialStatus::REFUND_REQUESTED,
|
||||
'status' => $this->refundStatusForStorage(FinancialStatus::REFUND_REQUESTED),
|
||||
'request' => 'overpayment',
|
||||
'source_type' => 'invoice_overpayment',
|
||||
'source_id' => $iid,
|
||||
@@ -393,7 +412,7 @@ class RefundController extends BaseController
|
||||
'approved_amount_cents' => null,
|
||||
'currency' => 'USD',
|
||||
'refund_paid_amount' => 0.00, // IMPORTANT: your column is NOT NULL
|
||||
'status' => FinancialStatus::REFUND_REQUESTED,
|
||||
'status' => $this->refundStatusForStorage(FinancialStatus::REFUND_REQUESTED),
|
||||
'reason' => $reason,
|
||||
'request' => $requestType, // <- store the source/type here
|
||||
'source_type' => $sourceType,
|
||||
@@ -470,7 +489,7 @@ class RefundController extends BaseController
|
||||
$this->refundEligibilityService->validateRequestedAmount($eligibility, $requestedCents);
|
||||
|
||||
$ok = $this->refundModel->update($refundId, [
|
||||
'status' => FinancialStatus::REFUND_APPROVED,
|
||||
'status' => $this->refundStatusForStorage(FinancialStatus::REFUND_APPROVED),
|
||||
'approved_amount_cents' => $requestedCents,
|
||||
'approved_at' => utc_now(),
|
||||
'approved_by' => session()->get('user_id'),
|
||||
@@ -513,7 +532,7 @@ class RefundController extends BaseController
|
||||
}
|
||||
|
||||
$ok = $this->refundModel->update($refundId, [
|
||||
'status' => FinancialStatus::REFUND_REJECTED,
|
||||
'status' => $this->refundStatusForStorage(FinancialStatus::REFUND_REJECTED),
|
||||
'reason' => $this->request->getPost('reason') ?: ($refund['reason'] ?? null),
|
||||
'approved_at' => utc_now(),
|
||||
'approved_by' => session()->get('user_id'),
|
||||
@@ -697,7 +716,7 @@ class RefundController extends BaseController
|
||||
];
|
||||
if (!$isOnline) {
|
||||
$refundProjection['refund_paid_amount'] = $total;
|
||||
$refundProjection['status'] = $newStatus;
|
||||
$refundProjection['status'] = $this->refundStatusForStorage($newStatus);
|
||||
$refundProjection['refunded_at'] = utc_now();
|
||||
}
|
||||
if (!$this->refundModel->update($refundId, $refundProjection)) {
|
||||
@@ -865,7 +884,7 @@ class RefundController extends BaseController
|
||||
|
||||
if (!$this->refundModel->update($refundId, [
|
||||
'refund_paid_amount' => $netPaidCents / 100,
|
||||
'status' => $newStatus,
|
||||
'status' => $this->refundStatusForStorage($newStatus),
|
||||
'refunded_at' => $netPaidCents > 0 ? ($refund['refunded_at'] ?? utc_now()) : null,
|
||||
'updated_at' => utc_now(),
|
||||
'updated_by' => session()->get('user_id'),
|
||||
@@ -912,15 +931,17 @@ class RefundController extends BaseController
|
||||
/** Keep your listing; added extra fields for clarity */
|
||||
public function listRefunds()
|
||||
{
|
||||
// NOTE: We no longer auto-create/adjust refunds on page load to avoid duplicate lines
|
||||
// when staff are recording payouts. Use the "Recalculate" buttons to run detection on demand.
|
||||
// Repair legacy withdrawal placeholders only; overpayment recalculation remains explicit.
|
||||
$this->repairPendingWithdrawalRefunds();
|
||||
|
||||
// 2) List refunds with joins
|
||||
$refunds = $this->refundModel
|
||||
->select('refunds.*,
|
||||
i.invoice_number,
|
||||
u.firstname, u.lastname, u.school_id,
|
||||
a.firstname AS approved_by_firstname,
|
||||
a.lastname AS approved_by_lastname')
|
||||
->join('invoices i', 'refunds.invoice_id = i.id', 'left')
|
||||
->join('users u', 'refunds.parent_id = u.id')
|
||||
->join('users a', 'refunds.approved_by = a.id', 'left')
|
||||
->orderBy('refunds.created_at', 'DESC')
|
||||
@@ -970,6 +991,59 @@ class RefundController extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
private function repairPendingWithdrawalRefunds(): void
|
||||
{
|
||||
try {
|
||||
$rows = $this->refundModel
|
||||
->groupStart()
|
||||
->where('refund_amount <=', 0)
|
||||
->orWhere('source_type IS NULL', null, false)
|
||||
->orWhere("(request = 'tuition' AND source_type = 'invoice_overpayment')", null, false)
|
||||
->orWhere('source_id IS NULL', null, false)
|
||||
->groupEnd()
|
||||
->whereIn('status', ['Pending', 'pending', 'requested'])
|
||||
->like('reason', 'Withdrawal under review')
|
||||
->findAll();
|
||||
|
||||
foreach ($rows as $refund) {
|
||||
$parentId = (int)($refund['parent_id'] ?? 0);
|
||||
$schoolYear = (string)($refund['school_year'] ?? '');
|
||||
if ($parentId <= 0 || $schoolYear === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$invoice = $this->invoiceModel
|
||||
->where('parent_id', $parentId)
|
||||
->where('school_year', $schoolYear)
|
||||
->orderBy('created_at', 'DESC')
|
||||
->first();
|
||||
if (!$invoice) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$students = $this->enrollmentModel
|
||||
->where('parent_id', $parentId)
|
||||
->where('school_year', $schoolYear)
|
||||
->findAll();
|
||||
$refundAmount = $this->feeCalculationService->calculateRefund($students, $parentId);
|
||||
$refundCents = max(0, (int)round($refundAmount * 100));
|
||||
|
||||
$this->refundModel->update((int)$refund['id'], [
|
||||
'invoice_id' => (int)$invoice['id'],
|
||||
'refund_amount' => $refundAmount,
|
||||
'requested_amount_cents' => $refundCents,
|
||||
'currency' => 'USD',
|
||||
'request' => 'tuition',
|
||||
'source_type' => 'tuition_withdrawal',
|
||||
'source_id' => (int)$invoice['id'],
|
||||
'updated_at' => utc_now(),
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', 'Pending withdrawal refund repair failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** Manual endpoint to recalculate/sync overpayments and optionally notify newly created entries. */
|
||||
public function recalculateOverpayments()
|
||||
{
|
||||
@@ -991,7 +1065,7 @@ class RefundController extends BaseController
|
||||
->where('invoice_id', $iid)
|
||||
->where('source_type', 'invoice_overpayment')
|
||||
->where('source_id', $iid)
|
||||
->whereIn('status', ['Pending','Approved','Partial'])
|
||||
->whereIn('status', $this->refundOpenStatuses())
|
||||
->orderBy('id', 'DESC')
|
||||
->first();
|
||||
if ($openRow) {
|
||||
@@ -1028,7 +1102,7 @@ class RefundController extends BaseController
|
||||
'approved_amount_cents' => null,
|
||||
'currency' => 'USD',
|
||||
'refund_paid_amount' => 0.00,
|
||||
'status' => FinancialStatus::REFUND_REQUESTED,
|
||||
'status' => $this->refundStatusForStorage(FinancialStatus::REFUND_REQUESTED),
|
||||
'request' => 'overpayment',
|
||||
'source_type' => 'invoice_overpayment',
|
||||
'source_id' => $iid,
|
||||
@@ -1113,7 +1187,7 @@ class RefundController extends BaseController
|
||||
}
|
||||
|
||||
$ok = $this->refundModel->update($refundId, [
|
||||
'status' => $status,
|
||||
'status' => $this->refundStatusForStorage($status),
|
||||
'reason' => $reason,
|
||||
'approved_amount_cents' => $approvedCents,
|
||||
'approved_at' => utc_now(),
|
||||
@@ -1188,7 +1262,7 @@ class RefundController extends BaseController
|
||||
|
||||
private function lockRefundSourceForUpdate(string $sourceType, int $sourceId, ?int $invoiceId): void
|
||||
{
|
||||
if ($sourceType === 'invoice_overpayment') {
|
||||
if (in_array($sourceType, ['invoice_overpayment', 'tuition_withdrawal'], true)) {
|
||||
$this->db->query('SELECT id FROM invoices WHERE id = ? FOR UPDATE', [$invoiceId ?: $sourceId])->getRowArray();
|
||||
} elseif (in_array($sourceType, ['payment_duplicate', 'payment_correction'], true)) {
|
||||
$payment = $this->db->query('SELECT * FROM payments WHERE id = ? FOR UPDATE', [$sourceId])->getRowArray();
|
||||
|
||||
@@ -44,7 +44,7 @@ class RegisterController extends Controller
|
||||
$this->parentModel = new ParentModel();
|
||||
$this->policyAcceptanceModel = new ParentPolicyAcceptanceModel();
|
||||
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class ReimbursementController extends BaseController
|
||||
$this->batchItemModel = new ReimbursementBatchItemModel();
|
||||
$this->batchAdminFileModel = new ReimbursementBatchAdminFileModel();
|
||||
|
||||
$this->semester = $this->configModel->getConfig('semester') ?? 'Fall';
|
||||
$this->semester = getSemester() ?? 'Fall';
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year') ?? date('Y');
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ class RolePermissionController extends Controller
|
||||
$this->request = \Config\Services::request();
|
||||
$this->db = \Config\Database::connect();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
}
|
||||
|
||||
public function index()
|
||||
|
||||
@@ -28,7 +28,7 @@ class RoleSwitcherController extends BaseController
|
||||
}
|
||||
$this->configModel = new ConfigurationModel();
|
||||
$this->userRoleModel = new UserRoleModel();
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class SchoolCalendarController extends BaseController
|
||||
$this->meetingModel = new ParentMeetingScheduleModel();
|
||||
$this->db = \Config\Database::connect();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
|
||||
// Load helpers
|
||||
helper(['form', 'url']);
|
||||
|
||||
@@ -39,7 +39,7 @@ class ScoreCommentController extends BaseController
|
||||
$this->semesterScoreModel = new SemesterScoreModel();
|
||||
$this->gradingLockModel = new GradingLockModel();
|
||||
$this->missingScoreOverrideModel = new MissingScoreOverrideModel();
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ class ScoreController extends BaseController
|
||||
$this->gradingLockModel = new GradingLockModel();
|
||||
$this->missingScoreOverrideModel = new MissingScoreOverrideModel();
|
||||
// Retrieve the configuration values
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->targetHigh = $this->configModel->getConfig('trophy_score');
|
||||
$this->targetLow = $this->configModel->getConfig('pass_score');
|
||||
@@ -1030,7 +1030,7 @@ class ScoreController extends BaseController
|
||||
$fallStartCfg = (string)($this->configModel->getConfig('fall_semester_start') ?? '');
|
||||
$fallEndCfg = (string)($this->configModel->getConfig('fall_end_date') ?? ''); // e.g., 'YYYY-01-15'
|
||||
$springStartCfg = (string)($this->configModel->getConfig('spring_semester_start') ?? ''); // e.g., 'YYYY-01-16'
|
||||
$springEndCfg = (string)($this->configModel->getConfig('last_school_day') ?? '');
|
||||
$springEndCfg = (string)($this->configModel->getConfig('last_day_of_school') ?? '');
|
||||
|
||||
if ($norm === 'fall') {
|
||||
$start = ($fallStartCfg !== '') ? date($y1 . '-m-d', strtotime($fallStartCfg)) : "{$y1}-09-01";
|
||||
|
||||
@@ -36,7 +36,7 @@ class ScorePredictor extends BaseController
|
||||
$this->classSectionModel = new ClassSectionModel();
|
||||
|
||||
// Retrieve the configuration values
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->targetTrophy = $this->configModel->getConfig('trophy_score');
|
||||
$this->targetLow = $this->configModel->getConfig('pass_score');
|
||||
|
||||
@@ -28,7 +28,7 @@ class SlipPrinterController extends BaseController
|
||||
{
|
||||
helper(['form', 'url']);
|
||||
$this->configModel = new ConfigurationModel();
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
}
|
||||
/**
|
||||
@@ -66,7 +66,7 @@ class SlipPrinterController extends BaseController
|
||||
try {
|
||||
$printedBy = (int) (session()->get('user_id') ?? 0) ?: null;
|
||||
$cfg = new ConfigurationModel();
|
||||
$semester = (string)($cfg->getConfig('semester') ?? '');
|
||||
$semester = (string)(getSemester() ?? '');
|
||||
$logData = [
|
||||
'school_year' => $data['school_year'],
|
||||
'semester' => $semester,
|
||||
@@ -325,7 +325,7 @@ class SlipPrinterController extends BaseController
|
||||
try {
|
||||
$printedBy = (int) (session()->get('user_id') ?? 0) ?: null;
|
||||
$cfg = new ConfigurationModel();
|
||||
$semester = (string)($cfg->getConfig('semester') ?? '');
|
||||
$semester = (string)(getSemester() ?? '');
|
||||
$logData = [
|
||||
'school_year' => $data['school_year'],
|
||||
'semester' => $semester,
|
||||
|
||||
@@ -29,7 +29,7 @@ class StaffController extends BaseController
|
||||
$this->staffDirectorySync = service('staffDirectorySync');
|
||||
|
||||
// Retrieve the configuration values
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ class StudentController extends BaseController
|
||||
$this->classSectionModel = new ClassSectionModel();
|
||||
$this->emergencyContact = new EmergencyContactModel();
|
||||
$this->enrollmentModel = new EnrollmentModel();
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
helper(['url', 'form']);
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class TeacherController extends BaseController
|
||||
$this->staffAttendanceModel = new StaffAttendanceModel();
|
||||
|
||||
// Retrieve the configuration values
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
$this->schoolYear = $this->currentSchoolYearName((string) ($this->schoolYear ?? ''));
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class WhatsappController extends BaseController
|
||||
$this->membershipModel = new WhatsappGroupMembershipModel();
|
||||
$this->configModel = new ConfigurationModel();
|
||||
|
||||
$this->semester = $this->configModel->getConfig('semester');
|
||||
$this->semester = getSemester();
|
||||
$this->schoolYear = $this->configModel->getConfig('school_year');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user