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 @@ "> - - Role + + Role Members - - Admin + + Admin Action @@ -70,17 +70,17 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); -
- -
-
- + +
+
+
+
+
+ +
+ + +
-
@@ -152,4 +158,3 @@ $company_initials = nullable_htmlentities(initials($company_name));
-
- -
-
- -
- -
-
-
diff --git a/admin_settings_invoice.php b/admin_settings_invoice.php index a452af66..9633c3eb 100644 --- a/admin_settings_invoice.php +++ b/admin_settings_invoice.php @@ -77,7 +77,7 @@ require_once "includes/inc_all_admin.php";
- +
@@ -87,7 +87,7 @@ require_once "includes/inc_all_admin.php";
- + diff --git a/admin_settings_theme.php b/admin_settings_theme.php index a79841cb..c15bd49f 100644 --- a/admin_settings_theme.php +++ b/admin_settings_theme.php @@ -19,7 +19,7 @@ require_once "includes/inc_all_admin.php"; ?> -
+
> diff --git a/admin_user.php b/admin_user.php index dace2d78..35229cbf 100644 --- a/admin_user.php +++ b/admin_user.php @@ -6,16 +6,13 @@ $order = "ASC"; require_once "includes/inc_all_admin.php"; - -//Rebuild URL -$url_query_strings_sort = http_build_query($get_copy); - $sql = mysqli_query( $mysqli, - "SELECT SQL_CALC_FOUND_ROWS * FROM users, user_settings, user_roles - WHERE users.user_id = user_settings.user_id - AND user_settings.user_role = user_roles.user_role_id - AND (user_name LIKE '%$q%' OR user_email LIKE '%$q%') + "SELECT SQL_CALC_FOUND_ROWS * FROM users + LEFT JOIN user_roles ON user_role_id = role_id + LEFT JOIN user_settings ON users.user_id = user_settings.user_id + WHERE (user_name LIKE '%$q%' OR user_email LIKE '%$q%') + AND user_type = 1 AND user_archived_at IS NULL ORDER BY $sort $order LIMIT $record_from, $record_to" ); @@ -36,6 +33,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
-
- -
@@ -77,8 +72,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - - Role + + Role @@ -116,8 +111,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $mfa_status_display = ""; } $user_config_force_mfa = intval($row['user_config_force_mfa']); - $user_role = $row['user_role']; - $user_role_display = nullable_htmlentities($row['user_role_name']); + $user_role = intval($row['user_role_id']); + $user_role_display = nullable_htmlentities($row['role_name']); $user_initials = nullable_htmlentities(initials($user_name)); $sql_last_login = mysqli_query( @@ -139,7 +134,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); } // Get User Client Access Permissions - $user_client_access_sql = mysqli_query($mysqli,"SELECT client_id FROM user_permissions WHERE user_id = $user_id"); + $user_client_access_sql = mysqli_query($mysqli,"SELECT client_id FROM user_client_permissions WHERE user_id = $user_id"); $client_access_array = []; while ($row = mysqli_fetch_assoc($user_client_access_sql)) { $client_access_array[] = intval($row['client_id']); @@ -241,4 +236,4 @@ require_once "modals/admin_user_add_modal.php"; require_once "modals/admin_user_invite_modal.php"; require_once "modals/admin_user_export_modal.php"; require_once "modals/admin_user_all_reset_password_modal.php"; -require_once "includes/footer.php"; +require_once "includes/footer.php"; \ No newline at end of file diff --git a/ajax.php b/ajax.php index 2143d9bb..7e046691 100644 --- a/ajax.php +++ b/ajax.php @@ -8,7 +8,7 @@ require_once "config.php"; require_once "functions.php"; -require_once "check_login.php"; +require_once "includes/check_login.php"; require_once "plugins/totp/totp.php"; /* @@ -165,7 +165,7 @@ if (isset($_GET['ticket_query_views'])) { } /* - * Generates public/guest links for sharing logins/docs + * Generates public/guest links for sharing credentials/docs */ if (isset($_GET['share_generate_link'])) { enforceUserPermission('module_support', 2); @@ -207,23 +207,23 @@ if (isset($_GET['share_generate_link'])) { $item_name = sanitizeInput($row['file_name']); } - if ($item_type == "Login") { - $login = mysqli_query($mysqli, "SELECT login_name, login_username, login_password FROM logins WHERE login_id = $item_id AND login_client_id = $client_id LIMIT 1"); - $row = mysqli_fetch_array($login); + if ($item_type == "Credential") { + $credential = mysqli_query($mysqli, "SELECT credential_name, credential_username, credential_password FROM credentials WHERE credential_id = $item_id AND credential_client_id = $client_id LIMIT 1"); + $row = mysqli_fetch_array($credential); - $item_name = sanitizeInput($row['login_name']); + $item_name = sanitizeInput($row['credential_name']); // Decrypt & re-encrypt username/password for sharing - $login_encryption_key = randomString(); + $credential_encryption_key = randomString(); - $login_username_cleartext = decryptLoginEntry($row['login_username']); + $credential_username_cleartext = decryptCredentialEntry($row['credential_username']); $iv = randomString(); - $username_ciphertext = openssl_encrypt($login_username_cleartext, 'aes-128-cbc', $login_encryption_key, 0, $iv); + $username_ciphertext = openssl_encrypt($credential_username_cleartext, 'aes-128-cbc', $credential_encryption_key, 0, $iv); $item_encrypted_username = $iv . $username_ciphertext; - $login_password_cleartext = decryptLoginEntry($row['login_password']); + $credential_password_cleartext = decryptCredentialEntry($row['credential_password']); $iv = randomString(); - $password_ciphertext = openssl_encrypt($login_password_cleartext, 'aes-128-cbc', $login_encryption_key, 0, $iv); + $password_ciphertext = openssl_encrypt($credential_password_cleartext, 'aes-128-cbc', $credential_encryption_key, 0, $iv); $item_encrypted_credential = $iv . $password_ciphertext; } @@ -232,8 +232,8 @@ if (isset($_GET['share_generate_link'])) { $share_id = $mysqli->insert_id; // Return URL - if ($item_type == "Login") { - $url = "https://$config_base_url/guest/guest_view_item.php?id=$share_id&key=$item_key&ek=$login_encryption_key"; + if ($item_type == "Credential") { + $url = "https://$config_base_url/guest/guest_view_item.php?id=$share_id&key=$item_key&ek=$credential_encryption_key"; } else { $url = "https://$config_base_url/guest/guest_view_item.php?id=$share_id&key=$item_key"; @@ -242,7 +242,7 @@ if (isset($_GET['share_generate_link'])) { $sql = mysqli_query($mysqli,"SELECT * FROM companies WHERE company_id = 1"); $row = mysqli_fetch_array($sql); $company_name = sanitizeInput($row['company_name']); - $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); + $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'], $row['company_phone_country_code'])); // Sanitize Config vars from get_settings.php $config_ticket_from_name = sanitizeInput($config_ticket_from_name); @@ -333,24 +333,24 @@ if (isset($_GET['get_client_contacts'])) { if (isset($_GET['get_totp_token_via_id'])) { enforceUserPermission('module_credential'); - $login_id = intval($_GET['login_id']); + $credential_id = intval($_GET['credential_id']); - $sql = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT login_name, login_otp_secret, login_client_id FROM logins WHERE login_id = $login_id")); - $name = sanitizeInput($sql['login_name']); - $totp_secret = $sql['login_otp_secret']; - $client_id = intval($sql['login_client_id']); + $sql = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT credential_name, credential_otp_secret, credential_client_id FROM credentials WHERE credential_id = $credential_id")); + $name = sanitizeInput($sql['credential_name']); + $totp_secret = $sql['credential_otp_secret']; + $client_id = intval($sql['credential_client_id']); $otp = TokenAuth6238::getTokenCode(strtoupper($totp_secret)); echo json_encode($otp); // Logging // Only log the TOTP view if the user hasn't already viewed this specific login entry recently, this prevents logs filling if a user hovers across an entry a few times - $check_recent_totp_view_logged_sql = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(log_id) AS recent_totp_view FROM logs WHERE log_type = 'Login' AND log_action = 'View TOTP' AND log_user_id = $session_user_id AND log_entity_id = $login_id AND log_client_id = $client_id AND log_created_at > (NOW() - INTERVAL 5 MINUTE)")); + $check_recent_totp_view_logged_sql = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT(log_id) AS recent_totp_view FROM logs WHERE log_type = 'Credential' AND log_action = 'View TOTP' AND log_user_id = $session_user_id AND log_entity_id = $credential_id AND log_client_id = $client_id AND log_created_at > (NOW() - INTERVAL 5 MINUTE)")); $recent_totp_view_logged_count = intval($check_recent_totp_view_logged_sql['recent_totp_view']); if ($recent_totp_view_logged_count == 0) { // Logging - logAction("Credential", "View TOTP", "$session_name viewed credential TOTP code for $name", $client_id, $login_id); + logAction("Credential", "View TOTP", "$session_name viewed credential TOTP code for $name", $client_id, $credential_id); } } @@ -444,10 +444,10 @@ if (isset($_POST['update_kanban_ticket'])) { $config_base_url = sanitizeInput($config_base_url); // Get Company Info - $sql = mysqli_query($mysqli, "SELECT company_name, company_phone FROM companies WHERE company_id = 1"); + $sql = mysqli_query($mysqli, "SELECT company_name, company_phone, company_phone_country_code FROM companies WHERE company_id = 1"); $row = mysqli_fetch_array($sql); $company_name = sanitizeInput($row['company_name']); - $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); + $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'], $row['company_phone_country_code'])); // EMAIL $subject = "Ticket resolved - [$ticket_prefix$ticket_number] - $ticket_subject | (pending closure)"; @@ -599,4 +599,3 @@ if (isset($_POST['update_recurring_invoice_items_order'])) { echo json_encode(['status' => 'success']); exit; } - diff --git a/ajax/ajax_asset_details.php b/ajax/ajax_asset_details.php index b72660e0..3ffa50a2 100644 --- a/ajax/ajax_asset_details.php +++ b/ajax/ajax_asset_details.php @@ -50,6 +50,7 @@ $device_icon = getAssetIcon($asset_type); $contact_name = nullable_htmlentities($row['contact_name']); $contact_email = nullable_htmlentities($row['contact_email']); $contact_phone = nullable_htmlentities($row['contact_phone']); +$contact_extension = nullable_htmlentities($row['contact_extension']); $contact_mobile = nullable_htmlentities($row['contact_mobile']); $contact_archived_at = nullable_htmlentities($row['contact_archived_at']); if ($contact_archived_at) { @@ -111,46 +112,53 @@ $interface_count = mysqli_num_rows($sql_related_interfaces); // Related Credentials Query $sql_related_credentials = mysqli_query($mysqli, " SELECT - logins.login_id AS login_id, - logins.login_name, - logins.login_description, - logins.login_uri, - logins.login_username, - logins.login_password, - logins.login_otp_secret, - logins.login_note, - logins.login_important, - logins.login_contact_id, - logins.login_asset_id - FROM logins - LEFT JOIN login_tags ON login_tags.login_id = logins.login_id - LEFT JOIN tags ON tags.tag_id = login_tags.tag_id - WHERE login_asset_id = $asset_id - AND login_archived_at IS NULL - GROUP BY logins.login_id - ORDER BY login_name DESC + credentials.credential_id AS credential_id, + credentials.credential_name, + credentials.credential_description, + credentials.credential_uri, + credentials.credential_username, + credentials.credential_password, + credentials.credential_otp_secret, + credentials.credential_note, + credentials.credential_important, + credentials.credential_contact_id, + credentials.credential_asset_id + FROM credentials + LEFT JOIN credential_tags ON credential_tags.credential_id = credentials.credential_id + LEFT JOIN tags ON tags.tag_id = credential_tags.tag_id + WHERE credential_asset_id = $asset_id + AND credential_archived_at IS NULL + GROUP BY credentials.credential_id + ORDER BY credential_name DESC "); $credential_count = mysqli_num_rows($sql_related_credentials); // Related Tickets Query -$sql_related_tickets = mysqli_query($mysqli, "SELECT * FROM tickets - LEFT JOIN users on ticket_assigned_to = user_id +$sql_related_tickets = mysqli_query($mysqli, " + SELECT tickets.*, users.*, ticket_statuses.* + FROM tickets + LEFT JOIN users ON ticket_assigned_to = user_id LEFT JOIN ticket_statuses ON ticket_status_id = ticket_status - WHERE ticket_asset_id = $asset_id - ORDER BY ticket_number DESC" -); + LEFT JOIN ticket_assets ON tickets.ticket_id = ticket_assets.ticket_id + WHERE ticket_asset_id = $asset_id OR ticket_assets.asset_id = $asset_id + GROUP BY tickets.ticket_id + ORDER BY ticket_number DESC +"); $ticket_count = mysqli_num_rows($sql_related_tickets); // Related Recurring Tickets Query -$sql_related_recurring_tickets = mysqli_query($mysqli, "SELECT * FROM scheduled_tickets - WHERE scheduled_ticket_asset_id = $asset_id - ORDER BY scheduled_ticket_next_run DESC" +$sql_related_recurring_tickets = mysqli_query($mysqli, "SELECT * FROM recurring_tickets + LEFT JOIN recurring_ticket_assets ON recurring_tickets.recurring_ticket_id = recurring_ticket_assets.recurring_ticket_id + WHERE recurring_ticket_asset_id = $asset_id OR recurring_ticket_assets.asset_id = $asset_id + GROUP BY recurring_tickets.recurring_ticket_id + ORDER BY recurring_ticket_next_run DESC" ); $recurring_ticket_count = mysqli_num_rows($sql_related_recurring_tickets); // Related Documents -$sql_related_documents = mysqli_query($mysqli, "SELECT * FROM asset_documents +$sql_related_documents = mysqli_query($mysqli, "SELECT * FROM asset_documents LEFT JOIN documents ON asset_documents.document_id = documents.document_id + LEFT JOIN users ON user_id = document_created_by WHERE asset_documents.asset_id = $asset_id AND document_archived_at IS NULL ORDER BY document_name DESC" @@ -446,68 +454,69 @@ ob_start(); "; } - $login_password = nullable_htmlentities(decryptLoginEntry($row['login_password'])); - $login_otp_secret = nullable_htmlentities($row['login_otp_secret']); - $login_id_with_secret = '"' . $row['login_id'] . '","' . $row['login_otp_secret'] . '"'; - if (empty($login_otp_secret)) { + $credential_password = nullable_htmlentities(decryptCredentialEntry($row['credential_password'])); + $credential_otp_secret = nullable_htmlentities($row['credential_otp_secret']); + $credential_id_with_secret = '"' . $row['credential_id'] . '","' . $row['credential_otp_secret'] . '"'; + if (empty($credential_otp_secret)) { $otp_display = "-"; } else { - $otp_display = " Hover.."; + $otp_display = " Hover.."; } - $login_note = nullable_htmlentities($row['login_note']); - $login_important = intval($row['login_important']); - $login_contact_id = intval($row['login_contact_id']); - $login_asset_id = intval($row['login_asset_id']); + $credential_note = nullable_htmlentities($row['credential_note']); + $credential_important = intval($row['credential_important']); + $credential_contact_id = intval($row['credential_contact_id']); + $credential_asset_id = intval($row['credential_asset_id']); // Tags - $login_tag_name_display_array = array(); - $login_tag_id_array = array(); - $sql_login_tags = mysqli_query($mysqli, "SELECT * FROM login_tags LEFT JOIN tags ON login_tags.tag_id = tags.tag_id WHERE login_id = $login_id ORDER BY tag_name ASC"); - while ($row = mysqli_fetch_array($sql_login_tags)) { + $credential_tag_name_display_array = array(); + $credential_tag_id_array = array(); + $sql_credential_tags = mysqli_query($mysqli, "SELECT * FROM credential_tags LEFT JOIN tags ON credential_tags.tag_id = tags.tag_id WHERE credential_id = $credential_id ORDER BY tag_name ASC"); + while ($row = mysqli_fetch_array($sql_credential_tags)) { - $login_tag_id = intval($row['tag_id']); - $login_tag_name = nullable_htmlentities($row['tag_name']); - $login_tag_color = nullable_htmlentities($row['tag_color']); - if (empty($login_tag_color)) { - $login_tag_color = "dark"; + $credential_tag_id = intval($row['tag_id']); + $credential_tag_name = nullable_htmlentities($row['tag_name']); + $credential_tag_color = nullable_htmlentities($row['tag_color']); + if (empty($credential_tag_color)) { + $credential_tag_color = "dark"; } - $login_tag_icon = nullable_htmlentities($row['tag_icon']); - if (empty($login_tag_icon)) { - $login_tag_icon = "tag"; + $credential_tag_icon = nullable_htmlentities($row['tag_icon']); + if (empty($credential_tag_icon)) { + $credential_tag_icon = "tag"; } - $login_tag_id_array[] = $login_tag_id; - $login_tag_name_display_array[] = "$login_tag_name"; + $credential_tag_id_array[] = $credential_tag_id; + $credential_tag_name_display_array[] = "$credential_tag_name"; } - $login_tags_display = implode('', $login_tag_name_display_array); + $credential_tags_display = implode('', $credential_tag_name_display_array); ?> - + - + - + + - +
- + @@ -547,12 +556,13 @@ ob_start(); $ticket_number = intval($row['ticket_number']); $ticket_subject = nullable_htmlentities($row['ticket_subject']); $ticket_priority = nullable_htmlentities($row['ticket_priority']); + $ticket_status_id = intval($row['ticket_status_id']); $ticket_status_name = nullable_htmlentities($row['ticket_status_name']); $ticket_status_color = nullable_htmlentities($row['ticket_status_color']); $ticket_created_at = nullable_htmlentities($row['ticket_created_at']); $ticket_updated_at = nullable_htmlentities($row['ticket_updated_at']); if (empty($ticket_updated_at)) { - if ($ticket_status == "Closed") { + if ($ticket_status_name == "Closed") { $ticket_updated_at_display = "

Never

"; } else { $ticket_updated_at_display = "

Never

"; @@ -573,7 +583,7 @@ ob_start(); } $ticket_assigned_to = intval($row['ticket_assigned_to']); if (empty($ticket_assigned_to)) { - if ($ticket_status == 5) { + if ($ticket_status_id == 5) { $ticket_assigned_to_display = "

Not Assigned

"; } else { $ticket_assigned_to_display = "

Not Assigned

"; @@ -628,18 +638,18 @@ ob_start(); - - - - + + + + @@ -679,11 +689,6 @@ ob_start(); $seat_count = 0; - // Get Login - $login_id = intval($row['login_id']); - $login_username = nullable_htmlentities(decryptLoginEntry($row['login_username'])); - $login_password = nullable_htmlentities(decryptLoginEntry($row['login_password'])); - // Asset Licenses $asset_licenses_sql = mysqli_query($mysqli, "SELECT asset_id FROM software_assets WHERE software_id = $software_id"); $asset_licenses_array = array(); diff --git a/ajax/ajax_calendar_event_edit.php b/ajax/ajax_calendar_event_edit.php index c6e6a61c..937b9749 100644 --- a/ajax/ajax_calendar_event_edit.php +++ b/ajax/ajax_calendar_event_edit.php @@ -4,7 +4,7 @@ require_once '../includes/ajax_header.php'; $event_id = intval($_GET['id']); -$sql = mysqli_query($mysqli, "SELECT * FROM events LEFT JOIN calendars ON event_calendar_id = calendar_id WHERE event_id = $event_id LIMIT 1"); +$sql = mysqli_query($mysqli, "SELECT * FROM calendar_events LEFT JOIN calendars ON event_calendar_id = calendar_id WHERE event_id = $event_id LIMIT 1"); $row = mysqli_fetch_array($sql); $event_title = nullable_htmlentities($row['event_title']); diff --git a/ajax/ajax_contact_details.php b/ajax/ajax_contact_details.php index 660af8a3..4edce4a8 100644 --- a/ajax/ajax_contact_details.php +++ b/ajax/ajax_contact_details.php @@ -18,9 +18,11 @@ $client_name = nullable_htmlentities($row['client_name']); $contact_name = nullable_htmlentities($row['contact_name']); $contact_title = nullable_htmlentities($row['contact_title']); $contact_department =nullable_htmlentities($row['contact_department']); -$contact_phone = formatPhoneNumber($row['contact_phone']); +$contact_phone_country_code = nullable_htmlentities($row['contact_phone_country_code']); +$contact_phone = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_phone_country_code)); $contact_extension = nullable_htmlentities($row['contact_extension']); -$contact_mobile = formatPhoneNumber($row['contact_mobile']); +$contact_mobile_country_code = nullable_htmlentities($row['contact_mobile_country_code']); +$contact_mobile = nullable_htmlentities(formatPhoneNumber($row['contact_phone'], $contact_mobile_country_code)); $contact_email = nullable_htmlentities($row['contact_email']); $contact_photo = nullable_htmlentities($row['contact_photo']); $contact_pin = nullable_htmlentities($row['contact_pin']); @@ -51,21 +53,21 @@ $software_count = mysqli_num_rows($sql_linked_software); $linked_software = array(); -// Related Logins Query 1 to 1 relationship -$sql_related_logins = mysqli_query($mysqli, " +// Related Credentials Query 1 to 1 relationship +$sql_related_credentials = mysqli_query($mysqli, " SELECT - logins.login_id AS logins_login_id, -- Alias for logins.login_id - logins.*, -- All other columns from logins - login_tags.*, -- All columns from login_tags + credentials.credential_id AS credentials_credential_id, -- Alias for credentials.credential_id + credentials.*, -- All other columns from credentials + credential_tags.*, -- All columns from credential_tags tags.* -- All columns from tags - FROM logins - LEFT JOIN login_tags ON login_tags.login_id = logins.login_id - LEFT JOIN tags ON tags.tag_id = login_tags.tag_id - WHERE login_contact_id = $contact_id - GROUP BY logins.login_id - ORDER BY login_name DESC + FROM credentials + LEFT JOIN credential_tags ON credential_tags.credential_id = credentials.credential_id + LEFT JOIN tags ON tags.tag_id = credential_tags.tag_id + WHERE credential_contact_id = $contact_id + GROUP BY credentials.credential_id + ORDER BY credential_name DESC "); -$credential_count = mysqli_num_rows($sql_related_logins); +$credential_count = mysqli_num_rows($sql_related_credentials); // Related Tickets Query - 1 to 1 relationship $sql_related_tickets = mysqli_query($mysqli, "SELECT * FROM tickets @@ -75,9 +77,9 @@ $sql_related_tickets = mysqli_query($mysqli, "SELECT * FROM tickets $ticket_count = mysqli_num_rows($sql_related_tickets); // Related Recurring Tickets Query -$sql_related_recurring_tickets = mysqli_query($mysqli, "SELECT * FROM scheduled_tickets - WHERE scheduled_ticket_contact_id = $contact_id - ORDER BY scheduled_ticket_next_run DESC" +$sql_related_recurring_tickets = mysqli_query($mysqli, "SELECT * FROM recurring_tickets + WHERE recurring_ticket_contact_id = $contact_id + ORDER BY recurring_ticket_next_run DESC" ); $recurring_ticket_count = mysqli_num_rows($sql_related_recurring_tickets); @@ -189,11 +191,6 @@ ob_start();
Rcr Tickets ()
- - - - +
@@ -122,11 +125,11 @@ if (isset($_GET['recurring_id'])) {
- + - Email Notify + Email Notify - Email Notify + Email Notify @@ -135,7 +138,7 @@ if (isset($_GET['recurring_id'])) { Disable AutoPay () - + Create AutoPay @@ -152,16 +155,16 @@ if (isset($_GET['recurring_id'])) { Edit - + Force Send - + Delete
@@ -182,7 +185,7 @@ if (isset($_GET['recurring_id'])) {
-

Recurring Invoice
ly

+

Recurring Invoice
ly

@@ -213,15 +216,15 @@ if (isset($_GET['recurring_id'])) { - + - + - + @@ -229,7 +232,7 @@ if (isset($_GET['recurring_id'])) { - +
@@ -282,7 +285,7 @@ if (isset($_GET['recurring_id'])) { Edit - Delete + Delete
@@ -291,9 +294,9 @@ if (isset($_GET['recurring_id'])) {
- - - + + + - + @@ -363,13 +366,13 @@ if (isset($_GET['recurring_id'])) {
Notes
- +
@@ -378,23 +381,23 @@ if (isset($_GET['recurring_id'])) { - + - 0) { ?> + 0) { ?> - + 0) { ?> - + - +
Next Date
Last Sent
Created
- + -
Subtotal
Discount--
Tax
Total
@@ -503,7 +506,7 @@ $(document).ready(function() { method: 'POST', data: { update_recurring_invoice_items_order: true, - recurring_id: , + recurring_invoice_id: , positions: positions }, success: function(data) { diff --git a/recurring_invoices.php b/recurring_invoices.php index e4e8ecf3..7bcda6f2 100644 --- a/recurring_invoices.php +++ b/recurring_invoices.php @@ -1,13 +1,13 @@

Recurring Invoices

- +
@@ -115,18 +115,18 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); "> - - Number + + Number - - Next Date + + Next Date - - Scope + + Scope @@ -137,18 +137,18 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - - Amount + + Amount - - Frequency + + Frequency - - Last Sent + + Last Sent @@ -162,8 +162,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - - Status + + Status Action @@ -173,27 +173,27 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); + Create "; @@ -222,18 +222,18 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); - - + + - - + + - - ly - + + ly + @@ -251,13 +251,13 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); Edit - + Delete diff --git a/recurring_tickets.php b/recurring_tickets.php index 08c17716..246eb073 100644 --- a/recurring_tickets.php +++ b/recurring_tickets.php @@ -1,13 +1,13 @@ - - Subject + + Subject - - Priority + + Priority - - Frequency + + Frequency - - Next Run Date + + Next Run Date = 2) { ?> @@ -155,24 +155,24 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
- +
- + @@ -181,17 +181,17 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); data-toggle="ajax-modal" data-modal-size="lg" data-ajax-url="ajax/ajax_recurring_ticket_edit.php" - data-ajax-id="" + data-ajax-id="" > - + - + - + - + = 2) { ?> @@ -204,17 +204,17 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); data-toggle="ajax-modal" data-modal-size="lg" data-ajax-url="ajax/ajax_recurring_ticket_edit.php" - data-ajax-id="" + data-ajax-id="" > Edit - + Force Reoccur - + Delete diff --git a/report_credential_rotation.php b/report_credential_rotation.php index 16022147..af10bc0c 100644 --- a/report_credential_rotation.php +++ b/report_credential_rotation.php @@ -12,10 +12,10 @@ if (isset($_GET['days'])) { } $passwords_not_rotated_sql = mysqli_query($mysqli, - "SELECT login_id, login_name, login_description, login_password_changed_at, login_client_id, client_id, client_name - FROM logins - LEFT JOIN clients ON login_client_id = client_id - WHERE DATE(login_password_changed_at) < DATE_SUB(CURDATE(), INTERVAL $days DAY) + "SELECT credential_id, credential_name, credential_description, credential_password_changed_at, credential_client_id, client_id, client_name + FROM credentials + LEFT JOIN clients ON credential_client_id = client_id + WHERE DATE(credential_password_changed_at) < DATE_SUB(CURDATE(), INTERVAL $days DAY) ORDER BY client_name" ); @@ -46,10 +46,10 @@ $passwords_not_rotated_sql = mysqli_query($mysqli, while ($row = mysqli_fetch_array($passwords_not_rotated_sql)) { - $login_id = intval($row['login_id']); - $login_name = nullable_htmlentities($row['login_name']); - $login_description = nullable_htmlentities($row['login_description']); - $login_password_changed = nullable_htmlentities($row['login_password_changed_at']); + $credential_id = intval($row['credential_id']); + $credential_name = nullable_htmlentities($row['credential_name']); + $credential_description = nullable_htmlentities($row['credential_description']); + $credential_password_changed = nullable_htmlentities($row['credential_password_changed_at']); $client_id = intval($row['client_id']); $client_name = nullable_htmlentities($row['client_name']); @@ -57,9 +57,9 @@ $passwords_not_rotated_sql = mysqli_query($mysqli, - - - + + + diff --git a/report_recurring_by_client.php b/report_recurring_by_client.php index 9a2efc72..bb181194 100644 --- a/report_recurring_by_client.php +++ b/report_recurring_by_client.php @@ -5,12 +5,12 @@ require_once "includes/inc_all_reports.php"; validateAccountantRole(); $sql = mysqli_query($mysqli, " - SELECT clients.client_id, clients.client_name, - SUM(CASE WHEN recurring.recurring_frequency = 'month' THEN recurring.recurring_amount - WHEN recurring.recurring_frequency = 'year' THEN recurring.recurring_amount / 12 END) AS recurring_monthly_total + SELECT client_id, client_name, + SUM(CASE WHEN recurring_invoice_frequency = 'month' THEN recurring_invoice_amount + WHEN recurring_invoice_frequency = 'year' THEN recurring_invoice_amount / 12 END) AS recurring_monthly_total FROM clients - LEFT JOIN recurring ON clients.client_id = recurring.recurring_client_id - WHERE recurring.recurring_status = 1 + LEFT JOIN recurring_invoices ON client_id = recurring_invoice_client_id + WHERE recurring_invoice_status = 1 GROUP BY clients.client_id HAVING recurring_monthly_total > 0 ORDER BY recurring_monthly_total DESC diff --git a/report_tax_summary.php b/report_tax_summary.php index 6c64a989..55d52839 100644 --- a/report_tax_summary.php +++ b/report_tax_summary.php @@ -108,7 +108,7 @@ $sql_tax = mysqli_query($mysqli, "SELECT `tax_name` FROM `taxes`"); } } ?> - + diff --git a/report_time_by_tech.php b/report_time_by_tech.php new file mode 100644 index 00000000..9e6a1715 --- /dev/null +++ b/report_time_by_tech.php @@ -0,0 +1,181 @@ + (int)$days, + 'hour' => (int)$hours, + 'minute' => (int)$minutes, + 'second' => (int)$seconds, + ]; + + foreach ($sections as $name => $value){ + if ($value > 0){ + $timeParts[] = $value. ' '.$name.($value == 1 ? '' : 's'); + } + } + + return implode(', ', $timeParts); +} + +if (isset($_GET['year'])) { + $year = intval($_GET['year']); +} else { + $year = date('Y'); +} + +if (isset($_GET['month'])) { + $month = intval($_GET['month']); +} else { + $month = date('m'); +} + +$sql_ticket_years = mysqli_query($mysqli, "SELECT DISTINCT YEAR(ticket_created_at) AS ticket_year FROM tickets ORDER BY ticket_year DESC"); + +$sql_clients = mysqli_query($mysqli, "SELECT client_id, client_name FROM clients WHERE client_archived_at IS NULL ORDER BY client_name ASC"); + +$sql_users = mysqli_query($mysqli, " + SELECT users.user_id, user_name FROM users + LEFT JOIN user_settings on users.user_id = user_settings.user_id + WHERE user_type = 1 + AND user_status = 1 + AND user_archived_at IS NULL + ORDER BY user_name DESC" +); +// TODO: Maybe try and filter this to just users with the support module perm + +?> + +
+
+

Time Logged By Technician

+
+ +
+
+
+ + + + +
+
+

Yearly ()

+
+
+
+ + + + + + + + + + + + + + + + + + + + + +
TechnicianTickets assignedTickets touchedTotal time worked (H:M:S)
+
+
+
+ + + +
+
+ + 0) { logApp("Cron", "info", "Cron created notifications for new tickets that are pending assignment"); } -// Recurring (Scheduled) tickets +// Recurring tickets // Get recurring tickets for today -$sql_scheduled_tickets = mysqli_query($mysqli, "SELECT * FROM scheduled_tickets WHERE scheduled_ticket_next_run = CURDATE()"); +$sql_recurring_tickets = mysqli_query($mysqli, "SELECT * FROM recurring_tickets WHERE recurring_ticket_next_run = CURDATE()"); -if (mysqli_num_rows($sql_scheduled_tickets) > 0) { - while ($row = mysqli_fetch_array($sql_scheduled_tickets)) { +if (mysqli_num_rows($sql_recurring_tickets) > 0) { + while ($row = mysqli_fetch_array($sql_recurring_tickets)) { - $schedule_id = intval($row['scheduled_ticket_id']); - $subject = sanitizeInput($row['scheduled_ticket_subject']); - $details = mysqli_real_escape_string($mysqli, $row['scheduled_ticket_details']); - $priority = sanitizeInput($row['scheduled_ticket_priority']); - $frequency = sanitizeInput(strtolower($row['scheduled_ticket_frequency'])); - $billable = intval($row['scheduled_ticket_billable']); - $created_id = intval($row['scheduled_ticket_created_by']); - $assigned_id = intval($row['scheduled_ticket_assigned_to']); - $client_id = intval($row['scheduled_ticket_client_id']); - $contact_id = intval($row['scheduled_ticket_contact_id']); - $asset_id = intval($row['scheduled_ticket_asset_id']); + $recurring_ticket_id = intval($row['recurring_ticket_id']); + $subject = sanitizeInput($row['recurring_ticket_subject']); + $details = mysqli_real_escape_string($mysqli, $row['recurring_ticket_details']); + $priority = sanitizeInput($row['recurring_ticket_priority']); + $frequency = sanitizeInput(strtolower($row['recurring_ticket_frequency'])); + $billable = intval($row['recurring_ticket_billable']); + $created_id = intval($row['recurring_ticket_created_by']); + $assigned_id = intval($row['recurring_ticket_assigned_to']); + $client_id = intval($row['recurring_ticket_client_id']); + $contact_id = intval($row['recurring_ticket_contact_id']); + $asset_id = intval($row['recurring_ticket_asset_id']); $ticket_status = 1; // Default if ($assigned_id > 0) { @@ -314,9 +334,15 @@ if (mysqli_num_rows($sql_scheduled_tickets) > 0) { mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); // Raise the ticket - mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = '$ticket_status', ticket_billable = $billable, ticket_created_by = $created_id, ticket_assigned_to = $assigned_id, ticket_contact_id = $contact_id, ticket_client_id = $client_id, ticket_asset_id = $asset_id"); + mysqli_query($mysqli, "INSERT INTO tickets SET ticket_prefix = '$config_ticket_prefix', ticket_number = $ticket_number, ticket_subject = '$subject', ticket_details = '$details', ticket_priority = '$priority', ticket_status = '$ticket_status', ticket_billable = $billable, ticket_created_by = $created_id, ticket_assigned_to = $assigned_id, ticket_contact_id = $contact_id, ticket_client_id = $client_id, ticket_asset_id = $asset_id, ticket_recurring_ticket_id = $recurring_ticket_id"); $id = mysqli_insert_id($mysqli); + // Copy Additional Assets from Recurring ticket to new ticket + mysqli_query($mysqli, "INSERT INTO ticket_assets (ticket_id, asset_id) + SELECT $id, asset_id + FROM recurring_ticket_assets + WHERE recurring_ticket_id = $recurring_ticket_id"); + // Logging logAction("Ticket", "Create", "Cron created recurring scheduled $frequency ticket - $subject", $client_id, $id); @@ -408,11 +434,18 @@ if (mysqli_num_rows($sql_scheduled_tickets) > 0) { // Update the run date $next_run = $next_run->format('Y-m-d'); - $a = mysqli_query($mysqli, "UPDATE scheduled_tickets SET scheduled_ticket_next_run = '$next_run' WHERE scheduled_ticket_id = $schedule_id"); + $a = mysqli_query($mysqli, "UPDATE recurring_tickets SET recurring_ticket_next_run = '$next_run' WHERE recurring_ticket_id = $recurring_ticket_id"); } } +// Flag any active recurring "next run" dates that are in the past +$sql_invalid_recurring_tickets = mysqli_query($mysqli, "SELECT * FROM recurring_tickets WHERE recurring_ticket_next_run < CURDATE()"); +while ($row = mysqli_fetch_array($sql_invalid_recurring_tickets)) { + $subject = sanitizeInput($row['recurring_ticket_subject']); + appNotify("Ticket", "Recurring ticket $subject next run date is in the past!", "recurring_tickets.php"); +} + // Logging // logAction("Cron", "Task", "Cron created sent out recurring tickets"); @@ -541,27 +574,27 @@ if ($config_send_invoice_reminders == 1) { // Send Recurring Invoices that match todays date and are active //Loop through all recurring that match today's date and is active -$sql_recurring = mysqli_query($mysqli, "SELECT * FROM recurring - LEFT JOIN recurring_payments ON recurring_id = recurring_payment_recurring_invoice_id - LEFT JOIN clients ON client_id = recurring_client_id - WHERE recurring_next_date = CURDATE() - AND recurring_status = 1 +$sql_recurring_invoices = mysqli_query($mysqli, "SELECT * FROM recurring_invoices + LEFT JOIN recurring_payments ON recurring_invoice_id = recurring_payment_recurring_invoice_id + LEFT JOIN clients ON client_id = recurring_invoice_client_id + WHERE recurring_invoice_next_date = CURDATE() + AND recurring_invoice_status = 1 "); -while ($row = mysqli_fetch_array($sql_recurring)) { - $recurring_id = intval($row['recurring_id']); - $recurring_scope = sanitizeInput($row['recurring_scope']); - $recurring_frequency = sanitizeInput($row['recurring_frequency']); - $recurring_status = sanitizeInput($row['recurring_status']); - $recurring_last_sent = sanitizeInput($row['recurring_last_sent']); - $recurring_next_date = sanitizeInput($row['recurring_next_date']); - $recurring_discount_amount = floatval($row['recurring_discount_amount']); - $recurring_amount = floatval($row['recurring_amount']); - $recurring_currency_code = sanitizeInput($row['recurring_currency_code']); - $recurring_note = sanitizeInput($row['recurring_note']); +while ($row = mysqli_fetch_array($sql_recurring_invoices)) { + $recurring_invoice_id = intval($row['recurring_invoice_id']); + $recurring_invoice_scope = sanitizeInput($row['recurring_invoice_scope']); + $recurring_invoice_frequency = sanitizeInput($row['recurring_invoice_frequency']); + $recurring_invoice_status = sanitizeInput($row['recurring_invoice_status']); + $recurring_invoice_last_sent = sanitizeInput($row['recurring_invoice_last_sent']); + $recurring_invoice_next_date = sanitizeInput($row['recurring_invoice_next_date']); + $recurring_invoice_discount_amount = floatval($row['recurring_invoice_discount_amount']); + $recurring_invoice_amount = floatval($row['recurring_invoice_amount']); + $recurring_invoice_currency_code = sanitizeInput($row['recurring_invoice_currency_code']); + $recurring_invoice_note = sanitizeInput($row['recurring_invoice_note']); $recurring_invoice_email_notify = intval($row['recurring_invoice_email_notify']); - $category_id = intval($row['recurring_category_id']); - $client_id = intval($row['recurring_client_id']); + $category_id = intval($row['recurring_invoice_category_id']); + $client_id = intval($row['recurring_invoice_client_id']); $client_name = sanitizeInput($row['client_name']); $client_net_terms = intval($row['client_net_terms']); @@ -582,12 +615,12 @@ while ($row = mysqli_fetch_array($sql_recurring)) { //Generate a unique URL key for clients to access $url_key = randomString(156); - mysqli_query($mysqli, "INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $new_invoice_number, invoice_scope = '$recurring_scope', invoice_date = CURDATE(), invoice_due = DATE_ADD(CURDATE(), INTERVAL $client_net_terms day), invoice_discount_amount = $recurring_discount_amount, invoice_amount = $recurring_amount, invoice_currency_code = '$recurring_currency_code', invoice_note = '$recurring_note', invoice_category_id = $category_id, invoice_status = 'Sent', invoice_url_key = '$url_key', invoice_client_id = $client_id"); + mysqli_query($mysqli, "INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $new_invoice_number, invoice_scope = '$recurring_invoice_scope', invoice_date = CURDATE(), invoice_due = DATE_ADD(CURDATE(), INTERVAL $client_net_terms day), invoice_discount_amount = $recurring_invoice_discount_amount, invoice_amount = $recurring_invoice_amount, invoice_currency_code = '$recurring_invoice_currency_code', invoice_note = '$recurring_invoice_note', invoice_category_id = $category_id, invoice_status = 'Sent', invoice_url_key = '$url_key', invoice_recurring_invoice_id = $recurring_invoice_id, invoice_client_id = $client_id"); $new_invoice_id = mysqli_insert_id($mysqli); //Copy Items from original recurring invoice to new invoice - $sql_invoice_items = mysqli_query($mysqli, "SELECT * FROM invoice_items WHERE item_recurring_id = $recurring_id ORDER BY item_id ASC"); + $sql_invoice_items = mysqli_query($mysqli, "SELECT * FROM invoice_items WHERE item_recurring_invoice_id = $recurring_invoice_id ORDER BY item_id ASC"); while ($row = mysqli_fetch_array($sql_invoice_items)) { $item_id = intval($row['item_id']); @@ -614,7 +647,7 @@ while ($row = mysqli_fetch_array($sql_recurring)) { //Update recurring dates - mysqli_query($mysqli, "UPDATE recurring SET recurring_last_sent = CURDATE(), recurring_next_date = DATE_ADD(CURDATE(), INTERVAL 1 $recurring_frequency) WHERE recurring_id = $recurring_id"); + mysqli_query($mysqli, "UPDATE recurring_invoices SET recurring_invoice_last_sent = CURDATE(), recurring_invoice_next_date = DATE_ADD(CURDATE(), INTERVAL 1 $recurring_invoice_frequency) WHERE recurring_invoice_id = $recurring_invoice_id"); // Get details of the newly generated invoice $sql = mysqli_query( @@ -640,7 +673,7 @@ while ($row = mysqli_fetch_array($sql_recurring)) { if ($config_recurring_auto_send_invoice == 1 && $recurring_invoice_email_notify == 1) { $subject = "Invoice $invoice_prefix$invoice_number"; - $body = "Hello $contact_name,

An invoice regarding \"$invoice_scope\" has been generated. Please view the details below.

Invoice: $invoice_prefix$invoice_number
Issue Date: $invoice_date
Total: " . numfmt_format_currency($currency_format, $invoice_amount, $recurring_currency_code) . "
Due Date: $invoice_due


To view your invoice, please click here.


--
$company_name - Billing
$config_invoice_from_email
$company_phone"; + $body = "Hello $contact_name,

An invoice regarding \"$invoice_scope\" has been generated. Please view the details below.

Invoice: $invoice_prefix$invoice_number
Issue Date: $invoice_date
Total: " . numfmt_format_currency($currency_format, $invoice_amount, $recurring_invoice_currency_code) . "
Due Date: $invoice_due


To view your invoice, please click here.


--
$company_name - Billing
$config_invoice_from_email
$company_phone"; $mail = addToMailQueue([ [ @@ -845,6 +878,14 @@ while ($row = mysqli_fetch_array($sql_recurring)) { } //End Recurring Invoices Loop +// Flag any active recurring "next run" dates that are in the past +$sql_invalid_recurring_invoices = mysqli_query($mysqli, "SELECT * FROM recurring_invoices WHERE recurring_invoice_next_date < CURDATE()"); +while ($row = mysqli_fetch_array($sql_invalid_recurring_invoices)) { + $invoice_prefix = sanitizeInput($row['recurring_invoice_prefix']); + $invoice_number = intval($row['recurring_invoice_number']); + appNotify("Invoice", "Recurring invoice $invoice_prefix$invoice_number next run date is in the past!", "recurring_invoices.php"); +} + // Logging // logAction("Cron", "Task", "Cron created invoices from recurring invoices and sent emails out"); @@ -888,10 +929,17 @@ while ($row = mysqli_fetch_array($sql_recurring_expenses)) { mysqli_query($mysqli, "UPDATE recurring_expenses SET recurring_expense_last_sent = CURDATE(), recurring_expense_next_date = $next_date_query WHERE recurring_expense_id = $recurring_expense_id"); -} //End Recurring Invoices Loop +} //End Recurring expenses loop + +// Flag any active recurring "next run" dates that are in the past +$sql_invalid_recurring_expenses = mysqli_query($mysqli, "SELECT * FROM recurring_expenses WHERE recurring_expense_next_date < CURDATE() AND recurring_expense_status = 1"); +while ($row = mysqli_fetch_array($sql_invalid_recurring_expenses)) { + $recurring_expense_description = sanitizeInput($row['recurring_expense_description']); + appNotify("Expense", "Recurring expense $recurring_expense_description next run date is in the past!", "recurring_expenses.php"); +} // Logging -logApp("Cron", "info", "Cron created expenses from recurring expenses"); +//logApp("Cron", "info", "Cron created expenses from recurring expenses"); // TELEMETRY @@ -907,12 +955,12 @@ if ($config_telemetry > 0 || $config_telemetry == 2) { $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('recurring_id') AS num FROM tickets")); $ticket_count = $row['num']; - // Recurring (Scheduled) Ticket Count - $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('scheduled_ticket_id') AS num FROM scheduled_tickets")); - $scheduled_ticket_count = $row['num']; + // Recurring Ticket Count + $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('recurring_ticket_id') AS num FROM recurring_tickets")); + $recurring_ticket_count = $row['num']; // Calendar Event Count - $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('event_id') AS num FROM events")); + $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('event_id') AS num FROM calendar_events")); $calendar_event_count = $row['num']; // Quote Count @@ -927,9 +975,9 @@ if ($config_telemetry > 0 || $config_telemetry == 2) { $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('revenue_id') AS num FROM revenues")); $revenue_count = $row['num']; - // Recurring Count - $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('recurring_id') AS num FROM recurring")); - $recurring_count = $row['num']; + // Recurring Invoice Count + $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('recurring_invoice_id') AS num FROM recurring_invoices")); + $recurring_invoice_count = $row['num']; // Account Count $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('account_id') AS num FROM accounts")); @@ -983,9 +1031,9 @@ if ($config_telemetry > 0 || $config_telemetry == 2) { $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('software_id') AS num FROM software WHERE software_template = 1")); $software_template_count = $row['num']; - // Password Count - $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('login_id') AS num FROM logins")); - $password_count = $row['num']; + // Credential Count + $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('credential_id') AS num FROM credentials")); + $credential_count = $row['num']; // Network Count $row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT COUNT('network_id') AS num FROM networks")); @@ -1075,12 +1123,12 @@ if ($config_telemetry > 0 || $config_telemetry == 2) { 'currency' => "$company_currency", 'client_count' => $client_count, 'ticket_count' => $ticket_count, - 'scheduled_ticket_count' => $scheduled_ticket_count, + 'recurring_ticket_count' => $recurring_ticket_count, 'calendar_event_count' => $calendar_event_count, 'quote_count' => $quote_count, 'invoice_count' => $invoice_count, 'revenue_count' => $revenue_count, - 'recurring_count' => $recurring_count, + 'recurring_invoice_count' => $recurring_invoice_count, 'account_count' => $account_count, 'tax_count' => $tax_count, 'product_count' => $product_count, @@ -1094,7 +1142,7 @@ if ($config_telemetry > 0 || $config_telemetry == 2) { 'asset_count' => $asset_count, 'software_count' => $software_count, 'software_template_count' => $software_template_count, - 'password_count' => $password_count, + 'credential_count' => $credential_count, 'network_count' => $network_count, 'certificate_count' => $certificate_count, 'domain_count' => $domain_count, diff --git a/scripts/cron_certificate_refresher.php b/scripts/cron_certificate_refresher.php index 9cb2feb6..70149aba 100644 --- a/scripts/cron_certificate_refresher.php +++ b/scripts/cron_certificate_refresher.php @@ -11,7 +11,7 @@ if (php_sapi_name() !== 'cli') { require_once "../config.php"; // Set Timezone -require_once "../inc_set_timezone.php"; +require_once "../includes/inc_set_timezone.php"; require_once "../functions.php"; diff --git a/scripts/cron_domain_refresher.php b/scripts/cron_domain_refresher.php index 5132e0f0..2bb04656 100644 --- a/scripts/cron_domain_refresher.php +++ b/scripts/cron_domain_refresher.php @@ -11,7 +11,7 @@ if (php_sapi_name() !== 'cli') { require_once "../config.php"; // Set Timezone -require_once "../inc_set_timezone.php"; +require_once "../includes/inc_set_timezone.php"; require_once "../functions.php"; $sql_settings = mysqli_query($mysqli, "SELECT * FROM settings WHERE settings.company_id = 1"); diff --git a/scripts/cron_mail_queue.php b/scripts/cron_mail_queue.php index f536a57a..87e52447 100644 --- a/scripts/cron_mail_queue.php +++ b/scripts/cron_mail_queue.php @@ -10,7 +10,7 @@ if (php_sapi_name() !== 'cli') { require_once "../config.php"; // Set Timezone -require_once "../inc_set_timezone.php"; +require_once "../includes/inc_set_timezone.php"; require_once "../functions.php"; $sql_settings = mysqli_query($mysqli, "SELECT * FROM settings WHERE company_id = 1"); diff --git a/scripts/cron_ticket_email_parser.php b/scripts/cron_ticket_email_parser.php index f9276ad9..d558549c 100644 --- a/scripts/cron_ticket_email_parser.php +++ b/scripts/cron_ticket_email_parser.php @@ -19,11 +19,11 @@ if (php_sapi_name() !== 'cli') { require_once "../config.php"; // Set Timezone -require_once "../inc_set_timezone.php"; +require_once "../includes/inc_set_timezone.php"; require_once "../functions.php"; // Get settings for the "default" company -require_once "../get_settings.php"; +require_once "../includes/get_settings.php"; $config_ticket_prefix = sanitizeInput($config_ticket_prefix); $config_ticket_from_name = sanitizeInput($config_ticket_from_name); @@ -33,7 +33,7 @@ $config_ticket_email_parse_unknown_senders = intval($row['config_ticket_email_pa $sql = mysqli_query($mysqli, "SELECT * FROM companies, settings WHERE companies.company_id = settings.company_id AND companies.company_id = 1"); $row = mysqli_fetch_array($sql); $company_name = sanitizeInput($row['company_name']); -$company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); +$company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'], $row['company_phone_country_code'])); // Check setting enabled if ($config_ticket_email_parse == 0) { @@ -269,7 +269,8 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac mysqli_query($mysqli, "INSERT INTO ticket_replies SET ticket_reply = '$message_esc', ticket_reply_type = '$ticket_reply_type', ticket_reply_time_worked = '00:00:00', ticket_reply_by = $ticket_reply_contact, ticket_reply_ticket_id = $ticket_id"); $reply_id = mysqli_insert_id($mysqli); - mkdirMissing('../uploads/tickets/'); + $ticket_dir = "../uploads/tickets/" . $ticket_id . "/"; + mkdirMissing($ticket_dir); foreach ($attachments as $attachment) { $att_name = $attachment->getFilename(); $att_extarr = explode('.', $att_name); @@ -277,7 +278,7 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac if (in_array($att_extension, $allowed_extensions)) { $att_saved_filename = md5(uniqid(rand(), true)) . '.' . $att_extension; - $att_saved_path = "../uploads/tickets/" . $ticket_id . "/" . $att_saved_filename; + $att_saved_path = $ticket_dir . $att_saved_filename; file_put_contents($att_saved_path, $attachment->getContent()); $ticket_attachment_name = sanitizeInput($att_name); diff --git a/scripts/setup_cli.php b/scripts/setup_cli.php index 4e2411c5..7e174076 100644 --- a/scripts/setup_cli.php +++ b/scripts/setup_cli.php @@ -267,8 +267,8 @@ $password_hash = password_hash(trim($user_password_plain), PASSWORD_DEFAULT); $site_encryption_master_key = randomString(); $user_specific_encryption_ciphertext = setupFirstUserSpecificKey($user_password_plain, $site_encryption_master_key); -mysqli_query($mysqli,"INSERT INTO users SET user_name = '$user_name', user_email = '$user_email', user_password = '$password_hash', user_specific_encryption_ciphertext = '$user_specific_encryption_ciphertext'"); -mysqli_query($mysqli,"INSERT INTO user_settings SET user_id = 1, user_role = 3"); +mysqli_query($mysqli,"INSERT INTO users SET user_name = '$user_name', user_email = '$user_email', user_password = '$password_hash', user_specific_encryption_ciphertext = '$user_specific_encryption_ciphertext', user_role_id = 3"); +mysqli_query($mysqli,"INSERT INTO user_settings SET user_id = 1"); echo "User $user_name created successfully.\n"; // Company Details @@ -276,7 +276,7 @@ mysqli_query($mysqli,"INSERT INTO companies SET company_name = '$company_name', // Insert default settings and categories $latest_database_version = LATEST_DATABASE_VERSION; -mysqli_query($mysqli,"INSERT INTO settings SET company_id = 1, config_current_database_version = '$latest_database_version', config_invoice_prefix = 'INV-', config_invoice_next_number = 1, config_recurring_prefix = 'REC-', config_recurring_next_number = 1, config_invoice_overdue_reminders = '1,3,7', config_quote_prefix = 'QUO-', config_quote_next_number = 1, config_default_net_terms = 30, config_ticket_next_number = 1, config_ticket_prefix = 'TCK-'"); +mysqli_query($mysqli,"INSERT INTO settings SET company_id = 1, config_current_database_version = '$latest_database_version', config_invoice_prefix = 'INV-', config_invoice_next_number = 1, config_recurring_invoice_prefix = 'REC-', config_invoice_overdue_reminders = '1,3,7', config_quote_prefix = 'QUO-', config_quote_next_number = 1, config_default_net_terms = 30, config_ticket_next_number = 1, config_ticket_prefix = 'TCK-'"); // Categories mysqli_query($mysqli,"INSERT INTO categories SET category_name = 'Office Supplies', category_type = 'Expense', category_color = 'blue'"); @@ -308,27 +308,27 @@ mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_financial', mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_reporting', module_description = 'Access to all reports'"); // Roles -mysqli_query($mysqli, "INSERT INTO user_roles SET user_role_id = 1, user_role_name = 'Accountant', user_role_description = 'Built-in - Limited access to financial-focused modules'"); +mysqli_query($mysqli, "INSERT INTO user_roles SET role_id = 1, role_name = 'Accountant', role_description = 'Built-in - Limited access to financial-focused modules'"); mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 1, user_role_permission_level = 1"); mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 2, user_role_permission_level = 1"); mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 4, user_role_permission_level = 1"); mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 5, user_role_permission_level = 2"); mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 6, user_role_permission_level = 1"); -mysqli_query($mysqli, "INSERT INTO user_roles SET user_role_id = 2, user_role_name = 'Technician', user_role_description = 'Built-in - Limited access to technical-focused modules'"); +mysqli_query($mysqli, "INSERT INTO user_roles SET role_id = 2, role_name = 'Technician', role_description = 'Built-in - Limited access to technical-focused modules'"); mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 1, user_role_permission_level = 2"); mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 2, user_role_permission_level = 2"); mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 3, user_role_permission_level = 2"); mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 4, user_role_permission_level = 2"); -mysqli_query($mysqli, "INSERT INTO user_roles SET user_role_id = 3, user_role_name = 'Administrator', user_role_description = 'Built-in - Full administrative access', user_role_is_admin = 1"); +mysqli_query($mysqli, "INSERT INTO user_roles SET role_id = 3, role_name = 'Administrator', role_description = 'Built-in - Full administrative access', role_is_admin = 1"); // Custom Links mysqli_query($mysqli,"INSERT INTO custom_links SET custom_link_name = 'Docs', custom_link_uri = 'https://docs.itflow.org', custom_link_new_tab = 1, custom_link_icon = 'question-circle'"); // Finalizing mysqli_query($mysqli,"UPDATE companies SET company_locale = '$locale', company_currency = '$currency_code' WHERE company_id = 1"); -mysqli_query($mysqli,"UPDATE settings SET config_timezone = '$timezone', config_phone_mask = 1 WHERE company_id = 1"); +mysqli_query($mysqli,"UPDATE settings SET config_timezone = '$timezone' WHERE company_id = 1"); mysqli_query($mysqli,"INSERT INTO accounts SET account_name = 'Cash', account_currency_code = '$currency_code'"); // Telemetry (optional if interactive) diff --git a/scripts/update_cli.php b/scripts/update_cli.php index b505c128..55aa4820 100644 --- a/scripts/update_cli.php +++ b/scripts/update_cli.php @@ -127,4 +127,4 @@ if (isset($options['update_db'])) { } else { echo "Database is already at the latest version ($latest_db_version). No updates were applied.\n"; } -} +} \ No newline at end of file diff --git a/services.php b/services.php index 85c960f0..1bd881fa 100644 --- a/services.php +++ b/services.php @@ -18,6 +18,18 @@ if (isset($_GET['client_id'])) { // Perms enforceUserPermission('module_support'); +if (!$client_url) { + // Client Filter + if (isset($_GET['client']) & !empty($_GET['client'])) { + $client_query = 'AND (service_client_id = ' . intval($_GET['client']) . ')'; + $client = intval($_GET['client']); + } else { + // Default - any + $client_query = ''; + $client = ''; + } +} + // Overview SQL query $sql = mysqli_query( $mysqli, @@ -58,7 +70,38 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); -
+ +
+ +
+
+ +
+
+ + +
diff --git a/setup.php b/setup.php index dd955461..0b46651c 100644 --- a/setup.php +++ b/setup.php @@ -19,7 +19,7 @@ if ($config_enable_setup == 0) { exit; } -include_once "settings_localization_array.php"; +include_once "includes/settings_localization_array.php"; $errorLog = ini_get('error_log') ?: "Debian/Ubuntu default is usually /var/log/apache2/error.log"; // Get a list of all available timezones @@ -127,7 +127,7 @@ if (isset($_POST['add_user'])) { //Generate user specific key $user_specific_encryption_ciphertext = setupFirstUserSpecificKey(trim($_POST['password']), $site_encryption_master_key); - mysqli_query($mysqli,"INSERT INTO users SET user_name = '$name', user_email = '$email', user_password = '$password', user_specific_encryption_ciphertext = '$user_specific_encryption_ciphertext'"); + mysqli_query($mysqli,"INSERT INTO users SET user_name = '$name', user_email = '$email', user_password = '$password', user_specific_encryption_ciphertext = '$user_specific_encryption_ciphertext', user_role_id = 3"); mkdirMissing("uploads/users/1"); @@ -175,7 +175,7 @@ if (isset($_POST['add_user'])) { } //Create Settings - mysqli_query($mysqli,"INSERT INTO user_settings SET user_id = 1, user_role = 3"); + mysqli_query($mysqli,"INSERT INTO user_settings SET user_id = 1"); $_SESSION['alert_message'] = "User $name created"; @@ -244,7 +244,7 @@ if (isset($_POST['add_company_settings'])) { } $latest_database_version = LATEST_DATABASE_VERSION; - mysqli_query($mysqli,"INSERT INTO settings SET company_id = 1, config_current_database_version = '$latest_database_version', config_invoice_prefix = 'INV-', config_invoice_next_number = 1, config_recurring_prefix = 'REC-', config_recurring_next_number = 1, config_invoice_overdue_reminders = '1,3,7', config_quote_prefix = 'QUO-', config_quote_next_number = 1, config_default_net_terms = 30, config_ticket_next_number = 1, config_ticket_prefix = 'TCK-'"); + mysqli_query($mysqli,"INSERT INTO settings SET company_id = 1, config_current_database_version = '$latest_database_version', config_invoice_prefix = 'INV-', config_invoice_next_number = 1, config_recurring_invoice_prefix = 'REC-', config_invoice_overdue_reminders = '1,3,7', config_quote_prefix = 'QUO-', config_quote_next_number = 1, config_default_net_terms = 30, config_ticket_next_number = 1, config_ticket_prefix = 'TCK-'"); // Create Categories // Expense Categories Examples @@ -314,20 +314,20 @@ if (isset($_POST['add_company_settings'])) { mysqli_query($mysqli, "INSERT INTO modules SET module_name = 'module_reporting', module_description = 'Access to all reports'"); // Add default roles - mysqli_query($mysqli, "INSERT INTO user_roles SET user_role_id = 1, user_role_name = 'Accountant', user_role_description = 'Built-in - Limited access to financial-focused modules'"); + mysqli_query($mysqli, "INSERT INTO user_roles SET role_id = 1, role_name = 'Accountant', role_description = 'Built-in - Limited access to financial-focused modules'"); mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 1, user_role_permission_level = 1"); // Read clients mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 2, user_role_permission_level = 1"); // Read support mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 4, user_role_permission_level = 1"); // Read sales mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 5, user_role_permission_level = 2"); // Modify financial mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 1, module_id = 6, user_role_permission_level = 1"); // Read reports - mysqli_query($mysqli, "INSERT INTO user_roles SET user_role_id = 2, user_role_name = 'Technician', user_role_description = 'Built-in - Limited access to technical-focused modules'"); + mysqli_query($mysqli, "INSERT INTO user_roles SET role_id = 2, role_name = 'Technician', role_description = 'Built-in - Limited access to technical-focused modules'"); mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 1, user_role_permission_level = 2"); // Modify clients mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 2, user_role_permission_level = 2"); // Modify support mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 3, user_role_permission_level = 2"); // Modify credentials mysqli_query($mysqli, "INSERT INTO user_role_permissions SET user_role_id = 2, module_id = 4, user_role_permission_level = 2"); // Modify sales - mysqli_query($mysqli, "INSERT INTO user_roles SET user_role_id = 3, user_role_name = 'Administrator', user_role_description = 'Built-in - Full administrative access to all modules (including user management)', user_role_is_admin = 1"); + mysqli_query($mysqli, "INSERT INTO user_roles SET role_id = 3, role_name = 'Administrator', role_description = 'Built-in - Full administrative access to all modules (including user management)', role_is_admin = 1"); // Custom Links mysqli_query($mysqli,"INSERT INTO custom_links SET custom_link_name = 'Docs', custom_link_uri = 'https://docs.itflow.org', custom_link_new_tab = 1, custom_link_icon = 'question-circle'"); @@ -344,11 +344,10 @@ if (isset($_POST['add_localization_settings'])) { $locale = sanitizeInput($_POST['locale']); $currency_code = sanitizeInput($_POST['currency_code']); $timezone = sanitizeInput($_POST['timezone']); - $phone_mask = intval($_POST['phone_mask']); mysqli_query($mysqli,"UPDATE companies SET company_locale = '$locale', company_currency = '$currency_code' WHERE company_id = 1"); - mysqli_query($mysqli,"UPDATE settings SET config_timezone = '$timezone', config_phone_mask = $phone_mask WHERE company_id = 1"); + mysqli_query($mysqli,"UPDATE settings SET config_timezone = '$timezone' WHERE company_id = 1"); // Create Default Cash Account mysqli_query($mysqli,"INSERT INTO accounts SET account_name = 'Cash', account_currency_code = '$currency_code'"); @@ -1058,7 +1057,7 @@ if (isset($_POST['add_telemetry'])) {
- +
@@ -1146,19 +1145,6 @@ if (isset($_POST['add_telemetry'])) { -
- -
-
- -
- -
-
-
- - - = 2) { ?> - - Invoice - - - - - - Reopen - - - - - - Resolve - - - - - - Close - - - - - @@ -476,7 +484,7 @@ if (isset($_GET['ticket_id'])) { $ticket_closed_by_display = 'User'; if (!empty($ticket_closed_by)) { - $sql_closed_by = mysqli_query($mysqli, "SELECT * FROM tickets, users WHERE ticket_closed_by = user_id"); + $sql_closed_by = mysqli_query($mysqli, "SELECT user_name FROM users WHERE user_id = $ticket_closed_by"); $row = mysqli_fetch_array($sql_closed_by); $ticket_closed_by_display = nullable_htmlentities($row['user_name']); } @@ -498,9 +506,9 @@ if (isset($_GET['ticket_id'])) {
+ data-toggle = "ajax-modal" + data-ajax-url = "ajax/ajax_ticket_assign.php" + data-ajax-id = "">
@@ -513,11 +521,11 @@ if (isset($_GET['ticket_id'])) { = 2 && empty($ticket_closed_at)) { ?> - data-toggle = "ajax-modal" - data-ajax-url = "ajax/ajax_ticket_priority.php" - data-ajax-id = "" + data-toggle = "ajax-modal" + data-ajax-url = "ajax/ajax_ticket_priority.php" + data-ajax-id = "" - > + > @@ -539,10 +547,10 @@ if (isset($_GET['ticket_id'])) {
Ticket is + data-toggle = "ajax-modal" + data-ajax-url = "ajax/ajax_ticket_billable.php" + data-ajax-id = "" + > Billable"; @@ -651,7 +659,11 @@ if (isset($_GET['ticket_id'])) {
- +
@@ -780,31 +792,40 @@ if (isset($_GET['ticket_id'])) {
- -
- +
+ - +
@@ -846,9 +867,9 @@ if (isset($_GET['ticket_id'])) {
Contact
@@ -880,21 +901,21 @@ if (isset($_GET['ticket_id'])) {
-
-
Contact
- @@ -903,82 +924,82 @@ if (isset($_GET['ticket_id'])) { 0)) { ?>
- = 2) { ?> -
- -
-
- -
- + = 2) { ?> + + +
+
+ +
+ +
-
- - + + - - - - - - + + + + +
- - - = 2) { ?> - - - - - - - m - - - - -
- = 2) { ?> - + + + + - - -
+ + + = 2) { ?> + + + - -
-
+
+ + m + - + + +
+ = 2) { ?> + + +
+
+
@@ -1012,16 +1033,36 @@ if (isset($_GET['ticket_id'])) {
-
Asset
+
Asset(s)
+ +
+ + + +
+
@@ -1115,21 +1156,21 @@ require_once "includes/footer.php"; + +
+ +
+
+ +
+ +
+
diff --git a/vendors.php b/vendors.php index 46ee786c..f6327a31 100644 --- a/vendors.php +++ b/vendors.php @@ -167,7 +167,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); } else { $vendor_contact_name_display = "-"; } - $vendor_phone = formatPhoneNumber($row['vendor_phone']); + $vendor_phone_country_code = nullable_htmlentities($row['vendor_phone_country_code']); + $vendor_phone = nullable_htmlentities(formatPhoneNumber($row['vendor_phone'], $vendor_phone_country_code)); $vendor_extension = nullable_htmlentities($row['vendor_extension']); $vendor_email = nullable_htmlentities($row['vendor_email']); $vendor_website = nullable_htmlentities($row['vendor_website']);