Download schema.sql from Mbonea/WifiBiz: direct link, hf CLI and curl.
- Browser
- Download file 27.2 kB
-
https://huggingface.co/spaces/Mbonea/WifiBiz/resolve/main/schema.sql
- Command line
-
hf download hf://spaces/Mbonea/WifiBiz/schema.sql
-
curl -L -o schema.sql https://huggingface.co/spaces/Mbonea/WifiBiz/resolve/main/schema.sql
27.2 kB
| -- ============================================= | |
| -- OMADA MULTI-TENANT WIFI PLATFORM | |
| -- Final Schema | |
| -- ============================================= | |
| SET NAMES utf8mb4; | |
| SET FOREIGN_KEY_CHECKS = 0; | |
| -- ============================================= | |
| -- TABLE 1: clients | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `clients`; | |
| CREATE TABLE `clients` ( | |
| `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `business_name` VARCHAR(100) NOT NULL, | |
| `contact_name` VARCHAR(100) NOT NULL, | |
| `email` VARCHAR(255) NOT NULL, | |
| `phone` VARCHAR(20) NOT NULL, | |
| `payment_display_name` VARCHAR(100) DEFAULT NULL, | |
| `payment_phone` VARCHAR(20) DEFAULT NULL, | |
| `payment_provider` VARCHAR(32) DEFAULT NULL, | |
| `payment_instructions` VARCHAR(255) DEFAULT NULL, | |
| `payment_qr_image_url` VARCHAR(512) DEFAULT NULL, | |
| `manual_sales_enabled` TINYINT(1) NOT NULL DEFAULT 0, | |
| `portal_payment_mode` VARCHAR(24) NOT NULL DEFAULT 'both', | |
| `password_hash` VARCHAR(255) NOT NULL, | |
| `commission_rate` DECIMAL(5,4) NOT NULL DEFAULT 0.0500, | |
| `balance` DECIMAL(12,2) NOT NULL DEFAULT 0.00, | |
| `total_earned` DECIMAL(12,2) NOT NULL DEFAULT 0.00, | |
| `total_withdrawn` DECIMAL(12,2) NOT NULL DEFAULT 0.00, | |
| `is_active` TINYINT(1) NOT NULL DEFAULT 1, | |
| `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | |
| UNIQUE KEY `uk_email` (`email`), | |
| UNIQUE KEY `uk_phone` (`phone`) | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 2: client_staff | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `client_staff`; | |
| CREATE TABLE `client_staff` ( | |
| `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `client_id` INT UNSIGNED NOT NULL, | |
| `name` VARCHAR(100) NOT NULL, | |
| `email` VARCHAR(255) NOT NULL, | |
| `phone` VARCHAR(20) DEFAULT NULL, | |
| `password_hash` VARCHAR(255) NOT NULL, | |
| `role` ENUM('manager','viewer') NOT NULL DEFAULT 'viewer', | |
| `is_active` TINYINT(1) NOT NULL DEFAULT 1, | |
| `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| UNIQUE KEY `uk_staff_email` (`email`), | |
| KEY `idx_staff_client` (`client_id`), | |
| CONSTRAINT `fk_staff_client` FOREIGN KEY (`client_id`) | |
| REFERENCES `clients`(`id`) ON DELETE CASCADE | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 3: devices | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `devices`; | |
| CREATE TABLE `devices` ( | |
| `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `client_id` INT UNSIGNED NOT NULL, | |
| `name` VARCHAR(100) NOT NULL, | |
| `location` VARCHAR(255) DEFAULT NULL, | |
| `omada_site_id` VARCHAR(64) DEFAULT NULL, | |
| `omada_portal_id` VARCHAR(64) DEFAULT NULL, | |
| `device_username` VARCHAR(50) DEFAULT NULL, | |
| `device_password` VARCHAR(100) DEFAULT NULL, | |
| `ssid_name` VARCHAR(64) DEFAULT NULL, | |
| `ssid_password` VARCHAR(64) DEFAULT NULL, | |
| `omada_wlan_id` VARCHAR(64) DEFAULT NULL, | |
| `omada_ssid_id` VARCHAR(64) DEFAULT NULL, | |
| `omada_imported_portal_page_id` VARCHAR(64) DEFAULT NULL, | |
| `mac` VARCHAR(17) NOT NULL, | |
| `model` VARCHAR(50) DEFAULT NULL, | |
| `firmware_version` VARCHAR(100) DEFAULT NULL, | |
| `ip` VARCHAR(45) DEFAULT NULL, | |
| `public_ip` VARCHAR(45) DEFAULT NULL, | |
| `portal_html_path` VARCHAR(255) DEFAULT NULL, | |
| `status` ENUM('pending','adopting','online','offline','removed') NOT NULL DEFAULT 'pending', | |
| `registered_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| `adopted_at` TIMESTAMP NULL DEFAULT NULL, | |
| `last_seen_at` TIMESTAMP NULL DEFAULT NULL, | |
| `billing_status` ENUM('trial','active','suspended','cancelled') NOT NULL DEFAULT 'trial', | |
| `billing_expires_at` TIMESTAMP NULL DEFAULT NULL, | |
| `monthly_fee` DECIMAL(10,2) NOT NULL DEFAULT 20000.00, | |
| `cpu_util` TINYINT UNSIGNED DEFAULT NULL, | |
| `mem_util` TINYINT UNSIGNED DEFAULT NULL, | |
| `client_count` SMALLINT UNSIGNED DEFAULT 0, | |
| `guest_count` SMALLINT UNSIGNED DEFAULT 0, | |
| `uptime_seconds` INT UNSIGNED DEFAULT 0, | |
| `download_bytes` BIGINT UNSIGNED DEFAULT 0, | |
| `upload_bytes` BIGINT UNSIGNED DEFAULT 0, | |
| `needs_upgrade` TINYINT(1) NOT NULL DEFAULT 0, | |
| UNIQUE KEY `uk_device_mac` (`mac`), | |
| UNIQUE KEY `uk_omada_site` (`omada_site_id`), | |
| KEY `idx_device_client` (`client_id`), | |
| KEY `idx_device_status` (`status`), | |
| KEY `idx_device_billing` (`billing_status`, `billing_expires_at`), | |
| CONSTRAINT `fk_device_client` FOREIGN KEY (`client_id`) | |
| REFERENCES `clients`(`id`) | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 4: device_payments | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `device_payments`; | |
| CREATE TABLE `device_payments` ( | |
| `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `client_id` INT UNSIGNED NOT NULL, | |
| `device_id` INT UNSIGNED NOT NULL, | |
| `amount` DECIMAL(10,2) NOT NULL DEFAULT 20000.00, | |
| `reference` VARCHAR(64) NOT NULL, | |
| `snippe_payment_id` VARCHAR(100) DEFAULT NULL, | |
| `period_start` DATE NOT NULL, | |
| `period_end` DATE NOT NULL, | |
| `status` ENUM('pending','completed','failed') NOT NULL DEFAULT 'pending', | |
| `notes` VARCHAR(255) DEFAULT NULL, | |
| `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| `completed_at` TIMESTAMP NULL DEFAULT NULL, | |
| UNIQUE KEY `uk_devpay_ref` (`reference`), | |
| KEY `idx_devpay_client` (`client_id`), | |
| KEY `idx_devpay_device` (`device_id`), | |
| KEY `idx_devpay_status` (`status`), | |
| CONSTRAINT `fk_devpay_client` FOREIGN KEY (`client_id`) REFERENCES `clients`(`id`), | |
| CONSTRAINT `fk_devpay_device` FOREIGN KEY (`device_id`) REFERENCES `devices`(`id`) | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 5: device_status_log | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `device_status_log`; | |
| CREATE TABLE `device_status_log` ( | |
| `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `device_id` INT UNSIGNED NOT NULL, | |
| `old_status` VARCHAR(20) NOT NULL, | |
| `new_status` VARCHAR(20) NOT NULL, | |
| `reason` VARCHAR(255) DEFAULT NULL, | |
| `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| KEY `idx_devlog_device` (`device_id`), | |
| KEY `idx_devlog_time` (`created_at`), | |
| CONSTRAINT `fk_devlog_device` FOREIGN KEY (`device_id`) | |
| REFERENCES `devices`(`id`) ON DELETE CASCADE | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 6: wifi_plans | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `wifi_plans`; | |
| CREATE TABLE `wifi_plans` ( | |
| `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `client_id` INT UNSIGNED NOT NULL, | |
| `device_id` INT UNSIGNED NOT NULL, | |
| `name` VARCHAR(50) NOT NULL, | |
| `duration_seconds` INT UNSIGNED NOT NULL, | |
| `price` DECIMAL(10,2) NOT NULL, | |
| `down_limit` SMALLINT UNSIGNED NOT NULL DEFAULT 2, | |
| `down_unit` TINYINT UNSIGNED NOT NULL DEFAULT 2 COMMENT '1=Kbps 2=Mbps', | |
| `up_limit` SMALLINT UNSIGNED NOT NULL DEFAULT 2, | |
| `up_unit` TINYINT UNSIGNED NOT NULL DEFAULT 2 COMMENT '1=Kbps 2=Mbps', | |
| `display_order` TINYINT UNSIGNED NOT NULL DEFAULT 0, | |
| `is_active` TINYINT(1) NOT NULL DEFAULT 1, | |
| `omada_voucher_group_id` VARCHAR(64) DEFAULT NULL, | |
| `omada_voucher_group_synced_at` TIMESTAMP NULL DEFAULT NULL, | |
| `omada_voucher_group_error` VARCHAR(255) DEFAULT NULL, | |
| `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | |
| KEY `idx_plan_client` (`client_id`), | |
| KEY `idx_plan_device` (`device_id`), | |
| KEY `idx_plan_active` (`device_id`, `is_active`, `display_order`), | |
| CONSTRAINT `fk_plan_client` FOREIGN KEY (`client_id`) REFERENCES `clients`(`id`), | |
| CONSTRAINT `fk_plan_device` FOREIGN KEY (`device_id`) REFERENCES `devices`(`id`) | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 7: payments | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `payments`; | |
| CREATE TABLE `payments` ( | |
| `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `reference` VARCHAR(64) NOT NULL, | |
| `client_id` INT UNSIGNED NOT NULL, | |
| `device_id` INT UNSIGNED NOT NULL, | |
| `plan_id` INT UNSIGNED NOT NULL, | |
| `phone` VARCHAR(20) DEFAULT NULL, | |
| `phone_provider` ENUM('mpesa','selcom','airtel','tigo','halopesa','bank','other') NOT NULL DEFAULT 'mpesa', | |
| `payment_channel` ENUM('snippe','tenant_direct_manual') NOT NULL DEFAULT 'snippe', | |
| `provider_account_type` ENUM('platform','tenant') NOT NULL DEFAULT 'platform', | |
| `provider_account_client_id` INT UNSIGNED DEFAULT NULL, | |
| `verification_source` ENUM('webhook','tenant_confirmed') DEFAULT NULL, | |
| `counts_toward_balance` TINYINT(1) NOT NULL DEFAULT 1, | |
| `confirmed_by_client_id` INT UNSIGNED DEFAULT NULL, | |
| `confirmed_at` TIMESTAMP NULL DEFAULT NULL, | |
| `notes` VARCHAR(255) DEFAULT NULL, | |
| `gross_amount` DECIMAL(10,2) NOT NULL, | |
| `snippe_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00, | |
| `commission_rate` DECIMAL(5,4) NOT NULL DEFAULT 0.0500, | |
| `commission_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00, | |
| `client_credit` DECIMAL(10,2) NOT NULL DEFAULT 0.00, | |
| `status` ENUM('pending','completed','failed','refunded','voided') NOT NULL DEFAULT 'pending', | |
| `snippe_payment_id` VARCHAR(100) DEFAULT NULL, | |
| `access_token_code` VARCHAR(20) DEFAULT NULL, | |
| `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| `completed_at` TIMESTAMP NULL DEFAULT NULL, | |
| UNIQUE KEY `uk_reference` (`reference`), | |
| KEY `idx_pay_client` (`client_id`), | |
| KEY `idx_pay_device` (`device_id`), | |
| KEY `idx_pay_status` (`status`), | |
| KEY `idx_pay_completed` (`completed_at`), | |
| CONSTRAINT `fk_pay_client` FOREIGN KEY (`client_id`) REFERENCES `clients`(`id`), | |
| CONSTRAINT `fk_pay_device` FOREIGN KEY (`device_id`) REFERENCES `devices`(`id`), | |
| CONSTRAINT `fk_pay_plan` FOREIGN KEY (`plan_id`) REFERENCES `wifi_plans`(`id`) | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 7b: client_payment_credentials | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `client_payment_credentials`; | |
| CREATE TABLE `client_payment_credentials` ( | |
| `client_id` INT UNSIGNED NOT NULL, | |
| `provider` VARCHAR(32) NOT NULL DEFAULT 'snippe', | |
| `is_enabled` TINYINT(1) NOT NULL DEFAULT 0, | |
| `fallback_to_platform` TINYINT(1) NOT NULL DEFAULT 1, | |
| `api_key` TEXT DEFAULT NULL, | |
| `webhook_secret` TEXT DEFAULT NULL, | |
| `credential_status` VARCHAR(32) NOT NULL DEFAULT 'untested', | |
| `last_test_message` VARCHAR(255) DEFAULT NULL, | |
| `last_tested_at` TIMESTAMP NULL DEFAULT NULL, | |
| `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | |
| PRIMARY KEY (`client_id`, `provider`), | |
| CONSTRAINT `fk_client_payment_credentials_client` | |
| FOREIGN KEY (`client_id`) REFERENCES `clients`(`id`) ON DELETE CASCADE | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 8: access_tokens | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `access_tokens`; | |
| CREATE TABLE `access_tokens` ( | |
| `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `client_id` INT UNSIGNED NOT NULL, | |
| `device_id` INT UNSIGNED NOT NULL, | |
| `plan_id` INT UNSIGNED NOT NULL, | |
| `payment_id` BIGINT UNSIGNED DEFAULT NULL, | |
| `code` VARCHAR(20) NOT NULL, | |
| `omada_voucher_id` VARCHAR(64) DEFAULT NULL, | |
| `omada_voucher_group_id` VARCHAR(64) DEFAULT NULL, | |
| `duration_seconds` INT UNSIGNED NOT NULL, | |
| `down_limit` SMALLINT UNSIGNED NOT NULL DEFAULT 2, | |
| `down_unit` TINYINT UNSIGNED NOT NULL DEFAULT 2, | |
| `up_limit` SMALLINT UNSIGNED NOT NULL DEFAULT 2, | |
| `up_unit` TINYINT UNSIGNED NOT NULL DEFAULT 2, | |
| `status` ENUM('unused','active','expired','revoked') NOT NULL DEFAULT 'unused', | |
| `locked_mac` VARCHAR(17) DEFAULT NULL, | |
| `activated_at` TIMESTAMP NULL DEFAULT NULL, | |
| `expires_at` TIMESTAMP NULL DEFAULT NULL, | |
| `total_seconds_used` INT UNSIGNED NOT NULL DEFAULT 0, | |
| `total_bytes_down` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| `total_bytes_up` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| UNIQUE KEY `uk_token_code` (`code`), | |
| KEY `idx_token_client` (`client_id`), | |
| KEY `idx_token_device` (`device_id`), | |
| KEY `idx_token_status` (`status`), | |
| KEY `idx_token_expires` (`status`, `expires_at`), | |
| KEY `idx_token_payment` (`payment_id`), | |
| CONSTRAINT `fk_token_client` FOREIGN KEY (`client_id`) REFERENCES `clients`(`id`), | |
| CONSTRAINT `fk_token_device` FOREIGN KEY (`device_id`) REFERENCES `devices`(`id`), | |
| CONSTRAINT `fk_token_plan` FOREIGN KEY (`plan_id`) REFERENCES `wifi_plans`(`id`) | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 9: sessions | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `sessions`; | |
| CREATE TABLE `sessions` ( | |
| `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `access_token_id` BIGINT UNSIGNED DEFAULT NULL, | |
| `client_id` INT UNSIGNED NOT NULL, | |
| `device_id` INT UNSIGNED DEFAULT NULL, | |
| `client_mac` VARCHAR(17) NOT NULL, | |
| `client_ip` VARCHAR(45) DEFAULT NULL, | |
| `client_name` VARCHAR(100) DEFAULT NULL, | |
| `ssid` VARCHAR(64) DEFAULT NULL, | |
| `started_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| `last_seen_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| `ended_at` TIMESTAMP NULL DEFAULT NULL, | |
| `duration_seconds` INT UNSIGNED NOT NULL DEFAULT 0, | |
| `bytes_down` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| `bytes_up` BIGINT UNSIGNED NOT NULL DEFAULT 0, | |
| `rssi` SMALLINT DEFAULT NULL, | |
| `signal_level` TINYINT UNSIGNED DEFAULT NULL, | |
| `is_active` TINYINT(1) NOT NULL DEFAULT 1, | |
| KEY `idx_sess_token` (`access_token_id`), | |
| KEY `idx_sess_client` (`client_id`), | |
| KEY `idx_sess_device` (`device_id`), | |
| KEY `idx_sess_mac` (`client_mac`), | |
| KEY `idx_sess_active` (`is_active`, `client_id`), | |
| KEY `idx_sess_time` (`started_at`), | |
| CONSTRAINT `fk_sess_token` FOREIGN KEY (`access_token_id`) REFERENCES `access_tokens`(`id`), | |
| CONSTRAINT `fk_sess_client` FOREIGN KEY (`client_id`) REFERENCES `clients`(`id`), | |
| CONSTRAINT `fk_sess_device` FOREIGN KEY (`device_id`) REFERENCES `devices`(`id`) | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 10: token_deauth_events | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `token_deauth_events`; | |
| CREATE TABLE `token_deauth_events` ( | |
| `token_id` BIGINT UNSIGNED NOT NULL PRIMARY KEY, | |
| `client_id` INT UNSIGNED NOT NULL, | |
| `device_id` INT UNSIGNED NOT NULL, | |
| `omada_site_id` VARCHAR(64) NOT NULL, | |
| `locked_mac` VARCHAR(17) NOT NULL, | |
| `status` ENUM('succeeded') NOT NULL DEFAULT 'succeeded', | |
| `succeeded_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| KEY `idx_token_deauth_mac` (`device_id`, `locked_mac`), | |
| KEY `idx_token_deauth_site` (`omada_site_id`), | |
| CONSTRAINT `fk_token_deauth_token` FOREIGN KEY (`token_id`) REFERENCES `access_tokens`(`id`) ON DELETE CASCADE, | |
| CONSTRAINT `fk_token_deauth_client` FOREIGN KEY (`client_id`) REFERENCES `clients`(`id`) ON DELETE CASCADE, | |
| CONSTRAINT `fk_token_deauth_device` FOREIGN KEY (`device_id`) REFERENCES `devices`(`id`) ON DELETE CASCADE | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 11: payouts | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `payouts`; | |
| CREATE TABLE `payouts` ( | |
| `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `client_id` INT UNSIGNED NOT NULL, | |
| `amount` DECIMAL(10,2) NOT NULL, | |
| `payout_fee` DECIMAL(10,2) NOT NULL DEFAULT 0.00, | |
| `net_amount` DECIMAL(10,2) NOT NULL DEFAULT 0.00, | |
| `destination_phone` VARCHAR(20) NOT NULL, | |
| `destination_provider` ENUM('mpesa','airtel','tigo','halopesa') NOT NULL DEFAULT 'mpesa', | |
| `status` ENUM('pending','processing','completed','failed') NOT NULL DEFAULT 'pending', | |
| `snippe_disbursement_id` VARCHAR(100) DEFAULT NULL, | |
| `failure_reason` VARCHAR(255) DEFAULT NULL, | |
| `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| `completed_at` TIMESTAMP NULL DEFAULT NULL, | |
| KEY `idx_payout_client` (`client_id`), | |
| KEY `idx_payout_status` (`status`), | |
| CONSTRAINT `fk_payout_client` FOREIGN KEY (`client_id`) REFERENCES `clients`(`id`) | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 12: balance_ledger | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `balance_ledger`; | |
| CREATE TABLE `balance_ledger` ( | |
| `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `client_id` INT UNSIGNED NOT NULL, | |
| `type` ENUM('wifi_sale','payout','payout_reversal','adjustment','refund') NOT NULL, | |
| `amount` DECIMAL(10,2) NOT NULL, | |
| `balance_after` DECIMAL(12,2) NOT NULL, | |
| `reference_type` ENUM('payment','payout','manual') NOT NULL, | |
| `reference_id` BIGINT UNSIGNED NOT NULL, | |
| `description` VARCHAR(255) DEFAULT NULL, | |
| `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| KEY `idx_ledger_client` (`client_id`), | |
| KEY `idx_ledger_time` (`created_at`), | |
| KEY `idx_ledger_ref` (`reference_type`, `reference_id`), | |
| CONSTRAINT `fk_ledger_client` FOREIGN KEY (`client_id`) REFERENCES `clients`(`id`) | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 13: omada_alerts | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `omada_alerts`; | |
| CREATE TABLE `omada_alerts` ( | |
| `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `client_id` INT UNSIGNED NOT NULL, | |
| `device_id` INT UNSIGNED DEFAULT NULL, | |
| `omada_alert_id` VARCHAR(64) NOT NULL, | |
| `alert_key` VARCHAR(50) NOT NULL, | |
| `module` VARCHAR(50) DEFAULT NULL, | |
| `level` ENUM('info','warning','error','critical') NOT NULL DEFAULT 'info', | |
| `content` TEXT NOT NULL, | |
| `device_mac` VARCHAR(17) DEFAULT NULL, | |
| `resolved` TINYINT(1) NOT NULL DEFAULT 0, | |
| `alert_time` TIMESTAMP NOT NULL, | |
| `fetched_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| UNIQUE KEY `uk_omada_alert` (`omada_alert_id`), | |
| KEY `idx_alert_client` (`client_id`), | |
| KEY `idx_alert_device` (`device_id`), | |
| KEY `idx_alert_unresolved` (`client_id`, `resolved`), | |
| CONSTRAINT `fk_alert_client` FOREIGN KEY (`client_id`) REFERENCES `clients`(`id`), | |
| CONSTRAINT `fk_alert_device` FOREIGN KEY (`device_id`) REFERENCES `devices`(`id`) ON DELETE SET NULL | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 13: sms_log | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `sms_log`; | |
| CREATE TABLE `sms_log` ( | |
| `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `phone` VARCHAR(20) NOT NULL, | |
| `message` VARCHAR(320) NOT NULL, | |
| `status` ENUM('sent','delivered','failed') NOT NULL, | |
| `gateway_id` VARCHAR(100) DEFAULT NULL, | |
| `error` VARCHAR(255) DEFAULT NULL, | |
| `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| `delivered_at` TIMESTAMP NULL DEFAULT NULL, | |
| KEY `idx_sms_phone` (`phone`), | |
| KEY `idx_sms_status` (`status`), | |
| KEY `idx_sms_time` (`created_at`), | |
| KEY `idx_sms_gateway` (`gateway_id`) | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TRIGGERS | |
| -- ============================================= | |
| DROP TRIGGER IF EXISTS `trg_payment_completed`; | |
| DELIMITER $$ | |
| CREATE TRIGGER `trg_payment_completed` | |
| BEFORE UPDATE ON `payments` | |
| FOR EACH ROW | |
| BEGIN | |
| DECLARE v_new_balance DECIMAL(12,2); | |
| IF OLD.status = 'pending' AND NEW.status = 'completed' THEN | |
| IF NEW.counts_toward_balance = 1 THEN | |
| SET NEW.snippe_fee = ROUND(NEW.gross_amount * 0.005, 2); | |
| SET NEW.commission_amount = ROUND(NEW.gross_amount * NEW.commission_rate, 2); | |
| SET NEW.client_credit = NEW.gross_amount - NEW.snippe_fee - NEW.commission_amount; | |
| ELSE | |
| SET NEW.snippe_fee = 0.00; | |
| SET NEW.commission_amount = 0.00; | |
| SET NEW.client_credit = 0.00; | |
| END IF; | |
| SET NEW.completed_at = CURRENT_TIMESTAMP; | |
| IF NEW.counts_toward_balance = 1 THEN | |
| UPDATE `clients` | |
| SET `balance` = `balance` + NEW.client_credit, | |
| `total_earned` = `total_earned` + NEW.client_credit | |
| WHERE `id` = NEW.client_id; | |
| END IF; | |
| IF NEW.counts_toward_balance = 1 THEN | |
| SET v_new_balance = (SELECT `balance` FROM `clients` WHERE `id` = NEW.client_id); | |
| INSERT INTO `balance_ledger` | |
| (`client_id`, `type`, `amount`, `balance_after`, `reference_type`, `reference_id`, `description`) | |
| VALUES | |
| (NEW.client_id, 'wifi_sale', NEW.client_credit, v_new_balance, 'payment', NEW.id, | |
| CONCAT('WiFi sale: ', NEW.gross_amount, ' TZS (fee: ', NEW.snippe_fee, ', comm: ', NEW.commission_amount, ')')); | |
| END IF; | |
| END IF; | |
| END$$ | |
| DELIMITER ; | |
| DROP TRIGGER IF EXISTS `trg_payout_requested`; | |
| DELIMITER $$ | |
| CREATE TRIGGER `trg_payout_requested` | |
| AFTER INSERT ON `payouts` | |
| FOR EACH ROW | |
| BEGIN | |
| DECLARE v_new_balance DECIMAL(12,2); | |
| IF NEW.status = 'pending' THEN | |
| UPDATE `clients` | |
| SET `balance` = `balance` - NEW.amount | |
| WHERE `id` = NEW.client_id; | |
| SET v_new_balance = (SELECT `balance` FROM `clients` WHERE `id` = NEW.client_id); | |
| INSERT INTO `balance_ledger` | |
| (`client_id`, `type`, `amount`, `balance_after`, `reference_type`, `reference_id`, `description`) | |
| VALUES | |
| (NEW.client_id, 'payout', -NEW.amount, v_new_balance, 'payout', NEW.id, | |
| CONCAT('Payout request: ', NEW.amount, ' TZS to ', NEW.destination_phone)); | |
| END IF; | |
| END$$ | |
| DELIMITER ; | |
| DROP TRIGGER IF EXISTS `trg_payout_failed`; | |
| DELIMITER $$ | |
| CREATE TRIGGER `trg_payout_failed` | |
| BEFORE UPDATE ON `payouts` | |
| FOR EACH ROW | |
| BEGIN | |
| DECLARE v_new_balance DECIMAL(12,2); | |
| IF OLD.status = 'pending' AND NEW.status = 'failed' THEN | |
| UPDATE `clients` | |
| SET `balance` = `balance` + OLD.amount | |
| WHERE `id` = OLD.client_id; | |
| SET v_new_balance = (SELECT `balance` FROM `clients` WHERE `id` = OLD.client_id); | |
| INSERT INTO `balance_ledger` | |
| (`client_id`, `type`, `amount`, `balance_after`, `reference_type`, `reference_id`, `description`) | |
| VALUES | |
| (OLD.client_id, 'payout_reversal', OLD.amount, v_new_balance, 'payout', OLD.id, | |
| CONCAT('Payout failed: ', IFNULL(NEW.failure_reason, 'unknown'))); | |
| END IF; | |
| END$$ | |
| DELIMITER ; | |
| DROP TRIGGER IF EXISTS `trg_payout_completed`; | |
| DELIMITER $$ | |
| CREATE TRIGGER `trg_payout_completed` | |
| BEFORE UPDATE ON `payouts` | |
| FOR EACH ROW | |
| BEGIN | |
| IF OLD.status IN ('pending','processing') AND NEW.status = 'completed' THEN | |
| SET NEW.completed_at = CURRENT_TIMESTAMP; | |
| UPDATE `clients` | |
| SET `total_withdrawn` = `total_withdrawn` + OLD.amount | |
| WHERE `id` = OLD.client_id; | |
| END IF; | |
| END$$ | |
| DELIMITER ; | |
| -- ============================================= | |
| -- TABLE 14: device_portal_settings | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `device_portal_settings`; | |
| CREATE TABLE `device_portal_settings` ( | |
| `device_id` INT UNSIGNED NOT NULL PRIMARY KEY, | |
| `display_name` VARCHAR(100) DEFAULT NULL COMMENT 'Overrides business_name on the portal', | |
| `welcome_text` VARCHAR(255) DEFAULT NULL COMMENT 'Tagline shown under the name', | |
| `logo_url` VARCHAR(500) DEFAULT NULL COMMENT 'Full URL to logo image', | |
| `support_phone` VARCHAR(20) DEFAULT NULL COMMENT 'Support number shown to guests', | |
| `primary_color` VARCHAR(7) NOT NULL DEFAULT '#4361ee' COMMENT 'Buttons and accents', | |
| `bg_color` VARCHAR(7) NOT NULL DEFAULT '#1a1a2e' COMMENT 'Page background', | |
| `manual_sales_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'Enable direct/manual sales for this hotspot', | |
| `portal_payment_mode` VARCHAR(24) NOT NULL DEFAULT 'both' COMMENT 'online_only | direct_only | both', | |
| `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | |
| CONSTRAINT `fk_portal_settings_device` | |
| FOREIGN KEY (`device_id`) REFERENCES `devices`(`id`) ON DELETE CASCADE | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| DROP TABLE IF EXISTS `device_portal_payment_options`; | |
| CREATE TABLE `device_portal_payment_options` ( | |
| `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `device_id` INT UNSIGNED NOT NULL, | |
| `provider_name` VARCHAR(100) NOT NULL, | |
| `image_path` VARCHAR(255) NOT NULL, | |
| `display_order` INT NOT NULL DEFAULT 0, | |
| `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| KEY `idx_portal_payment_device` (`device_id`), | |
| CONSTRAINT `fk_portal_payment_option_device` | |
| FOREIGN KEY (`device_id`) REFERENCES `devices`(`id`) ON DELETE CASCADE | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 15: password_resets | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `password_resets`; | |
| CREATE TABLE `password_resets` ( | |
| `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, | |
| `phone` VARCHAR(20) NOT NULL, | |
| `otp` VARCHAR(6) NOT NULL, | |
| `expires_at` TIMESTAMP NOT NULL, | |
| `used` TINYINT(1) NOT NULL DEFAULT 0, | |
| `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| KEY `idx_reset_phone` (`phone`) | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| -- ============================================= | |
| -- TABLE 15: system_config | |
| -- ============================================= | |
| DROP TABLE IF EXISTS `system_config`; | |
| CREATE TABLE `system_config` ( | |
| `key` VARCHAR(64) NOT NULL PRIMARY KEY, | |
| `value` VARCHAR(255) NOT NULL, | |
| `description` VARCHAR(255) DEFAULT NULL | |
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | |
| INSERT INTO `system_config` (`key`, `value`, `description`) VALUES | |
| ('admin_phone', '+255769590766', 'System admin phone — receives critical error alerts'); | |
| SET FOREIGN_KEY_CHECKS = 1; | |