-- ========================================================================== -- MSP Quote Wizard - Database Schema -- Target: MySQL 5.7+ / MariaDB 10.3+ on cPanel -- Database: azcomputerguru_acg2025 -- Table prefix: acgq_ -- ========================================================================== SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- -------------------------------------------------------------------------- -- Quotes table - main quote records -- -------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `acgq_quotes` ( `id` CHAR(36) NOT NULL, `access_token` VARCHAR(64) NOT NULL, `status` VARCHAR(20) NOT NULL DEFAULT 'draft', `company_name` VARCHAR(255) DEFAULT NULL, `contact_name` VARCHAR(255) DEFAULT NULL, `contact_email` VARCHAR(255) DEFAULT NULL, `contact_phone` VARCHAR(50) DEFAULT NULL, `employee_count` INT DEFAULT NULL, `industry` VARCHAR(100) DEFAULT NULL, `current_it_situation` TEXT DEFAULT NULL, `monthly_total` DECIMAL(10,2) NOT NULL DEFAULT 0.00, `setup_total` DECIMAL(10,2) NOT NULL DEFAULT 0.00, `expires_at` DATETIME DEFAULT NULL, `submitted_at` DATETIME DEFAULT NULL, `ip_address` VARCHAR(45) DEFAULT NULL, `user_agent` TEXT DEFAULT NULL, `source` VARCHAR(50) DEFAULT 'website', `utm_source` VARCHAR(100) DEFAULT NULL, `utm_medium` VARCHAR(100) DEFAULT NULL, `utm_campaign` VARCHAR(100) DEFAULT NULL, `syncro_lead_id` VARCHAR(100) DEFAULT NULL, `syncro_synced_at` DATETIME DEFAULT NULL, `is_existing_customer` TINYINT(1) NOT NULL DEFAULT 0, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uq_quotes_access_token` (`access_token`), INDEX `idx_quotes_access_token` (`access_token`), INDEX `idx_quotes_status` (`status`), INDEX `idx_quotes_contact_email` (`contact_email`), INDEX `idx_quotes_created_at` (`created_at`), CONSTRAINT `ck_quotes_status` CHECK ( `status` IN ('draft', 'submitted', 'viewed', 'followed_up', 'converted', 'expired', 'archived') ) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- -------------------------------------------------------------------------- -- Quote items table - line items within a quote -- -------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `acgq_quote_items` ( `id` CHAR(36) NOT NULL, `quote_id` CHAR(36) NOT NULL, `category` VARCHAR(50) NOT NULL, `product_code` VARCHAR(50) NOT NULL, `product_name` VARCHAR(255) NOT NULL, `description` TEXT DEFAULT NULL, `quantity` INT NOT NULL DEFAULT 1, `unit_price` DECIMAL(10,2) NOT NULL, `setup_price` DECIMAL(10,2) NOT NULL DEFAULT 0.00, `billing_frequency` VARCHAR(20) NOT NULL DEFAULT 'monthly', `tier` VARCHAR(50) DEFAULT NULL, `is_recommended` TINYINT(1) NOT NULL DEFAULT 0, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_quote_items_quote_id` (`quote_id`), INDEX `idx_quote_items_category` (`category`), CONSTRAINT `fk_quote_items_quote` FOREIGN KEY (`quote_id`) REFERENCES `acgq_quotes` (`id`) ON DELETE CASCADE, CONSTRAINT `ck_quote_items_category` CHECK ( `category` IN ('gps_monitoring', 'support_plan', 'voip', 'web_hosting', 'email', 'hardware', 'addon', 'backup', 'security', 'other') ), CONSTRAINT `ck_quote_items_billing_frequency` CHECK ( `billing_frequency` IN ('monthly', 'yearly', 'one_time') ) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- -------------------------------------------------------------------------- -- Quote activity table - audit log of all actions on a quote -- -------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `acgq_quote_activity` ( `id` CHAR(36) NOT NULL, `quote_id` CHAR(36) NOT NULL, `action` VARCHAR(50) NOT NULL, `step_name` VARCHAR(50) DEFAULT NULL, `details` TEXT DEFAULT NULL, `ip_address` VARCHAR(45) DEFAULT NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_quote_activity_quote_id` (`quote_id`), INDEX `idx_quote_activity_action` (`action`), INDEX `idx_quote_activity_created_at` (`created_at`), CONSTRAINT `fk_quote_activity_quote` FOREIGN KEY (`quote_id`) REFERENCES `acgq_quotes` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- -------------------------------------------------------------------------- -- Quote notifications table - tracks emails and webhooks sent -- -------------------------------------------------------------------------- CREATE TABLE IF NOT EXISTS `acgq_quote_notifications` ( `id` CHAR(36) NOT NULL, `quote_id` CHAR(36) NOT NULL, `notification_type` VARCHAR(30) NOT NULL, `recipient` VARCHAR(255) NOT NULL, `subject` VARCHAR(255) DEFAULT NULL, `body` TEXT DEFAULT NULL, `status` VARCHAR(20) NOT NULL DEFAULT 'pending', `attempts` INT NOT NULL DEFAULT 0, `last_attempt_at` DATETIME DEFAULT NULL, `sent_at` DATETIME DEFAULT NULL, `error_message` TEXT DEFAULT NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_quote_notifications_quote_id` (`quote_id`), INDEX `idx_quote_notifications_type` (`notification_type`), INDEX `idx_quote_notifications_status` (`status`), CONSTRAINT `fk_quote_notifications_quote` FOREIGN KEY (`quote_id`) REFERENCES `acgq_quotes` (`id`) ON DELETE CASCADE, CONSTRAINT `ck_quote_notifications_type` CHECK ( `notification_type` IN ('email', 'webhook') ), CONSTRAINT `ck_quote_notifications_status` CHECK ( `status` IN ('pending', 'sent', 'failed') ) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; SET FOREIGN_KEY_CHECKS = 1;