From e4e89fb34a6bdd509c47c02eb846666068678163 Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Fri, 18 Sep 2026 05:17:22 +0530 Subject: [PATCH] feat(database): add migration for credit accounts, credit ledger, draft bills, and store user permissions --- ...eateCreditAccountsAndMobileDraftSchema.php | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 app/Database/Migrations/2026-09-18-000001_CreateCreditAccountsAndMobileDraftSchema.php diff --git a/app/Database/Migrations/2026-09-18-000001_CreateCreditAccountsAndMobileDraftSchema.php b/app/Database/Migrations/2026-09-18-000001_CreateCreditAccountsAndMobileDraftSchema.php new file mode 100644 index 0000000..e0386b2 --- /dev/null +++ b/app/Database/Migrations/2026-09-18-000001_CreateCreditAccountsAndMobileDraftSchema.php @@ -0,0 +1,234 @@ +db->tableExists('mst_credit_accounts')) { + $this->db->query("CREATE TABLE IF NOT EXISTS `mst_credit_accounts` ( + `account_id` INT AUTO_INCREMENT PRIMARY KEY, + `store_id` INT NOT NULL DEFAULT 1, + `account_type` ENUM('STAFF', 'DOCTOR', 'VIP_CUSTOMER', 'CORPORATE', 'OTHER') NOT NULL DEFAULT 'STAFF', + `account_name` VARCHAR(150) NOT NULL, + `phone` VARCHAR(20) DEFAULT NULL, + `staff_id` VARCHAR(50) DEFAULT NULL, + `department` VARCHAR(100) DEFAULT NULL, + `credit_limit` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `current_balance` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `is_active` TINYINT(1) NOT NULL DEFAULT 1, + `remarks` TEXT DEFAULT NULL, + `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + KEY `idx_store_acc` (`store_id`, `account_type`), + KEY `idx_phone` (`phone`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"); + } + + // 2. mst_credit_ledger + if (!$this->db->tableExists('mst_credit_ledger')) { + $this->db->query("CREATE TABLE IF NOT EXISTS `mst_credit_ledger` ( + `ledger_id` INT AUTO_INCREMENT PRIMARY KEY, + `store_id` INT NOT NULL DEFAULT 1, + `account_id` INT NOT NULL, + `entry_type` ENUM('CREDIT_SALE', 'PAYMENT_RECEIVED', 'ADJUSTMENT') NOT NULL DEFAULT 'CREDIT_SALE', + `sale_id` INT DEFAULT NULL, + `invoice_no` VARCHAR(50) DEFAULT NULL, + `debit_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `credit_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `balance_after` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `payment_mode` VARCHAR(50) DEFAULT 'Cash', + `payment_ref` VARCHAR(100) DEFAULT NULL, + `remarks` TEXT DEFAULT NULL, + `recorded_by_name` VARCHAR(100) DEFAULT NULL, + `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, + KEY `idx_acc_ledger` (`account_id`, `created_at`), + KEY `idx_store` (`store_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"); + } + + // 3. mst_draft_bills + if (!$this->db->tableExists('mst_draft_bills')) { + $this->db->query("CREATE TABLE IF NOT EXISTS `mst_draft_bills` ( + `draft_id` INT AUTO_INCREMENT PRIMARY KEY, + `store_id` INT NOT NULL DEFAULT 1, + `draft_code` VARCHAR(50) NOT NULL, + `patient_name` VARCHAR(100) DEFAULT 'Walk-in Customer', + `uhid` VARCHAR(50) DEFAULT NULL, + `patient_mobile` VARCHAR(20) DEFAULT NULL, + `bed_no` VARCHAR(50) DEFAULT NULL, + `ward_name` VARCHAR(50) DEFAULT NULL, + `doctor_name` VARCHAR(100) DEFAULT NULL, + `total_items` INT NOT NULL DEFAULT 0, + `estimated_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00, + `items_json` LONGTEXT NOT NULL, + `status` ENUM('DRAFT', 'CONVERTED_TO_SALE', 'CANCELLED') NOT NULL DEFAULT 'DRAFT', + `converted_sale_id` INT DEFAULT NULL, + `collected_by_name` VARCHAR(100) DEFAULT 'Pharmacist', + `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP, + `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + KEY `idx_store_status` (`store_id`, `status`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"); + } + + // 4. Alter mst_purchases + if ($this->db->tableExists('mst_purchases')) { + $fields = $this->db->getFieldNames('mst_purchases'); + $addPurchCols = []; + if (!in_array('invoice_photo', $fields, true)) { + $addPurchCols['invoice_photo'] = [ + 'type' => 'TEXT', + 'null' => true, + 'after' => in_array('remarks', $fields, true) ? 'remarks' : null + ]; + } + if (!in_array('is_verified', $fields, true)) { + $addPurchCols['is_verified'] = [ + 'type' => 'TINYINT', + 'constraint' => 1, + 'default' => 0 + ]; + } + if (!in_array('verified_by_name', $fields, true)) { + $addPurchCols['verified_by_name'] = [ + 'type' => 'VARCHAR', + 'constraint' => 100, + 'null' => true + ]; + } + if (!in_array('verified_at', $fields, true)) { + $addPurchCols['verified_at'] = [ + 'type' => 'DATETIME', + 'null' => true + ]; + } + if (!empty($addPurchCols)) { + $forge->addColumn('mst_purchases', $addPurchCols); + } + } + + // 5. Alter mst_sales + if ($this->db->tableExists('mst_sales')) { + $fields = $this->db->getFieldNames('mst_sales'); + if (!in_array('credit_account_id', $fields, true)) { + $forge->addColumn('mst_sales', [ + 'credit_account_id' => [ + 'type' => 'INT', + 'constraint' => 11, + 'null' => true, + 'after' => in_array('credit_amount', $fields, true) ? 'credit_amount' : null + ] + ]); + } + } + + // 6. Alter mst_store_users + if ($this->db->tableExists('mst_store_users')) { + $fields = $this->db->getFieldNames('mst_store_users'); + if (!in_array('permissions', $fields, true)) { + $forge->addColumn('mst_store_users', [ + 'permissions' => [ + 'type' => 'TEXT', + 'null' => true, + 'after' => in_array('user_role', $fields, true) ? 'user_role' : null + ] + ]); + } + + // Seed default permissions if null + $allPerms = json_encode(['admin', 'sale', 'purchase', 'sale_edit_after_1h', 'purchase_edit_verified', 'bank_upi_audit']); + $pharmaPerms = json_encode(['sale', 'purchase', 'sale_edit_after_1h']); + $cashierPerms = json_encode(['sale']); + $auditorPerms = json_encode(['purchase', 'bank_upi_audit']); + + $this->db->query("UPDATE `mst_store_users` SET `permissions` = '{$allPerms}' WHERE `user_role` = 'store_manager' AND (`permissions` IS NULL OR `permissions` = '')"); + $this->db->query("UPDATE `mst_store_users` SET `permissions` = '{$pharmaPerms}' WHERE `user_role` = 'pharmacist' AND (`permissions` IS NULL OR `permissions` = '')"); + $this->db->query("UPDATE `mst_store_users` SET `permissions` = '{$cashierPerms}' WHERE `user_role` = 'cashier' AND (`permissions` IS NULL OR `permissions` = '')"); + $this->db->query("UPDATE `mst_store_users` SET `permissions` = '{$auditorPerms}' WHERE `user_role` = 'auditor' AND (`permissions` IS NULL OR `permissions` = '')"); + } + + // 7. Seed sample credit accounts if table empty + if ($this->db->tableExists('mst_credit_accounts')) { + $count = $this->db->table('mst_credit_accounts')->countAllResults(); + if ($count === 0) { + $this->db->table('mst_credit_accounts')->insertBatch([ + [ + 'store_id' => 1, + 'account_type' => 'STAFF', + 'account_name' => 'Dr. Rajesh Sharma', + 'phone' => '9811002233', + 'staff_id' => 'EMP-DOC-101', + 'department' => 'General Medicine', + 'credit_limit' => 10000.00, + 'current_balance' => 0.00, + 'remarks' => 'Senior Consultant Staff Credit Account' + ], + [ + 'store_id' => 1, + 'account_type' => 'STAFF', + 'account_name' => 'Sister Sunita Rawat', + 'phone' => '9822003344', + 'staff_id' => 'EMP-NUR-205', + 'department' => 'ICU Nursing', + 'credit_limit' => 5000.00, + 'current_balance' => 0.00, + 'remarks' => 'Staff Nurse Credit Account' + ], + [ + 'store_id' => 1, + 'account_type' => 'VIP_CUSTOMER', + 'account_name' => 'Amitabh Hospital Trust', + 'phone' => '9833004455', + 'staff_id' => 'CORP-001', + 'department' => 'Trust VIP', + 'credit_limit' => 50000.00, + 'current_balance' => 0.00, + 'remarks' => 'Corporate VIP Credit Account' + ] + ]); + } + } + } + + public function down() + { + $forge = \Config\Database::forge(); + + if ($this->db->tableExists('mst_credit_ledger')) { + $forge->dropTable('mst_credit_ledger', true); + } + if ($this->db->tableExists('mst_credit_accounts')) { + $forge->dropTable('mst_credit_accounts', true); + } + if ($this->db->tableExists('mst_draft_bills')) { + $forge->dropTable('mst_draft_bills', true); + } + + if ($this->db->tableExists('mst_purchases')) { + $fields = $this->db->getFieldNames('mst_purchases'); + foreach (['invoice_photo', 'is_verified', 'verified_by_name', 'verified_at'] as $c) { + if (in_array($c, $fields, true)) { + $forge->dropColumn('mst_purchases', $c); + } + } + } + + if ($this->db->tableExists('mst_sales')) { + if (in_array('credit_account_id', $this->db->getFieldNames('mst_sales'), true)) { + $forge->dropColumn('mst_sales', 'credit_account_id'); + } + } + + if ($this->db->tableExists('mst_store_users')) { + if (in_array('permissions', $this->db->getFieldNames('mst_store_users'), true)) { + $forge->dropColumn('mst_store_users', 'permissions'); + } + } + } +}