db->query($sql); } /** * Create sync logs table */ function desk_moloni_create_sync_logs_table() { $CI = &get_instance(); $sql = "CREATE TABLE IF NOT EXISTS `" . db_prefix() . "deskmoloni_sync_logs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `queue_id` int(11) DEFAULT NULL, `entity_type` varchar(50) NOT NULL, `entity_id` int(11) NOT NULL, `action` varchar(50) NOT NULL, `direction` varchar(50) NOT NULL, `status` enum('started','success','error','warning') NOT NULL, `message` text DEFAULT NULL, `request_data` longtext DEFAULT NULL COMMENT 'JSON request data', `response_data` longtext DEFAULT NULL COMMENT 'JSON response data', `execution_time` decimal(10,4) DEFAULT NULL COMMENT 'Execution time in seconds', `memory_usage` int(11) DEFAULT NULL COMMENT 'Memory usage in bytes', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `created_by` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_queue_id` (`queue_id`), KEY `idx_entity_type_id` (`entity_type`, `entity_id`), KEY `idx_status` (`status`), KEY `idx_created_at` (`created_at`), CONSTRAINT `fk_sync_logs_queue` FOREIGN KEY (`queue_id`) REFERENCES `" . db_prefix() . "deskmoloni_sync_queue` (`id`) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; $CI->db->query($sql); } /** * Create entity mappings table */ function desk_moloni_create_entity_mappings_table() { $CI = &get_instance(); $sql = "CREATE TABLE IF NOT EXISTS `" . db_prefix() . "desk_moloni_entity_mappings` ( `id` int(11) NOT NULL AUTO_INCREMENT, `entity_type` varchar(50) NOT NULL, `perfex_id` int(11) NOT NULL, `moloni_id` int(11) NOT NULL, `perfex_hash` varchar(64) DEFAULT NULL COMMENT 'Hash of Perfex entity data', `moloni_hash` varchar(64) DEFAULT NULL COMMENT 'Hash of Moloni entity data', `sync_status` enum('synced','pending','error','conflict') NOT NULL DEFAULT 'synced', `last_sync_at` timestamp NULL DEFAULT NULL, `last_perfex_update` timestamp NULL DEFAULT NULL, `last_moloni_update` timestamp NULL DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `metadata` json DEFAULT NULL COMMENT 'Additional mapping metadata', PRIMARY KEY (`id`), UNIQUE KEY `uk_entity_perfex` (`entity_type`, `perfex_id`), UNIQUE KEY `uk_entity_moloni` (`entity_type`, `moloni_id`), KEY `idx_sync_status` (`sync_status`), KEY `idx_last_sync` (`last_sync_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; $CI->db->query($sql); } /** * Create sync rules table */ function desk_moloni_create_sync_rules_table() { $CI = &get_instance(); $sql = "CREATE TABLE IF NOT EXISTS `" . db_prefix() . "desk_moloni_sync_rules` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL, `entity_type` varchar(50) NOT NULL, `rule_type` enum('field_mapping','validation','transformation','filter') NOT NULL, `conditions` json DEFAULT NULL COMMENT 'Rule conditions', `actions` json DEFAULT NULL COMMENT 'Rule actions', `priority` int(11) NOT NULL DEFAULT 0, `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, `created_by` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_entity_type` (`entity_type`), KEY `idx_rule_type` (`rule_type`), KEY `idx_active_priority` (`active`, `priority`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; $CI->db->query($sql); } /** * Create webhook logs table */ function desk_moloni_create_webhook_logs_table() { $CI = &get_instance(); $sql = "CREATE TABLE IF NOT EXISTS `" . db_prefix() . "desk_moloni_webhook_logs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `webhook_id` varchar(100) DEFAULT NULL, `event_type` varchar(50) NOT NULL, `source` enum('moloni','perfex') NOT NULL, `payload` longtext DEFAULT NULL COMMENT 'JSON webhook payload', `headers` json DEFAULT NULL COMMENT 'Request headers', `signature` varchar(255) DEFAULT NULL, `signature_valid` tinyint(1) DEFAULT NULL, `processed` tinyint(1) NOT NULL DEFAULT 0, `processing_result` text DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `processed_at` timestamp NULL DEFAULT NULL, `ip_address` varchar(45) DEFAULT NULL, `user_agent` text DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_event_type` (`event_type`), KEY `idx_source` (`source`), KEY `idx_processed` (`processed`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; $CI->db->query($sql); } /** * Create performance metrics table */ function desk_moloni_create_performance_metrics_table() { $CI = &get_instance(); $sql = "CREATE TABLE IF NOT EXISTS `" . db_prefix() . "desk_moloni_performance_metrics` ( `id` int(11) NOT NULL AUTO_INCREMENT, `metric_type` varchar(50) NOT NULL, `metric_name` varchar(100) NOT NULL, `metric_value` decimal(15,4) NOT NULL, `metric_unit` varchar(20) DEFAULT NULL, `entity_type` varchar(50) DEFAULT NULL, `entity_id` int(11) DEFAULT NULL, `tags` json DEFAULT NULL COMMENT 'Additional metric tags', `recorded_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `date_hour` varchar(13) NOT NULL COMMENT 'YYYY-MM-DD HH for aggregation', `date_day` date NOT NULL COMMENT 'Date for daily aggregation', PRIMARY KEY (`id`), KEY `idx_metric_type_name` (`metric_type`, `metric_name`), KEY `idx_recorded_at` (`recorded_at`), KEY `idx_date_hour` (`date_hour`), KEY `idx_date_day` (`date_day`), KEY `idx_entity` (`entity_type`, `entity_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; $CI->db->query($sql); } /** * Create error logs table */ function desk_moloni_create_error_logs_table() { $CI = &get_instance(); $sql = "CREATE TABLE IF NOT EXISTS `" . db_prefix() . "desk_moloni_error_logs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `error_code` varchar(50) DEFAULT NULL, `error_type` varchar(50) NOT NULL, `severity` enum('low','medium','high','critical') NOT NULL DEFAULT 'medium', `message` text NOT NULL, `context` json DEFAULT NULL COMMENT 'Error context data', `stack_trace` longtext DEFAULT NULL, `entity_type` varchar(50) DEFAULT NULL, `entity_id` int(11) DEFAULT NULL, `queue_id` int(11) DEFAULT NULL, `resolved` tinyint(1) NOT NULL DEFAULT 0, `resolved_at` timestamp NULL DEFAULT NULL, `resolved_by` int(11) DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `ip_address` varchar(45) DEFAULT NULL, `user_agent` text DEFAULT NULL, `created_by` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_error_type` (`error_type`), KEY `idx_severity` (`severity`), KEY `idx_resolved` (`resolved`), KEY `idx_created_at` (`created_at`), KEY `idx_entity` (`entity_type`, `entity_id`), KEY `idx_queue_id` (`queue_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; $CI->db->query($sql); } /** * Create configuration table */ function desk_moloni_create_configuration_table() { $CI = &get_instance(); $sql = "CREATE TABLE IF NOT EXISTS `" . db_prefix() . "desk_moloni_configuration` ( `id` int(11) NOT NULL AUTO_INCREMENT, `config_key` varchar(100) NOT NULL, `config_value` longtext DEFAULT NULL, `config_type` enum('string','integer','boolean','json','encrypted') NOT NULL DEFAULT 'string', `description` text DEFAULT NULL, `category` varchar(50) DEFAULT NULL, `is_system` tinyint(1) NOT NULL DEFAULT 0, `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `updated_by` int(11) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_config_key` (`config_key`), KEY `idx_category` (`category`), KEY `idx_is_system` (`is_system`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; $CI->db->query($sql); } /** * Create API tokens table */ function desk_moloni_create_api_tokens_table() { $CI = &get_instance(); $sql = "CREATE TABLE IF NOT EXISTS `" . db_prefix() . "desk_moloni_api_tokens` ( `id` int(11) NOT NULL AUTO_INCREMENT, `token_type` enum('access_token','refresh_token','webhook_token') NOT NULL, `token_value` text NOT NULL COMMENT 'Encrypted token value', `expires_at` timestamp NULL DEFAULT NULL, `company_id` int(11) DEFAULT NULL, `scopes` json DEFAULT NULL, `metadata` json DEFAULT NULL, `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, `last_used_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_token_type` (`token_type`), KEY `idx_company_id` (`company_id`), KEY `idx_active` (`active`), KEY `idx_expires_at` (`expires_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; $CI->db->query($sql); } /** * Create document cache table */ function desk_moloni_create_document_cache_table() { $CI = &get_instance(); $sql = "CREATE TABLE IF NOT EXISTS `" . db_prefix() . "desk_moloni_document_cache` ( `id` int(11) NOT NULL AUTO_INCREMENT, `cache_key` varchar(255) NOT NULL, `entity_type` varchar(50) NOT NULL, `entity_id` int(11) NOT NULL, `document_type` varchar(50) DEFAULT NULL, `document_data` longtext DEFAULT NULL COMMENT 'Cached document data', `file_path` varchar(500) DEFAULT NULL, `file_size` int(11) DEFAULT NULL, `mime_type` varchar(100) DEFAULT NULL, `expires_at` timestamp NULL DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `accessed_at` timestamp NULL DEFAULT NULL, `access_count` int(11) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `uk_cache_key` (`cache_key`), KEY `idx_entity` (`entity_type`, `entity_id`), KEY `idx_expires_at` (`expires_at`), KEY `idx_document_type` (`document_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; $CI->db->query($sql); } /** * Insert default settings */ function desk_moloni_insert_default_settings() { $default_options = [ // API Configuration 'desk_moloni_api_base_url' => 'https://api.moloni.pt/v1/', 'desk_moloni_oauth_base_url' => 'https://www.moloni.pt/v1/', 'desk_moloni_api_timeout' => '30', 'desk_moloni_max_retries' => '3', // Sync Configuration 'desk_moloni_auto_sync_enabled' => '1', 'desk_moloni_realtime_sync_enabled' => '0', 'desk_moloni_sync_delay' => '300', 'desk_moloni_batch_sync_enabled' => '1', // Entity Sync Settings 'desk_moloni_sync_customers' => '1', 'desk_moloni_sync_invoices' => '1', 'desk_moloni_sync_estimates' => '1', 'desk_moloni_sync_credit_notes' => '1', 'desk_moloni_sync_receipts' => '0', 'desk_moloni_sync_products' => '0', // Performance Settings 'desk_moloni_enable_monitoring' => '1', 'desk_moloni_enable_performance_tracking' => '1', 'desk_moloni_enable_caching' => '1', 'desk_moloni_cache_ttl' => '3600', // Security Settings 'desk_moloni_enable_encryption' => '1', 'desk_moloni_webhook_signature_verification' => '1', 'desk_moloni_enable_audit_logging' => '1', // Logging Settings 'desk_moloni_enable_logging' => '1', 'desk_moloni_log_level' => 'info', 'desk_moloni_log_api_requests' => '0', 'desk_moloni_log_retention_days' => '30', // Queue Settings 'desk_moloni_enable_queue' => '1', 'desk_moloni_queue_batch_size' => '10', 'desk_moloni_queue_max_attempts' => '3', 'desk_moloni_queue_retry_delay' => '300', // Webhook Settings 'desk_moloni_enable_webhooks' => '1', 'desk_moloni_webhook_timeout' => '30', 'desk_moloni_webhook_max_retries' => '3', // Client Portal Settings 'desk_moloni_enable_client_portal' => '0', 'desk_moloni_client_can_download_pdfs' => '1', // Error Handling 'desk_moloni_continue_on_error' => '1', 'desk_moloni_max_consecutive_errors' => '5', 'desk_moloni_enable_error_notifications' => '1', // Cleanup Settings 'desk_moloni_enable_cleanup' => '1', 'desk_moloni_cleanup_interval' => '24', 'desk_moloni_temp_file_retention' => '1', // Rate Limiting 'desk_moloni_enable_rate_limiting' => '1', 'desk_moloni_requests_per_minute' => '60', 'desk_moloni_rate_limit_window' => '60', // Redis Settings 'desk_moloni_enable_redis' => '0', 'desk_moloni_redis_host' => '127.0.0.1', 'desk_moloni_redis_port' => '6379', 'desk_moloni_redis_database' => '0', // Module Metadata 'desk_moloni_module_version' => (defined('DESK_MOLONI_MODULE_VERSION') ? DESK_MOLONI_MODULE_VERSION : (defined('DESK_MOLONI_VERSION') ? DESK_MOLONI_VERSION : '3.0.1')), 'desk_moloni_installation_date' => date('Y-m-d H:i:s'), 'desk_moloni_last_update' => date('Y-m-d H:i:s') ]; foreach ($default_options as $name => $value) { // Only add if doesn't exist if (!get_option($name)) { update_option($name, $value); } } } /** * Create default sync configurations */ function desk_moloni_create_default_sync_configs() { $CI = &get_instance(); // Default field mapping rules $field_mapping_rules = [ [ 'name' => 'Customer Basic Fields', 'entity_type' => 'customer', 'rule_type' => 'field_mapping', 'conditions' => json_encode(['always' => true]), 'actions' => json_encode([ 'perfex_to_moloni' => [ 'company' => 'name', 'vat' => 'vat', 'billing_street' => 'address', 'billing_city' => 'city', 'billing_zip' => 'zip_code', 'email' => 'email', 'phonenumber' => 'phone' ], 'moloni_to_perfex' => [ 'name' => 'company', 'vat' => 'vat', 'address' => 'billing_street', 'city' => 'billing_city', 'zip_code' => 'billing_zip', 'email' => 'email', 'phone' => 'phonenumber' ] ]), 'priority' => 10, 'active' => 1 ], [ 'name' => 'Invoice Basic Fields', 'entity_type' => 'invoice', 'rule_type' => 'field_mapping', 'conditions' => json_encode(['always' => true]), 'actions' => json_encode([ 'perfex_to_moloni' => [ 'number' => 'number', 'date' => 'date', 'duedate' => 'due_date', 'subtotal' => 'net_value', 'total' => 'gross_value' ], 'moloni_to_perfex' => [ 'number' => 'number', 'date' => 'date', 'due_date' => 'duedate', 'net_value' => 'subtotal', 'gross_value' => 'total' ] ]), 'priority' => 10, 'active' => 1 ] ]; foreach ($field_mapping_rules as $rule) { $CI->db->insert(db_prefix() . 'desk_moloni_sync_rules', $rule); } } /** * Insert default configuration values */ function desk_moloni_insert_default_configurations() { $CI = &get_instance(); $default_configs = [ [ 'config_key' => 'api_configuration', 'config_value' => json_encode([ 'base_url' => 'https://api.moloni.pt/v1/', 'oauth_url' => 'https://www.moloni.pt/v1/', 'timeout' => 30, 'max_retries' => 3, 'user_agent' => 'Desk-Moloni-Integration/3.0.0' ]), 'config_type' => 'json', 'description' => 'API connection configuration', 'category' => 'api', 'is_system' => 1 ], [ 'config_key' => 'sync_configuration', 'config_value' => json_encode([ 'auto_sync' => true, 'realtime_sync' => false, 'batch_size' => 10, 'default_delay' => 300, 'max_attempts' => 3 ]), 'config_type' => 'json', 'description' => 'Synchronization behavior configuration', 'category' => 'sync', 'is_system' => 0 ], [ 'config_key' => 'performance_configuration', 'config_value' => json_encode([ 'monitoring_enabled' => true, 'caching_enabled' => true, 'cache_ttl' => 3600, 'log_slow_queries' => true, 'slow_query_threshold' => 1000 ]), 'config_type' => 'json', 'description' => 'Performance and monitoring settings', 'category' => 'performance', 'is_system' => 0 ] ]; foreach ($default_configs as $config) { // Check if configuration already exists $existing = $CI->db->get_where(db_prefix() . 'desk_moloni_configuration', ['config_key' => $config['config_key']])->row(); if (!$existing) { $CI->db->insert(db_prefix() . 'desk_moloni_configuration', $config); } } } /** * Create desk_moloni_config table (for existing model compatibility) */ function desk_moloni_create_config_table() { $CI = &get_instance(); $sql = "CREATE TABLE IF NOT EXISTS `" . db_prefix() . "deskmoloni_config` ( `id` int(11) NOT NULL AUTO_INCREMENT, `setting_key` varchar(255) NOT NULL, `setting_value` longtext DEFAULT NULL, `encrypted` tinyint(1) NOT NULL DEFAULT 0, `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_setting_key` (`setting_key`), KEY `idx_setting_key` (`setting_key`), KEY `idx_encrypted` (`encrypted`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; $CI->db->query($sql); } /** * Create desk_moloni_mapping table (for existing model compatibility) */ function desk_moloni_create_mapping_table() { $CI = &get_instance(); $sql = "CREATE TABLE IF NOT EXISTS `" . db_prefix() . "deskmoloni_mapping` ( `id` int(11) NOT NULL AUTO_INCREMENT, `entity_type` enum('client','product','invoice','estimate','credit_note') NOT NULL, `perfex_id` int(11) NOT NULL, `moloni_id` int(11) NOT NULL, `sync_direction` enum('perfex_to_moloni','moloni_to_perfex','bidirectional') NOT NULL DEFAULT 'bidirectional', `last_sync_at` timestamp NULL DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `unique_perfex_mapping` (`entity_type`, `perfex_id`), UNIQUE KEY `unique_moloni_mapping` (`entity_type`, `moloni_id`), KEY `idx_entity_perfex` (`entity_type`, `perfex_id`), KEY `idx_entity_moloni` (`entity_type`, `moloni_id`), KEY `idx_last_sync` (`last_sync_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; $CI->db->query($sql); } /** * Create desk_moloni_sync_log table (for existing model compatibility) */ function desk_moloni_create_sync_log_table() { $CI = &get_instance(); $sql = "CREATE TABLE IF NOT EXISTS `" . db_prefix() . "deskmoloni_sync_log` ( `id` int(11) NOT NULL AUTO_INCREMENT, `operation_type` enum('create','update','delete','status_change') NOT NULL, `entity_type` enum('client','product','invoice','estimate','credit_note') NOT NULL, `perfex_id` int(11) DEFAULT NULL, `moloni_id` int(11) DEFAULT NULL, `direction` enum('perfex_to_moloni','moloni_to_perfex') NOT NULL, `status` enum('success','error','warning') NOT NULL, `request_data` longtext DEFAULT NULL COMMENT 'JSON request data', `response_data` longtext DEFAULT NULL COMMENT 'JSON response data', `error_message` text DEFAULT NULL, `execution_time_ms` int(11) DEFAULT NULL COMMENT 'Execution time in milliseconds', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_entity_status` (`entity_type`, `status`), KEY `idx_perfex_entity` (`perfex_id`, `entity_type`), KEY `idx_moloni_entity` (`moloni_id`, `entity_type`), KEY `idx_created_at` (`created_at`), KEY `idx_status_direction` (`status`, `direction`), KEY `idx_log_analytics` (`entity_type`, `operation_type`, `status`, `created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"; $CI->db->query($sql); }