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:
@@ -435,7 +435,7 @@ class AttendanceTrackingModel extends Model
|
||||
?string $semester = null,
|
||||
?string $schoolYear = null
|
||||
): bool {
|
||||
$semester = $semester ?? (new \App\Models\ConfigurationModel())->getConfig('semester');
|
||||
$semester = $semester ?? getSemester();
|
||||
$schoolYear = $schoolYear ?? (new \App\Models\ConfigurationModel())->getConfig('school_year');
|
||||
|
||||
$day = substr($ymd, 0, 10);
|
||||
|
||||
@@ -86,12 +86,9 @@ class ConfigurationModel extends Model
|
||||
|
||||
if ($key === 'semester') {
|
||||
try {
|
||||
$semester = (new \App\Services\SemesterRangeService($this))->getSemesterForDate();
|
||||
if ($semester !== '') {
|
||||
return $semester;
|
||||
}
|
||||
return (new \App\Services\SemesterRangeService($this))->getSemesterForDate() ?: 'Fall';
|
||||
} catch (\Throwable $e) {
|
||||
// ignore and fall back
|
||||
return 'Fall';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace App\Models;
|
||||
|
||||
use CodeIgniter\Model;
|
||||
use App\Models\Concerns\SchoolYearAutoFillTrait;
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Validation\ValidationInterface;
|
||||
|
||||
class ManualPaymentModel extends Model
|
||||
{
|
||||
@@ -23,6 +25,38 @@ class ManualPaymentModel extends Model
|
||||
'school_year' => 'required|string|max_length[9]',
|
||||
];
|
||||
|
||||
|
||||
protected $useTimestamps = false;
|
||||
|
||||
private array $manualPaymentColumns = [];
|
||||
|
||||
public function __construct(?ConnectionInterface $db = null, ?ValidationInterface $validation = null)
|
||||
{
|
||||
parent::__construct($db, $validation);
|
||||
|
||||
$this->manualPaymentColumns = $this->getTableColumns($this->table);
|
||||
$this->allowedFields = array_values(array_filter(
|
||||
$this->allowedFields,
|
||||
fn (string $field): bool => in_array($field, $this->manualPaymentColumns, true)
|
||||
));
|
||||
|
||||
foreach (array_keys($this->validationRules) as $field) {
|
||||
if (!in_array($field, $this->allowedFields, true)) {
|
||||
unset($this->validationRules[$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getTableColumns(string $table): array
|
||||
{
|
||||
try {
|
||||
return $this->db->getFieldNames($table);
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', '[ManualPaymentModel] Could not read table columns for {table}: {error}', [
|
||||
'table' => $table,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Model;
|
||||
use App\Models\Concerns\SchoolYearAutoFillTrait;
|
||||
use CodeIgniter\Validation\ValidationInterface;
|
||||
|
||||
class RefundModel extends Model
|
||||
{
|
||||
@@ -50,6 +52,39 @@ class RefundModel extends Model
|
||||
protected $createdField = 'created_at';
|
||||
protected $updatedField = 'updated_at';
|
||||
|
||||
private array $refundColumns = [];
|
||||
|
||||
public function __construct(?ConnectionInterface $db = null, ?ValidationInterface $validation = null)
|
||||
{
|
||||
parent::__construct($db, $validation);
|
||||
|
||||
$this->refundColumns = $this->getTableColumns($this->table);
|
||||
$this->allowedFields = array_values(array_filter(
|
||||
$this->allowedFields,
|
||||
fn (string $field): bool => in_array($field, $this->refundColumns, true)
|
||||
));
|
||||
|
||||
foreach (array_keys($this->validationRules) as $field) {
|
||||
if (!in_array($field, $this->allowedFields, true)) {
|
||||
unset($this->validationRules[$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getTableColumns(string $table): array
|
||||
{
|
||||
try {
|
||||
return $this->db->getFieldNames($table);
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', '[RefundModel] Could not read table columns for {table}: {error}', [
|
||||
'table' => $table,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total approved refund for a parent in a specific school year.
|
||||
*
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use CodeIgniter\Database\ConnectionInterface;
|
||||
use CodeIgniter\Model;
|
||||
use App\Models\Concerns\SchoolYearAutoFillTrait;
|
||||
use CodeIgniter\Validation\ValidationInterface;
|
||||
|
||||
class RefundPayoutModel extends Model
|
||||
{
|
||||
@@ -49,4 +51,37 @@ class RefundPayoutModel extends Model
|
||||
'operation_type' => 'permit_empty|max_length[50]',
|
||||
'request_fingerprint_hash' => 'permit_empty|exact_length[64]',
|
||||
];
|
||||
|
||||
private array $refundPayoutColumns = [];
|
||||
|
||||
public function __construct(?ConnectionInterface $db = null, ?ValidationInterface $validation = null)
|
||||
{
|
||||
parent::__construct($db, $validation);
|
||||
|
||||
$this->refundPayoutColumns = $this->getTableColumns($this->table);
|
||||
$this->allowedFields = array_values(array_filter(
|
||||
$this->allowedFields,
|
||||
fn (string $field): bool => in_array($field, $this->refundPayoutColumns, true)
|
||||
));
|
||||
|
||||
foreach (array_keys($this->validationRules) as $field) {
|
||||
if (!in_array($field, $this->allowedFields, true)) {
|
||||
unset($this->validationRules[$field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getTableColumns(string $table): array
|
||||
{
|
||||
try {
|
||||
return $this->db->getFieldNames($table);
|
||||
} catch (\Throwable $e) {
|
||||
log_message('error', '[RefundPayoutModel] Could not read table columns for {table}: {error}', [
|
||||
'table' => $table,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,11 +157,22 @@ class StudentClassModel extends Model
|
||||
);
|
||||
}
|
||||
|
||||
return $builder
|
||||
$rows = $builder
|
||||
->orderBy('students.lastname', 'ASC')
|
||||
->orderBy('students.firstname', 'ASC')
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
$unique = [];
|
||||
foreach ($rows as $row) {
|
||||
$studentId = (int) ($row['student_id'] ?? 0);
|
||||
if ($studentId <= 0 || isset($unique[$studentId])) {
|
||||
continue;
|
||||
}
|
||||
$unique[$studentId] = $row;
|
||||
}
|
||||
|
||||
return array_values($unique);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,4 +541,4 @@ class StudentClassModel extends Model
|
||||
|
||||
return $counts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,17 +150,14 @@ class StudentModel extends Model
|
||||
*/
|
||||
public function getStudentsWithAssignments()
|
||||
{
|
||||
// Retrieve school year and semester from the configuration table
|
||||
// Retrieve school year from configuration and derive the current semester from calendar dates.
|
||||
$configTable = $this->db->table('configuration');
|
||||
$schoolYear = $configTable->select('config_value')
|
||||
->where('config_key', 'school_year')
|
||||
->get()
|
||||
->getRowArray()['config_value'];
|
||||
|
||||
$semester = $configTable->select('config_value')
|
||||
->where('config_key', 'semester')
|
||||
->get()
|
||||
->getRowArray()['config_value'];
|
||||
$semester = getSemester();
|
||||
|
||||
return $this->db->table('students')
|
||||
->select('
|
||||
|
||||
Reference in New Issue
Block a user