diff --git a/.gitignore b/.gitignore index 1dc7ee4b..f60657bd 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,6 @@ plugins/htmlpurifier/standalone/HTMLPurifier/DefinitionCache/Serializer/CSS/* xcustom/* !xcustom/readme.php post/xcustom +custom/* !post/xcustom/readme.php .zed diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cc0a40c..9fcc1cb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,92 @@ This file documents all notable changes made to ITFlow. +## [25.09] + +***BACK UP*** before updating. + +--- + +### Breaking Changes and Notes +- We strongly recommend updating from the command line, however if performed via the webui and after performed it will return a 404. thats normal as the directory structure has changed, just close your browser then log back in then go back to update to perform the many database updates. +- This is a major release with significant changes. While the community has done a great job identifying bugs, some may still remain — continued testing is encouraged. +- All AI settings will be **reset** and must be reconfigured using the new AI provider backend. +- The `xcustom` directory has been renamed to `custom`. All custom libraries and post-processing scripts should now be placed here. + +--- + +### Added / Changed +- Numerous UI improvements and refinements across the application. +- Enhanced visual clarity by thickening the left border on ticket comments to help identify comment types. +- Ticket details UI redesigned to use less space at the top of the screen. +- Introduced tracking for the **first response date/time** on tickets. +- New reporting feature: **Average time to first response** on tickets. +- Stripe integration rebuilt using the new **payment provider backend**. +- Clients can now save and manage **multiple payment methods**. +- Support for selecting saved cards for **recurring invoices** in both the client and agent portals. +- Initial database structure and logic added for **credit management** (feature not yet enabled). +- Major **backend directory restructuring**. +- Introduced **stock/inventory management**, including a stock ledger backend. +- Stock quantities now update automatically when invoice items are added or removed. +- Invoice autocomplete now includes: **name, description, price, tax, stock levels**, and links `product_id` to `item_id`. +- Added a **category filter** to invoices. +- Linked stock to related expenses. +- New product fields: **location, code, and type**. +- Products now separated into two types: **Service** and **Product**. +- **Dark mode** introduced. +- Projects: Now support linking **closed tickets**. +- Clients: Added bulk actions for tags, referral source, industry, hourly rate, email, archive, and restore. +- Invoices: Bulk action added to **assign categories**. +- Assets: New `client_uri` field, visible in both the agent and client portals. +- Client Portal: Clients can now **select an asset** during ticket creation. +- Client Portal: Company logo now **displays in the header**. +- Client Portal: Dashboard cards are now **clickable** for more detail. +- Assets: Option added to include **MAC Address** in additional columns. +- Asset Interface: Bulk actions added — set DHCP, network type, and delete. +- API: + - Added `/location` endpoint. + - Ticket content now supports **HTML formatting**. +- New option to filter and display **500 records per page** in the footer. +- Payment methods are now treated as a **separate entity** instead of being grouped under categories. +- Updated libraries: + - **TinyMCE** + - **Chart.js** (major upgrade) + - **DataTables** + - **Bootstrap** + - **FullCalendar** + - **php-stripe** + +--- + +### Fixed +- Several security vulnerabilities patched. +- Ticket status is no longer updated when scheduling. +- Client Portal: Tech contacts can no longer edit their own details. +- Fixed overlapping logo issue in Invoice/Quote PDF exports. +- Refactored `check_login.php` into multiple files for modular login functionality. +- Removed redundant logging comments for redirects. +- Renamed `get_settings.php` to `load_global_settings.php`. +- Simplified syntax for `ajax-modal` and updated usage throughout the app. +- Fixed issue where primary contact text wasn’t displaying. +- Corrected client **Net Terms** display. +- Fixed logic for recurring expense **next run date**. +- Resolved broken **IMAP test button**. +- Archived clients can no longer log into the portal. +- Searching closed tickets no longer reverts to open tickets. +- Fixed project search filter not showing completed projects. +- Fixed issue where company logo was not being removed correctly. +- Resolved API bugs: + - Default rate and net terms. + - Contact location. + - Document endpoint. + +--- + +### Developer Updates +- Replaced legacy code with newer functions like `redirect()`, `getFieldById()`, and `flash_alert()`. +- Significantly improved performance of queries used for filter selection boxes. + + ## [25.06.1] ### Fixed diff --git a/admin/ai_model.php b/admin/ai_model.php new file mode 100644 index 00000000..cca1f863 --- /dev/null +++ b/admin/ai_model.php @@ -0,0 +1,108 @@ + + +
+
+

AI Models

+
+ +
+
+
+
+ + "> + + + + + + + + + + + + + + + + + + + No Records Here"; + } + + ?> + + +
+ + Model + + + + Provider + + + + Use Case + + + Prompt + Action
+ + + + + +
+ +
+
+
+ + + +
+
+

AI Providers

+
+ +
+
+
+
+ + "> + + + + + + + + + + + + + + + + + + + No Records Here"; + } + + ?> + + +
+ + Provider + + + + URL + + + + Key + + + Models + Action
+ + + + + +
+ +
+
+
+ + - - + - $client_name"; + $client_name_display = "$client_name"; } $log_entity_id = intval($row['log_entity_id']); @@ -292,11 +292,11 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); -
-
">Referral - Payment - Method - + @@ -155,11 +145,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - + Edit - - + @@ -118,7 +115,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - CURRENT_DATABASE_VERSION) { //Dropping patch panel as a patch panel can be documented as an asset with interfaces. mysqli_query($mysqli, "DROP TABLE `patch_panel_ports`"); mysqli_query($mysqli, "DROP TABLE `patch_panels`"); - + mysqli_query($mysqli, "RENAME TABLE `events` TO `calendar_events`"); mysqli_query($mysqli, "RENAME TABLE `event_attendees` TO `calendar_event_attendees`"); @@ -2957,7 +2957,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) { ALTER TABLE `calendar_events` ADD FOREIGN KEY (`event_calendar_id`) REFERENCES `calendars`(`calendar_id`) ON DELETE CASCADE "); - + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.0.3'"); } @@ -2974,7 +2974,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) { ALTER TABLE `certificate_history` ADD FOREIGN KEY (`certificate_history_certificate_id`) REFERENCES `certificates`(`certificate_id`) ON DELETE CASCADE "); - + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.0.4'"); } @@ -3335,14 +3335,14 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) { ADD FOREIGN KEY (`vendor_id`) REFERENCES `vendors`(`vendor_id`) ON DELETE CASCADE, ADD FOREIGN KEY (`file_id`) REFERENCES `files`(`file_id`) ON DELETE CASCADE "); - + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.0.5'"); } if (CURRENT_DATABASE_VERSION == '2.0.5') { // CONVERT All tables TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci - + $tables = [ 'accounts', 'api_keys', 'app_logs', 'asset_credentials', 'asset_custom', 'asset_documents', 'asset_files', 'asset_history', 'asset_interface_links', 'asset_interfaces', 'asset_notes', 'assets', @@ -3381,14 +3381,14 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) { } if (CURRENT_DATABASE_VERSION == '2.0.7') { - + mysqli_query($mysqli, "ALTER TABLE `files` DROP `file_hash`"); mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.0.8'"); } if (CURRENT_DATABASE_VERSION == '2.0.8') { - + mysqli_query($mysqli, "ALTER TABLE `files` DROP `file_has_thumbnail`"); mysqli_query($mysqli, "ALTER TABLE `files` DROP `file_has_preview`"); mysqli_query($mysqli, "ALTER TABLE `files` DROP `file_asset_id`"); @@ -3397,7 +3397,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) { } if (CURRENT_DATABASE_VERSION == '2.0.9') { - + mysqli_query($mysqli, "ALTER TABLE `contacts` ADD `contact_phone_country_code` VARCHAR(10) DEFAULT 1 AFTER `contact_email`"); mysqli_query($mysqli, "ALTER TABLE `contacts` ADD `contact_mobile_country_code` VARCHAR(10) DEFAULT 1 AFTER `contact_extension`"); @@ -3425,7 +3425,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) { } if (CURRENT_DATABASE_VERSION == '2.1.2') { - + // Update country_code to NULL for `contacts` table mysqli_query($mysqli, "ALTER TABLE `contacts` MODIFY `contact_phone_country_code` VARCHAR(10) DEFAULT NULL"); mysqli_query($mysqli, "ALTER TABLE `contacts` MODIFY `contact_mobile_country_code` VARCHAR(10) DEFAULT NULL"); @@ -3460,7 +3460,7 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) { if (CURRENT_DATABASE_VERSION == '2.1.3') { mysqli_query($mysqli, "ALTER TABLE `client_stripe` ADD `stripe_pm_details` VARCHAR(200) DEFAULT NULL AFTER `stripe_pm`"); mysqli_query($mysqli, "ALTER TABLE `client_stripe` ADD `stripe_pm_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `stripe_pm_details`"); - + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.1.4'"); } @@ -3675,13 +3675,306 @@ if (LATEST_DATABASE_VERSION > CURRENT_DATABASE_VERSION) { mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.0'"); } - // if (CURRENT_DATABASE_VERSION == '2.2.0') { - // // Insert queries here required to update to DB version 2.2.1 + if (CURRENT_DATABASE_VERSION == '2.2.0') { + mysqli_query($mysqli, "ALTER TABLE `tickets` ADD `ticket_quote_id` INT(11) NOT NULL DEFAULT 0 AFTER `ticket_asset_id`"); + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.1'"); + } + + if (CURRENT_DATABASE_VERSION == '2.2.1') { + mysqli_query($mysqli, "CREATE TABLE `ai_providers` ( + `ai_provider_id` INT(11) NOT NULL AUTO_INCREMENT, + `ai_provider_name` VARCHAR(200) NOT NULL, + `ai_provider_api_url` VARCHAR(200) NOT NULL, + `ai_provider_api_key` VARCHAR(200) DEFAULT NULL, + `ai_provider_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `ai_provider_updated_at` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`ai_provider_id`) + )"); + + mysqli_query($mysqli, " + CREATE TABLE `ai_models` ( + `ai_model_id` INT(11) NOT NULL AUTO_INCREMENT, + `ai_model_name` VARCHAR(200) NOT NULL, + `ai_model_prompt` TEXT DEFAULT NULL, + `ai_model_use_case` VARCHAR(200) DEFAULT NULL, + `ai_model_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `ai_model_updated_at` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + `ai_model_ai_provider_id` INT(11) NOT NULL, + PRIMARY KEY (`ai_model_id`), + FOREIGN KEY (`ai_model_ai_provider_id`) + REFERENCES `ai_providers`(`ai_provider_id`) + ON DELETE CASCADE + ) + "); + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.2'"); + } + + if (CURRENT_DATABASE_VERSION == '2.2.2') { + mysqli_query($mysqli, "CREATE TABLE `payment_methods` ( + `payment_method_id` INT(11) NOT NULL AUTO_INCREMENT, + `payment_method_name` VARCHAR(200) NOT NULL, + `payment_method_description` VARCHAR(250) DEFAULT NULL, + `payment_method_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `payment_method_updated_at` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`payment_method_id`) + )"); + + mysqli_query($mysqli, "CREATE TABLE `payment_providers` ( + `payment_provider_id` INT(11) NOT NULL AUTO_INCREMENT, + `payment_provider_name` VARCHAR(200) NOT NULL, + `payment_provider_description` VARCHAR(250) DEFAULT NULL, + `payment_provider_public_key` VARCHAR(250) DEFAULT NULL, + `payment_provider_private_key` VARCHAR(250) DEFAULT NULL, + `payment_provider_threshold` DECIMAL(15,2) DEFAULT NULL, + `payment_provider_active` TINYINT(1) NOT NULL DEFAULT 1, + `payment_provider_account` INT(11) NOT NULL, + `payment_provider_expense_vendor` INT(11) NOT NULL DEFAULT 0, + `payment_provider_expense_category` INT(11) NOT NULL DEFAULT 0, + `payment_provider_expense_percentage_fee` DECIMAL(4,4) DEFAULT NULL, + `payment_provider_expense_flat_fee` DECIMAL(15,2) DEFAULT NULL, + `payment_provider_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `payment_provider_updated_at` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`payment_provider_id`) + )"); + + mysqli_query($mysqli, "CREATE TABLE `client_saved_payment_methods` ( + `saved_payment_id` INT(11) NOT NULL AUTO_INCREMENT, + `saved_payment_provider_method` VARCHAR(200) NOT NULL, + `saved_payment_description` VARCHAR(200) DEFAULT NULL, + `saved_payment_client_id` INT(11) NOT NULL, + `saved_payment_provider_id` INT(11) NOT NULL, + `saved_payment_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `saved_payment_updated_at` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`saved_payment_id`), + FOREIGN KEY (`saved_payment_client_id`) REFERENCES clients(`client_id`) ON DELETE CASCADE, + FOREIGN KEY (`saved_payment_provider_id`) REFERENCES payment_providers(`payment_provider_id`) ON DELETE CASCADE + )"); + + mysqli_query($mysqli, "CREATE TABLE `client_payment_provider` ( + `client_id` INT(11) NOT NULL, + `payment_provider_id` INT(11) NOT NULL, + `payment_provider_client` VARCHAR(200) NOT NULL, + `client_payment_provider_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`client_id`, `payment_provider_id`), + FOREIGN KEY (`client_id`) REFERENCES clients(`client_id`) ON DELETE CASCADE, + FOREIGN KEY (`payment_provider_id`) REFERENCES payment_providers(`payment_provider_id`) ON DELETE CASCADE + )"); + + mysqli_query($mysqli, "ALTER TABLE `recurring_payments` ADD `recurring_payment_saved_payment_id` INT(11) DEFAULT NULL AFTER `recurring_payment_recurring_invoice_id`"); + + mysqli_query($mysqli, "ALTER TABLE `recurring_payments` ADD CONSTRAINT `fk_recurring_saved_payment` FOREIGN KEY (`recurring_payment_saved_payment_id`) REFERENCES `client_saved_payment_methods`(`saved_payment_id`) ON DELETE CASCADE"); + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.3'"); + } + + if (CURRENT_DATABASE_VERSION == '2.2.3') { + + mysqli_query($mysqli, "CREATE TABLE `credits` ( + `credit_id` INT(11) NOT NULL AUTO_INCREMENT, + `credit_amount` DECIMAL(15,2) NOT NULL, + `credit_reference` VARCHAR(250) DEFAULT NULL, + `credit_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP(), + `credit_created_by` INT(11) NOT NULL, + `credit_expire_at` DATE DEFAULT NULL, + `credit_client_id` INT(11) NOT NULL, + PRIMARY KEY (`credit_id`) + )"); + + mysqli_query($mysqli, "ALTER TABLE `invoices` ADD `invoice_credit_amount` DECIMAL(15,2) NOT NULL DEFAULT 0.00 AFTER `invoice_discount_amount`"); + + mysqli_query($mysqli, "CREATE TABLE `discount_codes` ( + `discount_code_id` INT(11) NOT NULL AUTO_INCREMENT, + `discount_code_description` VARCHAR(250) DEFAULT NULL, + `discount_code_amount` DECIMAL(15,2) NOT NULL, + `discount_code` VARCHAR(200) NOT NULL, + `discount_code_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP(), + `discount_code_created_by` INT(11) NOT NULL, + `discount_code_updated_at` DATETIME NULL ON UPDATE CURRENT_TIMESTAMP, + `discount_code_archived_at` DATETIME NULL DEFAULT NULL, + `discount_code_expire_at` DATE DEFAULT NULL, + PRIMARY KEY (`discount_code_id`) + )"); + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.4'"); + } + + if (CURRENT_DATABASE_VERSION == '2.2.4') { + mysqli_query($mysqli, "ALTER TABLE `settings` ADD `config_theme_dark` TINYINT(1) NOT NULL DEFAULT 0 AFTER `config_theme`"); + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.5'"); + } + + if (CURRENT_DATABASE_VERSION == '2.2.5') { + mysqli_query($mysqli, "ALTER TABLE `assets` ADD `asset_uri_client` VARCHAR(500) NULL DEFAULT NULL AFTER `asset_uri_2`"); + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.6'"); + } + + if (CURRENT_DATABASE_VERSION == '2.2.6') { + mysqli_query($mysqli, "ALTER TABLE `credits` DROP `credit_reference`"); + mysqli_query($mysqli, "ALTER TABLE `credits` ADD `credit_type` ENUM('prepaid', 'manual', 'refund', 'promotion', 'usage') NOT NULL DEFAULT 'manual' AFTER `credit_amount`"); + mysqli_query($mysqli, "ALTER TABLE `credits` ADD `credit_note` TEXT NULL DEFAULT NULL AFTER `credit_type`"); + mysqli_query($mysqli, "ALTER TABLE `credits` ADD `credit_invoice_id` INT(11) NULL DEFAULT NULL AFTER `credit_expire_at`"); + mysqli_query($mysqli, "ALTER TABLE `credits` ADD INDEX (`credit_client_id`)"); + mysqli_query($mysqli, "ALTER TABLE `credits` ADD INDEX (`credit_invoice_id`)"); + mysqli_query($mysqli, "ALTER TABLE `credits` ADD INDEX (`credit_created_at`)"); + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.7'"); + } + + if (CURRENT_DATABASE_VERSION == '2.2.7') { + mysqli_query($mysqli, "ALTER TABLE `user_settings` ADD `user_config_theme_dark` TINYINT(1) NOT NULL DEFAULT 0 AFTER `user_config_signature`"); + mysqli_query($mysqli, "ALTER TABLE `settings` DROP `config_theme_dark`"); + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.8'"); + } + + if (CURRENT_DATABASE_VERSION == '2.2.8') { + + mysqli_query($mysqli, "ALTER TABLE `products` ADD `product_type` ENUM('service', 'product') NOT NULL DEFAULT 'service' AFTER `product_name`"); + mysqli_query($mysqli, "ALTER TABLE `products` ADD `product_code` VARCHAR(200) DEFAULT NULL AFTER `product_description`"); + mysqli_query($mysqli, "ALTER TABLE `products` ADD `product_location` VARCHAR(250) DEFAULT NULL AFTER `product_code`"); + + mysqli_query($mysqli, "CREATE TABLE `product_stock` ( + `stock_id` INT(11) NOT NULL AUTO_INCREMENT, + `stock_qty` INT(11) NOT NULL, + `stock_note` TEXT DEFAULT NULL, + `stock_created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP(), + `stock_expense_id` INT(11) DEFAULT NULL, + `stock_item_id` INT(11) DEFAULT NULL, + `stock_product_id` INT(11) NOT NULL, + PRIMARY KEY (`stock_id`) + )"); + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.9'"); + } + + if (CURRENT_DATABASE_VERSION == '2.2.9') { + // Migrate Stripe Settings over to new Tables + + // Get Current Stripe Settings + $sql_stripe_settings = mysqli_query($mysqli, "SELECT * FROM settings WHERE company_id = 1"); + $row = mysqli_fetch_array($sql_stripe_settings); + $config_stripe_enable = intval($row['config_stripe_enable']); + if ($config_stripe_enable === 1) { + $config_stripe_publishable = mysqli_real_escape_string($mysqli, $row['config_stripe_publishable']); + $config_stripe_secret = mysqli_real_escape_string($mysqli, $row['config_stripe_secret']); + $config_stripe_account = intval($row['config_stripe_account']); + $config_stripe_expense_vendor = intval($row['config_stripe_expense_vendor']); + $config_stripe_expense_category = intval($row['config_stripe_expense_category']); + $config_stripe_percentage_fee = floatval($row['config_stripe_percentage_fee']); + $config_stripe_flat_fee = floatval($row['config_stripe_flat_fee']); + + mysqli_query($mysqli,"INSERT INTO payment_providers SET + payment_provider_name = 'Stripe', + payment_provider_public_key = '$config_stripe_publishable', + payment_provider_private_key = '$config_stripe_secret', + payment_provider_account = $config_stripe_account, + payment_provider_expense_vendor = $config_stripe_expense_vendor, + payment_provider_expense_category = $config_stripe_expense_category, + payment_provider_expense_percentage_fee = $config_stripe_percentage_fee, + payment_provider_expense_flat_fee = $config_stripe_flat_fee" + ); + + $provider_id = mysqli_insert_id($mysqli); + + // Migrate Clients and Payment Method over + $sql_stripe_clients = mysqli_query($mysqli, "SELECT * FROM client_stripe WHERE stripe_pm IS NOT NULL AND stripe_pm != ''"); + while ($row = mysqli_fetch_array($sql_stripe_clients)) { + $client_id = intval($row['client_id']); + $stripe_id = mysqli_real_escape_string($mysqli, $row['stripe_id']); + $stripe_pm = mysqli_real_escape_string($mysqli, $row['stripe_pm']); + $stripe_pm_details = mysqli_real_escape_string($mysqli, $row['stripe_pm_details'] ?? 'Saved Card'); + + mysqli_query($mysqli,"INSERT INTO client_payment_provider SET + client_id = $client_id, + payment_provider_id = $provider_id, + payment_provider_client = '$stripe_id'" + ); + + mysqli_query($mysqli,"INSERT INTO client_saved_payment_methods SET + saved_payment_provider_method = '$stripe_pm', + saved_payment_description = '$stripe_pm_details', + saved_payment_client_id = $client_id, + saved_payment_provider_id = $provider_id" + ); + } + } + + // Get Stripe provider id + $res = mysqli_query($mysqli, " + SELECT payment_provider_id + FROM payment_providers + WHERE payment_provider_name = 'Stripe' + ORDER BY payment_provider_id DESC + LIMIT 1 + "); + $stripe = mysqli_fetch_assoc($res); + $stripe_provider_id = intval($stripe['payment_provider_id']); + + // Correct mapping: RP -> Recurring Invoice -> Client -> Client's Stripe saved method + mysqli_query($mysqli, " + UPDATE recurring_payments rp + INNER JOIN recurring_invoices ri + ON ri.recurring_invoice_id = rp.recurring_payment_recurring_invoice_id + INNER JOIN client_saved_payment_methods spm + ON spm.saved_payment_client_id = ri.recurring_invoice_client_id + AND spm.saved_payment_provider_id = $stripe_provider_id + SET + rp.recurring_payment_method = 'Credit Card', + rp.recurring_payment_saved_payment_id = spm.saved_payment_id + WHERE rp.recurring_payment_method = 'Stripe' + "); + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.0'"); + } + + if (CURRENT_DATABASE_VERSION == '2.3.0') { + // Migrate Payment Methods from Categories Table to new payment_methods table + $sql_categories = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Payment Method' AND category_name != 'Stripe' AND category_archived_at IS NULL"); + + while ($row = mysqli_fetch_array($sql_categories)) { + $category_name = sanitizeInput($row['category_name']); + + mysqli_query($mysqli,"INSERT INTO payment_methods SET payment_method_name = '$category_name'"); + } + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.1'"); + } + + if (CURRENT_DATABASE_VERSION == '2.3.1') { + + // Delete all Recurring Payments that are Stripe + mysqli_query($mysqli, "DELETE FROM recurring_payments WHERE recurring_payment_method = 'Stripe'"); + + // Delete Stripe Specific ITFlow Client Stripe Client Relationship Table + mysqli_query($mysqli, "DROP TABLE client_stripe"); + + // Delete Unused Stripe and AI Settings now in their own tables + mysqli_query($mysqli, "ALTER TABLE `settings` + DROP `config_stripe_enable`, + DROP `config_stripe_publishable`, + DROP `config_stripe_secret`, + DROP `config_stripe_account`, + DROP `config_stripe_expense_vendor`, + DROP `config_stripe_expense_category`, + DROP `config_stripe_percentage_fee`, + DROP `config_stripe_flat_fee`, + DROP `config_ai_enable`, + DROP `config_ai_provider`, + DROP `config_ai_model`, + DROP `config_ai_url`, + DROP `config_ai_api_key` + "); + + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.2'"); + } + + // if (CURRENT_DATABASE_VERSION == '2.3.2') { + // // Insert queries here required to update to DB version 2.3.3 // // Then, update the database to the next sequential version - // mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.2.1'"); + // mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '2.3.3'"); // } } else { // Up-to-date } - \ No newline at end of file diff --git a/admin_debug.php b/admin/debug.php similarity index 99% rename from admin_debug.php rename to admin/debug.php index edd3cb9c..bab12f91 100644 --- a/admin_debug.php +++ b/admin/debug.php @@ -1,8 +1,8 @@ 'curl', 'php-mbstring' => 'mbstring', 'php-gd' => 'gd', + 'php-zip' => 'zip', ]; foreach ($extensions as $name => $ext) { @@ -245,7 +246,7 @@ $filePermissions[] = [ $uploadsStats = []; // Define the uploads directory path -$uploadsDir = __DIR__ . '/uploads'; // Adjust the path if needed +$uploadsDir = __DIR__ . '/../uploads'; // Adjust the path if needed if (is_dir($uploadsDir)) { // Function to recursively count files and calculate total size @@ -348,7 +349,7 @@ if ($tablesResult) { $dbComparison = []; // Path to the db.sql file -$dbSqlFile = __DIR__ . '/db.sql'; +$dbSqlFile = __DIR__ . '/../db.sql'; if (file_exists($dbSqlFile)) { // Read the db.sql file @@ -765,5 +766,5 @@ $mysqli->close(); - +
@@ -93,12 +93,9 @@ - + - - + + + Client Portal SSO via Microsoft Entra
- +
@@ -55,4 +55,4 @@ require_once "includes/inc_all_admin.php";
-Tell your admin: Your role does not have admin access."); +} +require_once "../includes/header.php"; +require_once "../includes/top_nav.php"; +require_once "includes/side_nav.php"; +require_once "../includes/inc_wrapper.php"; +require_once "../includes/inc_alert_feedback.php"; +require_once "../includes/filter_header.php"; +require_once "../includes/app_version.php"; diff --git a/includes/admin_side_nav.php b/admin/includes/side_nav.php similarity index 54% rename from includes/admin_side_nav.php rename to admin/includes/side_nav.php index f1b870e0..8e32ef66 100644 --- a/includes/admin_side_nav.php +++ b/admin/includes/side_nav.php @@ -1,6 +1,6 @@
- + - + + \ No newline at end of file diff --git a/admin/modals/ai/ai_model_edit.php b/admin/modals/ai/ai_model_edit.php new file mode 100644 index 00000000..87c5bbc2 --- /dev/null +++ b/admin/modals/ai/ai_model_edit.php @@ -0,0 +1,90 @@ + + + +
+ + + + + +
+ + + diff --git a/admin/modals/ai/ai_provider_edit.php b/admin/modals/ai/ai_provider_edit.php new file mode 100644 index 00000000..ee1ed12d --- /dev/null +++ b/admin/modals/ai/ai_provider_edit.php @@ -0,0 +1,69 @@ + + + +
+ + + + + +
+ +