259 lines
11 KiB
PHP
259 lines
11 KiB
PHP
<?php
|
|
|
|
namespace App\Database\Migrations;
|
|
|
|
use CodeIgniter\Database\Migration;
|
|
|
|
class CreateSchoolYears extends Migration
|
|
{
|
|
public function up(): void
|
|
{
|
|
if (! $this->db->tableExists('school_years')) {
|
|
$this->forge->addField([
|
|
'id' => [
|
|
'type' => 'INT',
|
|
'constraint' => 11,
|
|
'unsigned' => true,
|
|
'auto_increment' => true,
|
|
],
|
|
'name' => [
|
|
'type' => 'VARCHAR',
|
|
'constraint' => 9,
|
|
'null' => false,
|
|
],
|
|
'status' => [
|
|
'type' => 'VARCHAR',
|
|
'constraint' => 20,
|
|
'null' => false,
|
|
'default' => 'draft',
|
|
],
|
|
'starts_on' => [
|
|
'type' => 'DATE',
|
|
'null' => true,
|
|
],
|
|
'ends_on' => [
|
|
'type' => 'DATE',
|
|
'null' => true,
|
|
],
|
|
'description' => [
|
|
'type' => 'TEXT',
|
|
'null' => true,
|
|
],
|
|
'registration_starts_on' => [
|
|
'type' => 'DATE',
|
|
'null' => true,
|
|
],
|
|
'registration_ends_on' => [
|
|
'type' => 'DATE',
|
|
'null' => true,
|
|
],
|
|
'previous_school_year_id' => [
|
|
'type' => 'INT',
|
|
'constraint' => 11,
|
|
'unsigned' => true,
|
|
'null' => true,
|
|
],
|
|
'next_school_year_id' => [
|
|
'type' => 'INT',
|
|
'constraint' => 11,
|
|
'unsigned' => true,
|
|
'null' => true,
|
|
],
|
|
'activated_at' => [
|
|
'type' => 'DATETIME',
|
|
'null' => true,
|
|
],
|
|
'closing_started_at' => [
|
|
'type' => 'DATETIME',
|
|
'null' => true,
|
|
],
|
|
'closed_at' => [
|
|
'type' => 'DATETIME',
|
|
'null' => true,
|
|
],
|
|
'archived_at' => [
|
|
'type' => 'DATETIME',
|
|
'null' => 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('name', false, true);
|
|
$this->forge->addKey('status');
|
|
$this->forge->createTable('school_years');
|
|
} else {
|
|
$this->ensureSchoolYearColumns();
|
|
}
|
|
|
|
$this->createClosingTables();
|
|
|
|
$configuredYear = $this->configuredSchoolYear();
|
|
|
|
if ($configuredYear !== null && $this->isValidYearName($configuredYear)) {
|
|
$existing = $this->db->table('school_years')
|
|
->where('name', $configuredYear)
|
|
->get()
|
|
->getRowArray();
|
|
|
|
if ($existing === null) {
|
|
$this->db->table('school_years')->insert([
|
|
'name' => $configuredYear,
|
|
'status' => 'active',
|
|
'activated_at' => date('Y-m-d H:i:s'),
|
|
'created_at' => date('Y-m-d H:i:s'),
|
|
'updated_at' => date('Y-m-d H:i:s'),
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function down(): void
|
|
{
|
|
$this->forge->dropTable('school_year_transition_logs', true);
|
|
$this->forge->dropTable('school_year_closing_items', true);
|
|
$this->forge->dropTable('school_year_closing_batches', true);
|
|
$this->forge->dropTable('school_years', true);
|
|
}
|
|
|
|
private function ensureSchoolYearColumns(): void
|
|
{
|
|
$fields = $this->db->getFieldNames('school_years');
|
|
$add = [];
|
|
|
|
$definitions = [
|
|
'description' => ['type' => 'TEXT', 'null' => true],
|
|
'registration_starts_on' => ['type' => 'DATE', 'null' => true],
|
|
'registration_ends_on' => ['type' => 'DATE', 'null' => true],
|
|
'previous_school_year_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
|
|
'next_school_year_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
|
|
'activated_at' => ['type' => 'DATETIME', 'null' => true],
|
|
'closing_started_at' => ['type' => 'DATETIME', 'null' => true],
|
|
'closed_at' => ['type' => 'DATETIME', 'null' => true],
|
|
'archived_at' => ['type' => 'DATETIME', 'null' => true],
|
|
'created_by' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
|
|
'updated_by' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
|
|
];
|
|
|
|
foreach ($definitions as $field => $definition) {
|
|
if (! in_array($field, $fields, true)) {
|
|
$add[$field] = $definition;
|
|
}
|
|
}
|
|
|
|
if ($add !== []) {
|
|
$this->forge->addColumn('school_years', $add);
|
|
}
|
|
}
|
|
|
|
private function createClosingTables(): void
|
|
{
|
|
if (! $this->db->tableExists('school_year_closing_batches')) {
|
|
$this->forge->addField([
|
|
'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
|
|
'source_school_year_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
|
|
'target_school_year_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
|
|
'status' => ['type' => 'VARCHAR', 'constraint' => 30, 'null' => false, 'default' => 'preview'],
|
|
'preview_hash' => ['type' => 'CHAR', 'constraint' => 64, 'null' => true],
|
|
'total_families' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => false, 'default' => 0],
|
|
'total_positive_balance' => ['type' => 'DECIMAL', 'constraint' => '12,2', 'null' => false, 'default' => '0.00'],
|
|
'total_credit_balance' => ['type' => 'DECIMAL', 'constraint' => '12,2', 'null' => false, 'default' => '0.00'],
|
|
'started_by' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
|
|
'completed_by' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
|
|
'started_at' => ['type' => 'DATETIME', 'null' => true],
|
|
'completed_at' => ['type' => 'DATETIME', 'null' => true],
|
|
'failed_at' => ['type' => 'DATETIME', 'null' => true],
|
|
'failure_message' => ['type' => 'TEXT', 'null' => true],
|
|
'created_at' => ['type' => 'DATETIME', 'null' => true],
|
|
'updated_at' => ['type' => 'DATETIME', 'null' => true],
|
|
]);
|
|
$this->forge->addKey('id', true);
|
|
$this->forge->addKey(['source_school_year_id', 'status']);
|
|
$this->forge->createTable('school_year_closing_batches');
|
|
}
|
|
|
|
if (! $this->db->tableExists('school_year_closing_items')) {
|
|
$this->forge->addField([
|
|
'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
|
|
'closing_batch_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
|
|
'family_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
|
|
'source_balance' => ['type' => 'DECIMAL', 'constraint' => '12,2', 'null' => false, 'default' => '0.00'],
|
|
'credit_amount' => ['type' => 'DECIMAL', 'constraint' => '12,2', 'null' => false, 'default' => '0.00'],
|
|
'adjustment_amount' => ['type' => 'DECIMAL', 'constraint' => '12,2', 'null' => false, 'default' => '0.00'],
|
|
'carry_forward_amount' => ['type' => 'DECIMAL', 'constraint' => '12,2', 'null' => false, 'default' => '0.00'],
|
|
'target_invoice_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
|
|
'target_adjustment_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
|
|
'status' => ['type' => 'VARCHAR', 'constraint' => 30, 'null' => false, 'default' => 'pending'],
|
|
'error_message' => ['type' => 'TEXT', 'null' => true],
|
|
'created_at' => ['type' => 'DATETIME', 'null' => true],
|
|
'updated_at' => ['type' => 'DATETIME', 'null' => true],
|
|
]);
|
|
$this->forge->addKey('id', true);
|
|
$this->forge->addKey(['closing_batch_id', 'family_id'], false, true);
|
|
$this->forge->createTable('school_year_closing_items');
|
|
}
|
|
|
|
if (! $this->db->tableExists('school_year_transition_logs')) {
|
|
$this->forge->addField([
|
|
'id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'auto_increment' => true],
|
|
'school_year_id' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true],
|
|
'from_status' => ['type' => 'VARCHAR', 'constraint' => 20, 'null' => true],
|
|
'to_status' => ['type' => 'VARCHAR', 'constraint' => 20, 'null' => true],
|
|
'action' => ['type' => 'VARCHAR', 'constraint' => 80, 'null' => false],
|
|
'performed_by' => ['type' => 'INT', 'constraint' => 11, 'unsigned' => true, 'null' => true],
|
|
'metadata_json' => ['type' => 'TEXT', 'null' => true],
|
|
'created_at' => ['type' => 'DATETIME', 'null' => false],
|
|
]);
|
|
$this->forge->addKey('id', true);
|
|
$this->forge->addKey(['school_year_id', 'created_at']);
|
|
$this->forge->createTable('school_year_transition_logs');
|
|
}
|
|
}
|
|
|
|
private function configuredSchoolYear(): ?string
|
|
{
|
|
if (! $this->db->tableExists('configuration')) {
|
|
return null;
|
|
}
|
|
|
|
$row = $this->db->table('configuration')
|
|
->select('config_value')
|
|
->where('config_key', 'school_year')
|
|
->orderBy('id', 'DESC')
|
|
->get(1)
|
|
->getRowArray();
|
|
|
|
$value = trim((string) ($row['config_value'] ?? ''));
|
|
|
|
return $value !== '' ? $value : null;
|
|
}
|
|
|
|
private function isValidYearName(string $value): bool
|
|
{
|
|
if (! preg_match('/^(\d{4})-(\d{4})$/', $value, $matches)) {
|
|
return false;
|
|
}
|
|
|
|
return (int) $matches[2] === (int) $matches[1] + 1;
|
|
}
|
|
}
|