recreate project

This commit is contained in:
root
2026-02-10 22:11:06 -05:00
commit 663c0cdbda
10149 changed files with 1379710 additions and 0 deletions
View File
@@ -0,0 +1,34 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddEventCategoryToEvents extends Migration
{
public function up()
{
$fields = $this->db->getFieldNames('events');
if (!in_array('event_category', $fields, true)) {
$this->forge->addColumn('events', [
'event_category' => [
'type' => 'VARCHAR',
'constraint' => 100,
'null' => true,
'after' => 'event_name',
'comment' => 'Admin-only category label',
],
]);
}
}
public function down()
{
$fields = $this->db->getFieldNames('events');
if (in_array('event_category', $fields, true)) {
$this->forge->dropColumn('events', 'event_category');
}
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddBatchNumberToReimbursements extends Migration
{
public function up()
{
$fields = [
'batch_number' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'default' => 0,
'null' => false,
'after' => 'reimbursement_method',
],
];
$this->forge->addColumn('reimbursements', $fields);
}
public function down()
{
$this->forge->dropColumn('reimbursements', 'batch_number');
}
}
@@ -0,0 +1,47 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddYearlyBatchNumberToReimbursementBatches extends Migration
{
public function up()
{
$fields = [
'yearly_batch_number' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'default' => 0,
'null' => false,
'after' => 'title',
],
];
$this->forge->addColumn('reimbursement_batches', $fields);
$this->fillExistingBatchNumbers();
}
public function down()
{
$this->forge->dropColumn('reimbursement_batches', 'yearly_batch_number');
}
private function fillExistingBatchNumbers(): void
{
$this->db->query("SET @prev_school_year := '', @row_number := 0");
$this->db->query(
'UPDATE reimbursement_batches b
JOIN (
SELECT id,
(@row_number := IF(@prev_school_year = COALESCE(school_year, \'\'), @row_number + 1, 1)) AS seq,
@prev_school_year := COALESCE(school_year, \'\') AS school_year_marker
FROM reimbursement_batches
ORDER BY COALESCE(school_year, \'\'), COALESCE(opened_at, \'1970-01-01 00:00:00\'), id
) ranks ON b.id = ranks.id
SET b.yearly_batch_number = ranks.seq'
);
}
}
@@ -0,0 +1,119 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateInventoryTables extends Migration
{
public function up()
{
// --------- inventory_categories ----------
$this->forge->addField([
'id' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
'type' => ['type' => 'ENUM("classroom","book","office","kitchen")', 'null' => false, 'default' => 'office'],
'name' => ['type' => 'VARCHAR', 'constraint' => 120, 'null' => false],
'description' => ['type' => 'TEXT', 'null' => true],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey(['type', 'name']); // unique per type
$this->forge->createTable('inventory_categories', true, [
'ENGINE' => 'InnoDB',
]);
// --------- inventory_items ----------
$this->forge->addField([
'id' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
'type' => ['type' => 'ENUM("classroom","book","office","kitchen")', 'null' => false],
'category_id' => ['type' => 'INT', 'unsigned' => true, 'null' => true],
'name' => ['type' => 'VARCHAR', 'constraint' => 190, 'null' => false],
'description' => ['type' => 'TEXT', 'null' => true],
'quantity' => ['type' => 'INT', 'unsigned' => true, 'default' => 0],
'unit' => ['type' => 'VARCHAR', 'constraint' => 32, 'null' => true, 'default' => null],
// Classroom-only condition
'condition' => ['type' => 'ENUM("good","needs_repair","need_replace","cannot_find")', 'null' => true, 'default' => null],
// Book extras (author removed earlier)
'isbn' => ['type' => 'VARCHAR', 'constraint' => 32, 'null' => true],
'edition' => ['type' => 'VARCHAR', 'constraint' => 32, 'null' => true],
// Misc
'sku' => ['type' => 'VARCHAR', 'constraint' => 64, 'null' => true],
'notes' => ['type' => 'TEXT', 'null' => true],
// Academic tagging
'semester' => ['type' => 'ENUM("Spring","Fall")', 'null' => true],
'school_year' => ['type' => 'VARCHAR', 'constraint' => 16, 'null' => true], // e.g. 2025-2026
// Audit
'updated_by' => ['type' => 'INT', 'unsigned' => true, 'null' => true],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
// Indexes
$this->forge->addKey('id', true);
$this->forge->addKey(['type', 'name']);
$this->forge->addKey(['school_year', 'semester']);
$this->forge->addKey('category_id'); // ensure indexed for FK
$this->forge->addKey('updated_by'); // ensure indexed for FK
// Category FK (safe; category table we just created with InnoDB)
$this->forge->addForeignKey('category_id', 'inventory_categories', 'id', 'SET NULL', 'CASCADE');
// Create items table first, then add users FK conditionally
$this->forge->createTable('inventory_items', true, [
'ENGINE' => 'InnoDB',
]);
// ---- Add updated_by → users(id) FK conditionally & safely ----
$db = \Config\Database::connect();
if ($db->tableExists('users')) {
// Confirm users is InnoDB and id is an unsigned INT
$engineRow = $db->query("
SELECT ENGINE FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users'
")->getFirstRow('array');
$colRow = $db->query("
SELECT COLUMN_TYPE, IS_NULLABLE
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'id'
")->getFirstRow('array');
$isInno = isset($engineRow['ENGINE']) && strtoupper($engineRow['ENGINE']) === 'INNODB';
$isUint = isset($colRow['COLUMN_TYPE']) && stripos($colRow['COLUMN_TYPE'], 'int') !== false && stripos($colRow['COLUMN_TYPE'], 'unsigned') !== false;
if ($isInno && $isUint) {
// Add the FK now (separate ALTER avoids "incorrectly formed" during CREATE)
$db->query("
ALTER TABLE inventory_items
ADD CONSTRAINT fk_inventory_items_updated_by
FOREIGN KEY (updated_by) REFERENCES users(id)
ON DELETE SET NULL ON UPDATE CASCADE
");
}
// If not compatible, we skip the FK to avoid migration failure.
}
}
public function down()
{
// Drop children first
$db = \Config\Database::connect();
if ($db->tableExists('inventory_items')) {
// drop conditional FK if exists
try { $db->query("ALTER TABLE inventory_items DROP FOREIGN KEY fk_inventory_items_updated_by"); } catch (\Throwable $e) {}
}
$this->forge->dropTable('inventory_items', true);
$this->forge->dropTable('inventory_categories', true);
}
}
@@ -0,0 +1,63 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateInventoryMovements extends Migration
{
public function up()
{
$this->forge->addField([
'id' => ['type'=>'INT','unsigned'=>true,'auto_increment'=>true],
'item_id' => ['type'=>'INT','unsigned'=>true,'null'=>false],
'qty_change' => ['type'=>'INT','null'=>false], // +in / -out
'movement_type' => ['type'=>'ENUM("initial","in","out","adjust","distribution")','null'=>false,'default'=>'adjust'],
'reason' => ['type'=>'VARCHAR','constraint'=>120,'null'=>true],
'note' => ['type'=>'TEXT','null'=>true],
// tagging
'semester' => ['type'=>'ENUM("Spring","Fall")','null'=>true],
'school_year' => ['type'=>'VARCHAR','constraint'=>16,'null'=>true], // e.g. 2025-2026
// who/for whom
'performed_by' => ['type'=>'INT','unsigned'=>true,'null'=>true], // user id
'teacher_id' => ['type'=>'INT','unsigned'=>true,'null'=>true],
'student_id' => ['type'=>'INT','unsigned'=>true,'null'=>true],
'class_section_id'=> ['type'=>'INT','unsigned'=>true,'null'=>true],
'created_at' => ['type'=>'DATETIME','null'=>true],
'updated_at' => ['type'=>'DATETIME','null'=>true],
]);
$this->forge->addKey('id', true);
$this->forge->addKey('item_id');
$this->forge->addKey(['school_year','semester']);
$this->forge->addKey('movement_type');
$this->forge->addForeignKey('item_id', 'inventory_items', 'id', 'CASCADE', 'CASCADE');
// Create table as InnoDB
$this->forge->createTable('inventory_movements', true, ['ENGINE'=>'InnoDB']);
// Optional FKs to users if compatible
$db = \Config\Database::connect();
if ($db->tableExists('users')) {
// try add FK; ignore failures (engine/type mismatch)
try {
$db->query("ALTER TABLE inventory_movements
ADD CONSTRAINT fk_inv_mov_performed_by FOREIGN KEY (performed_by) REFERENCES users(id)
ON DELETE SET NULL ON UPDATE CASCADE");
} catch (\Throwable $e) {}
try {
$db->query("ALTER TABLE inventory_movements
ADD CONSTRAINT fk_inv_mov_teacher FOREIGN KEY (teacher_id) REFERENCES users(id)
ON DELETE SET NULL ON UPDATE CASCADE");
} catch (\Throwable $e) {}
}
}
public function down()
{
$this->forge->dropTable('inventory_movements', true);
}
}
@@ -0,0 +1,124 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddClassroomConditionBuckets extends Migration
{
public function up()
{
$db = \Config\Database::connect();
// --- Ensure columns exist on inventory_items ---
$cols = $db->getFieldNames('inventory_items');
// 1) Academic tagging
$addAcademic = [];
if (!in_array('school_year', $cols, true)) {
$addAcademic['school_year'] = ['type' => 'VARCHAR', 'constraint' => 16, 'null' => true];
}
if (!in_array('semester', $cols, true)) {
$addAcademic['semester'] = ['type' => 'ENUM("Spring","Fall")', 'null' => true];
}
if ($addAcademic) {
$this->forge->addColumn('inventory_items', $addAcademic);
}
// 2) Condition buckets (classroom)
$addBuckets = [];
if (!in_array('good_qty', $cols, true)) $addBuckets['good_qty'] = ['type'=>'INT','unsigned'=>true,'default'=>0,'null'=>false,'after'=>'quantity'];
if (!in_array('needs_repair_qty', $cols, true)) $addBuckets['needs_repair_qty'] = ['type'=>'INT','unsigned'=>true,'default'=>0,'null'=>false,'after'=>'good_qty'];
if (!in_array('need_replace_qty', $cols, true)) $addBuckets['need_replace_qty'] = ['type'=>'INT','unsigned'=>true,'default'=>0,'null'=>false,'after'=>'needs_repair_qty'];
if (!in_array('cannot_find_qty', $cols, true)) $addBuckets['cannot_find_qty'] = ['type'=>'INT','unsigned'=>true,'default'=>0,'null'=>false,'after'=>'need_replace_qty'];
if ($addBuckets) {
$this->forge->addColumn('inventory_items', $addBuckets);
}
// 3) Backfill school_year/semester from configuration (or fallback)
[$sem, $yr] = $this->resolveCurrentAcademic($db);
// Only fill when missing
$db->query("
UPDATE inventory_items
SET school_year = COALESCE(NULLIF(school_year, ''), ?),
semester = COALESCE(semester, ?)
WHERE school_year IS NULL OR school_year = '' OR semester IS NULL
", [$yr, $sem]);
// 4) Backfill buckets for classroom rows (first-time init)
$db->query("
UPDATE inventory_items
SET
good_qty = quantity,
needs_repair_qty = COALESCE(needs_repair_qty,0),
need_replace_qty = COALESCE(need_replace_qty,0),
cannot_find_qty = COALESCE(cannot_find_qty,0)
WHERE type='classroom'
AND (COALESCE(good_qty,0) = 0
AND COALESCE(needs_repair_qty,0) = 0
AND COALESCE(need_replace_qty,0) = 0
AND COALESCE(cannot_find_qty,0) = 0)
");
// 5) Add index on (school_year, semester) if not present
$idx = $db->query("
SELECT 1
FROM information_schema.statistics
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'inventory_items'
AND INDEX_NAME = 'idx_items_schoolyear_semester'
LIMIT 1
")->getFirstRow();
if (!$idx) {
$db->query("ALTER TABLE inventory_items ADD INDEX idx_items_schoolyear_semester (school_year, semester)");
}
}
public function down()
{
// Buckets can be dropped safely
try { $this->forge->dropColumn('inventory_items', 'good_qty'); } catch (\Throwable $e) {}
try { $this->forge->dropColumn('inventory_items', 'needs_repair_qty'); } catch (\Throwable $e) {}
try { $this->forge->dropColumn('inventory_items', 'need_replace_qty'); } catch (\Throwable $e) {}
try { $this->forge->dropColumn('inventory_items', 'cannot_find_qty'); } catch (\Throwable $e) {}
// school_year/semester may be used elsewhere; drop only if you really want to
try { $this->forge->dropColumn('inventory_items', 'school_year'); } catch (\Throwable $e) {}
try { $this->forge->dropColumn('inventory_items', 'semester'); } catch (\Throwable $e) {}
// Drop index if it exists
$db = \Config\Database::connect();
try { $db->query("ALTER TABLE inventory_items DROP INDEX idx_items_schoolyear_semester"); } catch (\Throwable $e) {}
}
/**
* Resolve current academic year/semester from configuration table,
* fallback to date-based inference if not set.
*/
private function resolveCurrentAcademic($db): array
{
$sem = null; $yr = null;
if ($db->tableExists('configuration')) {
$sem = $db->table('configuration')->select('config_value')
->where('config_key','semester')->get()->getRow('config_value');
$yr = $db->table('configuration')->select('config_value')
->where('config_key','school_year')->get()->getRow('config_value');
}
if (!$sem || !$yr) {
$now = new \DateTime('now', new \DateTimeZone('America/New_York'));
$y = (int)$now->format('Y');
$m = (int)$now->format('n');
// Adjust these rules if your academic calendar differs
$sem = $sem ?: (($m >= 2 && $m <= 5) ? 'Spring' : 'Fall');
$yr = $yr ?: (($m >= 6) ? sprintf('%d-%d', $y, $y+1) : sprintf('%d-%d', $y-1, $y));
}
return [$sem, $yr];
}
}
@@ -0,0 +1,46 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddGradeRangeToBookCategories extends Migration
{
public function up()
{
$db = \Config\Database::connect();
$cols = $db->getFieldNames('inventory_categories');
$fields = [];
if (!in_array('grade_min', $cols, true)) {
$fields['grade_min'] = ['type' => 'TINYINT', 'unsigned' => true, 'null' => true, 'after' => 'description'];
}
if (!in_array('grade_max', $cols, true)) {
$fields['grade_max'] = ['type' => 'TINYINT', 'unsigned' => true, 'null' => true, 'after' => 'grade_min'];
}
if ($fields) {
$this->forge->addColumn('inventory_categories', $fields);
}
// Optional: composite index to speed up book-category lookups by range
$idx = $db->query("
SELECT 1 FROM information_schema.statistics
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME='inventory_categories'
AND INDEX_NAME='idx_cat_type_grade_range'
LIMIT 1
")->getFirstRow();
if (!$idx) {
$db->query("ALTER TABLE inventory_categories ADD INDEX idx_cat_type_grade_range (type, grade_min, grade_max)");
}
}
public function down()
{
$db = \Config\Database::connect();
try { $db->query("ALTER TABLE inventory_categories DROP INDEX idx_cat_type_grade_range"); } catch (\Throwable $e) {}
try { $this->forge->dropColumn('inventory_categories', 'grade_min'); } catch (\Throwable $e) {}
try { $this->forge->dropColumn('inventory_categories', 'grade_max'); } catch (\Throwable $e) {}
}
}
@@ -0,0 +1,35 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateStaffAttendance extends Migration
{
public function up()
{
$this->forge->addField([
'id' => ['type'=>'INT','unsigned'=>true,'auto_increment'=>true],
'user_id' => ['type'=>'INT','unsigned'=>true,'null'=>false],
'role_name' => ['type'=>'VARCHAR','constraint'=>64,'null'=>true],
'date' => ['type'=>'DATE','null'=>false],
'semester' => ['type'=>'VARCHAR','constraint'=>32,'null'=>false],
'school_year' => ['type'=>'VARCHAR','constraint'=>16,'null'=>false],
'status' => ['type'=>'ENUM','constraint'=>['present','absent','late'],'null'=>false,'default'=>'present'],
'reason' => ['type'=>'VARCHAR','constraint'=>255,'null'=>true],
'created_at' => ['type'=>'DATETIME','null'=>true],
'updated_at' => ['type'=>'DATETIME','null'=>true],
'created_by' => ['type'=>'INT','unsigned'=>true,'null'=>true],
'updated_by' => ['type'=>'INT','unsigned'=>true,'null'=>true],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey(['user_id','date','semester','school_year']);
$this->forge->addForeignKey('user_id','users','id','CASCADE','CASCADE');
$this->forge->createTable('staff_attendance', true);
}
public function down()
{
$this->forge->dropTable('staff_attendance', true);
}
}
@@ -0,0 +1,72 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateBadgePrintLogs extends Migration
{
public function up()
{
// Defensive: only create if not exists
if ($this->db->tableExists('badge_print_logs')) {
return;
}
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'user_id' => [
'type' => 'INT',
'constraint' => 11,
'null' => false,
],
'printed_by' => [
'type' => 'INT',
'constraint' => 11,
'null' => true,
],
'school_year' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => true,
],
'printed_at' => [
'type' => 'DATETIME',
'null' => false,
],
'role' => [
'type' => 'VARCHAR',
'constraint' => 128,
'null' => true,
],
'class_section_name' => [
'type' => 'VARCHAR',
'constraint' => 191,
'null' => true,
],
'copies' => [
'type' => 'INT',
'constraint' => 11,
'null' => false,
'default' => 1,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['user_id']);
$this->forge->addKey(['school_year']);
$this->forge->createTable('badge_print_logs', true);
}
public function down()
{
if ($this->db->tableExists('badge_print_logs')) {
$this->forge->dropTable('badge_print_logs', true);
}
}
}
@@ -0,0 +1,93 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateFamiliesTables extends Migration
{
public function up()
{
// families
$this->forge->addField([
'id' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
'family_code' => ['type' => 'VARCHAR', 'constraint' => 64, 'null' => false],
'household_name' => ['type' => 'VARCHAR', 'constraint' => 128, 'null' => true],
'address_line1' => ['type' => 'VARCHAR', 'constraint' => 128, 'null' => true],
'address_line2' => ['type' => 'VARCHAR', 'constraint' => 128, 'null' => true],
'city' => ['type' => 'VARCHAR', 'constraint' => 64, 'null' => true],
'state' => ['type' => 'VARCHAR', 'constraint' => 32, 'null' => true],
'postal_code' => ['type' => 'VARCHAR', 'constraint' => 16, 'null' => true],
'country' => ['type' => 'VARCHAR', 'constraint' => 64, 'null' => true],
'primary_phone' => ['type' => 'VARCHAR', 'constraint' => 32, 'null' => true],
'preferred_lang' => ['type' => 'VARCHAR', 'constraint' => 16, 'null' => true],
'preferred_contact_method' => ['type' => 'VARCHAR', 'constraint' => 32, 'null' => true],
'is_active' => ['type' => 'TINYINT', 'constraint' => 1, 'default' => 1, 'null' => false],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey('family_code');
$this->forge->createTable('families', true);
// family_students
$this->forge->addField([
'id' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
'family_id' => ['type' => 'INT', 'unsigned' => true, 'null' => false],
'student_id' => ['type' => 'INT', 'unsigned' => true, 'null' => false],
'is_primary_home' => ['type' => 'TINYINT', 'constraint' => 1, 'default' => 0, 'null' => false],
'notes' => ['type' => 'TEXT', 'null' => true],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey(['family_id', 'student_id']);
$this->forge->addForeignKey('family_id', 'families', 'id', 'CASCADE', 'CASCADE');
$this->forge->addForeignKey('student_id', 'students', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('family_students', true);
// family_guardians
$this->forge->addField([
'id' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
'family_id' => ['type' => 'INT', 'unsigned' => true, 'null' => false],
'user_id' => ['type' => 'INT', 'unsigned' => true, 'null' => false],
'relation' => ['type' => 'VARCHAR', 'constraint' => 32, 'null' => false, 'default' => 'guardian'],
'is_primary' => ['type' => 'TINYINT', 'constraint' => 1, 'default' => 0, 'null' => false],
'receive_emails' => ['type' => 'TINYINT', 'constraint' => 1, 'default' => 1, 'null' => false],
'receive_sms' => ['type' => 'TINYINT', 'constraint' => 1, 'default' => 0, 'null' => false],
'custody_notes' => ['type' => 'TEXT', 'null' => true],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey(['family_id', 'user_id']);
$this->forge->addForeignKey('family_id', 'families', 'id', 'CASCADE', 'CASCADE');
$this->forge->addForeignKey('user_id', 'users', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('family_guardians', true);
// family_comm_prefs
$this->forge->addField([
'id' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
'family_id' => ['type' => 'INT', 'unsigned' => true, 'null' => false],
'category' => ['type' => 'VARCHAR', 'constraint' => 32, 'null' => false],
'via_email' => ['type' => 'TINYINT', 'constraint' => 1, 'default' => 1, 'null' => false],
'via_sms' => ['type' => 'TINYINT', 'constraint' => 1, 'default' => 0, 'null' => false],
'cc_all_guardians' => ['type' => 'TINYINT', 'constraint' => 1, 'default' => 1, 'null' => false],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey(['family_id', 'category']);
$this->forge->addForeignKey('family_id', 'families', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('family_comm_prefs', true);
}
public function down()
{
$this->forge->dropTable('family_comm_prefs', true);
$this->forge->dropTable('family_guardians', true);
$this->forge->dropTable('family_students', true);
$this->forge->dropTable('families', true);
}
}
@@ -0,0 +1,41 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddNoSchoolToCalendarEvents extends Migration
{
public function up()
{
$db = \Config\Database::connect();
$cols = [];
try {
$cols = $db->getFieldNames('calendar_events');
} catch (\Throwable $e) {
$cols = [];
}
if (!in_array('no_school', $cols, true)) {
$this->forge->addColumn('calendar_events', [
'no_school' => [
'type' => 'TINYINT',
'constraint' => 1,
'null' => false,
'default' => 0,
'after' => 'notify_teacher',
],
]);
}
}
public function down()
{
try {
$this->forge->dropColumn('calendar_events', 'no_school');
} catch (\Throwable $e) {
// ignore
}
}
}
@@ -0,0 +1,83 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Forge; // optional
use CodeIgniter\Database\RawSql;
use CodeIgniter\Database\Migration;
class CreateWhatsappGroupMemberships extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'class_section_id' => [
'type' => 'INT',
'constraint' => 11,
'null' => false,
],
'school_year' => [
'type' => 'VARCHAR',
'constraint' => 25,
'null' => false,
],
'semester' => [
'type' => 'VARCHAR',
'constraint' => 20,
'null' => false,
],
'subject_type' => [
'type' => 'ENUM',
'constraint' => ['primary', 'second'],
'null' => false,
'default' => 'primary',
],
'subject_id' => [
'type' => 'INT',
'constraint' => 11,
'null' => false,
'comment' => 'users.id for primary; parents.id for second',
],
'is_member' => [
'type' => 'TINYINT',
'constraint' => 1,
'null' => false,
'default' => 0,
],
'verified_by' => [
'type' => 'INT',
'constraint' => 11,
'null' => true,
],
'verified_at' => [
'type' => 'DATETIME',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['class_section_id', 'school_year', 'semester']);
$this->forge->addUniqueKey(['class_section_id', 'school_year', 'semester', 'subject_type', 'subject_id'], 'uniq_whatsapp_membership');
$this->forge->createTable('whatsapp_group_memberships');
}
public function down()
{
$this->forge->dropTable('whatsapp_group_memberships', true);
}
}
@@ -0,0 +1,79 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateLateSlipLogs extends Migration
{
public function up()
{
if ($this->db->tableExists('late_slip_logs')) {
return;
}
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'school_year' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => true,
],
'student_name' => [
'type' => 'VARCHAR',
'constraint' => 191,
'null' => false,
],
'slip_date' => [
'type' => 'DATE',
'null' => true,
],
'time_in' => [
'type' => 'TIME',
'null' => true,
],
'grade' => [
'type' => 'VARCHAR',
'constraint' => 64,
'null' => true,
],
'reason' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => true,
],
'admin_name' => [
'type' => 'VARCHAR',
'constraint' => 191,
'null' => true,
],
'printed_by' => [
'type' => 'INT',
'constraint' => 11,
'null' => true,
],
'printed_at' => [
'type' => 'DATETIME',
'null' => false,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['school_year']);
$this->forge->addKey(['student_name']);
$this->forge->createTable('late_slip_logs', true);
}
public function down()
{
if ($this->db->tableExists('late_slip_logs')) {
$this->forge->dropTable('late_slip_logs', true);
}
}
}
@@ -0,0 +1,119 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreatePaymentNotificationLogs extends Migration
{
public function up()
{
if ($this->db->tableExists('payment_notification_logs')) {
return;
}
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'parent_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => false,
],
'invoice_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
'school_year' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => true,
],
'period_year' => [
'type' => 'SMALLINT',
'constraint' => 4,
'null' => false,
],
'period_month' => [
'type' => 'TINYINT',
'constraint' => 2,
'null' => false,
],
'type' => [
'type' => 'ENUM',
'constraint' => ['no_payment', 'installment'],
'default' => 'installment',
],
'to_email' => [
'type' => 'VARCHAR',
'constraint' => 191,
'null' => true,
],
'cc_email' => [
'type' => 'VARCHAR',
'constraint' => 191,
'null' => true,
],
'head_fa_notified' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 0,
],
'subject' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => true,
],
'body' => [
'type' => 'MEDIUMTEXT',
'null' => true,
],
'status' => [
'type' => 'ENUM',
'constraint' => ['sent', 'failed'],
'default' => 'sent',
],
'error_message' => [
'type' => 'TEXT',
'null' => true,
],
'balance_snapshot' => [
'type' => 'DECIMAL',
'constraint' => '10,2',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'sent_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['parent_id']);
$this->forge->addKey(['invoice_id']);
$this->forge->addKey(['school_year']);
$this->forge->addKey(['period_year', 'period_month']);
// Unique combination to prevent duplicate sends per period/type
$this->forge->addKey(['parent_id', 'period_year', 'period_month', 'type'], false, true);
$this->forge->createTable('payment_notification_logs', true);
}
public function down()
{
if ($this->db->tableExists('payment_notification_logs')) {
$this->forge->dropTable('payment_notification_logs', true);
}
}
}
@@ -0,0 +1,100 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateParentAttendanceReports extends Migration
{
public function up()
{
if ($this->db->tableExists('parent_attendance_reports')) {
return; // already created
}
$this->forge->addField([
'id' => [
'type' => 'INT',
'unsigned' => true,
'auto_increment' => true,
],
'parent_id' => [
'type' => 'INT',
'unsigned' => true,
'null' => false,
'comment' => 'users.id of the reporting parent (primary parent id)'
],
'student_id' => [
'type' => 'INT',
'unsigned' => true,
'null' => false,
],
'class_section_id' => [
'type' => 'INT',
'unsigned' => true,
'null' => true,
],
'report_date' => [
'type' => 'DATE',
'null' => false,
],
'type' => [
'type' => 'ENUM',
'constraint' => ['absent', 'late', 'early_dismissal'],
'default' => 'absent',
'null' => false,
],
'arrival_time' => [
'type' => 'TIME', // for late
'null' => true,
],
'dismiss_time' => [
'type' => 'TIME', // for early dismissal
'null' => true,
],
'reason' => [
'type' => 'TEXT',
'null' => true,
],
'semester' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => true,
],
'school_year' => [
'type' => 'VARCHAR',
'constraint' => 16,
'null' => true,
],
'status' => [
'type' => 'ENUM',
'constraint' => ['new','seen','processed'],
'default' => 'new',
'null' => false,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['report_date']);
$this->forge->addKey(['student_id', 'report_date']);
$this->forge->addForeignKey('parent_id', 'users', 'id', 'CASCADE', 'CASCADE');
$this->forge->addForeignKey('student_id', 'students', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('parent_attendance_reports', true);
}
public function down()
{
if ($this->db->tableExists('parent_attendance_reports')) {
$this->forge->dropTable('parent_attendance_reports', true);
}
}
}
@@ -0,0 +1,44 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddSemesterToLateSlipLogs extends Migration
{
public function up()
{
// Add nullable semester column and an index with school_year if not present
if ($this->db->tableExists('late_slip_logs')) {
$fields = $this->db->getFieldNames('late_slip_logs');
if (!in_array('semester', $fields, true)) {
$this->forge->addColumn('late_slip_logs', [
'semester' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => true,
'after' => 'school_year',
],
]);
}
// Best-effort: add composite index if missing
try {
$sql = "SHOW INDEX FROM late_slip_logs WHERE Key_name = 'idx_late_slip_year_sem'";
$exists = $this->db->query($sql)->getResultArray();
if (empty($exists)) {
$this->db->query('ALTER TABLE late_slip_logs ADD INDEX idx_late_slip_year_sem (school_year, semester)');
}
} catch (\Throwable $e) { /* ignore */ }
}
}
public function down()
{
if ($this->db->tableExists('late_slip_logs')) {
try { $this->db->query('ALTER TABLE late_slip_logs DROP INDEX idx_late_slip_year_sem'); } catch (\Throwable $e) {}
try { $this->forge->dropColumn('late_slip_logs', 'semester'); } catch (\Throwable $e) {}
}
}
}
@@ -0,0 +1,89 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreatePromotionQueue extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'student_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => false,
],
'from_class_section_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
'default' => null,
],
'to_class_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => false,
],
'to_class_section_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
'default' => null,
],
'school_year_from' => [
'type' => 'VARCHAR',
'constraint' => 9,
'null' => false,
],
'school_year_to' => [
'type' => 'VARCHAR',
'constraint' => 9,
'null' => false,
],
'status' => [
'type' => 'ENUM',
'constraint' => ['queued','assigned','applied','cancelled'],
'default' => 'queued',
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
'default' => null,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
'default' => null,
],
'updated_by' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
'default' => null,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['student_id', 'school_year_to'], false, true, 'uq_student_year_to');
$this->forge->addKey('to_class_id');
$this->forge->createTable('promotion_queue', true);
}
public function down()
{
$this->forge->dropTable('promotion_queue', true);
}
}
@@ -0,0 +1,82 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateEarlyDismissalSignatures extends Migration
{
public function up()
{
if ($this->db->tableExists('early_dismissal_signatures')) {
return;
}
$this->forge->addField([
'id' => [
'type' => 'INT',
'unsigned' => true,
'auto_increment' => true,
],
'report_date' => [
'type' => 'DATE',
'null' => false,
],
'school_year' => [
'type' => 'VARCHAR',
'constraint' => 16,
'null' => true,
],
'semester' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => true,
],
'filename' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => false,
],
'original_name' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => true,
],
'mime_type' => [
'type' => 'VARCHAR',
'constraint' => 128,
'null' => true,
],
'file_size' => [
'type' => 'INT',
'unsigned' => true,
'null' => true,
],
'uploaded_by' => [
'type' => 'INT',
'unsigned' => true,
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['report_date']);
$this->forge->addUniqueKey(['report_date', 'school_year', 'semester'], 'uniq_early_dismissal_signature');
$this->forge->createTable('early_dismissal_signatures', true);
}
public function down()
{
if ($this->db->tableExists('early_dismissal_signatures')) {
$this->forge->dropTable('early_dismissal_signatures', true);
}
}
}
@@ -0,0 +1,47 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddTimezoneToPreferencesAndSettings extends Migration
{
public function up()
{
// user_preferences.timezone
if ($this->db->tableExists('user_preferences') && ! $this->db->fieldExists('timezone', 'user_preferences')) {
$this->forge->addColumn('user_preferences', [
'timezone' => [
'type' => 'VARCHAR',
'constraint' => 64,
'null' => true,
'after' => 'language',
],
]);
}
// settings.timezone (only if table exists)
if ($this->db->tableExists('settings') && ! $this->db->fieldExists('timezone', 'settings')) {
$this->forge->addColumn('settings', [
'timezone' => [
'type' => 'VARCHAR',
'constraint' => 64,
'null' => true,
'after' => 'name',
],
]);
}
}
public function down()
{
if ($this->db->tableExists('user_preferences') && $this->db->fieldExists('timezone', 'user_preferences')) {
$this->forge->dropColumn('user_preferences', 'timezone');
}
if ($this->db->tableExists('settings') && $this->db->fieldExists('timezone', 'settings')) {
$this->forge->dropColumn('settings', 'timezone');
}
}
}
@@ -0,0 +1,40 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddDonationCategoryToExpenses extends Migration
{
/**
* Allow marking expenses as non-reimbursable donations.
*/
public function up()
{
$this->forge->modifyColumn('expenses', [
'category' => [
'type' => "ENUM('Expense','Purchase','Reimbursement','Donation')",
'null' => false,
],
]);
}
/**
* Revert to the original enum; donation rows are coerced back to Expense.
*/
public function down()
{
// Convert any donation rows to Expense to keep rollback safe.
$this->db->table('expenses')
->where('category', 'Donation')
->set('category', 'Expense')
->update();
$this->forge->modifyColumn('expenses', [
'category' => [
'type' => "ENUM('Expense','Purchase','Reimbursement')",
'null' => false,
],
]);
}
}
@@ -0,0 +1,65 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateReimbursementBatchAdminFiles extends Migration
{
public function up()
{
$fields = [
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'batch_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => false,
],
'admin_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => false,
'default' => 0,
],
'filename' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => false,
],
'original_filename' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => true,
],
'uploaded_at' => [
'type' => 'DATETIME',
'null' => false,
],
'uploaded_by' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
];
$this->forge->addField($fields);
$this->forge->addKey('id', true);
$this->forge->addKey('batch_id');
$this->forge->addKey('admin_id');
$this->forge->addUniqueKey(['batch_id', 'admin_id']);
$this->forge->createTable('reimbursement_batch_admin_files');
}
public function down()
{
$this->forge->dropTable('reimbursement_batch_admin_files', true);
}
}
@@ -0,0 +1,83 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddReviewFieldsToScoreComments extends Migration
{
public function up()
{
if (!$this->db->tableExists('score_comments')) {
return;
}
$fields = $this->db->getFieldNames('score_comments');
if (!in_array('comment_review', $fields, true)) {
$this->forge->addColumn('score_comments', [
'comment_review' => [
'type' => 'TEXT',
'null' => true,
'after' => 'comment',
],
]);
}
if (!in_array('reviewed_by', $fields, true)) {
$this->forge->addColumn('score_comments', [
'reviewed_by' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
'after' => 'comment_review',
],
]);
}
// Allow comments to be nullable so reviewers can attach feedback without a comment body
if (in_array('comment', $fields, true)) {
try {
$this->forge->modifyColumn('score_comments', [
'comment' => [
'type' => 'TEXT',
'null' => true,
],
]);
} catch (\Throwable $e) {
// Best effort only
}
}
}
public function down()
{
if (!$this->db->tableExists('score_comments')) {
return;
}
try {
$this->forge->modifyColumn('score_comments', [
'comment' => [
'type' => 'TEXT',
'null' => false,
],
]);
} catch (\Throwable $e) {
// Ignore rollback errors
}
try {
$this->forge->dropColumn('score_comments', 'reviewed_by');
} catch (\Throwable $e) {
// Ignore rollback errors
}
try {
$this->forge->dropColumn('score_comments', 'comment_review');
} catch (\Throwable $e) {
// Ignore rollback errors
}
}
}
@@ -0,0 +1,175 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateAttendanceCommentTemplates extends Migration
{
public function up()
{
if ($this->db->tableExists('attendance_comment_template')) {
return;
}
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'min_score' => [
'type' => 'DECIMAL',
'constraint' => '5,2',
'null' => false,
],
'max_score' => [
'type' => 'DECIMAL',
'constraint' => '5,2',
'null' => false,
],
'template_text' => [
'type' => 'TEXT',
'null' => false,
],
'is_active' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 1,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['min_score', 'max_score']);
$this->forge->addKey('is_active');
$this->forge->createTable('attendance_comment_template', true);
$now = date('Y-m-d H:i:s');
$rows = [
[
'min_score' => 100.00,
'max_score' => 100.00,
'template_text' => '{name} has maintained perfect attendance this semester, showing exceptional commitment and reliability. Outstanding consistency in attending every class session without fail. Keep it up and stay strong - bravo!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'min_score' => 95.00,
'max_score' => 99.99,
'template_text' => '{name} has maintained excellent attendance this semester, demonstrating strong commitment and reliability. Only rare absences, with a consistent routine of being present for class. Keep up the great consistency - well done!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'min_score' => 90.00,
'max_score' => 94.99,
'template_text' => '{name} has demonstrated very good attendance this semester, reflecting dependable responsibility. Missed only a few class sessions and remains consistent overall. Keep striving for even stronger attendance - great work!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'min_score' => 85.00,
'max_score' => 89.99,
'template_text' => '{name} has maintained good attendance this semester and displays responsible habits. A few absences were noted, yet overall consistency is solid. Aim for even fewer missed days to strengthen your routine - keep it going!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'min_score' => 80.00,
'max_score' => 84.99,
'template_text' => '{name}\'s attendance has been fairly consistent this semester, with some missed class sessions. Being present more regularly will support steady progress. You\'re on the right track - keep improving!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'min_score' => 75.00,
'max_score' => 79.99,
'template_text' => '{name}\'s attendance has been moderate this semester, with several absences impacting consistency. Prioritizing attendance will help strengthen routines and lead to success. Let\'s keep improving week by week - you\'ve got this!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'min_score' => 70.00,
'max_score' => 74.99,
'template_text' => '{name}\'s attendance has been somewhat inconsistent this semester, with missed sessions creating noticeable gaps. Focusing on regular attendance will help build a steady and reliable routine. Keep working on it - you can do it!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'min_score' => 65.00,
'max_score' => 69.99,
'template_text' => '{name}\'s attendance needs improvement this semester due to several missed classes. A renewed focus on being present regularly will help build stronger habits and lead to a successful finish. Keep going - you\'ve got this!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'min_score' => 60.00,
'max_score' => 64.99,
'template_text' => '{name}\'s attendance has been a concern this semester, with many missed classes. Making a consistent effort to attend regularly will help strengthen routines and keep learning on track. Stay focused - you can do this!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'min_score' => 55.00,
'max_score' => 59.99,
'template_text' => '{name}\'s attendance has been challenging this semester, with numerous absences recorded. A stronger commitment to being present each week will help build better habits and steady progress. Keep pushing forward!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'min_score' => 50.00,
'max_score' => 54.99,
'template_text' => '{name}\'s attendance has been inconsistent this semester, with many missed sessions. Prioritizing daily attendance will help rebuild consistency, strengthen learning routines, and support steady progress. You\'ve got this - stay committed!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'min_score' => 45.00,
'max_score' => 49.99,
'template_text' => '{name}\'s attendance has been irregular this semester, with several missed classes. Refocusing on regular attendance will help restore consistency and lay the foundation for success. Keep at it - you\'re capable!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
[
'min_score' => 0.00,
'max_score' => 44.99,
'template_text' => '{name}\'s attendance has been significantly limited this semester, with many absences. Starting fresh with consistent attendance can help you rebuild strong habits and finish the term successfully. A new start - let\'s go!',
'is_active' => 1,
'created_at' => $now,
'updated_at' => $now,
],
];
$this->db->table('attendance_comment_template')->insertBatch($rows);
}
public function down()
{
if (!$this->db->tableExists('attendance_comment_template')) {
return;
}
$this->forge->dropTable('attendance_comment_template', true);
}
}
@@ -0,0 +1,74 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreatePrintRequests extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'teacher_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'admin_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
'class_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'file_path' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'num_copies' => [
'type' => 'INT',
'constraint' => 11,
],
'required_by' => [
'type' => 'DATETIME',
],
'pickup_method' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'status' => [
'type' => 'ENUM',
'constraint' => ['not_assigned', 'assigned', 'done', 'delivered'],
'default' => 'not_assigned',
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addForeignKey('teacher_id', 'users', 'id', 'CASCADE', 'CASCADE');
$this->forge->addForeignKey('admin_id', 'users', 'id', 'CASCADE', 'SET NULL');
$this->forge->addForeignKey('class_id', 'classes', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('print_requests');
}
public function down()
{
$this->forge->dropTable('print_requests');
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class FixPrintRequestsForeignKey extends Migration
{
public function up()
{
// Drop the old foreign key if it exists
$this->forge->dropForeignKey('print_requests', 'print_requests_class_id_foreign');
// Add the new foreign key
$this->forge->addForeignKey('class_id', 'class_sections', 'id', 'CASCADE', 'CASCADE');
// Process the modifications
$this->forge->processIndexes('print_requests');
}
public function down()
{
// Drop the new foreign key
$this->forge->dropForeignKey('print_requests', 'print_requests_class_id_foreign');
// Re-add the old foreign key
$this->forge->addForeignKey('class_id', 'classes', 'id', 'CASCADE', 'CASCADE');
// Process the modifications
$this->forge->processIndexes('print_requests');
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class RevertPrintRequestsForeignKey extends Migration
{
public function up()
{
// Drop the old foreign key if it exists
$this->forge->dropForeignKey('print_requests', 'print_requests_class_id_foreign');
// Add the new foreign key
$this->forge->addForeignKey('class_id', 'classes', 'id', 'CASCADE', 'CASCADE');
// Process the modifications
$this->forge->processIndexes('print_requests');
}
public function down()
{
// Drop the new foreign key
$this->forge->dropForeignKey('print_requests', 'print_requests_class_id_foreign');
// Re-add the old foreign key
$this->forge->addForeignKey('class_id', 'class_sections', 'id', 'CASCADE', 'CASCADE');
// Process the modifications
$this->forge->processIndexes('print_requests');
}
}
@@ -0,0 +1,23 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class DropPrintRequestsForeignKey extends Migration
{
public function up()
{
// Drop the old foreign key if it exists
$this->forge->dropForeignKey('print_requests', 'print_requests_class_id_foreign');
}
public function down()
{
// Re-add the old foreign key
$this->forge->addForeignKey('class_id', 'class_sections', 'id', 'CASCADE', 'CASCADE');
// Process the modifications
$this->forge->processIndexes('print_requests');
}
}
@@ -0,0 +1,28 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class FixPrintRequestsForeignKeyOnceAndForAll extends Migration
{
public function up()
{
$db = \Config\Database::connect();
$keys = $db->getForeignKeyData('print_requests');
foreach ($keys as $key) {
if ($key->constraint_name === 'print_requests_class_id_foreign') {
$this->forge->dropForeignKey('print_requests', 'print_requests_class_id_foreign');
break;
}
}
$this->forge->addForeignKey('class_id', 'classSection', 'id', 'CASCADE', 'CASCADE');
$this->forge->processIndexes('print_requests');
}
public function down()
{
$this->forge->dropForeignKey('print_requests', 'print_requests_class_id_foreign');
}
}
@@ -0,0 +1,28 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class FixPrintRequestsForeignKeyAgain extends Migration
{
public function up()
{
$db = \Config\Database::connect();
$keys = $db->getForeignKeyData('print_requests');
foreach ($keys as $key) {
if ($key->constraint_name === 'print_requests_class_id_foreign') {
$this->forge->dropForeignKey('print_requests', 'print_requests_class_id_foreign');
break;
}
}
$this->forge->addForeignKey('class_id', 'classSection', 'class_id', 'CASCADE', 'CASCADE');
$this->forge->processIndexes('print_requests');
}
public function down()
{
$this->forge->dropForeignKey('print_requests', 'print_requests_class_id_foreign');
}
}
@@ -0,0 +1,40 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateCompetitions extends Migration
{
public function up()
{
$this->forge->addField([
'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'title' => ['type' => 'VARCHAR', 'constraint' => 150],
'semester' => ['type' => 'VARCHAR', 'constraint' => 30, 'null' => true],
'school_year' => ['type' => 'VARCHAR', 'constraint' => 20, 'null' => true],
'class_section_id' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
'start_date' => ['type' => 'DATE', 'null' => true],
'end_date' => ['type' => 'DATE', 'null' => true],
'winners_count' => ['type' => 'INT', 'constraint' => 11, 'default' => 3],
'prize_per_point' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'default' => 1],
'is_published' => ['type' => 'TINYINT', 'constraint' => 1, 'default' => 0],
'published_at' => ['type' => 'DATETIME', 'null' => true],
'created_by' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
'deleted_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['class_section_id']);
$this->forge->addKey(['is_published']);
$this->forge->createTable('competitions', true);
}
public function down()
{
$this->forge->dropTable('competitions', true);
}
}
@@ -0,0 +1,35 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateCompetitionScores extends Migration
{
public function up()
{
$this->forge->addField([
'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'competition_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
'student_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
'class_section_id' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
'score' => ['type' => 'DECIMAL', 'constraint' => '6,2', 'default' => 0],
'notes' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['competition_id']);
$this->forge->addKey(['student_id']);
$this->forge->addKey(['class_section_id']);
$this->forge->addUniqueKey(['competition_id', 'student_id', 'class_section_id']);
$this->forge->createTable('competition_scores', true);
}
public function down()
{
$this->forge->dropTable('competition_scores', true);
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateCompetitionWinners extends Migration
{
public function up()
{
$this->forge->addField([
'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'competition_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
'student_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
'class_section_id' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
'rank' => ['type' => 'INT', 'constraint' => 11],
'score' => ['type' => 'DECIMAL', 'constraint' => '6,2', 'default' => 0],
'prize_amount' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'default' => 0],
'created_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['competition_id']);
$this->forge->addKey(['class_section_id']);
$this->forge->addUniqueKey(['competition_id', 'class_section_id', 'rank']);
$this->forge->addUniqueKey(['competition_id', 'class_section_id', 'student_id']);
$this->forge->createTable('competition_winners', true);
}
public function down()
{
$this->forge->dropTable('competition_winners', true);
}
}
@@ -0,0 +1,139 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddCompetitionNavItems extends Migration
{
public function up()
{
$db = \Config\Database::connect();
if (!$db->tableExists('nav_items')) {
return;
}
$parentColumn = null;
if ($db->fieldExists('menu_parent_id', 'nav_items')) {
$parentColumn = 'menu_parent_id';
} elseif ($db->fieldExists('parent_id', 'nav_items')) {
$parentColumn = 'parent_id';
}
if ($parentColumn === null) {
return;
}
$navBuilder = $db->table('nav_items');
$now = date('Y-m-d H:i:s');
$eventRow = $navBuilder
->select('id')
->where('label', 'Event Management')
->where($parentColumn, null)
->get()
->getRowArray();
if ($eventRow) {
$eventId = (int) $eventRow['id'];
} else {
$navBuilder->insert([
$parentColumn => null,
'label' => 'Event Management',
'url' => null,
'sort_order' => 110,
'is_enabled' => 1,
'created_at' => $now,
'updated_at' => $now,
]);
$eventId = (int) $db->insertID();
}
$competitionUrl = 'admin/competition-winners';
$existing = $navBuilder->select('id')
->where('url', $competitionUrl)
->get()
->getRowArray();
if (!$existing) {
$navBuilder->insert([
$parentColumn => $eventId ?: null,
'label' => 'Competition Winners',
'url' => $competitionUrl,
'sort_order' => 3,
'is_enabled' => 1,
'created_at' => $now,
'updated_at' => $now,
]);
$competitionId = (int) $db->insertID();
} else {
$competitionId = (int) $existing['id'];
}
if (!$db->tableExists('role_nav_items') || !$db->tableExists('roles')) {
return;
}
if ($competitionId <= 0) {
return;
}
$roleRows = $db->table('roles')
->select('id, name')
->whereIn('name', ['administrator', 'principal', 'vice_principal'])
->get()
->getResultArray();
$roleMap = $db->table('role_nav_items');
foreach ($roleRows as $role) {
$roleId = (int) ($role['id'] ?? 0);
if ($roleId <= 0) {
continue;
}
$exists = $roleMap
->where('role_id', $roleId)
->where('nav_item_id', $competitionId)
->get()
->getRowArray();
if ($exists) {
continue;
}
$roleMap->insert([
'role_id' => $roleId,
'nav_item_id' => $competitionId,
'created_at' => $now,
'updated_at' => $now,
]);
}
}
public function down()
{
$db = \Config\Database::connect();
if (!$db->tableExists('nav_items')) {
return;
}
$competitionUrl = 'admin/competition-winners';
$row = $db->table('nav_items')
->select('id')
->where('url', $competitionUrl)
->get()
->getRowArray();
if ($row && $db->tableExists('role_nav_items')) {
$db->table('role_nav_items')
->where('nav_item_id', (int) $row['id'])
->delete();
}
$db->table('nav_items')
->where('url', $competitionUrl)
->delete();
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateCompetitionClassWinners extends Migration
{
public function up()
{
$this->forge->addField([
'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'competition_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
'class_section_id' => ['type' => 'INT', 'constraint' => 11],
'winners_override' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
'question_count' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
'prize_1' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
'prize_2' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
'prize_3' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
'prize_4' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
'prize_5' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
'prize_6' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addUniqueKey(['competition_id', 'class_section_id']);
$this->forge->createTable('competition_class_winners', true);
}
public function down()
{
$this->forge->dropTable('competition_class_winners', true);
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddQuestionCountToCompetitionClassWinners extends Migration
{
public function up()
{
$db = \Config\Database::connect();
if (!$db->tableExists('competition_class_winners')) {
return;
}
if ($db->fieldExists('question_count', 'competition_class_winners')) {
return;
}
$this->forge->addColumn('competition_class_winners', [
'question_count' => ['type' => 'INT', 'constraint' => 11, 'null' => true],
]);
}
public function down()
{
$db = \Config\Database::connect();
if (!$db->tableExists('competition_class_winners')) {
return;
}
if (!$db->fieldExists('question_count', 'competition_class_winners')) {
return;
}
$this->forge->dropColumn('competition_class_winners', 'question_count');
}
}
@@ -0,0 +1,51 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddPrizeColumnsToCompetitionClassWinners extends Migration
{
public function up()
{
$db = \Config\Database::connect();
if (!$db->tableExists('competition_class_winners')) {
return;
}
$columns = [
'prize_1' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
'prize_2' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
'prize_3' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
'prize_4' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
'prize_5' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
'prize_6' => ['type' => 'DECIMAL', 'constraint' => '10,2', 'null' => true],
];
$add = [];
foreach ($columns as $name => $def) {
if (!$db->fieldExists($name, 'competition_class_winners')) {
$add[$name] = $def;
}
}
if (!empty($add)) {
$this->forge->addColumn('competition_class_winners', $add);
}
}
public function down()
{
$db = \Config\Database::connect();
if (!$db->tableExists('competition_class_winners')) {
return;
}
$names = ['prize_1','prize_2','prize_3','prize_4','prize_5','prize_6'];
foreach ($names as $name) {
if ($db->fieldExists($name, 'competition_class_winners')) {
$this->forge->dropColumn('competition_class_winners', $name);
}
}
}
}
@@ -0,0 +1,42 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AllowCompetitionWinnerRankTies extends Migration
{
public function up()
{
$db = \Config\Database::connect();
if (!$db->tableExists('competition_winners')) {
return;
}
if ($db->indexExists('competition_winners', 'competition_id_class_section_id_rank')) {
$db->query('DROP INDEX competition_id_class_section_id_rank ON competition_winners');
}
if (!$db->indexExists('competition_winners', 'competition_id_class_section_id_rank')) {
$db->query(
'CREATE INDEX competition_id_class_section_id_rank ON competition_winners (competition_id, class_section_id, rank)'
);
}
}
public function down()
{
$db = \Config\Database::connect();
if (!$db->tableExists('competition_winners')) {
return;
}
if ($db->indexExists('competition_winners', 'competition_id_class_section_id_rank')) {
$db->query('DROP INDEX competition_id_class_section_id_rank ON competition_winners');
}
$db->query(
'ALTER TABLE competition_winners ADD UNIQUE KEY competition_id_class_section_id_rank (competition_id, class_section_id, rank)'
);
}
}
@@ -0,0 +1,57 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateAdminNotificationSubjects extends Migration
{
public function up()
{
if ($this->db->tableExists('admin_notification_subjects')) {
return;
}
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'admin_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => false,
],
'subject' => [
'type' => 'VARCHAR',
'constraint' => 100,
'null' => false,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey('admin_id');
$this->forge->addUniqueKey(['admin_id', 'subject']);
$this->forge->createTable('admin_notification_subjects', true);
}
public function down()
{
if (!$this->db->tableExists('admin_notification_subjects')) {
return;
}
$this->forge->dropTable('admin_notification_subjects', true);
}
}
@@ -0,0 +1,52 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddCompetitionLockFields extends Migration
{
public function up()
{
$db = \Config\Database::connect();
if (!$db->tableExists('competitions')) {
return;
}
$fields = [];
if (!$db->fieldExists('is_locked', 'competitions')) {
$fields['is_locked'] = ['type' => 'TINYINT', 'constraint' => 1, 'default' => 0];
}
if (!$db->fieldExists('locked_at', 'competitions')) {
$fields['locked_at'] = ['type' => 'DATETIME', 'null' => true];
}
if (!$db->fieldExists('locked_by', 'competitions')) {
$fields['locked_by'] = ['type' => 'INT', 'constraint' => 11, 'null' => true];
}
if (!empty($fields)) {
$this->forge->addColumn('competitions', $fields);
}
}
public function down()
{
$db = \Config\Database::connect();
if (!$db->tableExists('competitions')) {
return;
}
$columns = [];
foreach (['is_locked', 'locked_at', 'locked_by'] as $column) {
if ($db->fieldExists($column, 'competitions')) {
$columns[] = $column;
}
}
if (!empty($columns)) {
$this->forge->dropColumn('competitions', $columns);
}
}
}
@@ -0,0 +1,41 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddStudentActiveFlag extends Migration
{
public function up()
{
$db = \Config\Database::connect();
if (!$db->tableExists('students')) {
return;
}
if (!$db->fieldExists('is_active', 'students')) {
$this->forge->addColumn('students', [
'is_active' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 1,
'null' => false,
],
]);
}
}
public function down()
{
$db = \Config\Database::connect();
if (!$db->tableExists('students')) {
return;
}
if ($db->fieldExists('is_active', 'students')) {
$this->forge->dropColumn('students', 'is_active');
}
}
}
@@ -0,0 +1,46 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateClassProgressReports extends Migration
{
public function up()
{
if ($this->db->tableExists('class_progress_reports')) {
return;
}
$this->forge->addField([
'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'class_section_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
'teacher_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
'week_start' => ['type' => 'DATE'],
'week_end' => ['type' => 'DATE'],
'subject' => ['type' => 'VARCHAR', 'constraint' => 160, 'null' => true],
'unit_title' => ['type' => 'VARCHAR', 'constraint' => 120, 'null' => true],
'covered' => ['type' => 'TEXT'],
'homework' => ['type' => 'TEXT', 'null' => true],
'assessment' => ['type' => 'TEXT', 'null' => true],
'status' => ['type' => 'VARCHAR', 'constraint' => 20],
'status_notes' => ['type' => 'VARCHAR', 'constraint' => 200, 'null' => true],
'class_notes' => ['type' => 'TEXT', 'null' => true],
'next_week_plan' => ['type' => 'TEXT'],
'support_needed' => ['type' => 'TEXT', 'null' => true],
'flags_json' => ['type' => 'TEXT', 'null' => true],
'attachment_path' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['class_section_id', 'week_start', 'week_end']);
$this->forge->createTable('class_progress_reports');
}
public function down()
{
$this->forge->dropTable('class_progress_reports', true);
}
}
@@ -0,0 +1,64 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateSubjectCurriculumItems extends Migration
{
public function up()
{
if ($this->db->tableExists('subject_curriculum_items')) {
return;
}
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'class_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'subject' => [
'type' => 'ENUM',
'constraint' => ['islamic', 'quran'],
'default' => 'islamic',
],
'unit_number' => [
'type' => 'SMALLINT',
'null' => true,
],
'unit_title' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => true,
],
'chapter_name' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['class_id', 'subject']);
$this->forge->createTable('subject_curriculum_items');
}
public function down()
{
$this->forge->dropTable('subject_curriculum_items', true);
}
}
@@ -0,0 +1,42 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class RemoveClassAssignmentSemester extends Migration
{
public function up()
{
if ($this->db->tableExists('teacher_class')) {
$this->forge->dropColumn('teacher_class', 'semester');
}
if ($this->db->tableExists('student_class')) {
$this->forge->dropColumn('student_class', 'semester');
}
}
public function down()
{
if ($this->db->tableExists('teacher_class')) {
$this->forge->addColumn('teacher_class', [
'semester' => [
'type' => 'VARCHAR',
'constraint' => 25,
'null' => false,
'default' => '',
],
]);
}
if ($this->db->tableExists('student_class')) {
$this->forge->addColumn('student_class', [
'semester' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => false,
'default' => '',
],
]);
}
}
}
@@ -0,0 +1,72 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateTeacherSubmissionNotificationHistory extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'unsigned' => true,
'auto_increment' => true,
],
'teacher_id' => [
'type' => 'INT',
'unsigned' => true,
'null' => false,
],
'class_section_id' => [
'type' => 'INT',
'unsigned' => true,
'null' => false,
],
'admin_id' => [
'type' => 'INT',
'unsigned' => true,
'null' => false,
],
'notification_category' => [
'type' => 'VARCHAR',
'constraint' => 64,
'default' => 'teacher_submissions',
],
'message' => [
'type' => 'TEXT',
'null' => true,
],
'status' => [
'type' => 'VARCHAR',
'constraint' => 32,
'default' => 'sent',
],
'school_year' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => true,
],
'semester' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => true,
],
'sent_at' => [
'type' => 'DATETIME',
'null' => false,
'default' => 'CURRENT_TIMESTAMP',
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['teacher_id', 'class_section_id']);
$this->forge->createTable('teacher_submission_notification_history');
}
public function down()
{
$this->forge->dropTable('teacher_submission_notification_history');
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddEventTypeToCalendarEvents extends Migration
{
public function up()
{
$db = \Config\Database::connect();
if (!$db->fieldExists('event_type', 'calendar_events')) {
$this->forge->addColumn('calendar_events', [
'event_type' => [
'type' => 'VARCHAR',
'constraint' => 120,
'null' => true,
'default' => null,
'after' => 'description',
],
]);
}
}
public function down()
{
$db = \Config\Database::connect();
if ($db->fieldExists('event_type', 'calendar_events')) {
$this->forge->dropColumn('calendar_events', 'event_type');
}
}
}
@@ -0,0 +1,109 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateExamDraftSubmissions extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'unsigned' => true,
'auto_increment' => true,
],
'teacher_id' => [
'type' => 'INT',
'unsigned' => true,
'null' => false,
],
'class_section_id' => [
'type' => 'INT',
'unsigned' => true,
'null' => false,
],
'semester' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => false,
],
'school_year' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => false,
],
'exam_type' => [
'type' => 'VARCHAR',
'constraint' => 64,
'null' => true,
],
'draft_title' => [
'type' => 'VARCHAR',
'constraint' => 191,
'null' => false,
],
'description' => [
'type' => 'TEXT',
'null' => true,
],
'teacher_file' => [
'type' => 'VARCHAR',
'constraint' => 512,
'null' => true,
],
'teacher_filename' => [
'type' => 'VARCHAR',
'constraint' => 512,
'null' => true,
],
'status' => [
'type' => 'VARCHAR',
'constraint' => 32,
'default' => 'pending',
],
'admin_id' => [
'type' => 'INT',
'unsigned' => true,
'null' => true,
],
'admin_comments' => [
'type' => 'TEXT',
'null' => true,
],
'reviewed_at' => [
'type' => 'DATETIME',
'null' => true,
],
'final_file' => [
'type' => 'VARCHAR',
'constraint' => 512,
'null' => true,
],
'final_filename' => [
'type' => 'VARCHAR',
'constraint' => 512,
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => false,
'default' => 'CURRENT_TIMESTAMP',
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['teacher_id', 'class_section_id']);
$this->forge->createTable('exam_drafts');
}
public function down()
{
$this->forge->dropTable('exam_drafts');
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddVersionToExamDrafts extends Migration
{
public function up()
{
$this->forge->addColumn('exam_drafts', [
'version' => [
'type' => 'INT',
'unsigned' => true,
'default' => 1,
],
'previous_draft_id' => [
'type' => 'INT',
'unsigned' => true,
'null' => true,
],
]);
$this->forge->addKey('version');
}
public function down()
{
$this->forge->dropColumn('exam_drafts', ['version', 'previous_draft_id']);
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddFinalPdfToExamDrafts extends Migration
{
public function up()
{
$this->forge->addColumn('exam_drafts', [
'final_pdf_file' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => true,
'after' => 'final_filename',
],
]);
}
public function down()
{
$this->forge->dropColumn('exam_drafts', 'final_pdf_file');
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddIsLegacyToExamDrafts extends Migration
{
public function up()
{
$this->forge->addColumn('exam_drafts', [
'is_legacy' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 0,
'after' => 'admin_id',
],
]);
}
public function down()
{
$this->forge->dropColumn('exam_drafts', 'is_legacy');
}
}
@@ -0,0 +1,71 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddClassSectionIdToScoreComments extends Migration
{
public function up()
{
if (!$this->db->tableExists('score_comments')) {
return;
}
$fields = $this->db->getFieldNames('score_comments');
if (!in_array('class_section_id', $fields, true)) {
$this->forge->addColumn('score_comments', [
'class_section_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
'after' => 'student_id',
],
]);
try {
$this->db->query('CREATE INDEX idx_score_comments_class_section ON score_comments(class_section_id)');
} catch (\Throwable $e) {
// Index may already exist; ignore
}
}
if (in_array('subject_id', $fields, true)) {
try {
$this->forge->dropColumn('score_comments', 'subject_id');
} catch (\Throwable $e) {
// ignore if drop fails
}
}
}
public function down()
{
if (!$this->db->tableExists('score_comments')) {
return;
}
$fields = $this->db->getFieldNames('score_comments');
if (!in_array('subject_id', $fields, true)) {
$this->forge->addColumn('score_comments', [
'subject_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
'after' => 'student_id',
],
]);
}
if (in_array('class_section_id', $fields, true)) {
try {
$this->forge->dropColumn('score_comments', 'class_section_id');
} catch (\Throwable $e) {
// ignore if drop fails
}
}
}
}
@@ -0,0 +1,104 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateParentMeetingSchedules extends Migration
{
public function up()
{
if ($this->db->tableExists('parent_meeting_schedules')) {
return;
}
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'student_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'parent_user_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
'parent_name' => [
'type' => 'VARCHAR',
'constraint' => 200,
'null' => true,
],
'student_name' => [
'type' => 'VARCHAR',
'constraint' => 200,
'null' => true,
],
'class_section_name' => [
'type' => 'VARCHAR',
'constraint' => 200,
'null' => true,
],
'date' => [
'type' => 'DATE',
],
'time' => [
'type' => 'TIME',
'null' => true,
],
'notes' => [
'type' => 'TEXT',
'null' => true,
],
'semester' => [
'type' => 'VARCHAR',
'constraint' => 20,
'null' => true,
],
'school_year' => [
'type' => 'VARCHAR',
'constraint' => 20,
'null' => true,
],
'status' => [
'type' => 'VARCHAR',
'constraint' => 20,
'default' => 'scheduled',
],
'created_by' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey('student_id');
$this->forge->addKey('parent_user_id');
$this->forge->addKey('date');
$this->forge->createTable('parent_meeting_schedules', true);
}
public function down()
{
if ($this->db->tableExists('parent_meeting_schedules')) {
$this->forge->dropTable('parent_meeting_schedules', true);
}
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateClassProgressAttachments extends Migration
{
public function up()
{
if ($this->db->tableExists('class_progress_attachments')) {
return;
}
$this->forge->addField([
'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'report_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
'file_path' => ['type' => 'VARCHAR', 'constraint' => 255],
'original_name' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'mime_type' => ['type' => 'VARCHAR', 'constraint' => 120, 'null' => true],
'file_size' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
'created_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addKey('report_id');
$this->forge->createTable('class_progress_attachments');
}
public function down()
{
$this->forge->dropTable('class_progress_attachments', true);
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreatePlacementLevels extends Migration
{
public function up()
{
if ($this->db->tableExists('placement_levels')) {
return;
}
$this->forge->addField([
'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'student_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
'level' => ['type' => 'SMALLINT', 'constraint' => 3, 'unsigned' => true],
'school_year' => ['type' => 'VARCHAR', 'constraint' => 20],
'created_by' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
'updated_by' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['student_id', 'school_year'], true);
$this->forge->addKey('school_year');
$this->forge->createTable('placement_levels');
}
public function down()
{
$this->forge->dropTable('placement_levels', true);
}
}
@@ -0,0 +1,40 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class UpdatePlacementLevelRange extends Migration
{
public function up()
{
if (!$this->db->tableExists('placement_levels')) {
return;
}
$fields = $this->db->getFieldNames('placement_levels');
if (!in_array('level', $fields, true)) {
return;
}
$this->forge->modifyColumn('placement_levels', [
'level' => ['type' => 'SMALLINT', 'constraint' => 3, 'unsigned' => true],
]);
}
public function down()
{
if (!$this->db->tableExists('placement_levels')) {
return;
}
$fields = $this->db->getFieldNames('placement_levels');
if (!in_array('level', $fields, true)) {
return;
}
$this->forge->modifyColumn('placement_levels', [
'level' => ['type' => 'TINYINT', 'constraint' => 1, 'unsigned' => true],
]);
}
}
@@ -0,0 +1,34 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreatePlacementBatches extends Migration
{
public function up()
{
if ($this->db->tableExists('placement_batches')) {
return;
}
$this->forge->addField([
'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'placement_test' => ['type' => 'VARCHAR', 'constraint' => 30],
'school_year' => ['type' => 'VARCHAR', 'constraint' => 20],
'created_by' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
'updated_by' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['placement_test', 'school_year']);
$this->forge->createTable('placement_batches');
}
public function down()
{
$this->forge->dropTable('placement_batches', true);
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreatePlacementScores extends Migration
{
public function up()
{
if ($this->db->tableExists('placement_scores')) {
return;
}
$this->forge->addField([
'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
'batch_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
'student_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
'score' => ['type' => 'SMALLINT', 'constraint' => 3, 'unsigned' => true],
'created_by' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
'updated_by' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
'created_at' => ['type' => 'DATETIME', 'null' => true],
'updated_at' => ['type' => 'DATETIME', 'null' => true],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['batch_id', 'student_id'], true);
$this->forge->addKey('batch_id');
$this->forge->createTable('placement_scores');
}
public function down()
{
$this->forge->dropTable('placement_scores', true);
}
}
@@ -0,0 +1,38 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddEventOnlyToStudentClass extends Migration
{
public function up()
{
if (!$this->db->tableExists('student_class')) {
return;
}
$fields = $this->db->getFieldNames('student_class');
if (in_array('is_event_only', $fields, true)) {
return;
}
$this->forge->addColumn('student_class', [
'is_event_only' => ['type' => 'TINYINT', 'constraint' => 1, 'unsigned' => true, 'default' => 0],
]);
}
public function down()
{
if (!$this->db->tableExists('student_class')) {
return;
}
$fields = $this->db->getFieldNames('student_class');
if (!in_array('is_event_only', $fields, true)) {
return;
}
$this->forge->dropColumn('student_class', 'is_event_only');
}
}
@@ -0,0 +1,69 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateGradingLocks extends Migration
{
public function up()
{
if ($this->db->tableExists('grading_locks')) {
return;
}
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'class_section_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'semester' => [
'type' => 'VARCHAR',
'constraint' => 20,
],
'school_year' => [
'type' => 'VARCHAR',
'constraint' => 9,
],
'is_locked' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 0,
],
'locked_by' => [
'type' => 'INT',
'constraint' => 11,
'null' => true,
],
'locked_at' => [
'type' => 'DATETIME',
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['class_section_id', 'semester', 'school_year'], false, false, 'idx_grading_locks_section_term');
$this->forge->addUniqueKey(['class_section_id', 'semester', 'school_year'], 'uniq_grading_locks_section_term');
$this->forge->createTable('grading_locks', true);
}
public function down()
{
$this->forge->dropTable('grading_locks', true);
}
}
@@ -0,0 +1,76 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateMissingScoreOverrides extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'student_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'class_section_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'semester' => [
'type' => 'VARCHAR',
'constraint' => 20,
],
'school_year' => [
'type' => 'VARCHAR',
'constraint' => 20,
],
'item_type' => [
'type' => 'VARCHAR',
'constraint' => 30,
],
'item_index' => [
'type' => 'INT',
'constraint' => 11,
'null' => true,
],
'is_allowed' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 0,
],
'updated_by' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->addKey(['class_section_id', 'semester', 'school_year', 'item_type'], false, 'missing_score_overrides_scope');
$this->forge->addKey(['student_id', 'class_section_id', 'semester', 'school_year', 'item_type', 'item_index'], true, 'missing_score_overrides_unique');
$this->forge->createTable('missing_score_overrides', true);
}
public function down()
{
$this->forge->dropTable('missing_score_overrides', true);
}
}
@@ -0,0 +1,85 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddStyleToPreferences extends Migration
{
public function up()
{
if ($this->db->tableExists('user_preferences')) {
if (! $this->db->fieldExists('style_color', 'user_preferences')) {
$this->forge->addColumn('user_preferences', [
'style_color' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => true,
'after' => 'timezone',
],
]);
}
if (! $this->db->fieldExists('menu_color', 'user_preferences')) {
$this->forge->addColumn('user_preferences', [
'menu_color' => [
'type' => 'VARCHAR',
'constraint' => 32,
'null' => true,
'after' => 'style_color',
],
]);
}
if (! $this->db->fieldExists('menu_custom_bg', 'user_preferences')) {
$this->forge->addColumn('user_preferences', [
'menu_custom_bg' => [
'type' => 'VARCHAR',
'constraint' => 16,
'null' => true,
'after' => 'menu_color',
],
]);
}
if (! $this->db->fieldExists('menu_custom_text', 'user_preferences')) {
$this->forge->addColumn('user_preferences', [
'menu_custom_text' => [
'type' => 'VARCHAR',
'constraint' => 16,
'null' => true,
'after' => 'menu_custom_bg',
],
]);
}
if (! $this->db->fieldExists('menu_custom_mode', 'user_preferences')) {
$this->forge->addColumn('user_preferences', [
'menu_custom_mode' => [
'type' => 'VARCHAR',
'constraint' => 8,
'null' => true,
'after' => 'menu_custom_text',
],
]);
}
}
}
public function down()
{
if ($this->db->tableExists('user_preferences')) {
if ($this->db->fieldExists('menu_custom_mode', 'user_preferences')) {
$this->forge->dropColumn('user_preferences', 'menu_custom_mode');
}
if ($this->db->fieldExists('menu_custom_text', 'user_preferences')) {
$this->forge->dropColumn('user_preferences', 'menu_custom_text');
}
if ($this->db->fieldExists('menu_custom_bg', 'user_preferences')) {
$this->forge->dropColumn('user_preferences', 'menu_custom_bg');
}
if ($this->db->fieldExists('menu_color', 'user_preferences')) {
$this->forge->dropColumn('user_preferences', 'menu_color');
}
if ($this->db->fieldExists('style_color', 'user_preferences')) {
$this->forge->dropColumn('user_preferences', 'style_color');
}
}
}
}
@@ -0,0 +1,40 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class ExtendStaffAttendanceForTeachers extends Migration
{
public function up()
{
$this->forge->addColumn('staff_attendance', [
'class_section_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'null' => true,
'after' => 'user_id',
],
'position' => [
'type' => 'ENUM',
'constraint' => ['main','ta'],
'null' => true,
'after' => 'class_section_id',
],
]);
// Unique constraint: 1 record per user per date
$this->db->query("
ALTER TABLE staff_attendance
ADD UNIQUE KEY uniq_user_date (user_id, date)
");
}
public function down()
{
$this->forge->dropColumn('staff_attendance', 'class_section_id');
$this->forge->dropColumn('staff_attendance', 'position');
$this->db->query("ALTER TABLE staff_attendance DROP INDEX uniq_user_date");
}
}