update code
This commit is contained in:
@@ -797,26 +797,25 @@ public function showUpdateAttendanceForm()
|
||||
}
|
||||
|
||||
$noSchoolDays = [];
|
||||
if ($schoolYearForRange !== '') {
|
||||
$events = [];
|
||||
try {
|
||||
$events = $this->calendarModel->getEvents();
|
||||
} catch (\Throwable $e) {
|
||||
$events = [];
|
||||
try {
|
||||
$events = $this->calendarModel->getEventsBySchoolYearAndSemester(
|
||||
$schoolYearForRange,
|
||||
$semesterNorm !== '' ? $semesterNorm : null
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
$events = [];
|
||||
}
|
||||
foreach ($events as $event) {
|
||||
$d = substr((string)($event['date'] ?? ''), 0, 10);
|
||||
if ($d === '' || empty($event['no_school'])) {
|
||||
continue;
|
||||
}
|
||||
foreach ($events as $event) {
|
||||
$d = substr((string)($event['date'] ?? ''), 0, 10);
|
||||
if ($d === '' || empty($event['no_school'])) {
|
||||
continue;
|
||||
}
|
||||
if ($d < $rangeStart || $d > $rangeEnd) {
|
||||
continue;
|
||||
}
|
||||
$noSchoolDays[$d] = true;
|
||||
if ($d < $rangeStart || $d > $rangeEnd) {
|
||||
continue;
|
||||
}
|
||||
$eventYear = trim((string)($event['school_year'] ?? ''));
|
||||
if ($schoolYearForRange !== '' && $eventYear !== '' && $eventYear !== $schoolYearForRange) {
|
||||
continue;
|
||||
}
|
||||
$noSchoolDays[$d] = true;
|
||||
}
|
||||
|
||||
// Total passed attendance days up to this Sunday (inclusive), excluding no-school days.
|
||||
|
||||
@@ -60,13 +60,15 @@ class ClassPreparationController extends BaseController
|
||||
$limitToSemester = $this->hasRosterForSemester($schoolYear, $semester);
|
||||
|
||||
// 1) Get student count per class-section (distinct students, correct semester)
|
||||
$scQ = $this->studentClassModel
|
||||
->select('class_section_id, COUNT(DISTINCT student_id) AS student_count')
|
||||
->where('school_year', $schoolYear);
|
||||
$scQ = $this->db->table('student_class sc')
|
||||
->select('sc.class_section_id, COUNT(DISTINCT sc.student_id) AS student_count', false)
|
||||
->join('students s', 's.id = sc.student_id', 'inner')
|
||||
->where('s.is_active', 1)
|
||||
->where('sc.school_year', $schoolYear);
|
||||
if ($limitToSemester && $semester !== '') {
|
||||
$scQ->where('semester', $semester);
|
||||
$scQ->where('sc.semester', $semester);
|
||||
}
|
||||
$classSections = $scQ->groupBy('class_section_id')->findAll();
|
||||
$classSections = $scQ->groupBy('sc.class_section_id')->get()->getResultArray();
|
||||
|
||||
// 2) Inventory availability — prefer good_qty when present, else condition='good' quantity.
|
||||
$inventoryMap = $this->buildInventoryAvailability($schoolYear, $semester, $limitToSemester, $allowed);
|
||||
@@ -298,14 +300,16 @@ class ClassPreparationController extends BaseController
|
||||
$className = $this->classSectionModel->getClassSectionNameBySectionId($classSectionId) ?? $classSectionId;
|
||||
|
||||
// Distinct student count for this term
|
||||
$studentQ = $this->studentClassModel
|
||||
->select('COUNT(DISTINCT student_id) AS cnt')
|
||||
->where('class_section_id', $classSectionId)
|
||||
->where('school_year', $schoolYear);
|
||||
$studentQ = $this->db->table('student_class sc')
|
||||
->select('COUNT(DISTINCT sc.student_id) AS cnt', false)
|
||||
->join('students s', 's.id = sc.student_id', 'inner')
|
||||
->where('s.is_active', 1)
|
||||
->where('sc.class_section_id', $classSectionId)
|
||||
->where('sc.school_year', $schoolYear);
|
||||
if ($limitToSemester && $semester !== '') {
|
||||
$studentQ->where('semester', $semester);
|
||||
$studentQ->where('sc.semester', $semester);
|
||||
}
|
||||
$studentRow = $studentQ->first();
|
||||
$studentRow = $studentQ->get()->getRowArray();
|
||||
$studentCount = (int)($studentRow['cnt'] ?? 0);
|
||||
|
||||
// Live calc + adjustments
|
||||
@@ -355,13 +359,15 @@ class ClassPreparationController extends BaseController
|
||||
$limitToSemester = $this->hasRosterForSemester($schoolYear, $semester);
|
||||
|
||||
// Student counts
|
||||
$scQ = $this->studentClassModel
|
||||
->select('class_section_id, COUNT(DISTINCT student_id) AS student_count')
|
||||
->where('school_year', $schoolYear);
|
||||
$scQ = $this->db->table('student_class sc')
|
||||
->select('sc.class_section_id, COUNT(DISTINCT sc.student_id) AS student_count', false)
|
||||
->join('students s', 's.id = sc.student_id', 'inner')
|
||||
->where('s.is_active', 1)
|
||||
->where('sc.school_year', $schoolYear);
|
||||
if ($limitToSemester && $semester !== '') {
|
||||
$scQ->where('semester', $semester);
|
||||
$scQ->where('sc.semester', $semester);
|
||||
}
|
||||
$classSections = $scQ->groupBy('class_section_id')->findAll();
|
||||
$classSections = $scQ->groupBy('sc.class_section_id')->get()->getResultArray();
|
||||
|
||||
// Build inventory availability maps
|
||||
$inventoryMap = $this->buildInventoryAvailability($schoolYear, $semester, $limitToSemester, $allowed);
|
||||
@@ -448,14 +454,16 @@ class ClassPreparationController extends BaseController
|
||||
$now = utc_now();
|
||||
$count = 0;
|
||||
foreach ($ids as $classSectionId) {
|
||||
$studentQ = $this->studentClassModel
|
||||
->select('COUNT(DISTINCT student_id) AS cnt')
|
||||
->where('class_section_id', $classSectionId)
|
||||
->where('school_year', $schoolYear);
|
||||
$studentQ = $this->db->table('student_class sc')
|
||||
->select('COUNT(DISTINCT sc.student_id) AS cnt', false)
|
||||
->join('students s', 's.id = sc.student_id', 'inner')
|
||||
->where('s.is_active', 1)
|
||||
->where('sc.class_section_id', $classSectionId)
|
||||
->where('sc.school_year', $schoolYear);
|
||||
if ($limitToSemester && $semester !== '') {
|
||||
$studentQ->where('semester', $semester);
|
||||
$studentQ->where('sc.semester', $semester);
|
||||
}
|
||||
$studentRow = $studentQ->first();
|
||||
$studentRow = $studentQ->get()->getRowArray();
|
||||
$studentCount = (int)($studentRow['cnt'] ?? 0);
|
||||
$classLevel = $this->getClassLevelBySection((string)$classSectionId);
|
||||
$className = $this->classSectionModel->getClassSectionNameBySectionId($classSectionId) ?? $classSectionId;
|
||||
|
||||
@@ -71,12 +71,15 @@ class ParentAttendanceReportController extends BaseController
|
||||
->orderBy('s.lastname', 'ASC')
|
||||
->get()->getResultArray();
|
||||
|
||||
$cutoffThreshold = $this->normalizeCutoffTime((string) $this->configModel->getConfig('parent_report_cutoff'));
|
||||
|
||||
return view('parent/report_attendance', [
|
||||
'students' => $students,
|
||||
'today' => local_date(utc_now(), 'Y-m-d'),
|
||||
'sundays' => $sundays, // array of ['value'=>Y-m-d, 'label'=>...] items
|
||||
'defaultDate' => $defaultDate, // preselected date (upcoming Sunday)
|
||||
'myReports' => $previewRows,
|
||||
'cutoffThreshold' => $cutoffThreshold,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -141,6 +144,17 @@ class ParentAttendanceReportController extends BaseController
|
||||
|
||||
// Enforce Sunday-only and future-or-today selection
|
||||
$todayCheck = new \DateTime('today');
|
||||
$cutoffThreshold = $this->normalizeCutoffTime((string) $this->configModel->getConfig('parent_report_cutoff'));
|
||||
$tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone());
|
||||
$tz = null;
|
||||
$nowTz = null;
|
||||
try {
|
||||
$tz = new \DateTimeZone($tzName ?: 'UTC');
|
||||
$nowTz = new \DateTime('now', $tz);
|
||||
} catch (\Throwable $e) {
|
||||
$tz = null;
|
||||
$nowTz = null;
|
||||
}
|
||||
$validDates = [];
|
||||
|
||||
$cap = $this->firstSundayOfJune((string) $this->configModel->getConfig('school_year'));
|
||||
@@ -150,9 +164,11 @@ class ParentAttendanceReportController extends BaseController
|
||||
$lateTodayStr = $todayCheck->format('Y-m-d');
|
||||
if ($type === 'late') {
|
||||
try {
|
||||
$tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone());
|
||||
$tz = new \DateTimeZone($tzName ?: 'UTC');
|
||||
$lateNow = new \DateTime('now', $tz);
|
||||
if (!$tz) {
|
||||
$tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone());
|
||||
$tz = new \DateTimeZone($tzName ?: 'UTC');
|
||||
}
|
||||
$lateNow = $nowTz ?: new \DateTime('now', $tz);
|
||||
$cm = $this->configModel;
|
||||
$cutoffStr = (string) ($cm->getConfig('late_report_cutoff')
|
||||
?: $cm->getConfig('late_cutoff_time')
|
||||
@@ -183,6 +199,17 @@ class ParentAttendanceReportController extends BaseController
|
||||
if ($dt < $todayCheck) {
|
||||
return redirect()->back()->withInput()->with('error', 'Past dates are not allowed. Please select today or a future Sunday.');
|
||||
}
|
||||
if ($nowTz && $tz) {
|
||||
try {
|
||||
$cutoff = new \DateTime($entry . ' ' . $cutoffThreshold . ':00', $tz);
|
||||
if ($nowTz >= $cutoff) {
|
||||
$abbr = $cutoff->format('T');
|
||||
return redirect()->back()->withInput()->with('error', 'Reporting closes at ' . $cutoffThreshold . ' ' . $abbr . ' on the report date.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// ignore and continue
|
||||
}
|
||||
}
|
||||
if ($dt > $cap) {
|
||||
return redirect()->back()->withInput()->with('error', 'The last available date is the first Sunday of June.');
|
||||
}
|
||||
@@ -800,6 +827,36 @@ class ParentAttendanceReportController extends BaseController
|
||||
$update['reported'] = 1;
|
||||
}
|
||||
$this->attendanceDataModel->update((int) $existing['id'], $update);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($existingStatus === '' || $existingStatus === 'present') {
|
||||
$update = [
|
||||
'status' => $status,
|
||||
'reason' => $existing['reason'] ?: $reasonText,
|
||||
'updated_at' => utc_now(),
|
||||
];
|
||||
if ($hasIsReported) {
|
||||
$update['is_reported'] = 'yes';
|
||||
}
|
||||
if ($hasLegacyReported) {
|
||||
$update['reported'] = 1;
|
||||
}
|
||||
$this->attendanceDataModel->update((int) $existing['id'], $update);
|
||||
|
||||
$classSectionId = (int) ($existing['class_section_id'] ?? $classSectionId ?? 0);
|
||||
$schoolId = (string) ($existing['school_id'] ?? '');
|
||||
if ($classSectionId > 0 && $schoolId !== '') {
|
||||
$this->adjustAttendanceRecordForStatusChange(
|
||||
studentId: $studentId,
|
||||
classSectionId: $classSectionId,
|
||||
schoolId: $schoolId,
|
||||
semester: $semester,
|
||||
schoolYear: $schoolYear,
|
||||
oldStatus: $existingStatus,
|
||||
newStatus: $status
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -856,6 +913,61 @@ class ParentAttendanceReportController extends BaseController
|
||||
$this->attendanceRecordModel->insert($insert);
|
||||
}
|
||||
|
||||
private function adjustAttendanceRecordForStatusChange(
|
||||
int $studentId,
|
||||
int $classSectionId,
|
||||
string $schoolId,
|
||||
string $semester,
|
||||
string $schoolYear,
|
||||
string $oldStatus,
|
||||
string $newStatus
|
||||
): void {
|
||||
$oldStatus = strtolower($oldStatus);
|
||||
$newStatus = strtolower($newStatus);
|
||||
|
||||
if ($oldStatus === $newStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
$record = $this->attendanceRecordModel->where([
|
||||
'student_id' => $studentId,
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear,
|
||||
])->first();
|
||||
|
||||
if (!$record) {
|
||||
$this->bumpAttendanceRecordFromParent($studentId, $classSectionId, $schoolId, $newStatus, $semester, $schoolYear);
|
||||
return;
|
||||
}
|
||||
|
||||
$updates = [
|
||||
'total_presence' => (int) ($record['total_presence'] ?? 0),
|
||||
'total_absence' => (int) ($record['total_absence'] ?? 0),
|
||||
'total_late' => (int) ($record['total_late'] ?? 0),
|
||||
'total_attendance' => (int) ($record['total_attendance'] ?? 0),
|
||||
'updated_at' => utc_now(),
|
||||
'modified_by' => (int) (session()->get('user_id') ?? 0),
|
||||
];
|
||||
|
||||
if ($oldStatus === 'present') {
|
||||
$updates['total_presence'] = max(0, $updates['total_presence'] - 1);
|
||||
} elseif ($oldStatus === 'absent') {
|
||||
$updates['total_absence'] = max(0, $updates['total_absence'] - 1);
|
||||
} elseif ($oldStatus === 'late') {
|
||||
$updates['total_late'] = max(0, $updates['total_late'] - 1);
|
||||
}
|
||||
|
||||
if ($newStatus === 'present') {
|
||||
$updates['total_presence']++;
|
||||
} elseif ($newStatus === 'absent') {
|
||||
$updates['total_absence']++;
|
||||
} elseif ($newStatus === 'late') {
|
||||
$updates['total_late']++;
|
||||
}
|
||||
|
||||
$this->attendanceRecordModel->update((int) $record['id'], $updates);
|
||||
}
|
||||
|
||||
// GET: staff view to list reports (today & upcoming by default)
|
||||
public function list()
|
||||
{
|
||||
@@ -1503,6 +1615,20 @@ class ParentAttendanceReportController extends BaseController
|
||||
return $userId; // fallback
|
||||
}
|
||||
|
||||
private function normalizeCutoffTime(string $raw, string $default = '09:00'): string
|
||||
{
|
||||
$raw = trim($raw);
|
||||
if ($raw === '') {
|
||||
return $default;
|
||||
}
|
||||
if (preg_match('/^(\d{1,2}):(\d{2})$/', $raw, $m)) {
|
||||
$hh = str_pad((string) ((int) $m[1]), 2, '0', STR_PAD_LEFT);
|
||||
$mm = $m[2];
|
||||
return $hh . ':' . $mm;
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send confirmation/notification emails for parent attendance submissions.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user