diff --git a/clients.php b/clients.php index ebaf6a3a..c294cb55 100644 --- a/clients.php +++ b/clients.php @@ -131,6 +131,45 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); class="btn btn-"> Archived + @@ -246,251 +285,277 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
-
- - text-nowrap"> - - - - - = 1) && $config_module_enable_accounting == 1) { ?> - = 2) { ?> - - - - $location_address
$location_city $location_state $location_zip
$location_country"; - } - $contact_id = intval($row['contact_id']); - $contact_name = nullable_htmlentities($row['contact_name']); - $contact_title = nullable_htmlentities($row['contact_title']); - $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_country_code = nullable_htmlentities($row['contact_mobile_country_code']); - $contact_mobile = nullable_htmlentities(formatPhoneNumber($row['contact_mobile'], $contact_mobile_country_code)); - $contact_email = nullable_htmlentities($row['contact_email']); - $client_website = nullable_htmlentities($row['client_website']); - $client_rate = floatval($row['client_rate']); - $client_currency_code = nullable_htmlentities($row['client_currency_code']); - $client_net_terms = intval($row['client_net_terms']); - $client_tax_id_number = nullable_htmlentities($row['client_tax_id_number']); - $client_referral = nullable_htmlentities($row['client_referral']); - $client_abbreviation = nullable_htmlentities($row['client_abbreviation']); - $client_notes = nullable_htmlentities($row['client_notes']); - $client_created_at = date('Y-m-d', strtotime($row['client_created_at'])); - $client_updated_at = nullable_htmlentities($row['client_updated_at']); - $client_archived_at = nullable_htmlentities($row['client_archived_at']); - $client_is_lead = intval($row['client_lead']); - - // Abbreviation - if (empty($client_abbreviation)) { - $client_abbreviation = shortenClient($client_name); - } - - // Client Tags - - $client_tag_name_display_array = array(); - $client_tag_id_array = array(); - $sql_client_tags = mysqli_query($mysqli, "SELECT * FROM client_tags LEFT JOIN tags ON client_tags.tag_id = tags.tag_id WHERE client_id = $client_id ORDER BY tag_name ASC"); - while ($row = mysqli_fetch_array($sql_client_tags)) { - - $client_tag_id = intval($row['tag_id']); - $client_tag_name = nullable_htmlentities($row['tag_name']); - $client_tag_color = nullable_htmlentities($row['tag_color']); - if (empty($client_tag_color)) { - $client_tag_color = "dark"; - } - $client_tag_icon = nullable_htmlentities($row['tag_icon']); - if (empty($client_tag_icon)) { - $client_tag_icon = "tag"; - } - - $client_tag_id_array[] = $client_tag_id; - $client_tag_name_display_array[] = "$client_tag_name"; - } - $client_tags_display = implode('', $client_tag_name_display_array); - - //Add up all the payments for the invoice and get the total amount paid to the invoice - $sql_invoice_amounts = mysqli_query($mysqli, "SELECT SUM(invoice_amount) AS invoice_amounts FROM invoices WHERE invoice_client_id = $client_id AND invoice_status != 'Draft' AND invoice_status != 'Cancelled' AND invoice_status != 'Non-Billable' "); - $row = mysqli_fetch_array($sql_invoice_amounts); - - $invoice_amounts = floatval($row['invoice_amounts']); - - $sql_amount_paid = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS amount_paid FROM payments, invoices WHERE payment_invoice_id = invoice_id AND invoice_client_id = $client_id"); - $row = mysqli_fetch_array($sql_amount_paid); - - $amount_paid = floatval($row['amount_paid']); - - $balance = $invoice_amounts - $amount_paid; - //set Text color on balance - if ($balance > 0) { - $balance_text_color = "text-danger font-weight-bold"; - } else { - $balance_text_color = ""; - } - - //Get Monthly Recurring Total - $sql_recurring_monthly_total = mysqli_query($mysqli, "SELECT SUM(recurring_invoice_amount) AS recurring_monthly_total FROM recurring_invoices WHERE recurring_invoice_status = 1 AND recurring_invoice_frequency = 'month' AND recurring_invoice_client_id = $client_id"); - $row = mysqli_fetch_array($sql_recurring_monthly_total); - - $recurring_monthly_total = floatval($row['recurring_monthly_total']); - - //Get Yearly Recurring Total - $sql_recurring_yearly_total = mysqli_query($mysqli, "SELECT SUM(recurring_invoice_amount) AS recurring_yearly_total FROM recurring_invoices WHERE recurring_invoice_status = 1 AND recurring_invoice_frequency = 'year' AND recurring_invoice_client_id = $client_id"); - $row = mysqli_fetch_array($sql_recurring_yearly_total); - - $recurring_yearly_total = floatval($row['recurring_yearly_total']) / 12; - - $recurring_monthly = $recurring_monthly_total + $recurring_yearly_total; - - ?> + + +
+
- - Client Name - - - - Primary Location - - - - Primary Contact - - - Billing Action
+ text-nowrap"> - - - + + = 1) && $config_module_enable_accounting == 1) { ?> + = 2) { ?> + + + + $location_address
$location_city $location_state $location_zip
$location_country"; + } + $contact_id = intval($row['contact_id']); + $contact_name = nullable_htmlentities($row['contact_name']); + $contact_title = nullable_htmlentities($row['contact_title']); + $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_country_code = nullable_htmlentities($row['contact_mobile_country_code']); + $contact_mobile = nullable_htmlentities(formatPhoneNumber($row['contact_mobile'], $contact_mobile_country_code)); + $contact_email = nullable_htmlentities($row['contact_email']); + $client_website = nullable_htmlentities($row['client_website']); + $client_rate = floatval($row['client_rate']); + $client_currency_code = nullable_htmlentities($row['client_currency_code']); + $client_net_terms = intval($row['client_net_terms']); + $client_tax_id_number = nullable_htmlentities($row['client_tax_id_number']); + $client_referral = nullable_htmlentities($row['client_referral']); + $client_abbreviation = nullable_htmlentities($row['client_abbreviation']); + $client_notes = nullable_htmlentities($row['client_notes']); + $client_created_at = date('Y-m-d', strtotime($row['client_created_at'])); + $client_updated_at = nullable_htmlentities($row['client_updated_at']); + $client_archived_at = nullable_htmlentities($row['client_archived_at']); + $client_is_lead = intval($row['client_lead']); + + // Abbreviation + if (empty($client_abbreviation)) { + $client_abbreviation = shortenClient($client_name); + } + + // Client Tags + + $client_tag_name_display_array = array(); + $client_tag_id_array = array(); + $sql_client_tags = mysqli_query($mysqli, "SELECT * FROM client_tags LEFT JOIN tags ON client_tags.tag_id = tags.tag_id WHERE client_id = $client_id ORDER BY tag_name ASC"); + while ($row = mysqli_fetch_array($sql_client_tags)) { + + $client_tag_id = intval($row['tag_id']); + $client_tag_name = nullable_htmlentities($row['tag_name']); + $client_tag_color = nullable_htmlentities($row['tag_color']); + if (empty($client_tag_color)) { + $client_tag_color = "dark"; + } + $client_tag_icon = nullable_htmlentities($row['tag_icon']); + if (empty($client_tag_icon)) { + $client_tag_icon = "tag"; } - if (!empty($contact_name)) { ?> -
- - -
- $client_tag_name"; + } + $client_tags_display = implode('', $client_tag_name_display_array); - if (!empty($contact_phone)) { ?> -
- -
- -
- -
- -
- -
- - + $sql_amount_paid = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS amount_paid FROM payments, invoices WHERE payment_invoice_id = invoice_id AND invoice_client_id = $client_id"); + $row = mysqli_fetch_array($sql_amount_paid); - - = 1) && $config_module_enable_accounting == 1) { ?> - + - - - - = 2) { ?> + + + + + = 1) && $config_module_enable_accounting == 1) { ?> + + + + + = 2) { ?> + - - + + + - + - -
- - - -
- -
- - -
- -
- -
- Abbreviation:
- Created:
+
+
+
-
- + + Client Name + + + + + Primary Location + + + + Primary Contact + + + Billing Action
-
- Balance -
-
- Paid -
-
- Monthly -
-
- Hourly Rate + $amount_paid = floatval($row['amount_paid']); + + $balance = $invoice_amounts - $amount_paid; + //set Text color on balance + if ($balance > 0) { + $balance_text_color = "text-danger font-weight-bold"; + } else { + $balance_text_color = ""; + } + + //Get Monthly Recurring Total + $sql_recurring_monthly_total = mysqli_query($mysqli, "SELECT SUM(recurring_invoice_amount) AS recurring_monthly_total FROM recurring_invoices WHERE recurring_invoice_status = 1 AND recurring_invoice_frequency = 'month' AND recurring_invoice_client_id = $client_id"); + $row = mysqli_fetch_array($sql_recurring_monthly_total); + + $recurring_monthly_total = floatval($row['recurring_monthly_total']); + + //Get Yearly Recurring Total + $sql_recurring_yearly_total = mysqli_query($mysqli, "SELECT SUM(recurring_invoice_amount) AS recurring_yearly_total FROM recurring_invoices WHERE recurring_invoice_status = 1 AND recurring_invoice_frequency = 'year' AND recurring_invoice_client_id = $client_id"); + $row = mysqli_fetch_array($sql_recurring_yearly_total); + + $recurring_yearly_total = floatval($row['recurring_yearly_total']) / 12; + + $recurring_monthly = $recurring_monthly_total + $recurring_yearly_total; + + ?> +
+
+
- + +
+ + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ Balance +
+
+ Paid +
+
+ Monthly +
+
+ Hourly Rate +
+
+ - -
-
+ + + + + + + + + \ No newline at end of file diff --git a/modals/client_bulk_edit_hourly_rate_modal.php b/modals/client_bulk_edit_hourly_rate_modal.php new file mode 100644 index 00000000..fd8bf169 --- /dev/null +++ b/modals/client_bulk_edit_hourly_rate_modal.php @@ -0,0 +1,31 @@ + diff --git a/modals/client_bulk_edit_industry_modal.php b/modals/client_bulk_edit_industry_modal.php new file mode 100644 index 00000000..cbfa8b1b --- /dev/null +++ b/modals/client_bulk_edit_industry_modal.php @@ -0,0 +1,31 @@ + diff --git a/modals/client_bulk_edit_referral_modal.php b/modals/client_bulk_edit_referral_modal.php new file mode 100644 index 00000000..9417ae4b --- /dev/null +++ b/modals/client_bulk_edit_referral_modal.php @@ -0,0 +1,49 @@ + diff --git a/modals/client_bulk_email_modal.php b/modals/client_bulk_email_modal.php new file mode 100644 index 00000000..fc61ce54 --- /dev/null +++ b/modals/client_bulk_email_modal.php @@ -0,0 +1,100 @@ + \ No newline at end of file diff --git a/post/user/client.php b/post/user/client.php index 8ff8812d..c2ff552d 100644 --- a/post/user/client.php +++ b/post/user/client.php @@ -184,11 +184,12 @@ if (isset($_GET['archive_client'])) { header("Location: " . $_SERVER["HTTP_REFERER"]); } -if (isset($_GET['undo_archive_client'])) { +if (isset($_GET['restore_client'])) { + validateCSRFToken($_GET['csrf_token']); enforceUserPermission('module_client', 2); - $client_id = intval($_GET['undo_archive_client']); + $client_id = intval($_GET['restore_client']); // Get Client Name $sql = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_id = $client_id"); @@ -198,9 +199,9 @@ if (isset($_GET['undo_archive_client'])) { mysqli_query($mysqli, "UPDATE clients SET client_archived_at = NULL WHERE client_id = $client_id"); // Logging - logAction("Client", "Unarchive", "$session_name unarchived client $client_name", $client_id, $client_id); + logAction("Client", "Restored", "$session_name restored client $client_name", $client_id); - $_SESSION['alert_message'] = "Client $client_name unarchived"; + $_SESSION['alert_message'] = "Client $client_name restored"; header("Location: " . $_SERVER["HTTP_REFERER"]); } @@ -589,6 +590,317 @@ if (isset($_GET['download_clients_csv_template'])) { } +if (isset($_POST['bulk_edit_client_industry'])) { + + enforceUserPermission('module_client', 2); + + validateCSRFToken($_POST['csrf_token']); + + $industry = sanitizeInput($_POST['bulk_industry']); + + if (isset($_POST['client_ids'])) { + + $count = count($_POST['client_ids']); + + foreach($_POST['client_ids'] as $client_id) { + $client_id = intval($client_id); + + $sql = mysqli_query($mysqli,"SELECT client_name FROM clients WHERE client_id = $client_id"); + $row = mysqli_fetch_array($sql); + $client_name = sanitizeInput($row['client_name']); + + mysqli_query($mysqli,"UPDATE clients SET client_type = '$industry' WHERE client_id = $client_id"); + + //Logging + logAction("Client", "Edit", "$session_name set Industry to $industry for $client_name", $client_id); + + } + + // Bulk Log + logAction("Client", "Bulk Edit", "$session_name set the department $industry for $count client(s)", $client_id); + + $_SESSION['alert_message'] = "Set the Industry to $industry for $count clients"; + } + + header("Location: " . $_SERVER["HTTP_REFERER"]); + +} + +if (isset($_POST['bulk_edit_client_referral'])) { + + enforceUserPermission('module_client', 2); + + validateCSRFToken($_POST['csrf_token']); + + $referral = sanitizeInput($_POST['bulk_referral']); + + if (isset($_POST['client_ids'])) { + + $count = count($_POST['client_ids']); + + foreach($_POST['client_ids'] as $client_id) { + $client_id = intval($client_id); + + $sql = mysqli_query($mysqli,"SELECT client_name FROM clients WHERE client_id = $client_id"); + $row = mysqli_fetch_array($sql); + $client_name = sanitizeInput($row['client_name']); + + mysqli_query($mysqli,"UPDATE clients SET client_referral = '$referral' WHERE client_id = $client_id"); + + //Logging + logAction("Client", "Edit", "$session_name set Referral to $referral for $client_name", $client_id); + + } + + // Bulk Log + logAction("Client", "Bulk Edit", "$session_name set the referral $referral for $count client(s)", $client_id); + + $_SESSION['alert_message'] = "Set the Referral to $referral for $count clients"; + } + + header("Location: " . $_SERVER["HTTP_REFERER"]); + +} + +if (isset($_POST['bulk_edit_client_hourly_rate'])) { + + enforceUserPermission('module_client', 2); + + validateCSRFToken($_POST['csrf_token']); + + $rate = floatval($_POST['bulk_rate']); + + if (isset($_POST['client_ids'])) { + + $count = count($_POST['client_ids']); + + foreach($_POST['client_ids'] as $client_id) { + $client_id = intval($client_id); + + $sql = mysqli_query($mysqli,"SELECT client_name FROM clients WHERE client_id = $client_id"); + $row = mysqli_fetch_array($sql); + $client_name = sanitizeInput($row['client_name']); + + mysqli_query($mysqli,"UPDATE clients SET client_rate = '$rate' WHERE client_id = $client_id"); + + //Logging + logAction("Client", "Edit", "$session_name set Hourly Rate to" . numfmt_format_currency($currency_format, $rate, $session_company_currency) . "for $client_name", $client_id); + + } + + // Bulk Log + logAction("Client", "Bulk Edit", "$session_name set the hourly rate" . numfmt_format_currency($currency_format, $rate, $session_company_currency) . "for $count client(s)", $client_id); + + $_SESSION['alert_message'] = "Set the Hourly Rate to " . numfmt_format_currency($currency_format, $rate, $session_company_currency) . " for $count client(s)"; + } + + header("Location: " . $_SERVER["HTTP_REFERER"]); + +} + +if (isset($_POST['bulk_assign_client_tags'])) { + + enforceUserPermission('module_client', 2); + + validateCSRFToken($_POST['csrf_token']); + + if (isset($_POST['client_ids'])) { + + $count = count($_POST['client_ids']); + + foreach($_POST['client_ids'] as $client_id) { + $client_id = intval($client_id); + + $sql = mysqli_query($mysqli,"SELECT client_name FROM clients WHERE client_id = $client_id"); + $row = mysqli_fetch_array($sql); + $client_name = sanitizeInput($row['client_name']); + + if ($_POST['bulk_remove_tags']) { + mysqli_query($mysqli, "DELETE FROM client_tags WHERE client_id = $client_id"); + } + + if (isset($_POST['bulk_tags'])) { + foreach($_POST['bulk_tags'] as $tag) { + $tag = intval($tag); + + $sql = mysqli_query($mysqli,"SELECT * FROM client_tags WHERE client_id = $client_id AND tag_id = $tag"); + if (mysqli_num_rows($sql) == 0) { + mysqli_query($mysqli, "INSERT INTO client_tags SET client_id = $client_id, tag_id = $tag"); + } + } + } + + logAction("Client", "Edit", "$session_name added tags to $client_name", $client_id, $client_id); + + } + + logAction("Client", "Bulk Edit", "$session_name added tags for $count clients", $client_id); + + $_SESSION['alert_message'] = "Assigned tags for $count clients"; + } + + header("Location: " . $_SERVER["HTTP_REFERER"]); + +} + +if (isset($_POST['bulk_send_client_email']) && isset($_POST['client_ids'])) { + + $client_ids = array_map('intval', $_POST['client_ids']); + $count = count($client_ids); + + // Email metadata + $mail_from = sanitizeInput($_POST['mail_from']); + $mail_from_name = sanitizeInput($_POST['mail_from_name']); + $subject = sanitizeInput($_POST['subject']); + $body = mysqli_real_escape_string($mysqli, $_POST['body']); + $queued_at = sanitizeInput($_POST['queued_at']); + + // Build contact type filters + $filters = []; + + if (!empty($_POST['primary_contacts'])) { + $filters[] = "contact_primary = 1"; + } + if (!empty($_POST['important_contacts'])) { + $filters[] = "contact_important = 1"; + } + if (!empty($_POST['billing_contacts'])) { + $filters[] = "contact_billing = 1"; + } + if (!empty($_POST['technical_contacts'])) { + $filters[] = "contact_technical = 1"; + } + + $contact_filter_query = ''; + if (!empty($filters)) { + $contact_filter_query = ' AND (' . implode(' OR ', $filters) . ')'; + } + + // Prepare client ID list for SQL + $client_ids_str = implode(',', $client_ids); + + // SQL to fetch matching contacts + $sql = "SELECT * FROM contacts + WHERE contact_client_id IN ($client_ids_str) + $contact_filter_query"; + + $result = mysqli_query($mysqli, $sql); + + $data = []; + $unique_contacts = []; + + while ($row = mysqli_fetch_array($result)) { + $contact_email = sanitizeInput($row['contact_email']); + + // Skip if email is missing or invalid + if (empty($contact_email) || !filter_var($contact_email, FILTER_VALIDATE_EMAIL)) { + continue; + } + + // Skip duplicates (same email) + if (isset($unique_contacts[$contact_email])) { + continue; + } + $unique_contacts[$contact_email] = true; + + $contact_name = sanitizeInput($row['contact_name']); + + $data[] = [ + 'from' => $mail_from, + 'from_name' => $mail_from_name, + 'recipient' => $contact_email, + 'recipient_name' => $contact_name, + 'subject' => $subject, + 'body' => $body, + 'queued_at' => $queued_at + ]; + } + + if (!empty($data)) { + addToMailQueue($data); + logAction("Bulk Mail", "Send", "$session_name sent " . count($data) . " messages via bulk mail"); + $_SESSION['alert_message'] = "" . count($data) . " messages queued"; + } else { + $_SESSION['alert_message'] = "No valid contacts found to queue emails."; + } + + // Redirect back + header("Location: " . $_SERVER["HTTP_REFERER"]); + exit; +} + +if (isset($_POST['bulk_archive_clients'])) { + + enforceUserPermission('module_client', 2); + + validateCSRFToken($_POST['csrf_token']); + + if (isset($_POST['client_ids'])) { + + $count = 0; + + foreach ($_POST['client_ids'] as $client_id) { + + $client_id = intval($client_id); + + $sql = mysqli_query($mysqli,"SELECT client_name FROM clients WHERE client_id = $client_id"); + $row = mysqli_fetch_array($sql); + $client_name = sanitizeInput($row['client_name']); + + mysqli_query($mysqli,"UPDATE clients SET client_archived_at = NOW() WHERE client_id = $client_id"); + + logAction("Client", "Archive", "$session_name archived $client_name", $client_id); + + $count++; + + } + + // Bulk Logging + logAction("Client", "Bulk Archive", "$session_name archived $count clients", $client_id); + + $_SESSION['alert_type'] = "error"; + $_SESSION['alert_message'] = "Archived $count client(s)"; + + } + + header("Location: " . $_SERVER["HTTP_REFERER"]); +} + +if (isset($_POST['bulk_unarchive_clients'])) { + + enforceUserPermission('module_client', 2); + + validateCSRFToken($_POST['csrf_token']); + + if (isset($_POST['client_ids'])) { + + $count = count($_POST['client_ids']); + + foreach ($_POST['client_ids'] as $client_id) { + + $client_id = intval($client_id); + + $sql = mysqli_query($mysqli,"SELECT client_name FROM clients WHERE client_id = $client_id"); + $row = mysqli_fetch_array($sql); + $client_name = sanitizeInput($row['client_name']); + + mysqli_query($mysqli,"UPDATE clients SET client_archived_at = NULL WHERE client_id = $client_id"); + + // Individual Contact logging + logAction("client", "Restore", "$session_name restored $client_name", $client_id); + + } + + // Bulk Logging + logAction("Client", "Bulk Restore", "$session_name restored $count client(s)", $client_id); + + $_SESSION['alert_message'] = "You restored $count client(s)"; + + } + + header("Location: " . $_SERVER["HTTP_REFERER"]); +} + if (isset($_POST["export_client_pdf"])) { // Enforce permissions diff --git a/post/user/contact.php b/post/user/contact.php index 14975611..5a0c43b7 100644 --- a/post/user/contact.php +++ b/post/user/contact.php @@ -661,14 +661,14 @@ if (isset($_POST['bulk_unarchive_contacts'])) { mysqli_query($mysqli,"UPDATE contacts SET contact_archived_at = NULL WHERE contact_id = $contact_id"); // Individual Contact logging - logAction("Contact", "Unarchive", "$session_name unarchived $contact_name", $client_id, $contact_id); + logAction("Contact", "Restore", "$session_name restored $contact_name", $client_id, $contact_id); } // Bulk Logging - logAction("Contact", "Bulk Unarchive", "$session_name Unarchived $count contacts", $client_id); + logAction("Contact", "Bulk Restore", "$session_name restored $count contacts", $client_id); - $_SESSION['alert_message'] = "You unarchived $count contact(s)"; + $_SESSION['alert_message'] = "Restored $count contact(s)"; }