diff --git a/CHANGELOG.md b/CHANGELOG.md
index 84bb8a77..b802af8a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,57 @@
This file documents all notable changes made to ITFlow.
+## [25.03]
+
+### Fixed
+- Resolved missing attachments in ticket replies processed via the email parser.
+- Fixed issue where the top half of portrait image uploads appeared cut off at the bottom.
+- Ensured all tables and fields use `CHARACTER SET utf8mb4` and `COLLATE utf8mb4_general_ci` for updates and new installations.
+- Converted `service_domains` table to use InnoDB instead of MyISAM.
+- Fixed the initials function to properly handle UTF-8 characters, preventing contact-related issues.
+- Interfaces can now start with `0`.
+- Adjusted AI prompt handling to focus solely on content, avoiding unnecessary additions.
+
+### Added / Changed
+- Introduced bulk delete functionality for assets.
+- Added the ability to redact ticket replies after a ticket is closed.
+- Added support for redacting specific text while a ticket is open.
+- Switched file upload hashing from SHA256 to MD5 to significantly improve performance.
+- Enabled assigning multiple assets to a single ticket.
+- Updated all many-to-many tables to support cascading deletes using foreign key associations, improving efficiency, performance, and data integrity.
+- Enabled caching for AJAX modals to reduce repeated reloads and enhance browser performance.
+- Upgraded DataTables from 2.2.1 to 2.2.2.
+- Upgraded TinyMCE from 7.6.1 to 7.7.1, providing a significant performance boost.
+- Added “Copy Credentials to Clipboard” button in AJAX asset and contact views.
+- Renamed and reorganized several tables.
+- Improved theme color organization by grouping primary colors and their related shades.
+- Displayed a user icon next to contacts who have user accounts.
+- New image uploads are now converted to optimized `.webp` format by default; original files are no longer saved. Existing images remain unchanged.
+- Added international phone number support throughout the system.
+- Introduced user signatures in preferences, which are now appended to all ticket replies.
+- Optimized search filters to only display defined tags.
+- Added “Projects” to the client-side navigation.
+- Enabled “Create New Ticket” from within project details.
+- Reintroduced batch payment functionality in client invoices.
+- Included client abbreviations in both client and global search options.
+- Added assigned software license details (User/Asset) to the client PDF export.
+- Replaced client-side `pdfMake` with the PHP-based `TCPDF` library for generating client export runbooks.
+- Introduced the ability to download documents as PDFs.
+- Added a “Reference” field to tickets and invoices generated from recurring templates (not yet in active use).
+
+### Breaking Changes
+> **Important:** To update to this version, you **must** run the following commands from the command line from the scripts directory:
+>
+> ```bash
+> php update_cli.php
+> php update_cli.php --db_update
+> ```
+>
+> Repeat `--db_update` until no further updates are found.
+>
+> **Back up your system before upgrading.**
+> This version includes numerous backend changes critical for future development.
+
## [25.02.4]
### Fixed
diff --git a/admin_role.php b/admin_role.php
index f79def74..5ea1e4c6 100644
--- a/admin_role.php
+++ b/admin_role.php
@@ -1,7 +1,7 @@
">
diff --git a/ajax/ajax_project_edit.php b/ajax/ajax_project_edit.php
index 71bf976a..af61d3ef 100644
--- a/ajax/ajax_project_edit.php
+++ b/ajax/ajax_project_edit.php
@@ -74,9 +74,8 @@ ob_start();
1 AND user_status = 1 AND user_archived_at IS NULL ORDER BY user_name ASC"
+ "SELECT user_id, user_name FROM users
+ WHERE user_role_id > 1 AND user_status = 1 AND user_archived_at IS NULL ORDER BY user_name ASC"
);
while ($row = mysqli_fetch_array($sql_project_managers_select)) {
$user_id_select = intval($row['user_id']);
diff --git a/ajax/ajax_recurring_invoice_edit.php b/ajax/ajax_recurring_invoice_edit.php
index e0ab9d56..0a62b4f4 100644
--- a/ajax/ajax_recurring_invoice_edit.php
+++ b/ajax/ajax_recurring_invoice_edit.php
@@ -2,33 +2,33 @@
require_once '../includes/ajax_header.php';
-$recurring_id = intval($_GET['id']);
+$recurring_invoice_id = intval($_GET['id']);
-$sql = mysqli_query($mysqli, "SELECT * FROM recurring WHERE recurring_id = $recurring_id LIMIT 1");
+$sql = mysqli_query($mysqli, "SELECT * FROM recurring_invoices WHERE recurring_invoice_id = $recurring_invoice_id LIMIT 1");
$row = mysqli_fetch_array($sql);
-$recurring_prefix = nullable_htmlentities($row['recurring_prefix']);
-$recurring_number = intval($row['recurring_number']);
-$recurring_scope = nullable_htmlentities($row['recurring_scope']);
-$recurring_frequency = nullable_htmlentities($row['recurring_frequency']);
-$recurring_status = nullable_htmlentities($row['recurring_status']);
-$recurring_created_at = date('Y-m-d', strtotime($row['recurring_created_at']));
-$recurring_next_date = nullable_htmlentities($row['recurring_next_date']);
-$recurring_discount = floatval($row['recurring_discount_amount']);
-$category_id = intval($row['recurring_category_id']);
+$recurring_invoice_prefix = nullable_htmlentities($row['recurring_invoice_prefix']);
+$recurring_invoice_number = intval($row['recurring_invoice_number']);
+$recurring_invoice_scope = nullable_htmlentities($row['recurring_invoice_scope']);
+$recurring_invoice_frequency = nullable_htmlentities($row['recurring_invoice_frequency']);
+$recurring_invoice_status = nullable_htmlentities($row['recurring_invoice_status']);
+$recurring_invoice_created_at = date('Y-m-d', strtotime($row['recurring_invoice_created_at']));
+$recurring_invoice_next_date = nullable_htmlentities($row['recurring_invoice_next_date']);
+$recurring_invoice_discount = floatval($row['recurring_invoice_discount_amount']);
+$category_id = intval($row['recurring_invoice_category_id']);
// Generate the HTML form content using output buffering.
ob_start();
?>
-
Editing Recur Invoice:
+
Editing Recur Invoice:
@@ -50,8 +50,8 @@ ob_start();
@@ -62,7 +62,7 @@ ob_start();
-
+
@@ -76,7 +76,7 @@ ob_start();
'$recurring_created_at' OR category_archived_at IS NULL) ORDER BY category_name ASC");
+ $sql_income_category = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Income' AND (category_archived_at > '$recurring_invoice_created_at' OR category_archived_at IS NULL) ORDER BY category_name ASC");
while ($row = mysqli_fetch_array($sql_income_category)) {
$category_id_select = intval($row['category_id']);
$category_name_select = nullable_htmlentities($row['category_name']);
@@ -104,7 +104,7 @@ ob_start();