From 129fbd300818648e28a6fb5bd24009e5bbe66505 Mon Sep 17 00:00:00 2001 From: o-psi Date: Tue, 27 Feb 2024 17:29:05 +0000 Subject: [PATCH 01/32] Credits --- credits.php | 169 +++++++++++++++++++++ db.sql | 23 +++ functions.php | 23 +++ inc_all_client.php | 14 +- payments.php | 197 +++++++++++++++--------- post/invoice.php | 364 ++++++++++++++++++++++++++++++++------------- 6 files changed, 602 insertions(+), 188 deletions(-) create mode 100644 credits.php diff --git a/credits.php b/credits.php new file mode 100644 index 000000000..1da307d0c --- /dev/null +++ b/credits.php @@ -0,0 +1,169 @@ + + +
+
+

Credits

+
+ +
+
+
+
+
+ +
+ + +
+
+
+
+
" + id="advancedFilter"> +
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+
+ + "> + + + + + + + + + + + + + + + + + + + + + + + + +
Client NameAccount + Name"> + Amount + Date + Reference + Actions
"> + + + + +
+
+
+
+ + \ No newline at end of file diff --git a/db.sql b/db.sql index 16b440e23..595809781 100644 --- a/db.sql +++ b/db.sql @@ -416,6 +416,29 @@ CREATE TABLE `contacts` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `credits` +-- + +DROP TABLE IF EXISTS `credits`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `credits` ( + `credit_id` int(11) NOT NULL AUTO_INCREMENT, + `credit_amount` decimal(15,2) NOT NULL DEFAULT 0.00, + `credit_currency_code` varchar(200) NOT NULL, + `credit_date` date NOT NULL, + `credit_reference` varchar(200) DEFAULT NULL, + `credit_payment_method` varchar(200) DEFAULT NULL, + `credit_created_at` datetime NOT NULL DEFAULT current_timestamp(), + `credit_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(), + `credit_archived_at` datetime DEFAULT NULL, + `credit_client_id` int(11) NOT NULL DEFAULT 0, + `credit_payment_id` int(11) NOT NULL DEFAULT 0, + `credit_account_id` int(11) DEFAULT NULL, + PRIMARY KEY (`credit_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + -- -- Table structure for table `custom_fields` -- diff --git a/functions.php b/functions.php index bf46ea319..ace0aec9c 100644 --- a/functions.php +++ b/functions.php @@ -1133,4 +1133,27 @@ function createiCalStrCancel($originaliCalStr) { // Return the modified iCal string return $cal_event->export(); } + +function getClientBalance($mysqli, $client_id, $credits = false) { + //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 NOT LIKE 'Draft' AND invoice_status NOT LIKE 'Cancelled'"); + $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']); + + if ($credits) { + $sql_credits = mysqli_query($mysqli, "SELECT SUM(credit_amount) AS credit_amounts FROM credits WHERE credit_client_id = $client_id"); + $row = mysqli_fetch_array($sql_credits); + $credit_amounts = floatval($row['credit_amounts']); + + return $invoice_amounts - ($amount_paid + $credit_amounts); + } else { + return $invoice_amounts - $amount_paid; + } +} diff --git a/inc_all_client.php b/inc_all_client.php index 8e26cfd05..e5d693be4 100644 --- a/inc_all_client.php +++ b/inc_all_client.php @@ -86,18 +86,8 @@ } $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 NOT LIKE 'Draft' AND invoice_status NOT LIKE 'Cancelled'"); - $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; + // Get Balance + $balance = getClientBalance($mysqli, $client_id, $credits = true); //Get Monthly Recurring Total $sql_recurring_monthly_total = mysqli_query($mysqli, "SELECT SUM(recurring_amount) AS recurring_monthly_total FROM recurring WHERE recurring_status = 1 AND recurring_frequency = 'month' AND recurring_client_id = $client_id"); diff --git a/payments.php b/payments.php index 96f36c6ef..2d2392ebf 100644 --- a/payments.php +++ b/payments.php @@ -23,75 +23,122 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); +// Credits SQL +$sql_credits = mysqli_query( + $mysqli, + "SELECT * FROM credits + WHERE credit_archived_at IS NULL" +); + +$credits_num_rows = mysqli_num_rows($sql_credits); ?> -
-
-

Payments

-
+
+
+

Payments

+
-
-
-
-
-
- -
- - -
+
+ +
+
+
+ +
+ +
-
" id="advancedFilter"> -
-
-
- - -
+ +
+
" + id="advancedFilter"> +
+
+
+ +
-
-
- - -
+
+
+
+ +
-
-
- - -
+
+
+
+ +
- -
-
- - "> + + +
+
+
+ "> - - - - - - - - + + + + + + + + - - + + - - - - - - - - - - + + + + + + + + + + - -
Payment DateInvoice DateInvoiceClientAmountPayment MethodReferenceAccountPayment + DateInvoice + DateInvoice + Client + Amount + Payment + MethodReference + Account +
+ + + +
-
- + +
+
+
+ ?> \ No newline at end of file diff --git a/post/invoice.php b/post/invoice.php index 0ffedaf03..69647197d 100644 --- a/post/invoice.php +++ b/post/invoice.php @@ -609,155 +609,171 @@ //Check to see if amount entered is greater than the balance of the invoice if ($amount > $balance) { - $_SESSION['alert_message'] = "Payment is more than the balance"; - header("Location: " . $_SERVER["HTTP_REFERER"]); + $payment_is_credit = true; + + // Calculate the overpayment amount + $credit_amount = $amount - $balance; + + // Set the payment amount to the invoice balance + $amount = $balance; } else { - mysqli_query($mysqli,"INSERT INTO payments SET payment_date = '$date', payment_amount = $amount, payment_currency_code = '$currency_code', payment_account_id = $account, payment_method = '$payment_method', payment_reference = '$reference', payment_invoice_id = $invoice_id"); + $payment_is_credit = false; + } - // Get Payment ID for reference - $payment_id = mysqli_insert_id($mysqli); - //Add up all the payments for the invoice and get the total amount paid to the invoice - $sql_total_payments_amount = mysqli_query($mysqli,"SELECT SUM(payment_amount) AS payments_amount FROM payments WHERE payment_invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql_total_payments_amount); - $total_payments_amount = floatval($row['payments_amount']); + mysqli_query($mysqli,"INSERT INTO payments SET payment_date = '$date', payment_amount = $amount, payment_currency_code = '$currency_code', payment_account_id = $account, payment_method = '$payment_method', payment_reference = '$reference', payment_invoice_id = $invoice_id"); - //Get the invoice total - $sql = mysqli_query($mysqli,"SELECT * FROM invoices - LEFT JOIN clients ON invoice_client_id = client_id - LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1 - WHERE invoice_id = $invoice_id" - ); + // Get payment ID for reference + $payment_id = mysqli_insert_id($mysqli); - $row = mysqli_fetch_array($sql); - $invoice_amount = floatval($row['invoice_amount']); - $invoice_prefix = sanitizeInput($row['invoice_prefix']); - $invoice_number = intval($row['invoice_number']); - $invoice_url_key = sanitizeInput($row['invoice_url_key']); - $invoice_currency_code = sanitizeInput($row['invoice_currency_code']); - $client_id = intval($row['client_id']); - $client_name = sanitizeInput($row['client_name']); - $contact_name = sanitizeInput($row['contact_name']); - $contact_email = sanitizeInput($row['contact_email']); - $contact_phone = sanitizeInput(formatPhoneNumber($row['contact_phone'])); - $contact_extension = preg_replace("/[^0-9]/", '',$row['contact_extension']); - $contact_mobile = sanitizeInput(formatPhoneNumber($row['contact_mobile'])); + if($payment_is_credit) { + //Create a credit for the overpayment + mysqli_query($mysqli,"INSERT INTO credits SET credit_amount = $credit_amount, credit_currency_code = '$currency_code', credit_date = '$date', credit_reference = 'Overpayment: $reference', credit_client_id = (SELECT invoice_client_id FROM invoices WHERE invoice_id = $invoice_id), credit_payment_id = $payment_id, credit_account_id = $account"); + // Get credit ID for reference + $credit_id = mysqli_insert_id($mysqli); + } - $sql = mysqli_query($mysqli,"SELECT * FROM companies WHERE company_id = 1"); - $row = mysqli_fetch_array($sql); + //Add up all the payments for the invoice and get the total amount paid to the invoice + $sql_total_payments_amount = mysqli_query($mysqli,"SELECT SUM(payment_amount) AS payments_amount FROM payments WHERE payment_invoice_id = $invoice_id"); + $row = mysqli_fetch_array($sql_total_payments_amount); + $total_payments_amount = floatval($row['payments_amount']); - $company_name = sanitizeInput($row['company_name']); - $company_country = sanitizeInput($row['company_country']); - $company_address = sanitizeInput($row['company_address']); - $company_city = sanitizeInput($row['company_city']); - $company_state = sanitizeInput($row['company_state']); - $company_zip = sanitizeInput($row['company_zip']); - $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); - $company_email = sanitizeInput($row['company_email']); - $company_website = sanitizeInput($row['company_website']); - $company_logo = sanitizeInput($row['company_logo']); + //Get the invoice total + $sql = mysqli_query($mysqli,"SELECT * FROM invoices + LEFT JOIN clients ON invoice_client_id = client_id + LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1 + WHERE invoice_id = $invoice_id" + ); - // Sanitize Config vars from get_settings.php - $config_invoice_from_name = sanitizeInput($config_invoice_from_name); - $config_invoice_from_email = sanitizeInput($config_invoice_from_email); + $row = mysqli_fetch_array($sql); + $invoice_amount = floatval($row['invoice_amount']); + $invoice_prefix = sanitizeInput($row['invoice_prefix']); + $invoice_number = intval($row['invoice_number']); + $invoice_url_key = sanitizeInput($row['invoice_url_key']); + $invoice_currency_code = sanitizeInput($row['invoice_currency_code']); + $client_id = intval($row['client_id']); + $client_name = sanitizeInput($row['client_name']); + $contact_name = sanitizeInput($row['contact_name']); + $contact_email = sanitizeInput($row['contact_email']); + $contact_phone = sanitizeInput(formatPhoneNumber($row['contact_phone'])); + $contact_extension = preg_replace("/[^0-9]/", '',$row['contact_extension']); + $contact_mobile = sanitizeInput(formatPhoneNumber($row['contact_mobile'])); - //Calculate the Invoice balance - $invoice_balance = $invoice_amount - $total_payments_amount; + $sql = mysqli_query($mysqli,"SELECT * FROM companies WHERE company_id = 1"); + $row = mysqli_fetch_array($sql); - $email_data = []; + $company_name = sanitizeInput($row['company_name']); + $company_country = sanitizeInput($row['company_country']); + $company_address = sanitizeInput($row['company_address']); + $company_city = sanitizeInput($row['company_city']); + $company_state = sanitizeInput($row['company_state']); + $company_zip = sanitizeInput($row['company_zip']); + $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); + $company_email = sanitizeInput($row['company_email']); + $company_website = sanitizeInput($row['company_website']); + $company_logo = sanitizeInput($row['company_logo']); - //Determine if invoice has been paid then set the status accordingly - if ($invoice_balance == 0) { + // Sanitize Config vars from get_settings.php + $config_invoice_from_name = sanitizeInput($config_invoice_from_name); + $config_invoice_from_email = sanitizeInput($config_invoice_from_email); - $invoice_status = "Paid"; + //Calculate the Invoice balance + $invoice_balance = $invoice_amount - $total_payments_amount; - if ($email_receipt == 1) { + $email_data = []; - $subject = "Payment Received - Invoice $invoice_prefix$invoice_number"; - $body = "Hello $contact_name,

We have received your payment in the amount of " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . " for invoice $invoice_prefix$invoice_number. Please keep this email as a receipt for your records.

Amount: " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . "
Balance: " . numfmt_format_currency($currency_format, $invoice_balance, $invoice_currency_code) . "

Thank you for your business!


--
$company_name - Billing Department
$config_invoice_from_email
$company_phone"; + //Determine if invoice has been paid then set the status accordingly + if ($invoice_balance == 0) { - // Queue Mail - $email = [ - 'from' => $config_invoice_from_email, - 'from_name' => $config_invoice_from_name, - 'recipient' => $contact_email, - 'recipient_name' => $contact_name, - 'subject' => $subject, - 'body' => $body - ]; + $invoice_status = "Paid"; - $email_data[] = $email; + if ($email_receipt == 1) { - // Get Email ID for reference - $email_id = mysqli_insert_id($mysqli); + $subject = "Payment Received - Invoice $invoice_prefix$invoice_number"; + $body = "Hello $contact_name,

We have received your payment in the amount of " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . " for invoice $invoice_prefix$invoice_number. Please keep this email as a receipt for your records.

Amount: " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . "
Balance: " . numfmt_format_currency($currency_format, $invoice_balance, $invoice_currency_code) . "

Thank you for your business!


--
$company_name - Billing Department
$config_invoice_from_email
$company_phone"; - // Email Logging + // Queue Mail + $email = [ + 'from' => $config_invoice_from_email, + 'from_name' => $config_invoice_from_name, + 'recipient' => $contact_email, + 'recipient_name' => $contact_name, + 'subject' => $subject, + 'body' => $body + ]; - $_SESSION['alert_message'] = "Email receipt sent "; + $email_data[] = $email; - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Emailed Receipt!', history_invoice_id = $invoice_id"); + // Get Email ID for reference + $email_id = mysqli_insert_id($mysqli); - } + // Email Logging - } else { + $_SESSION['alert_message'] = "Email receipt sent "; - $invoice_status = "Partial"; + mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Emailed Receipt!', history_invoice_id = $invoice_id"); - if ($email_receipt == 1) { + } + + } else { + $invoice_status = "Partial"; - $subject = "Partial Payment Recieved - Invoice $invoice_prefix$invoice_number"; - $body = "Hello $contact_name,

We have recieved partial payment in the amount of " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . " and it has been applied to invoice $invoice_prefix$invoice_number. Please keep this email as a receipt for your records.

Amount: " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . "
Balance: " . numfmt_format_currency($currency_format, $invoice_balance, $invoice_currency_code) . "

Thank you for your business!


~
$company_name - Billing
$config_invoice_from_email
$company_phone"; + if ($email_receipt == 1) { - // Queue Mail - $email = [ - 'from' => $config_invoice_from_email, - 'from_name' => $config_invoice_from_name, - 'recipient' => $contact_email, - 'recipient_name' => $contact_name, - 'subject' => $subject, - 'body' => $body - ]; - $email_data[] = $email; + $subject = "Partial Payment Recieved - Invoice $invoice_prefix$invoice_number"; + $body = "Hello $contact_name,

We have recieved partial payment in the amount of " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . " and it has been applied to invoice $invoice_prefix$invoice_number. Please keep this email as a receipt for your records.

Amount: " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . "
Balance: " . numfmt_format_currency($currency_format, $invoice_balance, $invoice_currency_code) . "

Thank you for your business!


~
$company_name - Billing
$config_invoice_from_email
$company_phone"; - // Get Email ID for reference - $email_id = mysqli_insert_id($mysqli); + // Queue Mail + $email = [ + 'from' => $config_invoice_from_email, + 'from_name' => $config_invoice_from_name, + 'recipient' => $contact_email, + 'recipient_name' => $contact_name, + 'subject' => $subject, + 'body' => $body + ]; - // Email Logging + $email_data[] = $email; - $_SESSION['alert_message'] .= "Email receipt sent "; + // Get Email ID for reference + $email_id = mysqli_insert_id($mysqli); - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Payment Receipt sent to mail queue ID: $email_id!', history_invoice_id = $invoice_id"); + // Email Logging - } + $_SESSION['alert_message'] .= "Email receipt sent "; - } + mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Payment Receipt sent to mail queue ID: $email_id!', history_invoice_id = $invoice_id"); - // Add emails to queue - if (!empty($email)) { - addToMailQueue($mysqli, $email_data); } - //Update Invoice Status - mysqli_query($mysqli,"UPDATE invoices SET invoice_status = '$invoice_status' WHERE invoice_id = $invoice_id"); + } - //Add Payment to History - mysqli_query($mysqli,"INSERT INTO history SET history_status = '$invoice_status', history_description = 'Payment added', history_invoice_id = $invoice_id"); + // Add emails to queue + if (!empty($email)) { + addToMailQueue($mysqli, $email_data); + } - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Create', log_description = '$payment_amount', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); + //Update Invoice Status + mysqli_query($mysqli,"UPDATE invoices SET invoice_status = '$invoice_status' WHERE invoice_id = $invoice_id"); - if ($email_receipt == 1) { - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Email', log_description = 'Payment receipt for invoice $invoice_prefix$invoice_number queued to $contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); - } + //Add Payment to History + mysqli_query($mysqli,"INSERT INTO history SET history_status = '$invoice_status', history_description = 'Payment added', history_invoice_id = $invoice_id"); - $_SESSION['alert_message'] .= "Payment added"; + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Create', log_description = '$payment_amount', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); - header("Location: " . $_SERVER["HTTP_REFERER"]); + if ($email_receipt == 1) { + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Email', log_description = 'Payment receipt for invoice $invoice_prefix$invoice_number queued to $contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); } + + $_SESSION['alert_message'] .= "Payment added"; + + header("Location: " . $_SERVER["HTTP_REFERER"]); } + if (isset($_POST['add_bulk_payment'])) { $client_id = intval($_POST['client_id']); @@ -1422,3 +1438,143 @@ header("Location: post.php?add_ticket_to_invoice=$invoice_id"); } + +if (isset($_POST['apply_credit_id'])) { + + $credit_id = intval($_POST['apply_credit_id']); + + $credit_sql = mysqli_query($mysqli,"SELECT * FROM credits WHERE credit_id = $credit_id"); + $credit_row = mysqli_fetch_array($credit_sql); + + $client_id = intval($credit_row['credit_client_id']); + $credit_amount = floatval($credit_row['credit_amount']); + + $client_balance = getClientBalance($mysqli, $client_id); + + if ($client_balance < $credit_amount) { + //create a new credit for the remaining amount + $new_credit_amount = $credit_amount - $client_balance; + $new_credit_query = "INSERT INTO credits (credit_date, credit_amount, credit_currency_code, credit_client_id) VALUES (CURDATE(), $new_credit_amount, '{$credit_row['credit_currency_code']}', $client_id)"; + mysqli_query($mysqli, $new_credit_query); + $new_credit_id = mysqli_insert_id($mysqli); + } + + // Apply payments similar to add bulk payment + + // Get Invoices + $sql_invoices = "SELECT * FROM invoices + WHERE invoice_status != 'Draft' + AND invoice_status != 'Paid' + AND invoice_status != 'Cancelled' + AND invoice_client_id = $client_id + ORDER BY invoice_number ASC"; + $result_invoices = mysqli_query($mysqli, $sql_invoices); + + // Loop Through Each Invoice + while ($row = mysqli_fetch_array($result_invoices)) { + $invoice_id = intval($row['invoice_id']); + $invoice_prefix = sanitizeInput($row['invoice_prefix']); + $invoice_number = intval($row['invoice_number']); + $invoice_amount = floatval($row['invoice_amount']); + $invoice_url_key = sanitizeInput($row['invoice_url_key']); + $invoice_balance_query = "SELECT SUM(payment_amount) AS amount_paid FROM payments WHERE payment_invoice_id = $invoice_id"; + $result_amount_paid = mysqli_query($mysqli, $invoice_balance_query); + $row_amount_paid = mysqli_fetch_array($result_amount_paid); + $amount_paid = floatval($row_amount_paid['amount_paid']); + $invoice_balance = $invoice_amount - $amount_paid; + + if ($credit_amount <= 0) { + break; // Exit the loop if no payment amount is left + } + + if ($credit_amount >= $invoice_balance) { + $payment_amount = $invoice_balance; + $invoice_status = "Paid"; + } else { + $payment_amount = $credit_amount; + $invoice_status = "Partial"; + } + + // Subtract the payment amount from the bulk payment amount + $credit_amount -= $payment_amount; + + // Get Invoice Remain Balance + $remaining_invoice_balance = $invoice_balance - $payment_amount; + + // Add Payment + $payment_query = "INSERT INTO payments (payment_date, payment_amount, payment_currency_code, payment_account_id, payment_method, payment_reference, payment_invoice_id) VALUES ('{$date}', {$payment_amount}, '{$currency_code}', {$account}, '{$payment_method}', '{$reference}', {$invoice_id})"; + mysqli_query($mysqli, $payment_query); + $payment_id = mysqli_insert_id($mysqli); + + // Update Invoice Status + $update_invoice_query = "UPDATE invoices SET invoice_status = '{$invoice_status}' WHERE invoice_id = {$invoice_id}"; + mysqli_query($mysqli, $update_invoice_query); + + // Add Payment to History + $history_description = "Payment added"; + $add_history_query = "INSERT INTO history (history_status, history_description, history_invoice_id) VALUES ('{$invoice_status}', '{$history_description}', {$invoice_id})"; + mysqli_query($mysqli, $add_history_query); + + // Add to Email Body Invoice Portion + + $email_body_invoices .= "
Invoice $invoice_prefix$invoice_number - Outstanding Amount: " . numfmt_format_currency($currency_format, $invoice_balance, $currency_code) . " - Payment Applied: " . numfmt_format_currency($currency_format, $payment_amount, $currency_code) . " - New Balance: " . numfmt_format_currency($currency_format, $remaining_invoice_balance, $currency_code); + + } // End Invoice Loop + + // Send Email + if ($email_receipt == 1) { + + // Get Client / Contact Info + $sql_client = mysqli_query($mysqli,"SELECT * FROM clients + LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id + AND contact_primary = 1 + WHERE client_id = $client_id" + ); + + $row = mysqli_fetch_array($sql_client); + $client_name = sanitizeInput($row['client_name']); + $contact_name = sanitizeInput($row['contact_name']); + $contact_email = sanitizeInput($row['contact_email']); + + $sql_company = mysqli_query($mysqli,"SELECT company_name, company_phone FROM companies WHERE company_id = 1"); + $row = mysqli_fetch_array($sql_company); + + $company_name = sanitizeInput($row['company_name']); + $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); + + // Sanitize Config vars from get_settings.php + $config_invoice_from_name = sanitizeInput($config_invoice_from_name); + $config_invoice_from_email = sanitizeInput($config_invoice_from_email); + + $subject = "Payment Received - Multiple Invoices"; + $body = "Hello $contact_name,

Thank you for your payment of " . numfmt_format_currency($currency_format, $bulk_payment_amount_static, $currency_code) . " We\'ve applied your payment to the following invoices, updating their balances accordingly:

$email_body_invoices


We appreciate your continued business!

Sincerely,
$company_name - Billing
$config_invoice_from_email
$company_phone"; + + // Queue Mail + mysqli_query($mysqli, "INSERT INTO email_queue SET email_recipient = '$contact_email', email_recipient_name = '$contact_name', email_from = '$config_invoice_from_email', email_from_name = '$config_invoice_from_name', email_subject = '$subject', email_content = '$body'"); + + // Get Email ID for reference + $email_id = mysqli_insert_id($mysqli); + + // Email Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Email', log_description = 'Bulk Payment receipt for multiple Invoices queued to $contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session"); + + $_SESSION['alert_message'] .= "Email receipt sent and "; + + } // End Email + + // Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Create', log_description = 'Bulk Payment of $bulk_payment_amount_static', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id"); + + $_SESSION['alert_message'] .= "Bulk Payment added"; + + header("Location: " . $_SERVER["HTTP_REFERER"]); +} + +if (isset($_POST['delete_credit_id'])) { + $credit_id = intval($_POST['delete_credit_id']); + + mysqli_query($mysqli,"DELETE FROM credits WHERE credit_id = $credit_id"); + + $_SESSION['alert_message'] = "Credit deleted"; + header("Location: " . $_SERVER["HTTP_REFERER"]); +} \ No newline at end of file From 02ebe848bd975aa3b7ca8642412618bab4db9540 Mon Sep 17 00:00:00 2001 From: o-psi Date: Tue, 27 Feb 2024 19:47:24 +0000 Subject: [PATCH 02/32] progress --- credits.php | 6 +++--- inc_all_client.php | 5 +++++ post/invoice.php | 13 ++++++------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/credits.php b/credits.php index 1da307d0c..49e3ea4e4 100644 --- a/credits.php +++ b/credits.php @@ -142,7 +142,7 @@ ?> - + "> @@ -151,9 +151,9 @@ - - diff --git a/inc_all_client.php b/inc_all_client.php index e5d693be4..b4b0d3b44 100644 --- a/inc_all_client.php +++ b/inc_all_client.php @@ -89,6 +89,11 @@ // Get Balance $balance = getClientBalance($mysqli, $client_id, $credits = true); + // Get Amount Paid + $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']); + //Get Monthly Recurring Total $sql_recurring_monthly_total = mysqli_query($mysqli, "SELECT SUM(recurring_amount) AS recurring_monthly_total FROM recurring WHERE recurring_status = 1 AND recurring_frequency = 'month' AND recurring_client_id = $client_id"); $row = mysqli_fetch_array($sql_recurring_monthly_total); diff --git a/post/invoice.php b/post/invoice.php index 69647197d..e2dfa7f17 100644 --- a/post/invoice.php +++ b/post/invoice.php @@ -91,7 +91,7 @@ //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 = $invoice_number, invoice_scope = '$invoice_scope', invoice_date = '$date', invoice_due = DATE_ADD('$date', INTERVAL $client_net_terms day), invoice_category_id = $category_id, invoice_status = 'Draft', invoice_amount = $invoice_amount, invoice_currency_code = '$invoice_currency_code', invoice_note = '$invoice_note', invoice_url_key = '$url_key', invoice_client_id = $client_id") or die(mysql_error()); + mysqli_query($mysqli,"INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $invoice_number, invoice_scope = '$invoice_scope', invoice_date = '$date', invoice_due = DATE_ADD('$date', INTERVAL $client_net_terms day), invoice_category_id = $category_id, invoice_status = 'Draft', invoice_amount = $invoice_amount, invoice_currency_code = '$invoice_currency_code', invoice_note = '$invoice_note', invoice_url_key = '$url_key', invoice_client_id = $client_id"); $new_invoice_id = mysqli_insert_id($mysqli); @@ -1439,9 +1439,8 @@ header("Location: post.php?add_ticket_to_invoice=$invoice_id"); } -if (isset($_POST['apply_credit_id'])) { - - $credit_id = intval($_POST['apply_credit_id']); +if (isset($_GET['apply_credit_id'])) { + $credit_id = intval($_GET['apply_credit_id']); $credit_sql = mysqli_query($mysqli,"SELECT * FROM credits WHERE credit_id = $credit_id"); $credit_row = mysqli_fetch_array($credit_sql); @@ -1570,9 +1569,9 @@ header("Location: " . $_SERVER["HTTP_REFERER"]); } -if (isset($_POST['delete_credit_id'])) { - $credit_id = intval($_POST['delete_credit_id']); - +if (isset($_GET['delete_credit'])) { + $credit_id = intval($_GET['delete_credit']); + mysqli_query($mysqli,"DELETE FROM credits WHERE credit_id = $credit_id"); $_SESSION['alert_message'] = "Credit deleted"; From e2e36014c7b4ca00920a638bf1ca530ffd6a2b4a Mon Sep 17 00:00:00 2001 From: o-psi Date: Tue, 27 Feb 2024 20:48:25 +0000 Subject: [PATCH 03/32] Credits DB updates. --- credits.php | 40 ++++++++++++++++++++++++++++++++++++---- database_updates.php | 12 ++++++++++-- database_version.php | 2 +- post/invoice.php | 42 +++++++++++++++++++++++++++++------------- 4 files changed, 76 insertions(+), 20 deletions(-) diff --git a/credits.php b/credits.php index 49e3ea4e4..0f0cd6130 100644 --- a/credits.php +++ b/credits.php @@ -110,6 +110,10 @@ Reference + Origin + + Actions @@ -127,14 +131,41 @@ $credit_account_id = $row['credit_account_id']; // Get client name from DB - $clientQuery = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_id = $credit_client_id LIMIT 1"); + $clientQuery = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_id = $credit_client_id"); $client = mysqli_fetch_array($clientQuery); $client_name = $client['client_name']; // Get account name from DB - $accountQuery = mysqli_query($mysqli, "SELECT * FROM accounts WHERE account_id = $credit_account_id LIMIT 1"); - $account = mysqli_fetch_array($accountQuery); - $account_name = $account['account_name']; + if($credit_account_id != NULL) { + $accountQuery = mysqli_query($mysqli, "SELECT * FROM accounts WHERE account_id = $credit_account_id"); + $account = mysqli_fetch_array($accountQuery); + $account_name = $account['account_name']; + } else { + $account_name = "Unassigned"; + } + + // Get payment invoice and reference from DB + if($credit_payment_id != NULL) { + $paymentQuery = mysqli_query($mysqli, "SELECT * FROM payments WHERE payment_id = $credit_payment_id"); + $payment = mysqli_fetch_array($paymentQuery); + $payment_invoice = $payment['payment_invoice_id']; + $payment_reference = $payment['payment_reference']; + } else { + $payment_invoice = "Unassigned"; + $payment_reference = "Unassigned"; + } + + // Get invoice prefix and number from DB + if($payment_invoice != NULL) { + $invoiceQuery = mysqli_query($mysqli, "SELECT * FROM invoices WHERE invoice_id = $payment_invoice"); + $invoice = mysqli_fetch_array($invoiceQuery); + $invoice_prefix = $invoice['invoice_prefix']; + $invoice_number = $invoice['invoice_number']; + $payment_invoice_display = "Payment for: " . $invoice_prefix . $invoice_number; + } else { + $invoice_prefix = "Unassigned"; + $invoice_number = "Unassigned"; + } $credit_display_amount = numfmt_format_currency($currency_format, $credit_amount, $credit_currency_code); @@ -150,6 +181,7 @@ + diff --git a/database_updates.php b/database_updates.php index ad500c0eb..869e85d4e 100644 --- a/database_updates.php +++ b/database_updates.php @@ -1615,12 +1615,20 @@ mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.0.7'"); } - // if (CURRENT_DATABASE_VERSION == '1.0.7') { + if (CURRENT_DATABASE_VERSION == '1.0.7') { // // Insert queries here required to update to DB version 1.0.8 + mysqli_query($mysqli, "CREATE TABLE `credits ` (`credit_id` int(11) NOT NULL AUTO_INCREMENT,`credit_amount` decimal(15,2) NOT NULL,`credit_currency_code` varchar(200) NOT NULL,`credit_date` date NOT NULL,`credit_reference` text DEFAULT NULL,`credit_created_at` datetime NOT NULL DEFAULT current_timestamp(),`credit_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(),`credit_archived_at` datetime DEFAULT NULL, `credit_client_id` int(11) NOT NULL,`credit_payment_id` int(11) NOT NULL,`credit_account_id` int(11) NOT NULL, PRIMARY KEY (`credit_id`))"); // // Then, update the database to the next sequential version - // mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.0.8'"); + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.0.8'"); + } + + // if (CURRENT_DATABASE_VERSION == '1.0.8') { + // // Insert queries here required to update to DB version 1.0.9 + // // Then, update the database to the next sequential version + // mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.0.9'"); // } + } else { // Up-to-date } diff --git a/database_version.php b/database_version.php index c1dcafe27..eb2233d5a 100644 --- a/database_version.php +++ b/database_version.php @@ -5,5 +5,5 @@ * It is used in conjunction with database_updates.php */ -DEFINE("LATEST_DATABASE_VERSION", "1.0.7"); +DEFINE("LATEST_DATABASE_VERSION", "1.0.8"); diff --git a/post/invoice.php b/post/invoice.php index e2dfa7f17..95b6d773a 100644 --- a/post/invoice.php +++ b/post/invoice.php @@ -780,7 +780,7 @@ $date = sanitizeInput($_POST['date']); $bulk_payment_amount = floatval($_POST['amount']); $bulk_payment_amount_static = floatval($_POST['amount']); - $total_account_balance = floatval($_POST['balance']); + $total_client_balance = floatval($_POST['balance']); $account = intval($_POST['account']); $currency_code = sanitizeInput($_POST['currency_code']); $payment_method = sanitizeInput($_POST['payment_method']); @@ -788,11 +788,15 @@ $email_receipt = intval($_POST['email_receipt']); // Check if bulk_payment_amount exceeds total_account_balance - if ($bulk_payment_amount > $total_account_balance) { - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "Payment exceeds Client Balance."; - header("Location: " . $_SERVER["HTTP_REFERER"]); - exit; + if ($bulk_payment_amount > $total_client_balance) { + // Create new credit for the overpayment + $credit_amount = $bulk_payment_amount - $total_client_balance; + $bulk_payment_amount = $total_client_balance; + + // Add Credit + $credit_query = "INSERT INTO credits SET credit_amount = $credit_amount, credit_currency_code = '$currency_code', credit_date = '$date', credit_reference = 'Overpayment: $reference', credit_client_id = $client_id, credit_account_id = $account"; + mysqli_query($mysqli, $credit_query); + $credit_id = mysqli_insert_id($mysqli); } // Get Invoices @@ -1439,8 +1443,8 @@ header("Location: post.php?add_ticket_to_invoice=$invoice_id"); } -if (isset($_GET['apply_credit_id'])) { - $credit_id = intval($_GET['apply_credit_id']); +if (isset($_GET['apply_credit'])) { + $credit_id = intval($_GET['apply_credit']); $credit_sql = mysqli_query($mysqli,"SELECT * FROM credits WHERE credit_id = $credit_id"); $credit_row = mysqli_fetch_array($credit_sql); @@ -1456,7 +1460,9 @@ $new_credit_query = "INSERT INTO credits (credit_date, credit_amount, credit_currency_code, credit_client_id) VALUES (CURDATE(), $new_credit_amount, '{$credit_row['credit_currency_code']}', $client_id)"; mysqli_query($mysqli, $new_credit_query); $new_credit_id = mysqli_insert_id($mysqli); - } + } + // Delete the original credit + mysqli_query($mysqli,"DELETE FROM credits WHERE credit_id = $credit_id"); // Apply payments similar to add bulk payment @@ -1468,6 +1474,7 @@ AND invoice_client_id = $client_id ORDER BY invoice_number ASC"; $result_invoices = mysqli_query($mysqli, $sql_invoices); + $invoice_applied_count = 0; // Loop Through Each Invoice while ($row = mysqli_fetch_array($result_invoices)) { @@ -1482,8 +1489,13 @@ $amount_paid = floatval($row_amount_paid['amount_paid']); $invoice_balance = $invoice_amount - $amount_paid; + if ($credit_amount <= 0) { - break; // Exit the loop if no payment amount is left + break; // Exit the loop if no credit amount is left + } + + if ($invoice_balance <= 0) { + continue; // Skip the invoice if it's already paid } if ($credit_amount >= $invoice_balance) { @@ -1493,15 +1505,17 @@ $payment_amount = $credit_amount; $invoice_status = "Partial"; } + + $invoice_applied_count++; - // Subtract the payment amount from the bulk payment amount + // Subtract the payment amount from the credit amount $credit_amount -= $payment_amount; // Get Invoice Remain Balance $remaining_invoice_balance = $invoice_balance - $payment_amount; // Add Payment - $payment_query = "INSERT INTO payments (payment_date, payment_amount, payment_currency_code, payment_account_id, payment_method, payment_reference, payment_invoice_id) VALUES ('{$date}', {$payment_amount}, '{$currency_code}', {$account}, '{$payment_method}', '{$reference}', {$invoice_id})"; + $payment_query = "INSERT INTO payments SET payment_date = CURDATE(), payment_amount = $payment_amount, payment_invoice_id = $invoice_id, payment_account_id = 1, payment_currency_code = '{$credit_row['credit_currency_code']}', payment_reference = 'Credit Applied'"; mysqli_query($mysqli, $payment_query); $payment_id = mysqli_insert_id($mysqli); @@ -1520,6 +1534,8 @@ } // End Invoice Loop + + // Send Email if ($email_receipt == 1) { @@ -1564,7 +1580,7 @@ // Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Create', log_description = 'Bulk Payment of $bulk_payment_amount_static', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id"); - $_SESSION['alert_message'] .= "Bulk Payment added"; + $_SESSION['alert_message'] .= "Credit applied to $credit_applied_count invoices"; header("Location: " . $_SERVER["HTTP_REFERER"]); } From 0f077597619239fe7f24a3fc785ed8114b5ed989 Mon Sep 17 00:00:00 2001 From: o-psi Date: Wed, 6 Mar 2024 16:10:25 +0000 Subject: [PATCH 04/32] Update DB --- database_updates.php | 43 ++++++++++++++++++++++++++++++++++++----- database_version.php | 2 +- db.sql | 46 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 6 deletions(-) diff --git a/database_updates.php b/database_updates.php index f509e09a4..0c8b778d7 100644 --- a/database_updates.php +++ b/database_updates.php @@ -1636,11 +1636,44 @@ mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.1.0'"); } - // if (CURRENT_DATABASE_VERSION == '1.1.0') { - // // Insert queries here required to update to DB version 1.1.1 - // // Then, update the database to the next sequential version - // mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.1.1'"); - // } + if (CURRENT_DATABASE_VERSION == '1.1.0') { + // Insert queries here required to update to DB version 1.1.1 + mysqli_query($mysqli, "CREATE TABLE `inventory` ( + `inventory_id` int(11) NOT NULL AUTO_INCREMENT, + `inventory_product_id` int(11) NOT NULL, + `inventory_location_id` int(11) NOT NULL, + `inventory_client_id` int(11) DEFAULT NULL, + `inventory_ticket_id` int(11) DEFAULT NULL, + `inventory_notes` text DEFAULT NULL, + `inventory_quantity` int(11) NOT NULL DEFAULT 0, + `inventory_cost` decimal(15,2) NOT NULL DEFAULT 0.00, + `inventory_created_at` datetime NOT NULL DEFAULT current_timestamp(), + `inventory_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(), + `inventory_archived_at` datetime DEFAULT NULL, + PRIMARY KEY (`inventory_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;"); + + mysqli_query($mysqli, "CREATE TABLE `inventory_locations` ( + `inventory_location_id` int(11) NOT NULL AUTO_INCREMENT, + `inventory_location_name` varchar(200) NOT NULL, + `inventory_location_description` text DEFAULT NULL, + `inventory_location_address` varchar(200) DEFAULT NULL, + `inventory_location_city` varchar(200) DEFAULT NULL, + `inventory_location_state` varchar(200) DEFAULT NULL, + `inventory_location_zip` varchar(200) DEFAULT NULL, + `inventory_location_country` varchar(200) DEFAULT NULL, + `inventory_location_created_at` datetime NOT NULL DEFAULT current_timestamp(), + `inventory_location_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(), + `inventory_location_archived_at` datetime DEFAULT NULL, + `inventory_location_user_id` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`inventory_location_id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; + /*!40101 SET character_set_client = @saved_cs_client */; + "); + + // Then, update the database to the next sequential version + mysqli_query($mysqli, "UPDATE `settings` SET `config_current_database_version` = '1.1.1'"); + } } else { // Up-to-date diff --git a/database_version.php b/database_version.php index c33ccb54c..04a479379 100644 --- a/database_version.php +++ b/database_version.php @@ -5,5 +5,5 @@ * It is used in conjunction with database_updates.php */ -DEFINE("LATEST_DATABASE_VERSION", "1.1.0"); +DEFINE("LATEST_DATABASE_VERSION", "1.1.1"); diff --git a/db.sql b/db.sql index d0f5546ec..60ede72f8 100644 --- a/db.sql +++ b/db.sql @@ -662,6 +662,52 @@ CREATE TABLE `interfaces` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `inventory` +-- + +DROP TABLE IF EXISTS `inventory`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `inventory` ( + `inventory_id` int(11) NOT NULL AUTO_INCREMENT, + `inventory_product_id` int(11) NOT NULL, + `inventory_location_id` int(11) NOT NULL, + `inventory_client_id` int(11) DEFAULT NULL, + `inventory_ticket_id` int(11) DEFAULT NULL, + `inventory_notes` text DEFAULT NULL, + `inventory_quantity` int(11) NOT NULL DEFAULT 0, + `inventory_cost` decimal(15,2) NOT NULL DEFAULT 0.00, + `inventory_created_at` datetime NOT NULL DEFAULT current_timestamp(), + `inventory_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(), + `inventory_archived_at` datetime DEFAULT NULL, + PRIMARY KEY (`inventory_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table Structure for `inventory_locations` +-- +DROP TABLE IF EXISTS `inventory_locations`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `inventory_locations` ( + `inventory_location_id` int(11) NOT NULL AUTO_INCREMENT, + `inventory_location_name` varchar(200) NOT NULL, + `inventory_location_description` text DEFAULT NULL, + `inventory_location_address` varchar(200) DEFAULT NULL, + `inventory_location_city` varchar(200) DEFAULT NULL, + `inventory_location_state` varchar(200) DEFAULT NULL, + `inventory_location_zip` varchar(200) DEFAULT NULL, + `inventory_location_country` varchar(200) DEFAULT NULL, + `inventory_location_created_at` datetime NOT NULL DEFAULT current_timestamp(), + `inventory_location_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(), + `inventory_location_archived_at` datetime DEFAULT NULL, + `inventory_location_user_id` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`inventory_location_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; +/*!40101 SET character_set_client = @saved_cs_client */; + -- -- Table structure for table `invoice_items` -- From 09878515a9a05190ba885da953b797355cdc7b21 Mon Sep 17 00:00:00 2001 From: o-psi Date: Thu, 7 Mar 2024 16:10:08 +0000 Subject: [PATCH 05/32] Inventory --- database_updates.php | 1 + db.sql | 44 ++++--- expense_add_modal.php | 38 ++++++ inventory.php | 135 ++++++++++++++++++++++ inventory_edit_item_location_modal.php | 45 ++++++++ inventory_locations.php | 137 ++++++++++++++++++++++ inventory_locations_manage.php | 153 +++++++++++++++++++++++++ inventory_manage.php | 147 ++++++++++++++++++++++++ post.php | 2 + post/expense.php | 27 ++++- post/expense_model.php | 2 + post/inventory.php | 26 +++++ post/inventory_model.php | 1 + post/ticket.php | 118 +++++++++++++++++++ products.php | 5 + ticket.php | 36 +++++- ticket_add_product_modal.php | 45 ++++++++ 17 files changed, 942 insertions(+), 20 deletions(-) create mode 100644 inventory.php create mode 100644 inventory_edit_item_location_modal.php create mode 100644 inventory_locations.php create mode 100644 inventory_locations_manage.php create mode 100644 inventory_manage.php create mode 100644 post/inventory.php create mode 100644 post/inventory_model.php create mode 100644 ticket_add_product_modal.php diff --git a/database_updates.php b/database_updates.php index 0c8b778d7..121d364cc 100644 --- a/database_updates.php +++ b/database_updates.php @@ -1645,6 +1645,7 @@ `inventory_client_id` int(11) DEFAULT NULL, `inventory_ticket_id` int(11) DEFAULT NULL, `inventory_notes` text DEFAULT NULL, + `inventory_vendor_id` int(11) DEFAULT NULL, `inventory_quantity` int(11) NOT NULL DEFAULT 0, `inventory_cost` decimal(15,2) NOT NULL DEFAULT 0.00, `inventory_created_at` datetime NOT NULL DEFAULT current_timestamp(), diff --git a/db.sql b/db.sql index 60ede72f8..cb190ba6d 100644 --- a/db.sql +++ b/db.sql @@ -674,7 +674,9 @@ CREATE TABLE `inventory` ( `inventory_product_id` int(11) NOT NULL, `inventory_location_id` int(11) NOT NULL, `inventory_client_id` int(11) DEFAULT NULL, - `inventory_ticket_id` int(11) DEFAULT NULL, + `inventory_serial` varchar(200) DEFAULT NULL, + `inventory_barcode` varchar(200) DEFAULT NULL, + `inventory_vendor_id` int(11) NOT NULL, `inventory_notes` text DEFAULT NULL, `inventory_quantity` int(11) NOT NULL DEFAULT 0, `inventory_cost` decimal(15,2) NOT NULL DEFAULT 0.00, @@ -692,19 +694,19 @@ DROP TABLE IF EXISTS `inventory_locations`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `inventory_locations` ( - `inventory_location_id` int(11) NOT NULL AUTO_INCREMENT, - `inventory_location_name` varchar(200) NOT NULL, - `inventory_location_description` text DEFAULT NULL, - `inventory_location_address` varchar(200) DEFAULT NULL, - `inventory_location_city` varchar(200) DEFAULT NULL, - `inventory_location_state` varchar(200) DEFAULT NULL, - `inventory_location_zip` varchar(200) DEFAULT NULL, - `inventory_location_country` varchar(200) DEFAULT NULL, - `inventory_location_created_at` datetime NOT NULL DEFAULT current_timestamp(), - `inventory_location_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(), - `inventory_location_archived_at` datetime DEFAULT NULL, - `inventory_location_user_id` int(11) NOT NULL DEFAULT 0, - PRIMARY KEY (`inventory_location_id`) + `inventory_locations_id` int(11) NOT NULL AUTO_INCREMENT, + `inventory_locations_name` varchar(200) NOT NULL, + `inventory_locations_description` text DEFAULT NULL, + `inventory_locations_address` varchar(200) DEFAULT NULL, + `inventory_locations_city` varchar(200) DEFAULT NULL, + `inventory_locations_state` varchar(200) DEFAULT NULL, + `inventory_locations_zip` varchar(200) DEFAULT NULL, + `inventory_locations_country` varchar(200) DEFAULT NULL, + `inventory_locations_created_at` datetime NOT NULL DEFAULT current_timestamp(), + `inventory_locations_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(), + `inventory_locations_archived_at` datetime DEFAULT NULL, + `inventory_locations_user_id` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`inventory_locations_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; @@ -1551,6 +1553,20 @@ CREATE TABLE `ticket_attachments` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; /*!40101 SET character_set_client = @saved_cs_client */; +-- +-- Table structure for table `ticket_products` +-- +DROP TABLE IF EXISTS `ticket_products`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `ticket_products` ( + `ticket_product_id` int(11) NOT NULL AUTO_INCREMENT, + `ticket_product_product_id` int(11) NOT NULL, + `ticket_product_ticket_id` int(11) NOT NULL, + `ticket_product_quantity` int(11) NOT NULL, + PRIMARY KEY (`ticket_product_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci; + -- -- Table structure for table `ticket_replies` -- diff --git a/expense_add_modal.php b/expense_add_modal.php index 6238759d3..63e58c7f5 100644 --- a/expense_add_modal.php +++ b/expense_add_modal.php @@ -117,7 +117,45 @@
+
+ +
+
+ +
+ +
+ +
+
+
+
+ +
+
+ +
+ +
+
+
+ +
diff --git a/inventory.php b/inventory.php new file mode 100644 index 000000000..2cfa2d0c7 --- /dev/null +++ b/inventory.php @@ -0,0 +1,135 @@ + + +
+
+

Inventory

+
+ +
+
+
+
+
+ +
+ + +
+
+
+
+
+ Locations + +
+
+
+
+
+
+
+ + "> + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
Product NameQuantityLocationsManage product
+
+ +
+
+
+ +
+
+
+ +
+ +
+
+ + + +" tabindex="-1"> + +
+ diff --git a/inventory_locations.php b/inventory_locations.php new file mode 100644 index 000000000..ead61408d --- /dev/null +++ b/inventory_locations.php @@ -0,0 +1,137 @@ + + +
+
+

Inventory locations

+
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+
+
+
+
+ + "> + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
NameDescriptionUser ResponsibleTotal QuantityManage Inventory
+
+ +
+
+
+ +
+
+
+ +
+ +
+
+ + + + + +
+
+

Inventory location:

+
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+
+
+
+
+ + "> + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
Item NameQuantityUnit CostLocationManage product
+
+ + +
+
+
+ + + +
+
+
+ +
+ +
+
+ + + + + +
+
+

Inventory product:

+
+ +
+
+
+
+
+ +
+ +
+
+
+ +
+
+
+
+
+ + "> + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
LocationQuantityUnit CostManage location
+
+ + +
+
+
+ +
+
+
+ +
+ +
+
+ + + +"; + header("Location: " . $_SERVER["HTTP_REFERER"]); + + + +} \ No newline at end of file diff --git a/post/inventory_model.php b/post/inventory_model.php new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/post/inventory_model.php @@ -0,0 +1 @@ + 0) { + $sql = mysqli_query($mysqli, "SELECT * FROM taxes WHERE tax_id = $product_tax_id"); + $row = mysqli_fetch_array($sql); + $product_tax_percent = floatval($row['tax_percent']); + $product_tax_amount = $product_subtotal * $product_tax_percent / 100; + } else { + $product_tax_amount = 0; + } + + $product_total = $product_subtotal + $product_tax_amount; + + mysqli_query($mysqli, "INSERT INTO invoice_items SET item_name = '$product_name', item_description = '$product_description', item_quantity = $product_qty, item_price = $product_price, item_subtotal = $product_subtotal, item_tax = $product_tax_amount, item_total = $product_total, item_order = 1, item_tax_id = $product_tax_id, item_invoice_id = $invoice_id"); + } + + //Update Invoice Balances $sql = mysqli_query($mysqli, "SELECT * FROM invoices WHERE invoice_id = $invoice_id"); @@ -1764,3 +1795,90 @@ header("Location: " . $_SERVER["HTTP_REFERER"]); } + + +if (isset($_POST['add_ticket_products'])) { + + validateTechRole(); + + $ticket_id = intval($_POST['ticket_id']); + $product_id = intval($_POST['product_id']); + $qty = intval($_POST['quantity']); + + //find user inventory location + $sql = mysqli_query($mysqli, "SELECT * FROM inventory_locations WHERE inventory_locations_user_id = $session_user_id"); + $num_rows = mysqli_num_rows($sql); + + if ($num_rows == 1) { + $row = mysqli_fetch_array($sql); + $session_location_id = intval($row['inventory_locations_id']); + } elseif ($num_rows > 1) { + $_SESSION['alert_type'] = "error"; + $_SESSION['alert_message'] = "You have more than one inventory location set. Please contact your administrator"; + header("Location: " . $_SERVER["HTTP_REFERER"]); + exit; + } else { + $_SESSION['alert_type'] = "error"; + $_SESSION['alert_message'] = "You do not have an inventory location set. Please contact your administrator"; + header("Location: " . $_SERVER["HTTP_REFERER"]); + exit; + } + + //check qty against inventory + $sql = mysqli_query($mysqli, "SELECT SUM(inventory_quantity) as inventory_quantity FROM inventory WHERE inventory_product_id = $product_id AND inventory_location_id = $session_location_id GROUP BY inventory_product_id, inventory_location_id;"); + $row = mysqli_fetch_array($sql); + $inventory_qty = intval($row['inventory_quantity']); + if ($qty > $inventory_qty) { + $_SESSION['alert_type'] = "error"; + $_SESSION['alert_message'] = "You do not have enough inventory to add that quantity, QTY: $qty, Inventory: $inventory_qty in location $session_location_id, $num_rows rows found."; + header("Location: " . $_SERVER["HTTP_REFERER"]); + exit; + } + + + + // Add to DB + mysqli_query($mysqli, "INSERT INTO ticket_products SET ticket_product_ticket_id = $ticket_id, ticket_product_product_id = $product_id, ticket_product_quantity = $qty"); + + // Delete one item per qty + mysqli_query($mysqli, "UPDATE inventory SET inventory_quantity = inventory_quantity - 1 WHERE inventory_product_id = $product_id AND inventory_location_id = $session_location_id AND inventory_quantity > 0 LIMIT $qty"); + + // Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Modify', log_description = '$session_name added product to ticket', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $ticket_id"); + + $_SESSION['alert_message'] = "Product added to ticket"; + header("Location: ". $_SERVER["HTTP_REFERER"]); + +} + +if (isset($_GET['delete_ticket_product'])) { + + validateTechRole(); + + $ticket_product_id = intval($_GET['delete_ticket_product']); + $ticket_id = intval($_GET['ticket_id']); + + // Get product ID + $sql = mysqli_query($mysqli, "SELECT * FROM ticket_products WHERE ticket_product_id = $ticket_product_id"); + $row = mysqli_fetch_array($sql); + $product_id = intval($row['ticket_product_product_id']); + $qty = intval($row['ticket_product_quantity']); + + // Delete + mysqli_query($mysqli, "DELETE FROM ticket_products WHERE ticket_product_id = $ticket_product_id"); + + //find user's inventory location + $sql = mysqli_query($mysqli, "SELECT * FROM inventory_locations WHERE inventory_locations_user_id = $session_user_id"); + $row = mysqli_fetch_array($sql); + $session_location_id = intval($row['inventory_locations_id']); + + // Restore inventory quantity + mysqli_query($mysqli, "UPDATE inventory SET inventory_quantity = inventory_quantity + 1 WHERE inventory_product_id = $product_id AND inventory_location_id = $session_location_id AND inventory_quantity = 0 LIMIT $qty"); + + // Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Modify', log_description = '$session_name deleted product from ticket', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $ticket_id"); + + $_SESSION['alert_message'] = "Product removed from ticket. Please see administrator to return inventory"; + header("Location: ". $_SERVER["HTTP_REFERER"]); + +} diff --git a/products.php b/products.php index f8cd081ef..d3d2c7a69 100644 --- a/products.php +++ b/products.php @@ -43,6 +43,11 @@
+
+
+ Inventory +
+

diff --git a/ticket.php b/ticket.php index 1f8859f95..3db452e9c 100644 --- a/ticket.php +++ b/ticket.php @@ -233,6 +233,25 @@ AND ticket_attachment_ticket_id = $ticket_id" ); + // Get Products in inventory attached to this ticket + $sql_ticket_products = mysqli_query( + $mysqli, + "SELECT * FROM ticket_products + LEFT JOIN products ON ticket_products.ticket_product_product_id = products.product_id + WHERE ticket_product_ticket_id = $ticket_id" + ); + + $ticket_products_display = ''; + while ($row = mysqli_fetch_array($sql_ticket_products)) { + $ticket_product_id = intval($row['ticket_product_id']); + $product_id = intval($row['product_id']); + $product_name = nullable_htmlentities($row['product_name']); + $product_quantity = intval($row['ticket_product_quantity']); + + + $ticket_products_display .= "
$product_name x$product_quantity✘
"; + } + ?> @@ -380,8 +399,8 @@
- - +
+
-
+
> @@ -881,6 +900,15 @@
+ +
+
Products
+ + +
+
@@ -952,6 +980,8 @@ require_once "ticket_merge_modal.php"; + require_once "ticket_add_product_modal.php"; + if ($config_module_enable_accounting) { require_once "ticket_edit_billable_modal.php"; require_once "ticket_invoice_add_modal.php"; diff --git a/ticket_add_product_modal.php b/ticket_add_product_modal.php new file mode 100644 index 000000000..73e9135da --- /dev/null +++ b/ticket_add_product_modal.php @@ -0,0 +1,45 @@ +
+ + + \ No newline at end of file From fb100d62c3a477aded86cc36b3dbec97ebdf7c17 Mon Sep 17 00:00:00 2001 From: o-psi Date: Thu, 7 Mar 2024 16:17:53 +0000 Subject: [PATCH 06/32] Sonarcloud Security --- inventory_locations_manage.php | 2 +- inventory_manage.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/inventory_locations_manage.php b/inventory_locations_manage.php index 31e8c1307..ccd983cce 100644 --- a/inventory_locations_manage.php +++ b/inventory_locations_manage.php @@ -6,7 +6,7 @@ require_once "inc_all.php"; -$inventory_location_id = $_GET["inventory_location_id"]; +$inventory_location_id = intval($_GET["inventory_location_id"]); //Rebuild URL $url_query_strings_sort = http_build_query($get_copy); diff --git a/inventory_manage.php b/inventory_manage.php index ea97b2fcc..f3763e95c 100644 --- a/inventory_manage.php +++ b/inventory_manage.php @@ -6,7 +6,7 @@ require_once "inc_all.php"; -$inventory_product_id = $_GET["inventory_product_id"]; +$inventory_product_id = intval($_GET["inventory_product_id"]); //Rebuild URL $url_query_strings_sort = http_build_query($get_copy); From 31876112e1ce066b578d6bbbbbd878bafb763a6f Mon Sep 17 00:00:00 2001 From: o-psi Date: Thu, 7 Mar 2024 20:43:52 +0000 Subject: [PATCH 07/32] Inventory final touches --- admin_inventory_locations.php | 108 +++++++++++++++++++++++ admin_inventory_locations_add_modal.php | 74 ++++++++++++++++ admin_inventory_locations_edit_modal.php | 76 ++++++++++++++++ admin_side_nav.php | 9 ++ inventory_edit_item_location_modal.php | 2 +- post/inventory.php | 92 ++++++++++++++++++- post/ticket.php | 9 +- ticket_add_product_modal.php | 1 + 8 files changed, 368 insertions(+), 3 deletions(-) create mode 100644 admin_inventory_locations.php create mode 100644 admin_inventory_locations_add_modal.php create mode 100644 admin_inventory_locations_edit_modal.php diff --git a/admin_inventory_locations.php b/admin_inventory_locations.php new file mode 100644 index 000000000..c02272a85 --- /dev/null +++ b/admin_inventory_locations.php @@ -0,0 +1,108 @@ + + +
+
+

Inventory Locations

+
+ +
+
+
+
+ + "> + + + + + + + + + + + + + + + + + + + No Records Here"; + } + + ?> + + +
NameDescriptionUser AssignedCityAction
+ +
+ +
+
+
+ + + +
\ No newline at end of file diff --git a/admin_inventory_locations_edit_modal.php b/admin_inventory_locations_edit_modal.php new file mode 100644 index 000000000..fce78b246 --- /dev/null +++ b/admin_inventory_locations_edit_modal.php @@ -0,0 +1,76 @@ + \ No newline at end of file diff --git a/admin_side_nav.php b/admin_side_nav.php index 863e24545..6415de905 100644 --- a/admin_side_nav.php +++ b/admin_side_nav.php @@ -80,6 +80,15 @@ + +
+
-
-
+
+
- -
-
- +
+
+ -
- -
- +
+ +
+ - -
- -
- +
+ +
+ - -
-
Contact
+ +
+
Contact
- + -
- - -
+
+ + +
- -
- -
- + +
+ -
- -
- + +
+ -
- -
- + +
+ -
- -
- +
+ +
+ - -
-
- Previous ticket: - -
-
- Status: - -
- +
+
+ Previous ticket: + +
+
+ Status: + +
+ - - - -
- + + + +
+ - - + 0) { ?> -
-
Watchers
+
+
Watchers
- - - + + + - -
- - - - - - -
+
+ + + + + + +
- -
- - - -
-
Details
-
- -
-
- Created: -
-
- Updated: -
+
+ + + +
+
Details
+
+ +
+
+ Created: + +
+
+ Updated: + +
- - + -
- Closed by: -
-
- Feedback: -
- - +
+ Closed by: + +
+
+ Feedback: + +
+ + - -
- Scheduled: -
- + Scheduled: + +
+ -
- Total time worked: -
- + Total time worked: + +
+ - - -
- + +
+ +
+ - -
-
Asset
+ +
+
Asset
- + - + - + -
- -
+
+ +
- -
- -
- +
+ +
+ -
- -
- + +
+ -
- Model: -
- + Model: + +
+ -
- Service Tag: -
- + Service Tag: + +
+ -
- Warranty expires: -
- + Warranty expires: + +
+ -
- -
- + +
+ 0) { ?> - - - + - - - - - - -
-
Vendor
- - - -
- -
- + + + +
+
Vendor
+ + + +
+ +
+ -
- -
- + +
+ -
- -
- + +
+ -
- -
- + + + -
- -
- + + + -
- -
- +
+ +
+ - - - + + - -
-
Products
- - -
+ + +
+
Products
+ + +
+ -
- - -
- -
-
- -
- + +
+ +
+
+ +
+ -
- -
-
+ } ?>>
- +
+
+ - - - + + Close Ticket + + - + + + + - - + + - - - + + + - + \ No newline at end of file From 6490cf63be06db031d7b33c408f9b0fbfabae5c1 Mon Sep 17 00:00:00 2001 From: o-psi Date: Fri, 15 Mar 2024 21:08:33 +0000 Subject: [PATCH 16/32] Add app notification for new ticket replies --- cron_ticket_email_parser.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cron_ticket_email_parser.php b/cron_ticket_email_parser.php index e45ae0463..2a2f6e48a 100644 --- a/cron_ticket_email_parser.php +++ b/cron_ticket_email_parser.php @@ -358,6 +358,9 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac // Update Ticket Last Response Field & set ticket to open as client has replied mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 'Open' WHERE ticket_id = $ticket_id AND ticket_client_id = $client_id LIMIT 1"); + // App Notification + mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Ticket', notification = 'Ticket $ticket_prefix$ticket_number - Subject: $subject - has been updated via email', notification_action = 'ticket.php?ticket_id=$ticket_id', notification_client_id = $client_id, notification_user_id = $ticket_assigned_to"); + echo "Updated existing ticket.
"; mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Client contact $from_email updated ticket $config_ticket_prefix$ticket_number ($subject)', log_client_id = $client_id"); From a59936255a97f0e7d36a08b07422bf70630222f2 Mon Sep 17 00:00:00 2001 From: o-psi Date: Sun, 17 Mar 2024 00:35:12 +0000 Subject: [PATCH 17/32] Functionify the post handler. --- functions.php | 1194 +--------------------- functions/account_functions.php | 127 +++ functions/accounting_functions.php | 131 +++ functions/api_functions.php | 28 + functions/credit_functions.php | 163 +++ functions/crpyto_functions.php | 37 + functions/domain_functions.php | 96 ++ functions/email_functions.php | 263 +++++ functions/file_functions.php | 63 ++ functions/invoice_functions.php | 519 ++++++++++ functions/invoice_item_functions.php | 313 ++++++ functions/misc_functions.php | 397 ++++++++ functions/payment_functions.php | 384 +++++++ functions/security_functions.php | 346 +++++++ post/account.php | 62 +- post/account_type.php | 62 +- post/api.php | 48 +- post/invoice.php | 1403 +++----------------------- 18 files changed, 3082 insertions(+), 2554 deletions(-) create mode 100644 functions/account_functions.php create mode 100644 functions/accounting_functions.php create mode 100644 functions/api_functions.php create mode 100644 functions/credit_functions.php create mode 100644 functions/crpyto_functions.php create mode 100644 functions/domain_functions.php create mode 100644 functions/email_functions.php create mode 100644 functions/file_functions.php create mode 100644 functions/invoice_functions.php create mode 100644 functions/invoice_item_functions.php create mode 100644 functions/misc_functions.php create mode 100644 functions/payment_functions.php create mode 100644 functions/security_functions.php diff --git a/functions.php b/functions.php index f1843dc75..b1882a9fc 100644 --- a/functions.php +++ b/functions.php @@ -3,1191 +3,17 @@ // Role check failed wording DEFINE("WORDING_ROLECHECK_FAILED", "You are not permitted to do that!"); -// PHP Mailer Libs -require_once "plugins/PHPMailer/src/Exception.php"; - -require_once "plugins/PHPMailer/src/PHPMailer.php"; - -require_once "plugins/PHPMailer/src/SMTP.php"; - -// Initiate PHPMailer -use PHPMailer\PHPMailer\PHPMailer; -use PHPMailer\PHPMailer\Exception; - -// Function to generate both crypto & URL safe random strings -function randomString($length = 16) -{ - // Generate some cryptographically safe random bytes - // Generate a little more than requested as we'll lose some later converting - $random_bytes = random_bytes($length + 5); - - // Convert the bytes to something somewhat human-readable - $random_base_64 = base64_encode($random_bytes); - - // Replace the nasty characters that come with base64 - $bad_chars = array("/", "+", "="); - $random_string = str_replace($bad_chars, random_int(0, 9), $random_base_64); - - // Truncate the string to the requested $length and return - return substr($random_string, 0, $length); -} - -// Older keygen function - only used for TOTP currently -function key32gen() -{ - $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - $chars .= "234567"; - while (1) { - $key = ''; - srand((float) microtime() * 1000000); - for ($i = 0; $i < 32; $i++) { - $key .= substr($chars, (rand() % (strlen($chars))), 1); - } - break; - } - return $key; -} - -function nullable_htmlentities($unsanitizedInput) -{ - return htmlentities($unsanitizedInput ?? ''); -} - -function initials($str) -{ - if (!empty($str)) { - $ret = ''; - foreach (explode(' ', $str) as $word) - $ret .= strtoupper($word[0]); - $ret = substr($ret, 0, 2); - return $ret; - } -} - -function removeDirectory($path) -{ - if (!file_exists($path)) { - return; - } - - $files = glob($path . '/*'); - foreach ($files as $file) { - is_dir($file) ? removeDirectory($file) : unlink($file); - } - rmdir($path); -} - -function getUserAgent() -{ - return $_SERVER['HTTP_USER_AGENT']; -} - -function getIP() -{ - if (defined("CONST_GET_IP_METHOD")) { - if (CONST_GET_IP_METHOD == "HTTP_X_FORWARDED_FOR") { - $ip = getenv('HTTP_X_FORWARDED_FOR'); - } else { - $ip = $_SERVER["HTTP_CF_CONNECTING_IP"] ?? $_SERVER['REMOTE_ADDR']; - } - } else { - $ip = $_SERVER["HTTP_CF_CONNECTING_IP"] ?? $_SERVER['REMOTE_ADDR']; - } - - if (!filter_var($ip, FILTER_VALIDATE_IP)) { - exit("Potential Security Violation"); - } - - return $ip; -} - -function getWebBrowser($user_browser) -{ - $browser = "Unknown Browser"; - $browser_array = array( - '/msie/i' => " Internet Explorer", - '/firefox/i' => " Firefox", - '/safari/i' => " Safari", - '/chrome/i' => " Chrome", - '/edge/i' => " Edge", - '/opera/i' => " Opera" - ); - foreach ($browser_array as $regex => $value) { - if (preg_match($regex, $user_browser)) { - $browser = $value; - } - } - return $browser; -} - -function getOS($user_os) -{ - $os_platform = "Unknown OS"; - $os_array = array( - '/windows nt 10/i' => " Windows 10", - '/windows nt 6.3/i' => " Windows 8.1", - '/windows nt 6.2/i' => " Windows 8", - '/windows nt 6.1/i' => " Windows 7", - '/windows nt 6.0/i' => " Windows Vista", - '/windows nt 5.2/i' => " Windows Server 2003/XP x64", - '/windows nt 5.1/i' => " Windows XP", - '/windows xp/i' => " Windows XP", - '/macintosh|mac os x/i' => " MacOS", - '/linux/i' => " Linux", - '/ubuntu/i' => " Ubuntu", - '/iphone/i' => " iPhone", - '/ipod/i' => " iPod", - '/ipad/i' => " iPad", - '/android/i' => " Android" - ); - foreach ($os_array as $regex => $value) { - if (preg_match($regex, $user_os)) { - $os_platform = $value; - } - } - return $os_platform; -} - -function getDevice() -{ - $tablet_browser = 0; - $mobile_browser = 0; - if (preg_match('/(tablet|ipad|playbook)|(android(?!.*(mobi|opera mini)))/i', strtolower($_SERVER['HTTP_USER_AGENT']))) { - $tablet_browser++; - } - if (preg_match('/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone|android|iemobile)/i', strtolower($_SERVER['HTTP_USER_AGENT']))) { - $mobile_browser++; - } - if ((strpos(strtolower($_SERVER['HTTP_ACCEPT']), 'application/vnd.wap.xhtml+xml') > 0) || ((isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_PROFILE'])))) { - $mobile_browser++; - } - $mobile_ua = strtolower(substr(getUserAgent(), 0, 4)); - $mobile_agents = array( - 'w3c ', 'acs-', 'alav', 'alca', 'amoi', 'audi', 'avan', 'benq', 'bird', 'blac', - 'blaz', 'brew', 'cell', 'cldc', 'cmd-', 'dang', 'doco', 'eric', 'hipt', 'inno', - 'ipaq', 'java', 'jigs', 'kddi', 'keji', 'leno', 'lg-c', 'lg-d', 'lg-g', 'lge-', - 'maui', 'maxo', 'midp', 'mits', 'mmef', 'mobi', 'mot-', 'moto', 'mwbp', 'nec-', - 'newt', 'noki', 'palm', 'pana', 'pant', 'phil', 'play', 'port', 'prox', - 'qwap', 'sage', 'sams', 'sany', 'sch-', 'sec-', 'send', 'seri', 'sgh-', 'shar', - 'sie-', 'siem', 'smal', 'smar', 'sony', 'sph-', 'symb', 't-mo', 'teli', 'tim-', - 'tosh', 'tsm-', 'upg1', 'upsi', 'vk-v', 'voda', 'wap-', 'wapa', 'wapi', 'wapp', - 'wapr', 'webc', 'winw', 'winw', 'xda ', 'xda-' - ); - if (in_array($mobile_ua, $mobile_agents)) { - $mobile_browser++; - } - if (strpos(strtolower(getUserAgent()), 'opera mini') > 0) { - $mobile_browser++; - //Check for tablets on Opera Mini alternative headers - $stock_ua = strtolower(isset($_SERVER['HTTP_X_OPERAMINI_PHONE_UA']) ? $_SERVER['HTTP_X_OPERAMINI_PHONE_UA'] : (isset($_SERVER['HTTP_DEVICE_STOCK_UA']) ? $_SERVER['HTTP_DEVICE_STOCK_UA'] : '')); - if (preg_match('/(tablet|ipad|playbook)|(android(?!.*mobile))/i', $stock_ua)) { - $tablet_browser++; - } - } - if ($tablet_browser > 0) { - //do something for tablet devices - return 'Tablet'; - } else if ($mobile_browser > 0) { - //do something for mobile devices - return 'Mobile'; - } else { - //do something for everything else - return 'Computer'; - } -} - -function truncate($text, $chars) -{ - if (strlen($text) <= $chars) { - return $text; - } - $text = $text . " "; - $text = substr($text, 0, $chars); - $lastSpacePos = strrpos($text, ' '); - if ($lastSpacePos !== false) { - $text = substr($text, 0, $lastSpacePos); - } - return $text . "..."; -} - -function formatPhoneNumber($phoneNumber) -{ - $phoneNumber = $phoneNumber ? preg_replace('/[^0-9]/', '', $phoneNumber) : ""; - - if (strlen($phoneNumber) > 10) { - $countryCode = substr($phoneNumber, 0, strlen($phoneNumber) - 10); - $areaCode = substr($phoneNumber, -10, 3); - $nextThree = substr($phoneNumber, -7, 3); - $lastFour = substr($phoneNumber, -4, 4); - - $phoneNumber = '+' . $countryCode . ' (' . $areaCode . ') ' . $nextThree . '-' . $lastFour; - } else if (strlen($phoneNumber) == 10) { - $areaCode = substr($phoneNumber, 0, 3); - $nextThree = substr($phoneNumber, 3, 3); - $lastFour = substr($phoneNumber, 6, 4); - - $phoneNumber = '(' . $areaCode . ') ' . $nextThree . '-' . $lastFour; - } else if (strlen($phoneNumber) == 7) { - $nextThree = substr($phoneNumber, 0, 3); - $lastFour = substr($phoneNumber, 3, 4); - - $phoneNumber = $nextThree . '-' . $lastFour; - } - - return $phoneNumber; -} - -function mkdirMissing($dir) -{ - if (!is_dir($dir)) { - mkdir($dir); - } -} - -// Called during initial setup -// Encrypts the master key with the user's password -function setupFirstUserSpecificKey($user_password, $site_encryption_master_key) -{ - $iv = randomString(); - $salt = randomString(); - - //Generate 128-bit (16 byte/char) kdhash of the users password - $user_password_kdhash = hash_pbkdf2('sha256', $user_password, $salt, 100000, 16); - - //Encrypt the master key with the users kdf'd hash and the IV - $ciphertext = openssl_encrypt($site_encryption_master_key, 'aes-128-cbc', $user_password_kdhash, 0, $iv); - - return $salt . $iv . $ciphertext; -} - -/* - * For additional users / password changes - * New Users: Requires the admin setting up their account have a Specific/Session key configured - * Password Changes: Will use the current info in the session. -*/ -function encryptUserSpecificKey($user_password) -{ - $iv = randomString(); - $salt = randomString(); - - // Get the session info. - $user_encryption_session_ciphertext = $_SESSION['user_encryption_session_ciphertext']; - $user_encryption_session_iv = $_SESSION['user_encryption_session_iv']; - $user_encryption_session_key = $_COOKIE['user_encryption_session_key']; - - // Decrypt the session key to get the master key - $site_encryption_master_key = openssl_decrypt($user_encryption_session_ciphertext, 'aes-128-cbc', $user_encryption_session_key, 0, $user_encryption_session_iv); - - // Generate 128-bit (16 byte/char) kdhash of the users (new) password - $user_password_kdhash = hash_pbkdf2('sha256', $user_password, $salt, 100000, 16); - - // Encrypt the master key with the users kdf'd hash and the IV - $ciphertext = openssl_encrypt($site_encryption_master_key, 'aes-128-cbc', $user_password_kdhash, 0, $iv); - - return $salt . $iv . $ciphertext; -} - -// Given a ciphertext (incl. IV) and the user's password, returns the site master key -// Ran at login, to facilitate generateUserSessionKey -function decryptUserSpecificKey($user_encryption_ciphertext, $user_password) -{ - //Get the IV, salt and ciphertext - $salt = substr($user_encryption_ciphertext, 0, 16); - $iv = substr($user_encryption_ciphertext, 16, 16); - $ciphertext = substr($user_encryption_ciphertext, 32); - - //Generate 128-bit (16 byte/char) kdhash of the users password - $user_password_kdhash = hash_pbkdf2('sha256', $user_password, $salt, 100000, 16); - - //Use this hash to get the original/master key - return openssl_decrypt($ciphertext, 'aes-128-cbc', $user_password_kdhash, 0, $iv); -} - -/* -Generates what is probably best described as a session key (ephemeral-ish) -- Allows us to store the master key on the server whilst the user is using the application, without prompting to type their password everytime they want to decrypt a credential -- Ciphertext/IV is stored on the server in the users' session, encryption key is controlled/provided by the user as a cookie -- Only the user can decrypt their session ciphertext to get the master key -- Encryption key never hits the disk in cleartext -*/ -function generateUserSessionKey($site_encryption_master_key) -{ - $user_encryption_session_key = randomString(); - $user_encryption_session_iv = randomString(); - $user_encryption_session_ciphertext = openssl_encrypt($site_encryption_master_key, 'aes-128-cbc', $user_encryption_session_key, 0, $user_encryption_session_iv); - - // Store ciphertext in the user's session - $_SESSION['user_encryption_session_ciphertext'] = $user_encryption_session_ciphertext; - $_SESSION['user_encryption_session_iv'] = $user_encryption_session_iv; - - // Give the user "their" key as a cookie - include 'config.php'; - - if ($config_https_only) { - setcookie("user_encryption_session_key", "$user_encryption_session_key", ['path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'None']); - } else { - setcookie("user_encryption_session_key", $user_encryption_session_key, 0, "/"); - $_SESSION['alert_message'] = "Unencrypted connection flag set: Using non-secure cookies."; - } -} - -// Decrypts an encrypted password (website/asset login), returns it as a string -function decryptLoginEntry($login_password_ciphertext) -{ - - // Split the login into IV and Ciphertext - $login_iv = substr($login_password_ciphertext, 0, 16); - $login_ciphertext = $salt = substr($login_password_ciphertext, 16); - - // Get the user session info. - $user_encryption_session_ciphertext = $_SESSION['user_encryption_session_ciphertext']; - $user_encryption_session_iv = $_SESSION['user_encryption_session_iv']; - $user_encryption_session_key = $_COOKIE['user_encryption_session_key']; - - // Decrypt the session key to get the master key - $site_encryption_master_key = openssl_decrypt($user_encryption_session_ciphertext, 'aes-128-cbc', $user_encryption_session_key, 0, $user_encryption_session_iv); - - // Decrypt the login password using the master key - return openssl_decrypt($login_ciphertext, 'aes-128-cbc', $site_encryption_master_key, 0, $login_iv); -} - -// Encrypts a website/asset login password -function encryptLoginEntry($login_password_cleartext) -{ - $iv = randomString(); - - // Get the user session info. - $user_encryption_session_ciphertext = $_SESSION['user_encryption_session_ciphertext']; - $user_encryption_session_iv = $_SESSION['user_encryption_session_iv']; - $user_encryption_session_key = $_COOKIE['user_encryption_session_key']; - - //Decrypt the session key to get the master key - $site_encryption_master_key = openssl_decrypt($user_encryption_session_ciphertext, 'aes-128-cbc', $user_encryption_session_key, 0, $user_encryption_session_iv); - - //Encrypt the website/asset login using the master key - $ciphertext = openssl_encrypt($login_password_cleartext, 'aes-128-cbc', $site_encryption_master_key, 0, $iv); - - return $iv . $ciphertext; -} - -// Get domain expiration date -function getDomainExpirationDate($name) -{ - - // Only run if we think the domain is valid - if (!filter_var($name, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) { - return "NULL"; - } - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, "http://lookup.itflow.org:8080/$name"); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - $response = json_decode(curl_exec($ch), 1); - - if ($response) { - if (is_array($response['expiration_date'])) { - $expiry = new DateTime($response['expiration_date'][1]); - } elseif (isset($response['expiration_date'])) { - $expiry = new DateTime($response['expiration_date']); - } else { - return "NULL"; - } - - return $expiry->format('Y-m-d'); - } - - // Default return - return "NULL"; -} - -// Get domain general info (whois + NS/A/MX records) -function getDomainRecords($name) -{ - - $records = array(); - - // Only run if we think the domain is valid - if (!filter_var($name, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) { - $records['a'] = ''; - $records['ns'] = ''; - $records['mx'] = ''; - $records['whois'] = ''; - return $records; - } - - $domain = escapeshellarg(str_replace('www.', '', $name)); - $records['a'] = substr(trim(strip_tags(shell_exec("dig +short $domain"))), 0, 254); - $records['ns'] = substr(trim(strip_tags(shell_exec("dig +short NS $domain"))), 0, 254); - $records['mx'] = substr(trim(strip_tags(shell_exec("dig +short MX $domain"))), 0, 254); - $records['txt'] = substr(trim(strip_tags(shell_exec("dig +short TXT $domain"))), 0, 254); - $records['whois'] = substr(trim(strip_tags(shell_exec("whois -H $domain | sed 's/ //g' | head -30"))), 0, 254); - - return $records; -} - -// Used to automatically attempt to get SSL certificates as part of adding domains -// The logic for the fetch (sync) button on the client_certificates page is in ajax.php, and allows ports other than 443 -function getSSL($name) -{ - - $certificate = array(); - $certificate['success'] = false; - - // Only run if we think the domain is valid - if (!filter_var($name, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) { - $certificate['expire'] = ''; - $certificate['issued_by'] = ''; - $certificate['public_key'] = ''; - return $certificate; - } - - // Get SSL/TSL certificate (using verify peer false to allow for self-signed certs) for domain on default port - $socket = "ssl://$name:443"; - $get = stream_context_create(array("ssl" => array("capture_peer_cert" => true, "verify_peer" => false,))); - $read = stream_socket_client($socket, $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $get); - - // If the socket connected - if ($read) { - $cert = stream_context_get_params($read); - $cert_public_key_obj = openssl_x509_parse($cert['options']['ssl']['peer_certificate']); - openssl_x509_export($cert['options']['ssl']['peer_certificate'], $export); - - if ($cert_public_key_obj) { - $certificate['success'] = true; - $certificate['expire'] = date('Y-m-d', $cert_public_key_obj['validTo_time_t']); - $certificate['issued_by'] = strip_tags($cert_public_key_obj['issuer']['O']); - $certificate['public_key'] = $export; - } - } - - return $certificate; -} - -function strtoAZaz09($string) -{ - - // Gets rid of non-alphanumerics - return preg_replace('/[^A-Za-z0-9_-]/', '', $string); -} - -// Cross-Site Request Forgery check for sensitive functions -// Validates the CSRF token provided matches the one in the users session -function validateCSRFToken($token) -{ - if (hash_equals($token, $_SESSION['csrf_token'])) { - return true; - } else { - $_SESSION['alert_type'] = "warning"; - $_SESSION['alert_message'] = "CSRF token verification failed. Try again, or log out to refresh your token."; - header("Location: index.php"); - exit(); - } -} - -/* - * Role validation - * Admin - 3 - * Tech - 2 - * Accountant - 1 - */ - -function validateAdminRole() -{ - if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] != 3) { - $_SESSION['alert_type'] = "danger"; - $_SESSION['alert_message'] = WORDING_ROLECHECK_FAILED; - header("Location: " . $_SERVER["HTTP_REFERER"]); - exit(); - } -} - -// Validates a user is a tech (or admin). Stops page load and attempts to direct away from the page if not (i.e. user is an accountant) -function validateTechRole() -{ - if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] == 1) { - $_SESSION['alert_type'] = "danger"; - $_SESSION['alert_message'] = WORDING_ROLECHECK_FAILED; - header("Location: " . $_SERVER["HTTP_REFERER"]); - exit(); - } -} - -// Validates a user is an accountant (or admin). Stops page load and attempts to direct away from the page if not (i.e. user is a tech) -function validateAccountantRole() -{ - if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] == 2) { - $_SESSION['alert_type'] = "danger"; - $_SESSION['alert_message'] = WORDING_ROLECHECK_FAILED; - header("Location: " . $_SERVER["HTTP_REFERER"]); - exit(); - } -} - -// Send a single email to a single recipient -function sendSingleEmail($config_smtp_host, $config_smtp_username, $config_smtp_password, $config_smtp_encryption, $config_smtp_port, $from_email, $from_name, $to_email, $to_name, $subject, $body, $ics_str) -{ - - $mail = new PHPMailer(true); - - if (empty($config_smtp_username)) { - $smtp_auth = false; - } else { - - $smtp_auth = true; - } - - try { - // Mail Server Settings - $mail->CharSet = "UTF-8"; // Specify UTF-8 charset to ensure symbols ($/£) load correctly - $mail->SMTPDebug = 0; // No Debugging - $mail->isSMTP(); // Set mailer to use SMTP - $mail->Host = $config_smtp_host; // Specify SMTP server - $mail->SMTPAuth = $smtp_auth; // Enable SMTP authentication - $mail->Username = $config_smtp_username; // SMTP username - $mail->Password = $config_smtp_password; // SMTP password - $mail->SMTPSecure = $config_smtp_encryption; // Enable TLS encryption, `ssl` also accepted - $mail->Port = $config_smtp_port; // TCP port to connect to - - //Recipients - $mail->setFrom($from_email, $from_name); - $mail->addAddress("$to_email", "$to_name"); // Add a recipient - - // Content - $mail->isHTML(true); // Set email format to HTML - $mail->Subject = "$subject"; // Subject - $mail->Body = " - - - - - - - - "; // Content - - // Attachments - todo - //$mail->addAttachment('/var/tmp/file.tar.gz'); // Add attachments - //$mail->addAttachment('/tmp/image.jpg', 'new.jpg'); // Optional name - - if (!empty($ics_str)) { - $mail->addStringAttachment($ics_str, 'Scheduled_ticket.ics', 'base64', 'text/calendar'); - } - - // Send - $mail->send(); - - // Return true if this was successful - return true; - } catch (Exception $e) { - // If we couldn't send the message return the error, so we can log it in the database (truncated) - error_log("ITFlow - Failed to send email: " . $mail->ErrorInfo); - return substr("Mailer Error: $mail->ErrorInfo", 0, 150) . "..."; - } -} - -function roundUpToNearestMultiple($n, $increment = 1000) -{ - return (int) ($increment * ceil($n / $increment)); -} - -function getAssetIcon($asset_type) -{ - if ($asset_type == 'Laptop') { - $device_icon = "laptop"; - } elseif ($asset_type == 'Desktop') { - $device_icon = "desktop"; - } elseif ($asset_type == 'Server') { - $device_icon = "server"; - } elseif ($asset_type == 'Printer') { - $device_icon = "print"; - } elseif ($asset_type == 'Camera') { - $device_icon = "video"; - } elseif ($asset_type == 'Switch' || $asset_type == 'Firewall/Router') { - $device_icon = "network-wired"; - } elseif ($asset_type == 'Access Point') { - $device_icon = "wifi"; - } elseif ($asset_type == 'Phone') { - $device_icon = "phone"; - } elseif ($asset_type == 'Mobile Phone') { - $device_icon = "mobile-alt"; - } elseif ($asset_type == 'Tablet') { - $device_icon = "tablet-alt"; - } elseif ($asset_type == 'TV') { - $device_icon = "tv"; - } elseif ($asset_type == 'Virtual Machine') { - $device_icon = "cloud"; - } else { - $device_icon = "tag"; - } - - return $device_icon; -} - -function getInvoiceBadgeColor($invoice_status) -{ - if ($invoice_status == "Sent") { - $invoice_badge_color = "warning text-white"; - } elseif ($invoice_status == "Viewed") { - $invoice_badge_color = "info"; - } elseif ($invoice_status == "Partial") { - $invoice_badge_color = "primary"; - } elseif ($invoice_status == "Paid") { - $invoice_badge_color = "success"; - } elseif ($invoice_status == "Cancelled") { - $invoice_badge_color = "danger"; - } else { - $invoice_badge_color = "secondary"; - } - - return $invoice_badge_color; -} - -// Pass $_FILE['file'] to check an uploaded file before saving it -function checkFileUpload($file, $allowed_extensions) -{ - // Variables - $name = $file['name']; - $tmp = $file['tmp_name']; - $size = $file['size']; - - $extarr = explode('.', $name); - $extension = strtolower(end($extarr)); - - // Check a file is actually attached/uploaded - if ($tmp === '') { - // No file uploaded - return false; - } - - // Check the extension is allowed - if (!in_array($extension, $allowed_extensions)) { - // Extension not allowed - return false; - } - - // Check the size is under 500 MB - $maxSizeBytes = 500 * 1024 * 1024; // 500 MB - if ($size > $maxSizeBytes) { - return "File size exceeds the limit."; - } - - // Read the file content - $fileContent = file_get_contents($tmp); - - // Hash the file content using SHA-256 - $hashedContent = hash('sha256', $fileContent); - - // Generate a secure filename using the hashed content - $secureFilename = $hashedContent . randomString(2) . '.' . $extension; - - return $secureFilename; -} - -function sanitizeInput($input) -{ - global $mysqli; - - // Remove HTML and PHP tags - $input = strip_tags((string) $input); - - // Remove white space from beginning and end of input - $input = trim($input); - - // Escape special characters - $input = mysqli_real_escape_string($mysqli, $input); - - // Return sanitized input - return $input; -} - -function sanitizeForEmail($data) -{ - $sanitized = htmlspecialchars($data); - $sanitized = strip_tags($sanitized); - $sanitized = trim($sanitized); - return $sanitized; -} - -function timeAgo($datetime) -{ - $time = strtotime($datetime); - $difference = $time - time(); // Changed to handle future dates - - if ($difference == 0) { - return 'right now'; - } - - $isFuture = $difference > 0; // Check if the date is in the future - $difference = abs($difference); // Absolute value for calculation - - $timeRules = array( - 31536000 => 'year', - 2592000 => 'month', - 604800 => 'week', - 86400 => 'day', - 3600 => 'hour', - 60 => 'minute', - 1 => 'second' - ); - - foreach ($timeRules as $secs => $str) { - $div = $difference / $secs; - if ($div >= 1) { - $t = round($div); - $timeStr = $t . ' ' . $str . ($t > 1 ? 's' : ''); - return $isFuture ? 'in ' . $timeStr : $timeStr . ' ago'; - } - } -} - -// Function to remove Emojis in messages, this seems to break the mail queue -function removeEmoji($text) -{ - return preg_replace('/\x{1F3F4}\x{E0067}\x{E0062}(?:\x{E0077}\x{E006C}\x{E0073}|\x{E0073}\x{E0063}\x{E0074}|\x{E0065}\x{E006E}\x{E0067})\x{E007F}|(?:\x{1F9D1}\x{1F3FF}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FF}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FF}\x{200D}\x{1FAF2})[\x{1F3FB}-\x{1F3FE}]|(?:\x{1F9D1}\x{1F3FE}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FE}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FE}\x{200D}\x{1FAF2})[\x{1F3FB}-\x{1F3FD}\x{1F3FF}]|(?:\x{1F9D1}\x{1F3FD}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FD}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FD}\x{200D}\x{1FAF2})[\x{1F3FB}\x{1F3FC}\x{1F3FE}\x{1F3FF}]|(?:\x{1F9D1}\x{1F3FC}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FC}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FC}\x{200D}\x{1FAF2})[\x{1F3FB}\x{1F3FD}-\x{1F3FF}]|(?:\x{1F9D1}\x{1F3FB}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FB}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FB}\x{200D}\x{1FAF2})[\x{1F3FC}-\x{1F3FF}]|\x{1F468}(?:\x{1F3FB}(?:\x{200D}(?:\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}])|\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}]))|\x{1F91D}\x{200D}\x{1F468}[\x{1F3FC}-\x{1F3FF}]|[\x{2695}\x{2696}\x{2708}]\x{FE0F}|[\x{2695}\x{2696}\x{2708}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]))?|[\x{1F3FC}-\x{1F3FF}]\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}])|\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}]))|\x{200D}(?:\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F468}|[\x{1F468}\x{1F469}]\x{200D}(?:\x{1F466}\x{200D}\x{1F466}|\x{1F467}\x{200D}[\x{1F466}\x{1F467}])|\x{1F466}\x{200D}\x{1F466}|\x{1F467}\x{200D}[\x{1F466}\x{1F467}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FF}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FE}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FE}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FD}\x{1F3FF}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FD}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}\x{1F3FC}\x{1F3FE}\x{1F3FF}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FC}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}\x{1F3FD}-\x{1F3FF}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])\x{FE0F}|\x{200D}(?:[\x{1F468}\x{1F469}]\x{200D}[\x{1F466}\x{1F467}]|[\x{1F466}\x{1F467}])|\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{200D}[\x{2695}\x{2696}\x{2708}])?|(?:\x{1F469}(?:\x{1F3FB}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])|\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}]))|[\x{1F3FC}-\x{1F3FF}]\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])|\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])))|\x{1F9D1}[\x{1F3FB}-\x{1F3FF}]\x{200D}\x{1F91D}\x{200D}\x{1F9D1})[\x{1F3FB}-\x{1F3FF}]|\x{1F469}\x{200D}\x{1F469}\x{200D}(?:\x{1F466}\x{200D}\x{1F466}|\x{1F467}\x{200D}[\x{1F466}\x{1F467}])|\x{1F469}(?:\x{200D}(?:\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])|\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}]))|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FF}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FE}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FD}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FC}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FB}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F9D1}(?:\x{200D}(?:\x{1F91D}\x{200D}\x{1F9D1}|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FF}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FE}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FD}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FC}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FB}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F469}\x{200D}\x{1F466}\x{200D}\x{1F466}|\x{1F469}\x{200D}\x{1F469}\x{200D}[\x{1F466}\x{1F467}]|\x{1F469}\x{200D}\x{1F467}\x{200D}[\x{1F466}\x{1F467}]|(?:\x{1F441}\x{FE0F}?\x{200D}\x{1F5E8}|\x{1F9D1}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F469}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F636}\x{200D}\x{1F32B}|\x{1F3F3}\x{FE0F}?\x{200D}\x{26A7}|\x{1F43B}\x{200D}\x{2744}|(?:[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}][\x{1F3FB}-\x{1F3FF}]|[\x{1F46F}\x{1F9DE}\x{1F9DF}])\x{200D}[\x{2640}\x{2642}]|[\x{26F9}\x{1F3CB}\x{1F3CC}\x{1F575}](?:[\x{FE0F}\x{1F3FB}-\x{1F3FF}]\x{200D}[\x{2640}\x{2642}]|\x{200D}[\x{2640}\x{2642}])|\x{1F3F4}\x{200D}\x{2620}|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93C}-\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}]\x{200D}[\x{2640}\x{2642}]|[\xA9\xAE\x{203C}\x{2049}\x{2122}\x{2139}\x{2194}-\x{2199}\x{21A9}\x{21AA}\x{231A}\x{231B}\x{2328}\x{23CF}\x{23ED}-\x{23EF}\x{23F1}\x{23F2}\x{23F8}-\x{23FA}\x{24C2}\x{25AA}\x{25AB}\x{25B6}\x{25C0}\x{25FB}\x{25FC}\x{25FE}\x{2600}-\x{2604}\x{260E}\x{2611}\x{2614}\x{2615}\x{2618}\x{2620}\x{2622}\x{2623}\x{2626}\x{262A}\x{262E}\x{262F}\x{2638}-\x{263A}\x{2640}\x{2642}\x{2648}-\x{2653}\x{265F}\x{2660}\x{2663}\x{2665}\x{2666}\x{2668}\x{267B}\x{267E}\x{267F}\x{2692}\x{2694}-\x{2697}\x{2699}\x{269B}\x{269C}\x{26A0}\x{26A7}\x{26AA}\x{26B0}\x{26B1}\x{26BD}\x{26BE}\x{26C4}\x{26C8}\x{26CF}\x{26D1}\x{26D3}\x{26E9}\x{26F0}-\x{26F5}\x{26F7}\x{26F8}\x{26FA}\x{2702}\x{2708}\x{2709}\x{270F}\x{2712}\x{2714}\x{2716}\x{271D}\x{2721}\x{2733}\x{2734}\x{2744}\x{2747}\x{2763}\x{27A1}\x{2934}\x{2935}\x{2B05}-\x{2B07}\x{2B1B}\x{2B1C}\x{2B55}\x{3030}\x{303D}\x{3297}\x{3299}\x{1F004}\x{1F170}\x{1F171}\x{1F17E}\x{1F17F}\x{1F202}\x{1F237}\x{1F321}\x{1F324}-\x{1F32C}\x{1F336}\x{1F37D}\x{1F396}\x{1F397}\x{1F399}-\x{1F39B}\x{1F39E}\x{1F39F}\x{1F3CD}\x{1F3CE}\x{1F3D4}-\x{1F3DF}\x{1F3F5}\x{1F3F7}\x{1F43F}\x{1F4FD}\x{1F549}\x{1F54A}\x{1F56F}\x{1F570}\x{1F573}\x{1F576}-\x{1F579}\x{1F587}\x{1F58A}-\x{1F58D}\x{1F5A5}\x{1F5A8}\x{1F5B1}\x{1F5B2}\x{1F5BC}\x{1F5C2}-\x{1F5C4}\x{1F5D1}-\x{1F5D3}\x{1F5DC}-\x{1F5DE}\x{1F5E1}\x{1F5E3}\x{1F5E8}\x{1F5EF}\x{1F5F3}\x{1F5FA}\x{1F6CB}\x{1F6CD}-\x{1F6CF}\x{1F6E0}-\x{1F6E5}\x{1F6E9}\x{1F6F0}\x{1F6F3}])\x{FE0F}|\x{1F441}\x{FE0F}?\x{200D}\x{1F5E8}|\x{1F9D1}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F469}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F3F3}\x{FE0F}?\x{200D}\x{1F308}|\x{1F469}\x{200D}\x{1F467}|\x{1F469}\x{200D}\x{1F466}|\x{1F636}\x{200D}\x{1F32B}|\x{1F3F3}\x{FE0F}?\x{200D}\x{26A7}|\x{1F635}\x{200D}\x{1F4AB}|\x{1F62E}\x{200D}\x{1F4A8}|\x{1F415}\x{200D}\x{1F9BA}|\x{1FAF1}(?:\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{1F3FB})?|\x{1F9D1}(?:\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{1F3FB})?|\x{1F469}(?:\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{1F3FB})?|\x{1F43B}\x{200D}\x{2744}|(?:[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}][\x{1F3FB}-\x{1F3FF}]|[\x{1F46F}\x{1F9DE}\x{1F9DF}])\x{200D}[\x{2640}\x{2642}]|[\x{26F9}\x{1F3CB}\x{1F3CC}\x{1F575}](?:[\x{FE0F}\x{1F3FB}-\x{1F3FF}]\x{200D}[\x{2640}\x{2642}]|\x{200D}[\x{2640}\x{2642}])|\x{1F3F4}\x{200D}\x{2620}|\x{1F1FD}\x{1F1F0}|\x{1F1F6}\x{1F1E6}|\x{1F1F4}\x{1F1F2}|\x{1F408}\x{200D}\x{2B1B}|\x{2764}(?:\x{FE0F}\x{200D}[\x{1F525}\x{1FA79}]|\x{200D}[\x{1F525}\x{1FA79}])|\x{1F441}\x{FE0F}?|\x{1F3F3}\x{FE0F}?|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93C}-\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}]\x{200D}[\x{2640}\x{2642}]|\x{1F1FF}[\x{1F1E6}\x{1F1F2}\x{1F1FC}]|\x{1F1FE}[\x{1F1EA}\x{1F1F9}]|\x{1F1FC}[\x{1F1EB}\x{1F1F8}]|\x{1F1FB}[\x{1F1E6}\x{1F1E8}\x{1F1EA}\x{1F1EC}\x{1F1EE}\x{1F1F3}\x{1F1FA}]|\x{1F1FA}[\x{1F1E6}\x{1F1EC}\x{1F1F2}\x{1F1F3}\x{1F1F8}\x{1F1FE}\x{1F1FF}]|\x{1F1F9}[\x{1F1E6}\x{1F1E8}\x{1F1E9}\x{1F1EB}-\x{1F1ED}\x{1F1EF}-\x{1F1F4}\x{1F1F7}\x{1F1F9}\x{1F1FB}\x{1F1FC}\x{1F1FF}]|\x{1F1F8}[\x{1F1E6}-\x{1F1EA}\x{1F1EC}-\x{1F1F4}\x{1F1F7}-\x{1F1F9}\x{1F1FB}\x{1F1FD}-\x{1F1FF}]|\x{1F1F7}[\x{1F1EA}\x{1F1F4}\x{1F1F8}\x{1F1FA}\x{1F1FC}]|\x{1F1F5}[\x{1F1E6}\x{1F1EA}-\x{1F1ED}\x{1F1F0}-\x{1F1F3}\x{1F1F7}-\x{1F1F9}\x{1F1FC}\x{1F1FE}]|\x{1F1F3}[\x{1F1E6}\x{1F1E8}\x{1F1EA}-\x{1F1EC}\x{1F1EE}\x{1F1F1}\x{1F1F4}\x{1F1F5}\x{1F1F7}\x{1F1FA}\x{1F1FF}]|\x{1F1F2}[\x{1F1E6}\x{1F1E8}-\x{1F1ED}\x{1F1F0}-\x{1F1FF}]|\x{1F1F1}[\x{1F1E6}-\x{1F1E8}\x{1F1EE}\x{1F1F0}\x{1F1F7}-\x{1F1FB}\x{1F1FE}]|\x{1F1F0}[\x{1F1EA}\x{1F1EC}-\x{1F1EE}\x{1F1F2}\x{1F1F3}\x{1F1F5}\x{1F1F7}\x{1F1FC}\x{1F1FE}\x{1F1FF}]|\x{1F1EF}[\x{1F1EA}\x{1F1F2}\x{1F1F4}\x{1F1F5}]|\x{1F1EE}[\x{1F1E8}-\x{1F1EA}\x{1F1F1}-\x{1F1F4}\x{1F1F6}-\x{1F1F9}]|\x{1F1ED}[\x{1F1F0}\x{1F1F2}\x{1F1F3}\x{1F1F7}\x{1F1F9}\x{1F1FA}]|\x{1F1EC}[\x{1F1E6}\x{1F1E7}\x{1F1E9}-\x{1F1EE}\x{1F1F1}-\x{1F1F3}\x{1F1F5}-\x{1F1FA}\x{1F1FC}\x{1F1FE}]|\x{1F1EB}[\x{1F1EE}-\x{1F1F0}\x{1F1F2}\x{1F1F4}\x{1F1F7}]|\x{1F1EA}[\x{1F1E6}\x{1F1E8}\x{1F1EA}\x{1F1EC}\x{1F1ED}\x{1F1F7}-\x{1F1FA}]|\x{1F1E9}[\x{1F1EA}\x{1F1EC}\x{1F1EF}\x{1F1F0}\x{1F1F2}\x{1F1F4}\x{1F1FF}]|\x{1F1E8}[\x{1F1E6}\x{1F1E8}\x{1F1E9}\x{1F1EB}-\x{1F1EE}\x{1F1F0}-\x{1F1F5}\x{1F1F7}\x{1F1FA}-\x{1F1FF}]|\x{1F1E7}[\x{1F1E6}\x{1F1E7}\x{1F1E9}-\x{1F1EF}\x{1F1F1}-\x{1F1F4}\x{1F1F6}-\x{1F1F9}\x{1F1FB}\x{1F1FC}\x{1F1FE}\x{1F1FF}]|\x{1F1E6}[\x{1F1E8}-\x{1F1EC}\x{1F1EE}\x{1F1F1}\x{1F1F2}\x{1F1F4}\x{1F1F6}-\x{1F1FA}\x{1F1FC}\x{1F1FD}\x{1F1FF}]|[#\*0-9]\x{FE0F}?\x{20E3}|\x{1F93C}[\x{1F3FB}-\x{1F3FF}]|\x{2764}\x{FE0F}?|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}][\x{1F3FB}-\x{1F3FF}]|[\x{26F9}\x{1F3CB}\x{1F3CC}\x{1F575}][\x{FE0F}\x{1F3FB}-\x{1F3FF}]?|\x{1F3F4}|[\x{270A}\x{270B}\x{1F385}\x{1F3C2}\x{1F3C7}\x{1F442}\x{1F443}\x{1F446}-\x{1F450}\x{1F466}\x{1F467}\x{1F46B}-\x{1F46D}\x{1F472}\x{1F474}-\x{1F476}\x{1F478}\x{1F47C}\x{1F483}\x{1F485}\x{1F48F}\x{1F491}\x{1F4AA}\x{1F57A}\x{1F595}\x{1F596}\x{1F64C}\x{1F64F}\x{1F6C0}\x{1F6CC}\x{1F90C}\x{1F90F}\x{1F918}-\x{1F91F}\x{1F930}-\x{1F934}\x{1F936}\x{1F977}\x{1F9B5}\x{1F9B6}\x{1F9BB}\x{1F9D2}\x{1F9D3}\x{1F9D5}\x{1FAC3}-\x{1FAC5}\x{1FAF0}\x{1FAF2}-\x{1FAF6}][\x{1F3FB}-\x{1F3FF}]|[\x{261D}\x{270C}\x{270D}\x{1F574}\x{1F590}][\x{FE0F}\x{1F3FB}-\x{1F3FF}]|[\x{261D}\x{270A}-\x{270D}\x{1F385}\x{1F3C2}\x{1F3C7}\x{1F408}\x{1F415}\x{1F43B}\x{1F442}\x{1F443}\x{1F446}-\x{1F450}\x{1F466}\x{1F467}\x{1F46B}-\x{1F46D}\x{1F472}\x{1F474}-\x{1F476}\x{1F478}\x{1F47C}\x{1F483}\x{1F485}\x{1F48F}\x{1F491}\x{1F4AA}\x{1F574}\x{1F57A}\x{1F590}\x{1F595}\x{1F596}\x{1F62E}\x{1F635}\x{1F636}\x{1F64C}\x{1F64F}\x{1F6C0}\x{1F6CC}\x{1F90C}\x{1F90F}\x{1F918}-\x{1F91F}\x{1F930}-\x{1F934}\x{1F936}\x{1F93C}\x{1F977}\x{1F9B5}\x{1F9B6}\x{1F9BB}\x{1F9D2}\x{1F9D3}\x{1F9D5}\x{1FAC3}-\x{1FAC5}\x{1FAF0}\x{1FAF2}-\x{1FAF6}]|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}]|[\x{1F46F}\x{1F9DE}\x{1F9DF}]|[\xA9\xAE\x{203C}\x{2049}\x{2122}\x{2139}\x{2194}-\x{2199}\x{21A9}\x{21AA}\x{231A}\x{231B}\x{2328}\x{23CF}\x{23ED}-\x{23EF}\x{23F1}\x{23F2}\x{23F8}-\x{23FA}\x{24C2}\x{25AA}\x{25AB}\x{25B6}\x{25C0}\x{25FB}\x{25FC}\x{25FE}\x{2600}-\x{2604}\x{260E}\x{2611}\x{2614}\x{2615}\x{2618}\x{2620}\x{2622}\x{2623}\x{2626}\x{262A}\x{262E}\x{262F}\x{2638}-\x{263A}\x{2640}\x{2642}\x{2648}-\x{2653}\x{265F}\x{2660}\x{2663}\x{2665}\x{2666}\x{2668}\x{267B}\x{267E}\x{267F}\x{2692}\x{2694}-\x{2697}\x{2699}\x{269B}\x{269C}\x{26A0}\x{26A7}\x{26AA}\x{26B0}\x{26B1}\x{26BD}\x{26BE}\x{26C4}\x{26C8}\x{26CF}\x{26D1}\x{26D3}\x{26E9}\x{26F0}-\x{26F5}\x{26F7}\x{26F8}\x{26FA}\x{2702}\x{2708}\x{2709}\x{270F}\x{2712}\x{2714}\x{2716}\x{271D}\x{2721}\x{2733}\x{2734}\x{2744}\x{2747}\x{2763}\x{27A1}\x{2934}\x{2935}\x{2B05}-\x{2B07}\x{2B1B}\x{2B1C}\x{2B55}\x{3030}\x{303D}\x{3297}\x{3299}\x{1F004}\x{1F170}\x{1F171}\x{1F17E}\x{1F17F}\x{1F202}\x{1F237}\x{1F321}\x{1F324}-\x{1F32C}\x{1F336}\x{1F37D}\x{1F396}\x{1F397}\x{1F399}-\x{1F39B}\x{1F39E}\x{1F39F}\x{1F3CD}\x{1F3CE}\x{1F3D4}-\x{1F3DF}\x{1F3F5}\x{1F3F7}\x{1F43F}\x{1F4FD}\x{1F549}\x{1F54A}\x{1F56F}\x{1F570}\x{1F573}\x{1F576}-\x{1F579}\x{1F587}\x{1F58A}-\x{1F58D}\x{1F5A5}\x{1F5A8}\x{1F5B1}\x{1F5B2}\x{1F5BC}\x{1F5C2}-\x{1F5C4}\x{1F5D1}-\x{1F5D3}\x{1F5DC}-\x{1F5DE}\x{1F5E1}\x{1F5E3}\x{1F5E8}\x{1F5EF}\x{1F5F3}\x{1F5FA}\x{1F6CB}\x{1F6CD}-\x{1F6CF}\x{1F6E0}-\x{1F6E5}\x{1F6E9}\x{1F6F0}\x{1F6F3}]|[\x{23E9}-\x{23EC}\x{23F0}\x{23F3}\x{25FD}\x{2693}\x{26A1}\x{26AB}\x{26C5}\x{26CE}\x{26D4}\x{26EA}\x{26FD}\x{2705}\x{2728}\x{274C}\x{274E}\x{2753}-\x{2755}\x{2757}\x{2795}-\x{2797}\x{27B0}\x{27BF}\x{2B50}\x{1F0CF}\x{1F18E}\x{1F191}-\x{1F19A}\x{1F201}\x{1F21A}\x{1F22F}\x{1F232}-\x{1F236}\x{1F238}-\x{1F23A}\x{1F250}\x{1F251}\x{1F300}-\x{1F320}\x{1F32D}-\x{1F335}\x{1F337}-\x{1F37C}\x{1F37E}-\x{1F384}\x{1F386}-\x{1F393}\x{1F3A0}-\x{1F3C1}\x{1F3C5}\x{1F3C6}\x{1F3C8}\x{1F3C9}\x{1F3CF}-\x{1F3D3}\x{1F3E0}-\x{1F3F0}\x{1F3F8}-\x{1F407}\x{1F409}-\x{1F414}\x{1F416}-\x{1F43A}\x{1F43C}-\x{1F43E}\x{1F440}\x{1F444}\x{1F445}\x{1F451}-\x{1F465}\x{1F46A}\x{1F479}-\x{1F47B}\x{1F47D}-\x{1F480}\x{1F484}\x{1F488}-\x{1F48E}\x{1F490}\x{1F492}-\x{1F4A9}\x{1F4AB}-\x{1F4FC}\x{1F4FF}-\x{1F53D}\x{1F54B}-\x{1F54E}\x{1F550}-\x{1F567}\x{1F5A4}\x{1F5FB}-\x{1F62D}\x{1F62F}-\x{1F634}\x{1F637}-\x{1F644}\x{1F648}-\x{1F64A}\x{1F680}-\x{1F6A2}\x{1F6A4}-\x{1F6B3}\x{1F6B7}-\x{1F6BF}\x{1F6C1}-\x{1F6C5}\x{1F6D0}-\x{1F6D2}\x{1F6D5}-\x{1F6D7}\x{1F6DD}-\x{1F6DF}\x{1F6EB}\x{1F6EC}\x{1F6F4}-\x{1F6FC}\x{1F7E0}-\x{1F7EB}\x{1F7F0}\x{1F90D}\x{1F90E}\x{1F910}-\x{1F917}\x{1F920}-\x{1F925}\x{1F927}-\x{1F92F}\x{1F93A}\x{1F93F}-\x{1F945}\x{1F947}-\x{1F976}\x{1F978}-\x{1F9B4}\x{1F9B7}\x{1F9BA}\x{1F9BC}-\x{1F9CC}\x{1F9D0}\x{1F9E0}-\x{1F9FF}\x{1FA70}-\x{1FA74}\x{1FA78}-\x{1FA7C}\x{1FA80}-\x{1FA86}\x{1FA90}-\x{1FAAC}\x{1FAB0}-\x{1FABA}\x{1FAC0}-\x{1FAC2}\x{1FAD0}-\x{1FAD9}\x{1FAE0}-\x{1FAE7}]/u', '', $text); -} - -function shortenClient($client) -{ - // Pre-process by removing any non-alphanumeric characters except for certain punctuations. - $client = html_entity_decode($client); // Decode any HTML entities - $client = str_replace("'", "", $client); // Removing all occurrences of ' - $cleaned = preg_replace('/[^a-zA-Z0-9&]+/', ' ', $client); - - // Break into words. - $words = explode(' ', trim($cleaned)); - - $shortened = ''; - - // If there's only one word. - if (count($words) == 1) { - $word = $words[0]; - - if (strlen($word) <= 3) { - return strtoupper($word); - } - - // Prefer starting and ending characters. - $shortened = $word[0] . substr($word, -2); - } else { - // Less weightage to common words. - $commonWords = ['the', 'of', 'and']; - - foreach ($words as $word) { - if (!in_array(strtolower($word), $commonWords) || strlen($shortened) < 2) { - $shortened .= $word[0]; - } - } - - // If there are still not enough characters, take from the last word. - while (strlen($shortened) < 3 && !empty($word)) { - $shortened .= substr($word, 1, 1); - $word = substr($word, 1); - } - } - - return strtoupper(substr($shortened, 0, 3)); -} - -function roundToNearest15($time) -{ - // Validate the input time format - if (!preg_match('/^(\d{2}):(\d{2}):(\d{2})$/', $time, $matches)) { - return false; // or throw an exception - } - - // Extract hours, minutes, and seconds from the matched time string - list(, $hours, $minutes, $seconds) = $matches; - - // Convert everything to seconds for easier calculation - $totalSeconds = ($hours * 3600) + ($minutes * 60) + $seconds; - - // Calculate the remainder when divided by 900 seconds (15 minutes) - $remainder = $totalSeconds % 900; - - if ($remainder > 450) { // If remainder is more than 7.5 minutes (450 seconds), round up - $totalSeconds += (900 - $remainder); - } else { // Else round down - $totalSeconds -= $remainder; - } - - // Convert total seconds to decimal hours - $decimalHours = $totalSeconds / 3600; - - // Return the decimal hours - return number_format($decimalHours, 2); -} - -// Get the value of a setting from the database -function getSettingValue($mysqli, $setting_name) -{ - //if starts with config_ then get from config table - if (substr($setting_name, 0, 7) == "config_") { - $sql = mysqli_query($mysqli, "SELECT $setting_name FROM settings"); - $row = mysqli_fetch_array($sql); - return $row[$setting_name]; - } elseif (substr($setting_name, 0, 7) == "company") { - $sql = mysqli_query($mysqli, "SELECT $setting_name FROM companies"); - $row = mysqli_fetch_array($sql); - return $row[$setting_name]; - } else { - return "Cannot Find Setting Name"; +//Function used to get rest of functions, can also be used for other folders. +function requireOnceAll($functionsPath) { + foreach (glob($functionsPath . '/*.php') as $file) { + require_once($file); } } -function getMonthlyTax($tax_name, $month, $year, $mysqli) -{ - // SQL to calculate monthly tax - $sql = "SELECT SUM(item_tax) AS monthly_tax FROM invoice_items - LEFT JOIN invoices ON invoice_items.item_invoice_id = invoices.invoice_id - LEFT JOIN payments ON invoices.invoice_id = payments.payment_invoice_id - WHERE YEAR(payments.payment_date) = $year AND MONTH(payments.payment_date) = $month - AND invoice_items.item_tax_id = (SELECT tax_id FROM taxes WHERE tax_name = '$tax_name')"; - $result = mysqli_query($mysqli, $sql); - $row = mysqli_fetch_assoc($result); - return $row['monthly_tax'] ?? 0; -} - -function getQuarterlyTax($tax_name, $quarter, $year, $mysqli) -{ - // Calculate start and end months for the quarter - $start_month = ($quarter - 1) * 3 + 1; - $end_month = $start_month + 2; - - // SQL to calculate quarterly tax - $sql = "SELECT SUM(item_tax) AS quarterly_tax FROM invoice_items - LEFT JOIN invoices ON invoice_items.item_invoice_id = invoices.invoice_id - LEFT JOIN payments ON invoices.invoice_id = payments.payment_invoice_id - WHERE YEAR(payments.payment_date) = $year AND MONTH(payments.payment_date) BETWEEN $start_month AND $end_month - AND invoice_items.item_tax_id = (SELECT tax_id FROM taxes WHERE tax_name = '$tax_name')"; - $result = mysqli_query($mysqli, $sql); - $row = mysqli_fetch_assoc($result); - return $row['quarterly_tax'] ?? 0; -} +// Other functions are categorized in different files +// Load all functions -function getTotalTax($tax_name, $year, $mysqli) -{ - // SQL to calculate total tax - $sql = "SELECT SUM(item_tax) AS total_tax FROM invoice_items - LEFT JOIN invoices ON invoice_items.item_invoice_id = invoices.invoice_id - LEFT JOIN payments ON invoices.invoice_id = payments.payment_invoice_id - WHERE YEAR(payments.payment_date) = $year - AND invoice_items.item_tax_id = (SELECT tax_id FROM taxes WHERE tax_name = '$tax_name')"; - $result = mysqli_query($mysqli, $sql); - $row = mysqli_fetch_assoc($result); - return $row['total_tax'] ?? 0; -} - -//Get account currency code -function getAccountCurrencyCode($mysqli, $account_id) -{ - $sql = mysqli_query($mysqli, "SELECT account_currency_code FROM accounts WHERE account_id = $account_id"); - $row = mysqli_fetch_array($sql); - $account_currency_code = nullable_htmlentities($row['account_currency_code']); - return $account_currency_code; -} - -function calculateAccountBalance($mysqli, $account_id) -{ - $sql_account = mysqli_query($mysqli, "SELECT * FROM accounts LEFT JOIN account_types ON accounts.account_type = account_types.account_type_id WHERE account_archived_at IS NULL AND account_id = $account_id ORDER BY account_name ASC; "); - $row = mysqli_fetch_array($sql_account); - $opening_balance = floatval($row['opening_balance']); - $account_id = intval($row['account_id']); - - $sql_payments = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS total_payments FROM payments WHERE payment_account_id = $account_id"); - $row = mysqli_fetch_array($sql_payments); - $total_payments = floatval($row['total_payments']); - - $sql_revenues = mysqli_query($mysqli, "SELECT SUM(revenue_amount) AS total_revenues FROM revenues WHERE revenue_account_id = $account_id"); - $row = mysqli_fetch_array($sql_revenues); - $total_revenues = floatval($row['total_revenues']); - - $sql_expenses = mysqli_query($mysqli, "SELECT SUM(expense_amount) AS total_expenses FROM expenses WHERE expense_account_id = $account_id"); - $row = mysqli_fetch_array($sql_expenses); - $total_expenses = floatval($row['total_expenses']); - - $balance = $opening_balance + $total_payments + $total_revenues - $total_expenses; - - if ($balance == '') { - $balance = '0.00'; - } - - return $balance; -} - - -function generateReadablePassword($security_level) -{ - // Cap security level at 5 - $security_level = intval($security_level); - $security_level = min($security_level, 5); - - // Arrays of words - $articles = ['The', 'A']; - $adjectives = ['Smart', 'Swift', 'Secure', 'Stable', 'Digital', 'Virtual', 'Active', 'Dynamic', 'Innovative', 'Efficient', 'Portable', 'Wireless', 'Rapid', 'Intuitive', 'Automated', 'Robust', 'Reliable', 'Sleek', 'Modern', 'Happy', 'Funny', 'Quick', 'Bright', 'Clever', 'Gentle', 'Brave', 'Calm', 'Eager', 'Fierce', 'Kind', 'Lucky', 'Proud', 'Silly', 'Witty', 'Bold', 'Curious', 'Elated', 'Gracious', 'Honest', 'Jolly', 'Merry', 'Noble', 'Optimistic', 'Playful', 'Quirky', 'Rustic', 'Steady', 'Tranquil', 'Upbeat']; - $nouns = ['Computer', 'Laptop', 'Tablet', 'Server', 'Router', 'Software', 'Hardware', 'Pixel', 'Byte', 'App', 'Network', 'Cloud', 'Firewall', 'Email', 'Database', 'Folder', 'Document', 'Interface', 'Program', 'Gadget', 'Dinosaur', 'Tiger', 'Elephant', 'Kangaroo', 'Monkey', 'Unicorn', 'Dragon', 'Puppy', 'Kitten', 'Parrot', 'Lion', 'Bear', 'Fox', 'Wolf', 'Rabbit', 'Deer', 'Owl', 'Hedgehog', 'Turtle', 'Frog', 'Butterfly', 'Panda', 'Giraffe', 'Zebra', 'Peacock', 'Koala', 'Raccoon', 'Squirrel', 'Hippo', 'Rhino', 'Book', "Monitor"]; - $verbs = ['Connects', 'Runs', 'Processes', 'Secures', 'Encrypts', 'Saves', 'Updates', 'Boots', 'Scans', 'Compiles', 'Executes', 'Restores', 'Installs', 'Configures', 'Downloads', 'Streams', 'BacksUp', 'Syncs', 'Browses', 'Navigates', 'Runs', 'Jumps', 'Flies', 'Swims', 'Dances', 'Sings', 'Hops', 'Skips', 'Races', 'Climbs', 'Crawls', 'Glides', 'Twirls', 'Swings', 'Sprints', 'Gallops', 'Trots', 'Wanders', 'Strolls', 'Marches']; - $adverbs = ['Quickly', 'Slowly', 'Gracefully', 'Wildly', 'Loudly', 'Silently', 'Cheerfully', 'Eagerly', 'Gently', 'Happily', 'Jovially', 'Kindly', 'Lazily', 'Merrily', 'Neatly', 'Politely', 'Quietly', 'Rapidly', 'Smoothly', 'Tightly', 'Swiftly', 'Securely', 'Efficiently', 'Rapidly', 'Smoothly', 'Reliably', 'Safely', 'Wirelessly', 'Instantly', 'Silently', 'Automatically', 'Seamlessly', 'Digitally', 'Virtually', 'Continuously', 'Regularly', 'Intelligently', 'Logically']; - - // Randomly select words from arrays - $adj = $adjectives[array_rand($adjectives)]; - $noun = $nouns[array_rand($nouns)]; - $verb = $verbs[array_rand($verbs)]; - $adv = $adverbs[array_rand($adverbs)]; - - // Combine to create a base password - $password = $adj . $noun . $verb . $adv; - - // Select an article randomly - $article = $articles[array_rand($articles)]; - - // Determine if we should use 'An' instead of 'A' - if ($article == 'A' && preg_match('/^[aeiouAEIOU]/', $adj)) { - $article = 'An'; - } - - // Add the article to the password - $password = $article . $password; - - // Mapping of letters to special characters and numbers - $mappings = [ - 'A' => '@', 'a' => '@', - 'E' => '3', 'e' => '3', - 'I' => '!', 'i' => '!', - 'O' => '0', 'o' => '0', - 'S' => '$', 's' => '$', - 'T' => '+', 't' => '+', - 'B' => '8', 'b' => '8' - ]; - - // Generate an array of indices based on the password length - $indices = range(0, strlen($password) - 1); - // Randomly shuffle the indices - shuffle($indices); - - // Iterate through the shuffled indices and replace characters based on the security level - for ($i = 0; $i < min($security_level, strlen($password)); $i++) { - $index = $indices[$i]; // Get a random index - $currentChar = $password[$index]; // Get the character at this index - // Check if the current character has a mapping and replace it - if (array_key_exists($currentChar, $mappings)) { - $password[$index] = $mappings[$currentChar]; - } - } - - // Add as many random numbers as the security level - $password .= rand(pow(10, $security_level - 1), pow(10, $security_level) - 1); - - return $password; -} - -function addToMailQueue($mysqli, $data) -{ - - foreach ($data as $email) { - $from = strval($email['from']); - $from_name = strval($email['from_name']); - $recipient = strval($email['recipient']); - $recipient_name = strval($email['recipient_name']); - $subject = strval($email['subject']); - $body = strval($email['body']); - - $cal_str = ''; - if (isset($email['cal_str'])) { - $cal_str = mysqli_escape_string($mysqli,$email['cal_str']); - } - - // Check if 'email_queued_at' is set and not empty - if (isset($email['queued_at']) && !empty($email['queued_at'])) { - $queued_at = $email['queued_at']; - } else { - // Use the current date and time if 'email_queued_at' is not set or empty - $queued_at = date('Y-m-d H:i:s'); - } - - mysqli_query($mysqli, "INSERT INTO email_queue SET email_recipient = '$recipient', email_recipient_name = '$recipient_name', email_from = '$from', email_from_name = '$from_name', email_subject = '$subject', email_content = '$body', email_queued_at = '$queued_at', email_cal_str = '$cal_str'"); - } - - return true; -} - -function calculateInvoiceBalance($mysqli, $invoice_id) -{ - $invoice_id_int = intval($invoice_id); - $sql_invoice = mysqli_query($mysqli, "SELECT * FROM invoices WHERE invoice_id = $invoice_id_int"); - $row = mysqli_fetch_array($sql_invoice); - $invoice_amount = floatval($row['invoice_amount']); - - $sql_payments = mysqli_query( - $mysqli, - "SELECT SUM(payment_amount) AS total_payments FROM payments - WHERE payment_invoice_id = $invoice_id - " - ); - - $row = mysqli_fetch_array($sql_payments); - $total_payments = floatval($row['total_payments']); - - $balance = $invoice_amount - $total_payments; - - if ($balance == '') { - $balance = '0.00'; - } - - return $balance; -} - -function createiCalStr($datetime, $title, $description, $location) -{ - require_once "plugins/zapcal/zapcallib.php"; - - // Create the iCal object - $cal_event = new ZCiCal(); - $event = new ZCiCalNode("VEVENT", $cal_event->curnode); - - - // Set the method to REQUEST to indicate an invite - $event->addNode(new ZCiCalDataNode("METHOD:REQUEST")); - $event->addNode(new ZCiCalDataNode("SUMMARY:" . $title)); - $event->addNode(new ZCiCalDataNode("DTSTART:" . ZCiCal::fromSqlDateTime($datetime))); - // Assuming the end time is the same as start time. - // Todo: adjust this for actual duration - $event->addNode(new ZCiCalDataNode("DTEND:" . ZCiCal::fromSqlDateTime($datetime))); - $event->addNode(new ZCiCalDataNode("DTSTAMP:" . ZCiCal::fromSqlDateTime())); - $uid = date('Y-m-d-H-i-s') . "@" . $_SERVER['SERVER_NAME']; - $event->addNode(new ZCiCalDataNode("UID:" . $uid)); - $event->addNode(new ZCiCalDataNode("LOCATION:" . $location)); - $event->addNode(new ZCiCalDataNode("DESCRIPTION:" . $description)); - // Todo: add organizer details - // $event->addNode(new ZCiCalDataNode("ORGANIZER;CN=Organizer Name:MAILTO:organizer@example.com")); - - // Return the iCal string - return $cal_event->export(); -} - -function isMobile() -{ - // Check if the user agent is a mobile device - return preg_match('/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|opera mini|palm|phone|pie|tablet|up.browser|up.link|webos|wos)/i', $_SERVER['HTTP_USER_AGENT']); -} - -function createiCalStrCancel($originaliCalStr) { - require_once "plugins/zapcal/zapcallib.php"; - - // Import the original iCal string - $cal_event = new ZCiCal($originaliCalStr); - - // Iterate through the iCalendar object to find VEVENT nodes - foreach($cal_event->tree->child as $node) { - if($node->getName() == "VEVENT") { - // Check if STATUS node exists, update it, or add a new one - $statusFound = false; - foreach($node->data as $key => $value) { - if($key == "STATUS") { - $value->setValue("CANCELLED"); - $statusFound = true; - break; // Exit the loop once the STATUS is updated - } - } - // If STATUS node is not found, add a new STATUS node - if (!$statusFound) { - $node->addNode(new ZCiCalDataNode("STATUS:CANCELLED")); - } - } - } - - // Return the modified iCal string - return $cal_event->export(); -} - - -function getClientBalance($mysqli, $client_id, $credits = false) { - //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 NOT LIKE 'Draft' AND invoice_status NOT LIKE 'Cancelled'"); - $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']); - - if ($credits) { - $sql_credits = mysqli_query($mysqli, "SELECT SUM(credit_amount) AS credit_amounts FROM credits WHERE credit_client_id = $client_id"); - $row = mysqli_fetch_array($sql_credits); - $credit_amounts = floatval($row['credit_amounts']); - - return $invoice_amounts - ($amount_paid + $credit_amounts); - } else { - return $invoice_amounts - $amount_paid; - } -} - - -function getTicketStatusColor($status) { - switch ($status) { - - case 'New': - return 'danger'; - - case 'Assigned': - return 'danger'; - - case 'Open': - return 'warning'; - - case 'On Hold': - return 'success'; - - case 'Closed': - return 'dark'; - - case 'Auto Close': - return 'dark'; - - case 'In-Progress': - return 'primary'; - - default: - return 'secondary'; - } -} \ No newline at end of file +// Set the path to the functions folder +$functionsPath = 'functions/'; +// Require Once All in the functions folder +requireOnceAll($functionsPath); \ No newline at end of file diff --git a/functions/account_functions.php b/functions/account_functions.php new file mode 100644 index 000000000..5c2c79046 --- /dev/null +++ b/functions/account_functions.php @@ -0,0 +1,127 @@ += $invoice_balance) { + $payment_amount = $invoice_balance; + $invoice_status = "Paid"; + } else { + $payment_amount = $credit_amount; + $invoice_status = "Partial"; + } + + $invoice_applied_count++; + + // Subtract the payment amount from the credit amount + $credit_amount -= $payment_amount; + + // Get Invoice Remain Balance + $remaining_invoice_balance = $invoice_balance - $payment_amount; + + // Add Payment + $payment_query = "INSERT INTO payments SET payment_date = CURDATE(), payment_amount = $payment_amount, payment_invoice_id = $invoice_id, payment_account_id = 1, payment_currency_code = '{$credit_row['credit_currency_code']}', payment_reference = 'Credit Applied'"; + mysqli_query($mysqli, $payment_query); + $payment_id = mysqli_insert_id($mysqli); + + // Update Invoice Status + $update_invoice_query = "UPDATE invoices SET invoice_status = '{$invoice_status}' WHERE invoice_id = {$invoice_id}"; + mysqli_query($mysqli, $update_invoice_query); + + // Add Payment to History + $history_description = "Payment added"; + $add_history_query = "INSERT INTO history (history_status, history_description, history_invoice_id) VALUES ('{$invoice_status}', '{$history_description}', {$invoice_id})"; + mysqli_query($mysqli, $add_history_query); + + // Add to Email Body Invoice Portion + + $email_body_invoices .= "
Invoice $invoice_prefix$invoice_number - Outstanding Amount: " . numfmt_format_currency($currency_format, $invoice_balance, $credit_currency_code) . " - Payment Applied: " . numfmt_format_currency($currency_format, $payment_amount, $credit_currency_code) . " - New Balance: " . numfmt_format_currency($currency_format, $remaining_invoice_balance, $credit_currency_code); + + } // End Invoice Loop + + //Todo add option to send receipts + $email_receipt = 1; + + // Send Email + if ($email_receipt == 1) { + + // Get Client / Contact Info + $sql_client = mysqli_query($mysqli,"SELECT * FROM clients + LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id + AND contact_primary = 1 + WHERE client_id = $client_id" + ); + + $row = mysqli_fetch_array($sql_client); + $client_name = sanitizeInput($row['client_name']); + $contact_name = sanitizeInput($row['contact_name']); + $contact_email = sanitizeInput($row['contact_email']); + + $sql_company = mysqli_query($mysqli,"SELECT company_name, company_phone FROM companies WHERE company_id = 1"); + $row = mysqli_fetch_array($sql_company); + + $company_name = sanitizeInput($row['company_name']); + $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); + + // Sanitize Config vars from get_settings.php + $config_invoice_from_name = sanitizeInput($config_invoice_from_name); + $config_invoice_from_email = sanitizeInput($config_invoice_from_email); + + $subject = "Payment Received - Multiple Invoices"; + $body = "Hello $contact_name,

Thank you for your payment of " . numfmt_format_currency($currency_format, $credit_amount, $credit_currency_code) . " We\'ve applied your payment to the following invoices, updating their balances accordingly:

$email_body_invoices


We appreciate your continued business!

Sincerely,
$company_name - Billing
$config_invoice_from_email
$company_phone"; + + // Queue Mail + mysqli_query($mysqli, "INSERT INTO email_queue SET email_recipient = '$contact_email', email_recipient_name = '$contact_name', email_from = '$config_invoice_from_email', email_from_name = '$config_invoice_from_name', email_subject = '$subject', email_content = '$body'"); + + // Get Email ID for reference + $email_id = mysqli_insert_id($mysqli); + + // Email Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Email', log_description = 'Bulk Payment receipt for multiple Invoices queued to $contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id"); + + $_SESSION['alert_message'] .= "Email receipt sent and "; + + } // End Email + + // Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Create', log_description = 'Bulk Payment of $credit_amount', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id"); + +} + +function deleteCredit( + $credit_id +){ + + // Access global variables + global $mysqli, $session_user_id, $session_ip, $session_user_agent; + + mysqli_query($mysqli,"DELETE FROM credits WHERE credit_id = $credit_id"); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Credit', log_action = 'Delete', log_description = 'Credit $credit_id deleted', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); +} \ No newline at end of file diff --git a/functions/crpyto_functions.php b/functions/crpyto_functions.php new file mode 100644 index 000000000..ac1787514 --- /dev/null +++ b/functions/crpyto_functions.php @@ -0,0 +1,37 @@ +format('Y-m-d'); + } + + // Default return + return "NULL"; +} + +// Get domain general info (whois + NS/A/MX records) +function getDomainRecords($name) +{ + + $records = array(); + + // Only run if we think the domain is valid + if (!filter_var($name, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) { + $records['a'] = ''; + $records['ns'] = ''; + $records['mx'] = ''; + $records['whois'] = ''; + return $records; + } + + $domain = escapeshellarg(str_replace('www.', '', $name)); + $records['a'] = substr(trim(strip_tags(shell_exec("dig +short $domain"))), 0, 254); + $records['ns'] = substr(trim(strip_tags(shell_exec("dig +short NS $domain"))), 0, 254); + $records['mx'] = substr(trim(strip_tags(shell_exec("dig +short MX $domain"))), 0, 254); + $records['txt'] = substr(trim(strip_tags(shell_exec("dig +short TXT $domain"))), 0, 254); + $records['whois'] = substr(trim(strip_tags(shell_exec("whois -H $domain | sed 's/ //g' | head -30"))), 0, 254); + + return $records; +} + +// Used to automatically attempt to get SSL certificates as part of adding domains +// The logic for the fetch (sync) button on the client_certificates page is in ajax.php, and allows ports other than 443 +function getSSL($name) +{ + + $certificate = array(); + $certificate['success'] = false; + + // Only run if we think the domain is valid + if (!filter_var($name, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) { + $certificate['expire'] = ''; + $certificate['issued_by'] = ''; + $certificate['public_key'] = ''; + return $certificate; + } + + // Get SSL/TSL certificate (using verify peer false to allow for self-signed certs) for domain on default port + $socket = "ssl://$name:443"; + $get = stream_context_create(array("ssl" => array("capture_peer_cert" => true, "verify_peer" => false,))); + $read = stream_socket_client($socket, $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $get); + + // If the socket connected + if ($read) { + $cert = stream_context_get_params($read); + $cert_public_key_obj = openssl_x509_parse($cert['options']['ssl']['peer_certificate']); + openssl_x509_export($cert['options']['ssl']['peer_certificate'], $export); + + if ($cert_public_key_obj) { + $certificate['success'] = true; + $certificate['expire'] = date('Y-m-d', $cert_public_key_obj['validTo_time_t']); + $certificate['issued_by'] = strip_tags($cert_public_key_obj['issuer']['O']); + $certificate['public_key'] = $export; + } + } + + return $certificate; +} diff --git a/functions/email_functions.php b/functions/email_functions.php new file mode 100644 index 000000000..fae5fb3e8 --- /dev/null +++ b/functions/email_functions.php @@ -0,0 +1,263 @@ +CharSet = "UTF-8"; // Specify UTF-8 charset to ensure symbols ($/£) load correctly + $mail->SMTPDebug = 0; // No Debugging + $mail->isSMTP(); // Set mailer to use SMTP + $mail->Host = $config_smtp_host; // Specify SMTP server + $mail->SMTPAuth = $smtp_auth; // Enable SMTP authentication + $mail->Username = $config_smtp_username; // SMTP username + $mail->Password = $config_smtp_password; // SMTP password + $mail->SMTPSecure = $config_smtp_encryption; // Enable TLS encryption, `ssl` also accepted + $mail->Port = $config_smtp_port; // TCP port to connect to + + //Recipients + $mail->setFrom($from_email, $from_name); + $mail->addAddress("$to_email", "$to_name"); // Add a recipient + + // Content + $mail->isHTML(true); // Set email format to HTML + $mail->Subject = "$subject"; // Subject + $mail->Body = " + + + + + + + + "; // Content + + // Attachments - todo + //$mail->addAttachment('/var/tmp/file.tar.gz'); // Add attachments + //$mail->addAttachment('/tmp/image.jpg', 'new.jpg'); // Optional name + + if (!empty($ics_str)) { + $mail->addStringAttachment($ics_str, 'Scheduled_ticket.ics', 'base64', 'text/calendar'); + } + + // Send + $mail->send(); + + // Return true if this was successful + return true; + } catch (Exception $e) { + // If we couldn't send the message return the error, so we can log it in the database (truncated) + error_log("ITFlow - Failed to send email: " . $mail->ErrorInfo); + return substr("Mailer Error: $mail->ErrorInfo", 0, 150) . "..."; + } +} + +// Add an email to the queue +function addToMailQueue($mysqli, $data) +{ + + foreach ($data as $email) { + $from = strval($email['from']); + $from_name = strval($email['from_name']); + $recipient = strval($email['recipient']); + $recipient_name = strval($email['recipient_name']); + $subject = strval($email['subject']); + $body = strval($email['body']); + + $cal_str = ''; + if (isset($email['cal_str'])) { + $cal_str = mysqli_escape_string($mysqli,$email['cal_str']); + } + + // Check if 'email_queued_at' is set and not empty + if (isset($email['queued_at']) && !empty($email['queued_at'])) { + $queued_at = $email['queued_at']; + } else { + // Use the current date and time if 'email_queued_at' is not set or empty + $queued_at = date('Y-m-d H:i:s'); + } + + mysqli_query($mysqli, "INSERT INTO email_queue SET email_recipient = '$recipient', email_recipient_name = '$recipient_name', email_from = '$from', email_from_name = '$from_name', email_subject = '$subject', email_content = '$body', email_queued_at = '$queued_at', email_cal_str = '$cal_str'"); + } + + return true; +} + +function emailInvoice( + $invoice_id +){ + // Access global variables + global $mysqli, $session_user_id, $session_ip, $session_user_agent, $config_base_url, $config_invoice_from_name, $config_invoice_from_email, $currency_format; + + $sql = mysqli_query($mysqli,"SELECT * FROM invoices + LEFT JOIN clients ON invoice_client_id = client_id + LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1 + WHERE invoice_id = $invoice_id" + ); + $row = mysqli_fetch_array($sql); + + $invoice_id = intval($row['invoice_id']); + $invoice_prefix = sanitizeInput($row['invoice_prefix']); + $invoice_number = intval($row['invoice_number']); + $invoice_scope = sanitizeInput($row['invoice_scope']); + $invoice_status = sanitizeInput($row['invoice_status']); + $invoice_date = sanitizeInput($row['invoice_date']); + $invoice_due = sanitizeInput($row['invoice_due']); + $invoice_amount = floatval($row['invoice_amount']); + $invoice_url_key = sanitizeInput($row['invoice_url_key']); + $invoice_currency_code = sanitizeInput($row['invoice_currency_code']); + $client_id = intval($row['client_id']); + $client_name = sanitizeInput($row['client_name']); + $contact_name = sanitizeInput($row['contact_name']); + $contact_email = sanitizeInput($row['contact_email']); + + $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'])); + + + // Sanitize Config vars from get_settings.php + $config_invoice_from_name = sanitizeInput($config_invoice_from_name); + $config_invoice_from_email = sanitizeInput($config_invoice_from_email); + + // Add up all the payments for the invoice and get the total amount paid to the invoice + $sql_amount_paid = mysqli_query($mysqli,"SELECT SUM(payment_amount) AS amount_paid FROM payments WHERE payment_invoice_id = $invoice_id"); + $row = mysqli_fetch_array($sql_amount_paid); + $amount_paid = floatval($row['amount_paid']); + + $balance = $invoice_amount - $amount_paid; + + if ($invoice_status == 'Paid') { + $subject = "$company_name Invoice $invoice_prefix$invoice_number Receipt"; + $body = "Hello $contact_name,

Please click on the link below to see your invoice regarding \"$invoice_scope\" marked paid.

Invoice Link


--
$company_name - Billing
$config_invoice_from_email
$company_phone"; + } else { + $subject = "$company_name Invoice $invoice_prefix$invoice_number"; + $body = "Hello $contact_name,

Please view the details of your invoice regarding \"$invoice_scope\" below.

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


To view your invoice, please click here.


--
$company_name - Billing
$config_invoice_from_email
$company_phone"; + } + + // Queue Mail + $data = [ + [ + 'from' => $config_invoice_from_email, + 'from_name' => $config_invoice_from_name, + 'recipient' => $contact_email, + 'recipient_name' => $contact_name, + 'subject' => $subject, + 'body' => $body + ] + ]; + + addToMailQueue($mysqli, $data); + + // Get Email ID for reference + $email_id = mysqli_insert_id($mysqli); + + $_SESSION['alert_message'] = "Invoice has been sent"; + mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Invoice sent to the mail queue ID: $email_id', history_invoice_id = $invoice_id"); + + // Don't change the status to sent if the status is anything but draft + if ($invoice_status == 'Draft') { + mysqli_query($mysqli,"UPDATE invoices SET invoice_status = 'Sent' WHERE invoice_id = $invoice_id"); + } + + // Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Email', log_description = 'Invoice $invoice_prefix$invoice_number queued to $contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $invoice_id"); + + // Send copies of the invoice to any additional billing contacts + $sql_billing_contacts = mysqli_query( + $mysqli, + "SELECT contact_name, contact_email FROM contacts + WHERE contact_billing = 1 + AND contact_email != '$contact_email' + AND contact_email != '' + AND contact_client_id = $client_id" + ); + + $data = []; + + while ($billing_contact = mysqli_fetch_array($sql_billing_contacts)) { + $billing_contact_name = sanitizeInput($billing_contact['contact_name']); + $billing_contact_email = sanitizeInput($billing_contact['contact_email']); + + $data = [ + [ + 'from' => $config_invoice_from_email, + 'from_name' => $config_invoice_from_name, + 'recipient' => $billing_contact_email, + 'recipient_name' => $billing_contact_name, + 'subject' => $subject, + 'body' => $body + ] + ]; + + // Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Email', log_description = 'Invoice $invoice_prefix$invoice_number queued to $billing_contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $invoice_id"); + } + + addToMailQueue($mysqli, $data); +} \ No newline at end of file diff --git a/functions/file_functions.php b/functions/file_functions.php new file mode 100644 index 000000000..05502b4b8 --- /dev/null +++ b/functions/file_functions.php @@ -0,0 +1,63 @@ + $maxSizeBytes) { + return "File size exceeds the limit."; + } + + // Read the file content + $fileContent = file_get_contents($tmp); + + // Hash the file content using SHA-256 + $hashedContent = hash('sha256', $fileContent); + + // Generate a secure filename using the hashed content + $secureFilename = $hashedContent . randomString(2) . '.' . $extension; + + return $secureFilename; +} diff --git a/functions/invoice_functions.php b/functions/invoice_functions.php new file mode 100644 index 000000000..c6701aa1c --- /dev/null +++ b/functions/invoice_functions.php @@ -0,0 +1,519 @@ + 0) { + $sql = mysqli_query($mysqli,"SELECT * FROM taxes WHERE tax_id = $tax_id"); + $row = mysqli_fetch_array($sql); + $tax_percent = floatval($row['tax_percent']); + $item_tax_amount = $item_subtotal * $tax_percent / 100; + } else { + $item_tax_amount = 0; + } + + $item_total = $item_subtotal + $item_tax_amount; + + //Update Recurring Items with new tax + mysqli_query($mysqli,"UPDATE invoice_items SET item_tax = $item_tax_amount, item_total = $item_total, item_tax_id = $tax_id, item_order = $item_order WHERE item_id = $item_id"); + + mysqli_query($mysqli,"INSERT INTO invoice_items SET item_name = '$item_name', item_description = '$item_description', item_quantity = $item_quantity, item_price = $item_price, item_subtotal = $item_subtotal, item_tax = $item_tax_amount, item_total = $item_total, item_tax_id = $tax_id, item_invoice_id = $new_invoice_id"); + } + + mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Invoice Generated from Recurring!', history_invoice_id = $new_invoice_id"); + + //Update Recurring Balances by tallying up recurring items also update recurring dates + $sql_recurring_total = mysqli_query($mysqli,"SELECT SUM(item_total) AS recurring_total FROM invoice_items WHERE item_recurring_id = $recurring_id"); + $row = mysqli_fetch_array($sql_recurring_total); + $new_recurring_amount = floatval($row['recurring_total']) - $recurring_discount_amount; + + mysqli_query($mysqli,"UPDATE recurring SET recurring_amount = $new_recurring_amount, recurring_last_sent = CURDATE(), recurring_next_date = DATE_ADD(CURDATE(), INTERVAL 1 $recurring_frequency) WHERE recurring_id = $recurring_id"); + + //Also update the newly created invoice with the new amounts + mysqli_query($mysqli,"UPDATE invoices SET invoice_amount = $new_recurring_amount WHERE invoice_id = $new_invoice_id"); + + if ($config_recurring_auto_send_invoice == 1) { + $sql = mysqli_query($mysqli,"SELECT * FROM invoices + LEFT JOIN clients ON invoice_client_id = client_id + LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1 + WHERE invoice_id = $new_invoice_id" + ); + $row = mysqli_fetch_array($sql); + + $invoice_prefix = sanitizeInput($row['invoice_prefix']); + $invoice_number = intval($row['invoice_number']); + $invoice_scope = sanitizeInput($row['invoice_scope']); + $invoice_date = sanitizeInput($row['invoice_date']); + $invoice_due = sanitizeInput($row['invoice_due']); + $invoice_amount = floatval($row['invoice_amount']); + $invoice_url_key = sanitizeInput($row['invoice_url_key']); + $client_id = intval($row['client_id']); + $contact_name = sanitizeInput($row['contact_name']); + $contact_email = sanitizeInput($row['contact_email']); + + $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'])); + + // Sanitize Config Vars + $config_invoice_from_email = sanitizeInput($config_invoice_from_email); + $config_invoice_from_name = sanitizeInput($config_invoice_from_name); + + // Email to client + + $subject = "$company_name 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: $$invoice_amount
Due Date: $invoice_due


To view your invoice, please click here.


--
$company_name - Billing
$company_phone"; + + + $data = [ + [ + 'from' => $config_invoice_from_email, + 'from_name' => $config_invoice_from_name, + 'recipient' => $contact_email, + 'recipient_name' => $contact_name, + 'subject' => $subject, + 'body' => $body + ] + ]; + $mail = addToMailQueue($mysqli, $data); + + if ($mail === true) { + // Add send history + mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Force Emailed Invoice!', history_invoice_id = $new_invoice_id"); + + // Update Invoice Status to Sent + mysqli_query($mysqli,"UPDATE invoices SET invoice_status = 'Sent', invoice_client_id = $client_id WHERE invoice_id = $new_invoice_id"); + + } else { + // Error reporting + mysqli_query($mysqli,"INSERT INTO notifications SET notification_type = 'Mail', notification = 'Failed to send email to $contact_email', notification_client_id = $client_id"); + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Mail', log_action = 'Error', log_description = 'Failed to send email to $contact_email regarding $subject. $mail', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + } + + } //End Recurring Invoices Loop + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = '$session_name forced recurring invoice into an invoice', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $new_invoice_id"); +} + +function addInvoiceToTicket( + $invoice_id, + $ticket_id +) { + // Access global variables + global $mysqli, $session_user_id, $session_ip, $session_user_agent; + + mysqli_query($mysqli,"UPDATE invoices SET invoice_ticket_id = $ticket_id WHERE invoice_id = $invoice_id"); +} diff --git a/functions/invoice_item_functions.php b/functions/invoice_item_functions.php new file mode 100644 index 000000000..795e3b7da --- /dev/null +++ b/functions/invoice_item_functions.php @@ -0,0 +1,313 @@ + 0) { + $sql = mysqli_query($mysqli,"SELECT * FROM taxes WHERE tax_id = $tax_id"); + $row = mysqli_fetch_array($sql); + $tax_percent = floatval($row['tax_percent']); + $tax_amount = $subtotal * $tax_percent / 100; + } else { + $tax_amount = 0; + } + + $total = $subtotal + $tax_amount; + + mysqli_query($mysqli, + "INSERT INTO invoice_items SET + item_name = '$name', + item_description = '$description', + item_quantity = $qty, + item_price = $price, + item_subtotal = $subtotal, + item_tax = $tax_amount, + item_total = $total, + item_tax_id = $tax_id, + item_order = $item_order, + item_recurring_id = $recurring_id + "); + + //Get Discount + + $sql = mysqli_query($mysqli,"SELECT * FROM recurring WHERE recurring_id = $recurring_id"); + $row = mysqli_fetch_array($sql); + $recurring_discount = floatval($row['recurring_discount_amount']); + + //add up all the items + $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_recurring_id = $recurring_id"); + $recurring_amount = 0; + while($row = mysqli_fetch_array($sql)) { + $item_total = floatval($row['item_total']); + $recurring_amount = $recurring_amount + $item_total; + } + $recurring_amount = $recurring_amount - $recurring_discount; + + mysqli_query($mysqli, + "UPDATE recurring SET + recurring_amount = $recurring_amount + WHERE recurring_id = $recurring_id + "); + } elseif ($type == 'invoice') { + + if ($tax_id > 0) { + $sql = mysqli_query($mysqli,"SELECT * FROM taxes WHERE tax_id = $tax_id"); + $row = mysqli_fetch_array($sql); + $tax_percent = floatval($row['tax_percent']); + $tax_amount = $subtotal * $tax_percent / 100; + } else { + $tax_amount = 0; + } + + $total = $subtotal + $tax_amount; + + mysqli_query($mysqli,"INSERT INTO invoice_items SET item_name = '$name', item_description = '$description', item_quantity = $qty, item_price = $price, item_subtotal = $subtotal, item_tax = $tax_amount, item_total = $total, item_order = $item_order, item_tax_id = $tax_id, item_invoice_id = $invoice_id"); + + //Get Discount + + $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id"); + $row = mysqli_fetch_array($sql); + if($invoice_id > 0){ + $invoice_discount = floatval($row['invoice_discount_amount']); + } else { + $invoice_discount = 0; + } + + //add up all line items + $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_invoice_id = $invoice_id"); + $invoice_total = 0; + while($row = mysqli_fetch_array($sql)) { + $item_total = floatval($row['item_total']); + $invoice_total = $invoice_total + $item_total; + } + $new_invoice_amount = $invoice_total - $invoice_discount; + + mysqli_query($mysqli,"UPDATE invoices SET invoice_amount = $new_invoice_amount WHERE invoice_id = $invoice_id"); + + + } +} + +function readInvoiceItem( + $type, + $item_id +) { + // Access global variables + global $mysqli; + + switch($type) { + case "invoice": + $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_id = $item_id"); + $row = mysqli_fetch_array($sql); + return $row; + break; + + case "recurring": + $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_id = $item_id"); + $row = mysqli_fetch_array($sql); + return $row; + break; + } +} + +function updateInvoiceItem( + $item +) { + // Access global variables + global $mysqli; + + $item_id = $item['item_id']; + $name = $item['name']; + $description = $item['description']; + $qty = $item['qty']; + $price = $item['price']; + $tax_id = $item['tax_id']; + $invoice_id = $item['invoice_id']; + $recurring_id = $item['recurring_id']; + $quote_id = $item['quote_id']; + + $subtotal = $price * $qty; + + if ($tax_id > 0) { + $sql = mysqli_query($mysqli,"SELECT * FROM taxes WHERE tax_id = $tax_id"); + $row = mysqli_fetch_array($sql); + $tax_percent = floatval($row['tax_percent']); + $tax_amount = $subtotal * $tax_percent / 100; + } else { + $tax_amount = 0; + } + + $total = $subtotal + $tax_amount; + + mysqli_query($mysqli,"UPDATE invoice_items SET item_name = '$name', item_description = '$description', item_quantity = $qty, item_price = $price, item_subtotal = $subtotal, item_tax = $tax_amount, item_total = $total, item_tax_id = $tax_id WHERE item_id = $item_id"); + + if ($invoice_id > 0) { + //Get Discount Amount + $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id"); + $row = mysqli_fetch_array($sql); + $invoice_discount = floatval($row['invoice_discount_amount']); + + //Update Invoice Balances by tallying up invoice items + $sql_invoice_total = mysqli_query($mysqli,"SELECT SUM(item_total) AS invoice_total FROM invoice_items WHERE item_invoice_id = $invoice_id"); + $row = mysqli_fetch_array($sql_invoice_total); + $new_invoice_amount = floatval($row['invoice_total']) - $invoice_discount; + + mysqli_query($mysqli,"UPDATE invoices SET invoice_amount = $new_invoice_amount WHERE invoice_id = $invoice_id"); + + }elseif ($quote_id > 0) { + //Get Discount Amount + $sql = mysqli_query($mysqli,"SELECT * FROM quotes WHERE quote_id = $quote_id"); + $row = mysqli_fetch_array($sql); + $quote_discount = floatval($row['quote_discount_amount']); + + //Update Quote Balances by tallying up items + $sql_quote_total = mysqli_query($mysqli,"SELECT SUM(item_total) AS quote_total FROM invoice_items WHERE item_quote_id = $quote_id"); + $row = mysqli_fetch_array($sql_quote_total); + $new_quote_amount = floatval($row['quote_total']) - $quote_discount; + + mysqli_query($mysqli,"UPDATE quotes SET quote_amount = $new_quote_amount WHERE quote_id = $quote_id"); + + } else { + //Get Discount Amount + $sql = mysqli_query($mysqli,"SELECT * FROM recurring WHERE recurring_id = $recurring_id"); + $row = mysqli_fetch_array($sql); + $recurring_discount = floatval($row['recurring_discount_amount']); + + //Update Invoice Balances by tallying up invoice items + $sql_recurring_total = mysqli_query($mysqli,"SELECT SUM(item_total) AS recurring_total FROM invoice_items WHERE item_recurring_id = $recurring_id"); + $row = mysqli_fetch_array($sql_recurring_total); + $new_recurring_amount = floatval($row['recurring_total']) - $recurring_discount; + + mysqli_query($mysqli,"UPDATE recurring SET recurring_amount = $new_recurring_amount WHERE recurring_id = $recurring_id"); + + } +} + +function deleteInvoiceItem( + $type, + $item_id +) { + // Access global variables + global $mysqli, $session_user_id, $session_ip, $session_user_agent; + + switch($type) { + case "invoice": + $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_id = $item_id"); + $row = mysqli_fetch_array($sql); + $invoice_id = intval($row['item_invoice_id']); + $item_total = floatval($row['item_total']); + + $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id"); + $row = mysqli_fetch_array($sql); + + $new_invoice_amount = floatval($row['invoice_amount']) - $item_total; + + mysqli_query($mysqli,"UPDATE invoices SET invoice_amount = $new_invoice_amount WHERE invoice_id = $invoice_id"); + + mysqli_query($mysqli,"DELETE FROM invoice_items WHERE item_id = $item_id"); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice Item', log_action = 'Delete', log_description = '$item_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + + break; + + case "recurring": + $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_id = $item_id"); + $row = mysqli_fetch_array($sql); + $recurring_id = intval($row['item_recurring_id']); + $item_total = floatval($row['item_total']); + + $sql = mysqli_query($mysqli,"SELECT * FROM recurring WHERE recurring_id = $recurring_id"); + $row = mysqli_fetch_array($sql); + + $new_recurring_amount = floatval($row['recurring_amount']) - $item_total; + + mysqli_query($mysqli,"UPDATE recurring SET recurring_amount = $new_recurring_amount WHERE recurring_id = $recurring_id"); + + mysqli_query($mysqli,"DELETE FROM invoice_items WHERE item_id = $item_id"); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Recurring Item', log_action = 'Delete', log_description = 'Item ID $item_id from Recurring ID $recurring_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + break; + } +} + +function updateItemOrder( + $type, + $item_id, + $item_recurring_id, + $update_direction +) { + // Access global variables + global $mysqli; + + if ($type == "invoice") { + $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_id = $item_id"); + $row = mysqli_fetch_array($sql); + $current_order = intval($row['item_order']); + + switch ($update_direction) + { + case 'up': + $new_order = $current_order - 1; + break; + case 'down': + $new_order = $current_order + 1; + break; + } + + //Find item_id of current item in $new_order + $other_sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_recurring_id = $item_recurring_id AND item_order = $new_order"); + $other_row = mysqli_fetch_array($other_sql); + $other_item_id = intval($other_row['item_id']); + + mysqli_query($mysqli,"UPDATE invoice_items SET item_order = $new_order WHERE item_id = $item_id"); + + mysqli_query($mysqli,"UPDATE invoice_items SET item_order = $current_order WHERE item_id = $other_item_id"); + } elseif ($type == "recurring") { + $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_id = $item_id"); + $row = mysqli_fetch_array($sql); + $current_order = intval($row['item_order']); + $item_invoice_id = intval($row['item_invoice_id']); + + switch ($update_direction) + { + case 'up': + $new_order = $current_order - 1; + break; + case 'down': + $new_order = $current_order + 1; + break; + } + + //Find item_id of current item in $new_order + $other_sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_invoice_id = $item_invoice_id AND item_order = $new_order"); + $other_row = mysqli_fetch_array($other_sql); + $other_item_id = intval($other_row['item_id']); + + mysqli_query($mysqli,"UPDATE invoice_items SET item_order = $new_order WHERE item_id = $item_id"); + + mysqli_query($mysqli,"UPDATE invoice_items SET item_order = $current_order WHERE item_id = $other_item_id"); + } +} \ No newline at end of file diff --git a/functions/misc_functions.php b/functions/misc_functions.php new file mode 100644 index 000000000..debb5bf08 --- /dev/null +++ b/functions/misc_functions.php @@ -0,0 +1,397 @@ + 10) { + $countryCode = substr($phoneNumber, 0, strlen($phoneNumber) - 10); + $areaCode = substr($phoneNumber, -10, 3); + $nextThree = substr($phoneNumber, -7, 3); + $lastFour = substr($phoneNumber, -4, 4); + + $phoneNumber = '+' . $countryCode . ' (' . $areaCode . ') ' . $nextThree . '-' . $lastFour; + } else if (strlen($phoneNumber) == 10) { + $areaCode = substr($phoneNumber, 0, 3); + $nextThree = substr($phoneNumber, 3, 3); + $lastFour = substr($phoneNumber, 6, 4); + + $phoneNumber = '(' . $areaCode . ') ' . $nextThree . '-' . $lastFour; + } else if (strlen($phoneNumber) == 7) { + $nextThree = substr($phoneNumber, 0, 3); + $lastFour = substr($phoneNumber, 3, 4); + + $phoneNumber = $nextThree . '-' . $lastFour; + } + + return $phoneNumber; +} + +function roundUpToNearestMultiple($n, $increment = 1000) +{ + return (int) ($increment * ceil($n / $increment)); +} + +function getAssetIcon($asset_type) +{ + if ($asset_type == 'Laptop') { + $device_icon = "laptop"; + } elseif ($asset_type == 'Desktop') { + $device_icon = "desktop"; + } elseif ($asset_type == 'Server') { + $device_icon = "server"; + } elseif ($asset_type == 'Printer') { + $device_icon = "print"; + } elseif ($asset_type == 'Camera') { + $device_icon = "video"; + } elseif ($asset_type == 'Switch' || $asset_type == 'Firewall/Router') { + $device_icon = "network-wired"; + } elseif ($asset_type == 'Access Point') { + $device_icon = "wifi"; + } elseif ($asset_type == 'Phone') { + $device_icon = "phone"; + } elseif ($asset_type == 'Mobile Phone') { + $device_icon = "mobile-alt"; + } elseif ($asset_type == 'Tablet') { + $device_icon = "tablet-alt"; + } elseif ($asset_type == 'TV') { + $device_icon = "tv"; + } elseif ($asset_type == 'Virtual Machine') { + $device_icon = "cloud"; + } else { + $device_icon = "tag"; + } + + return $device_icon; +} + +function getInvoiceBadgeColor($invoice_status) +{ + if ($invoice_status == "Sent") { + $invoice_badge_color = "warning text-white"; + } elseif ($invoice_status == "Viewed") { + $invoice_badge_color = "info"; + } elseif ($invoice_status == "Partial") { + $invoice_badge_color = "primary"; + } elseif ($invoice_status == "Paid") { + $invoice_badge_color = "success"; + } elseif ($invoice_status == "Cancelled") { + $invoice_badge_color = "danger"; + } else { + $invoice_badge_color = "secondary"; + } + + return $invoice_badge_color; +} + +function timeAgo($datetime) +{ + $time = strtotime($datetime); + $difference = $time - time(); // Changed to handle future dates + + if ($difference == 0) { + return 'right now'; + } + + $isFuture = $difference > 0; // Check if the date is in the future + $difference = abs($difference); // Absolute value for calculation + + $timeRules = array( + 31536000 => 'year', + 2592000 => 'month', + 604800 => 'week', + 86400 => 'day', + 3600 => 'hour', + 60 => 'minute', + 1 => 'second' + ); + + foreach ($timeRules as $secs => $str) { + $div = $difference / $secs; + if ($div >= 1) { + $t = round($div); + $timeStr = $t . ' ' . $str . ($t > 1 ? 's' : ''); + return $isFuture ? 'in ' . $timeStr : $timeStr . ' ago'; + } + } +} + +function removeEmoji($text) +{ + return preg_replace('/\x{1F3F4}\x{E0067}\x{E0062}(?:\x{E0077}\x{E006C}\x{E0073}|\x{E0073}\x{E0063}\x{E0074}|\x{E0065}\x{E006E}\x{E0067})\x{E007F}|(?:\x{1F9D1}\x{1F3FF}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FF}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FF}\x{200D}\x{1FAF2})[\x{1F3FB}-\x{1F3FE}]|(?:\x{1F9D1}\x{1F3FE}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FE}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FE}\x{200D}\x{1FAF2})[\x{1F3FB}-\x{1F3FD}\x{1F3FF}]|(?:\x{1F9D1}\x{1F3FD}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FD}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FD}\x{200D}\x{1FAF2})[\x{1F3FB}\x{1F3FC}\x{1F3FE}\x{1F3FF}]|(?:\x{1F9D1}\x{1F3FC}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FC}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FC}\x{200D}\x{1FAF2})[\x{1F3FB}\x{1F3FD}-\x{1F3FF}]|(?:\x{1F9D1}\x{1F3FB}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F9D1}|\x{1F469}\x{1F3FB}\x{200D}\x{1F91D}\x{200D}[\x{1F468}\x{1F469}]|\x{1FAF1}\x{1F3FB}\x{200D}\x{1FAF2})[\x{1F3FC}-\x{1F3FF}]|\x{1F468}(?:\x{1F3FB}(?:\x{200D}(?:\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}])|\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}]))|\x{1F91D}\x{200D}\x{1F468}[\x{1F3FC}-\x{1F3FF}]|[\x{2695}\x{2696}\x{2708}]\x{FE0F}|[\x{2695}\x{2696}\x{2708}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]))?|[\x{1F3FC}-\x{1F3FF}]\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}])|\x{200D}(?:\x{1F48B}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FF}]|\x{1F468}[\x{1F3FB}-\x{1F3FF}]))|\x{200D}(?:\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D})?|\x{200D}(?:\x{1F48B}\x{200D})?)\x{1F468}|[\x{1F468}\x{1F469}]\x{200D}(?:\x{1F466}\x{200D}\x{1F466}|\x{1F467}\x{200D}[\x{1F466}\x{1F467}])|\x{1F466}\x{200D}\x{1F466}|\x{1F467}\x{200D}[\x{1F466}\x{1F467}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FF}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FE}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FE}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}-\x{1F3FD}\x{1F3FF}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FD}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}\x{1F3FC}\x{1F3FE}\x{1F3FF}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FC}\x{200D}(?:\x{1F91D}\x{200D}\x{1F468}[\x{1F3FB}\x{1F3FD}-\x{1F3FF}]|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])\x{FE0F}|\x{200D}(?:[\x{1F468}\x{1F469}]\x{200D}[\x{1F466}\x{1F467}]|[\x{1F466}\x{1F467}])|\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{200D}[\x{2695}\x{2696}\x{2708}])?|(?:\x{1F469}(?:\x{1F3FB}\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])|\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}]))|[\x{1F3FC}-\x{1F3FF}]\x{200D}\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])|\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])))|\x{1F9D1}[\x{1F3FB}-\x{1F3FF}]\x{200D}\x{1F91D}\x{200D}\x{1F9D1})[\x{1F3FB}-\x{1F3FF}]|\x{1F469}\x{200D}\x{1F469}\x{200D}(?:\x{1F466}\x{200D}\x{1F466}|\x{1F467}\x{200D}[\x{1F466}\x{1F467}])|\x{1F469}(?:\x{200D}(?:\x{2764}(?:\x{FE0F}\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}])|\x{200D}(?:\x{1F48B}\x{200D}[\x{1F468}\x{1F469}]|[\x{1F468}\x{1F469}]))|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FF}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FE}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FD}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FC}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FB}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F9D1}(?:\x{200D}(?:\x{1F91D}\x{200D}\x{1F9D1}|[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F3FF}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FE}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FD}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FC}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}]|\x{1F3FB}\x{200D}[\x{1F33E}\x{1F373}\x{1F37C}\x{1F384}\x{1F393}\x{1F3A4}\x{1F3A8}\x{1F3EB}\x{1F3ED}\x{1F4BB}\x{1F4BC}\x{1F527}\x{1F52C}\x{1F680}\x{1F692}\x{1F9AF}-\x{1F9B3}\x{1F9BC}\x{1F9BD}])|\x{1F469}\x{200D}\x{1F466}\x{200D}\x{1F466}|\x{1F469}\x{200D}\x{1F469}\x{200D}[\x{1F466}\x{1F467}]|\x{1F469}\x{200D}\x{1F467}\x{200D}[\x{1F466}\x{1F467}]|(?:\x{1F441}\x{FE0F}?\x{200D}\x{1F5E8}|\x{1F9D1}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F469}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F636}\x{200D}\x{1F32B}|\x{1F3F3}\x{FE0F}?\x{200D}\x{26A7}|\x{1F43B}\x{200D}\x{2744}|(?:[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}][\x{1F3FB}-\x{1F3FF}]|[\x{1F46F}\x{1F9DE}\x{1F9DF}])\x{200D}[\x{2640}\x{2642}]|[\x{26F9}\x{1F3CB}\x{1F3CC}\x{1F575}](?:[\x{FE0F}\x{1F3FB}-\x{1F3FF}]\x{200D}[\x{2640}\x{2642}]|\x{200D}[\x{2640}\x{2642}])|\x{1F3F4}\x{200D}\x{2620}|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93C}-\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}]\x{200D}[\x{2640}\x{2642}]|[\xA9\xAE\x{203C}\x{2049}\x{2122}\x{2139}\x{2194}-\x{2199}\x{21A9}\x{21AA}\x{231A}\x{231B}\x{2328}\x{23CF}\x{23ED}-\x{23EF}\x{23F1}\x{23F2}\x{23F8}-\x{23FA}\x{24C2}\x{25AA}\x{25AB}\x{25B6}\x{25C0}\x{25FB}\x{25FC}\x{25FE}\x{2600}-\x{2604}\x{260E}\x{2611}\x{2614}\x{2615}\x{2618}\x{2620}\x{2622}\x{2623}\x{2626}\x{262A}\x{262E}\x{262F}\x{2638}-\x{263A}\x{2640}\x{2642}\x{2648}-\x{2653}\x{265F}\x{2660}\x{2663}\x{2665}\x{2666}\x{2668}\x{267B}\x{267E}\x{267F}\x{2692}\x{2694}-\x{2697}\x{2699}\x{269B}\x{269C}\x{26A0}\x{26A7}\x{26AA}\x{26B0}\x{26B1}\x{26BD}\x{26BE}\x{26C4}\x{26C8}\x{26CF}\x{26D1}\x{26D3}\x{26E9}\x{26F0}-\x{26F5}\x{26F7}\x{26F8}\x{26FA}\x{2702}\x{2708}\x{2709}\x{270F}\x{2712}\x{2714}\x{2716}\x{271D}\x{2721}\x{2733}\x{2734}\x{2744}\x{2747}\x{2763}\x{27A1}\x{2934}\x{2935}\x{2B05}-\x{2B07}\x{2B1B}\x{2B1C}\x{2B55}\x{3030}\x{303D}\x{3297}\x{3299}\x{1F004}\x{1F170}\x{1F171}\x{1F17E}\x{1F17F}\x{1F202}\x{1F237}\x{1F321}\x{1F324}-\x{1F32C}\x{1F336}\x{1F37D}\x{1F396}\x{1F397}\x{1F399}-\x{1F39B}\x{1F39E}\x{1F39F}\x{1F3CD}\x{1F3CE}\x{1F3D4}-\x{1F3DF}\x{1F3F5}\x{1F3F7}\x{1F43F}\x{1F4FD}\x{1F549}\x{1F54A}\x{1F56F}\x{1F570}\x{1F573}\x{1F576}-\x{1F579}\x{1F587}\x{1F58A}-\x{1F58D}\x{1F5A5}\x{1F5A8}\x{1F5B1}\x{1F5B2}\x{1F5BC}\x{1F5C2}-\x{1F5C4}\x{1F5D1}-\x{1F5D3}\x{1F5DC}-\x{1F5DE}\x{1F5E1}\x{1F5E3}\x{1F5E8}\x{1F5EF}\x{1F5F3}\x{1F5FA}\x{1F6CB}\x{1F6CD}-\x{1F6CF}\x{1F6E0}-\x{1F6E5}\x{1F6E9}\x{1F6F0}\x{1F6F3}])\x{FE0F}|\x{1F441}\x{FE0F}?\x{200D}\x{1F5E8}|\x{1F9D1}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F469}(?:\x{1F3FF}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FE}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FD}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FC}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{1F3FB}\x{200D}[\x{2695}\x{2696}\x{2708}]|\x{200D}[\x{2695}\x{2696}\x{2708}])|\x{1F3F3}\x{FE0F}?\x{200D}\x{1F308}|\x{1F469}\x{200D}\x{1F467}|\x{1F469}\x{200D}\x{1F466}|\x{1F636}\x{200D}\x{1F32B}|\x{1F3F3}\x{FE0F}?\x{200D}\x{26A7}|\x{1F635}\x{200D}\x{1F4AB}|\x{1F62E}\x{200D}\x{1F4A8}|\x{1F415}\x{200D}\x{1F9BA}|\x{1FAF1}(?:\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{1F3FB})?|\x{1F9D1}(?:\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{1F3FB})?|\x{1F469}(?:\x{1F3FF}|\x{1F3FE}|\x{1F3FD}|\x{1F3FC}|\x{1F3FB})?|\x{1F43B}\x{200D}\x{2744}|(?:[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}][\x{1F3FB}-\x{1F3FF}]|[\x{1F46F}\x{1F9DE}\x{1F9DF}])\x{200D}[\x{2640}\x{2642}]|[\x{26F9}\x{1F3CB}\x{1F3CC}\x{1F575}](?:[\x{FE0F}\x{1F3FB}-\x{1F3FF}]\x{200D}[\x{2640}\x{2642}]|\x{200D}[\x{2640}\x{2642}])|\x{1F3F4}\x{200D}\x{2620}|\x{1F1FD}\x{1F1F0}|\x{1F1F6}\x{1F1E6}|\x{1F1F4}\x{1F1F2}|\x{1F408}\x{200D}\x{2B1B}|\x{2764}(?:\x{FE0F}\x{200D}[\x{1F525}\x{1FA79}]|\x{200D}[\x{1F525}\x{1FA79}])|\x{1F441}\x{FE0F}?|\x{1F3F3}\x{FE0F}?|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93C}-\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}]\x{200D}[\x{2640}\x{2642}]|\x{1F1FF}[\x{1F1E6}\x{1F1F2}\x{1F1FC}]|\x{1F1FE}[\x{1F1EA}\x{1F1F9}]|\x{1F1FC}[\x{1F1EB}\x{1F1F8}]|\x{1F1FB}[\x{1F1E6}\x{1F1E8}\x{1F1EA}\x{1F1EC}\x{1F1EE}\x{1F1F3}\x{1F1FA}]|\x{1F1FA}[\x{1F1E6}\x{1F1EC}\x{1F1F2}\x{1F1F3}\x{1F1F8}\x{1F1FE}\x{1F1FF}]|\x{1F1F9}[\x{1F1E6}\x{1F1E8}\x{1F1E9}\x{1F1EB}-\x{1F1ED}\x{1F1EF}-\x{1F1F4}\x{1F1F7}\x{1F1F9}\x{1F1FB}\x{1F1FC}\x{1F1FF}]|\x{1F1F8}[\x{1F1E6}-\x{1F1EA}\x{1F1EC}-\x{1F1F4}\x{1F1F7}-\x{1F1F9}\x{1F1FB}\x{1F1FD}-\x{1F1FF}]|\x{1F1F7}[\x{1F1EA}\x{1F1F4}\x{1F1F8}\x{1F1FA}\x{1F1FC}]|\x{1F1F5}[\x{1F1E6}\x{1F1EA}-\x{1F1ED}\x{1F1F0}-\x{1F1F3}\x{1F1F7}-\x{1F1F9}\x{1F1FC}\x{1F1FE}]|\x{1F1F3}[\x{1F1E6}\x{1F1E8}\x{1F1EA}-\x{1F1EC}\x{1F1EE}\x{1F1F1}\x{1F1F4}\x{1F1F5}\x{1F1F7}\x{1F1FA}\x{1F1FF}]|\x{1F1F2}[\x{1F1E6}\x{1F1E8}-\x{1F1ED}\x{1F1F0}-\x{1F1FF}]|\x{1F1F1}[\x{1F1E6}-\x{1F1E8}\x{1F1EE}\x{1F1F0}\x{1F1F7}-\x{1F1FB}\x{1F1FE}]|\x{1F1F0}[\x{1F1EA}\x{1F1EC}-\x{1F1EE}\x{1F1F2}\x{1F1F3}\x{1F1F5}\x{1F1F7}\x{1F1FC}\x{1F1FE}\x{1F1FF}]|\x{1F1EF}[\x{1F1EA}\x{1F1F2}\x{1F1F4}\x{1F1F5}]|\x{1F1EE}[\x{1F1E8}-\x{1F1EA}\x{1F1F1}-\x{1F1F4}\x{1F1F6}-\x{1F1F9}]|\x{1F1ED}[\x{1F1F0}\x{1F1F2}\x{1F1F3}\x{1F1F7}\x{1F1F9}\x{1F1FA}]|\x{1F1EC}[\x{1F1E6}\x{1F1E7}\x{1F1E9}-\x{1F1EE}\x{1F1F1}-\x{1F1F3}\x{1F1F5}-\x{1F1FA}\x{1F1FC}\x{1F1FE}]|\x{1F1EB}[\x{1F1EE}-\x{1F1F0}\x{1F1F2}\x{1F1F4}\x{1F1F7}]|\x{1F1EA}[\x{1F1E6}\x{1F1E8}\x{1F1EA}\x{1F1EC}\x{1F1ED}\x{1F1F7}-\x{1F1FA}]|\x{1F1E9}[\x{1F1EA}\x{1F1EC}\x{1F1EF}\x{1F1F0}\x{1F1F2}\x{1F1F4}\x{1F1FF}]|\x{1F1E8}[\x{1F1E6}\x{1F1E8}\x{1F1E9}\x{1F1EB}-\x{1F1EE}\x{1F1F0}-\x{1F1F5}\x{1F1F7}\x{1F1FA}-\x{1F1FF}]|\x{1F1E7}[\x{1F1E6}\x{1F1E7}\x{1F1E9}-\x{1F1EF}\x{1F1F1}-\x{1F1F4}\x{1F1F6}-\x{1F1F9}\x{1F1FB}\x{1F1FC}\x{1F1FE}\x{1F1FF}]|\x{1F1E6}[\x{1F1E8}-\x{1F1EC}\x{1F1EE}\x{1F1F1}\x{1F1F2}\x{1F1F4}\x{1F1F6}-\x{1F1FA}\x{1F1FC}\x{1F1FD}\x{1F1FF}]|[#\*0-9]\x{FE0F}?\x{20E3}|\x{1F93C}[\x{1F3FB}-\x{1F3FF}]|\x{2764}\x{FE0F}?|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}][\x{1F3FB}-\x{1F3FF}]|[\x{26F9}\x{1F3CB}\x{1F3CC}\x{1F575}][\x{FE0F}\x{1F3FB}-\x{1F3FF}]?|\x{1F3F4}|[\x{270A}\x{270B}\x{1F385}\x{1F3C2}\x{1F3C7}\x{1F442}\x{1F443}\x{1F446}-\x{1F450}\x{1F466}\x{1F467}\x{1F46B}-\x{1F46D}\x{1F472}\x{1F474}-\x{1F476}\x{1F478}\x{1F47C}\x{1F483}\x{1F485}\x{1F48F}\x{1F491}\x{1F4AA}\x{1F57A}\x{1F595}\x{1F596}\x{1F64C}\x{1F64F}\x{1F6C0}\x{1F6CC}\x{1F90C}\x{1F90F}\x{1F918}-\x{1F91F}\x{1F930}-\x{1F934}\x{1F936}\x{1F977}\x{1F9B5}\x{1F9B6}\x{1F9BB}\x{1F9D2}\x{1F9D3}\x{1F9D5}\x{1FAC3}-\x{1FAC5}\x{1FAF0}\x{1FAF2}-\x{1FAF6}][\x{1F3FB}-\x{1F3FF}]|[\x{261D}\x{270C}\x{270D}\x{1F574}\x{1F590}][\x{FE0F}\x{1F3FB}-\x{1F3FF}]|[\x{261D}\x{270A}-\x{270D}\x{1F385}\x{1F3C2}\x{1F3C7}\x{1F408}\x{1F415}\x{1F43B}\x{1F442}\x{1F443}\x{1F446}-\x{1F450}\x{1F466}\x{1F467}\x{1F46B}-\x{1F46D}\x{1F472}\x{1F474}-\x{1F476}\x{1F478}\x{1F47C}\x{1F483}\x{1F485}\x{1F48F}\x{1F491}\x{1F4AA}\x{1F574}\x{1F57A}\x{1F590}\x{1F595}\x{1F596}\x{1F62E}\x{1F635}\x{1F636}\x{1F64C}\x{1F64F}\x{1F6C0}\x{1F6CC}\x{1F90C}\x{1F90F}\x{1F918}-\x{1F91F}\x{1F930}-\x{1F934}\x{1F936}\x{1F93C}\x{1F977}\x{1F9B5}\x{1F9B6}\x{1F9BB}\x{1F9D2}\x{1F9D3}\x{1F9D5}\x{1FAC3}-\x{1FAC5}\x{1FAF0}\x{1FAF2}-\x{1FAF6}]|[\x{1F3C3}\x{1F3C4}\x{1F3CA}\x{1F46E}\x{1F470}\x{1F471}\x{1F473}\x{1F477}\x{1F481}\x{1F482}\x{1F486}\x{1F487}\x{1F645}-\x{1F647}\x{1F64B}\x{1F64D}\x{1F64E}\x{1F6A3}\x{1F6B4}-\x{1F6B6}\x{1F926}\x{1F935}\x{1F937}-\x{1F939}\x{1F93D}\x{1F93E}\x{1F9B8}\x{1F9B9}\x{1F9CD}-\x{1F9CF}\x{1F9D4}\x{1F9D6}-\x{1F9DD}]|[\x{1F46F}\x{1F9DE}\x{1F9DF}]|[\xA9\xAE\x{203C}\x{2049}\x{2122}\x{2139}\x{2194}-\x{2199}\x{21A9}\x{21AA}\x{231A}\x{231B}\x{2328}\x{23CF}\x{23ED}-\x{23EF}\x{23F1}\x{23F2}\x{23F8}-\x{23FA}\x{24C2}\x{25AA}\x{25AB}\x{25B6}\x{25C0}\x{25FB}\x{25FC}\x{25FE}\x{2600}-\x{2604}\x{260E}\x{2611}\x{2614}\x{2615}\x{2618}\x{2620}\x{2622}\x{2623}\x{2626}\x{262A}\x{262E}\x{262F}\x{2638}-\x{263A}\x{2640}\x{2642}\x{2648}-\x{2653}\x{265F}\x{2660}\x{2663}\x{2665}\x{2666}\x{2668}\x{267B}\x{267E}\x{267F}\x{2692}\x{2694}-\x{2697}\x{2699}\x{269B}\x{269C}\x{26A0}\x{26A7}\x{26AA}\x{26B0}\x{26B1}\x{26BD}\x{26BE}\x{26C4}\x{26C8}\x{26CF}\x{26D1}\x{26D3}\x{26E9}\x{26F0}-\x{26F5}\x{26F7}\x{26F8}\x{26FA}\x{2702}\x{2708}\x{2709}\x{270F}\x{2712}\x{2714}\x{2716}\x{271D}\x{2721}\x{2733}\x{2734}\x{2744}\x{2747}\x{2763}\x{27A1}\x{2934}\x{2935}\x{2B05}-\x{2B07}\x{2B1B}\x{2B1C}\x{2B55}\x{3030}\x{303D}\x{3297}\x{3299}\x{1F004}\x{1F170}\x{1F171}\x{1F17E}\x{1F17F}\x{1F202}\x{1F237}\x{1F321}\x{1F324}-\x{1F32C}\x{1F336}\x{1F37D}\x{1F396}\x{1F397}\x{1F399}-\x{1F39B}\x{1F39E}\x{1F39F}\x{1F3CD}\x{1F3CE}\x{1F3D4}-\x{1F3DF}\x{1F3F5}\x{1F3F7}\x{1F43F}\x{1F4FD}\x{1F549}\x{1F54A}\x{1F56F}\x{1F570}\x{1F573}\x{1F576}-\x{1F579}\x{1F587}\x{1F58A}-\x{1F58D}\x{1F5A5}\x{1F5A8}\x{1F5B1}\x{1F5B2}\x{1F5BC}\x{1F5C2}-\x{1F5C4}\x{1F5D1}-\x{1F5D3}\x{1F5DC}-\x{1F5DE}\x{1F5E1}\x{1F5E3}\x{1F5E8}\x{1F5EF}\x{1F5F3}\x{1F5FA}\x{1F6CB}\x{1F6CD}-\x{1F6CF}\x{1F6E0}-\x{1F6E5}\x{1F6E9}\x{1F6F0}\x{1F6F3}]|[\x{23E9}-\x{23EC}\x{23F0}\x{23F3}\x{25FD}\x{2693}\x{26A1}\x{26AB}\x{26C5}\x{26CE}\x{26D4}\x{26EA}\x{26FD}\x{2705}\x{2728}\x{274C}\x{274E}\x{2753}-\x{2755}\x{2757}\x{2795}-\x{2797}\x{27B0}\x{27BF}\x{2B50}\x{1F0CF}\x{1F18E}\x{1F191}-\x{1F19A}\x{1F201}\x{1F21A}\x{1F22F}\x{1F232}-\x{1F236}\x{1F238}-\x{1F23A}\x{1F250}\x{1F251}\x{1F300}-\x{1F320}\x{1F32D}-\x{1F335}\x{1F337}-\x{1F37C}\x{1F37E}-\x{1F384}\x{1F386}-\x{1F393}\x{1F3A0}-\x{1F3C1}\x{1F3C5}\x{1F3C6}\x{1F3C8}\x{1F3C9}\x{1F3CF}-\x{1F3D3}\x{1F3E0}-\x{1F3F0}\x{1F3F8}-\x{1F407}\x{1F409}-\x{1F414}\x{1F416}-\x{1F43A}\x{1F43C}-\x{1F43E}\x{1F440}\x{1F444}\x{1F445}\x{1F451}-\x{1F465}\x{1F46A}\x{1F479}-\x{1F47B}\x{1F47D}-\x{1F480}\x{1F484}\x{1F488}-\x{1F48E}\x{1F490}\x{1F492}-\x{1F4A9}\x{1F4AB}-\x{1F4FC}\x{1F4FF}-\x{1F53D}\x{1F54B}-\x{1F54E}\x{1F550}-\x{1F567}\x{1F5A4}\x{1F5FB}-\x{1F62D}\x{1F62F}-\x{1F634}\x{1F637}-\x{1F644}\x{1F648}-\x{1F64A}\x{1F680}-\x{1F6A2}\x{1F6A4}-\x{1F6B3}\x{1F6B7}-\x{1F6BF}\x{1F6C1}-\x{1F6C5}\x{1F6D0}-\x{1F6D2}\x{1F6D5}-\x{1F6D7}\x{1F6DD}-\x{1F6DF}\x{1F6EB}\x{1F6EC}\x{1F6F4}-\x{1F6FC}\x{1F7E0}-\x{1F7EB}\x{1F7F0}\x{1F90D}\x{1F90E}\x{1F910}-\x{1F917}\x{1F920}-\x{1F925}\x{1F927}-\x{1F92F}\x{1F93A}\x{1F93F}-\x{1F945}\x{1F947}-\x{1F976}\x{1F978}-\x{1F9B4}\x{1F9B7}\x{1F9BA}\x{1F9BC}-\x{1F9CC}\x{1F9D0}\x{1F9E0}-\x{1F9FF}\x{1FA70}-\x{1FA74}\x{1FA78}-\x{1FA7C}\x{1FA80}-\x{1FA86}\x{1FA90}-\x{1FAAC}\x{1FAB0}-\x{1FABA}\x{1FAC0}-\x{1FAC2}\x{1FAD0}-\x{1FAD9}\x{1FAE0}-\x{1FAE7}]/u', '', $text); +} + +function shortenClient($client) +{ + // Pre-process by removing any non-alphanumeric characters except for certain punctuations. + $client = html_entity_decode($client); // Decode any HTML entities + $client = str_replace("'", "", $client); // Removing all occurrences of ' + $cleaned = preg_replace('/[^a-zA-Z0-9&]+/', ' ', $client); + + // Break into words. + $words = explode(' ', trim($cleaned)); + + $shortened = ''; + + // If there's only one word. + if (count($words) == 1) { + $word = $words[0]; + + if (strlen($word) <= 3) { + return strtoupper($word); + } + + // Prefer starting and ending characters. + $shortened = $word[0] . substr($word, -2); + } else { + // Less weightage to common words. + $commonWords = ['the', 'of', 'and']; + + foreach ($words as $word) { + if (!in_array(strtolower($word), $commonWords) || strlen($shortened) < 2) { + $shortened .= $word[0]; + } + } + + // If there are still not enough characters, take from the last word. + while (strlen($shortened) < 3 && !empty($word)) { + $shortened .= substr($word, 1, 1); + $word = substr($word, 1); + } + } + + return strtoupper(substr($shortened, 0, 3)); +} + +function roundToNearest15($time) +{ + // Validate the input time format + if (!preg_match('/^(\d{2}):(\d{2}):(\d{2})$/', $time, $matches)) { + return false; // or throw an exception + } + + // Extract hours, minutes, and seconds from the matched time string + list(, $hours, $minutes, $seconds) = $matches; + + // Convert everything to seconds for easier calculation + $totalSeconds = ($hours * 3600) + ($minutes * 60) + $seconds; + + // Calculate the remainder when divided by 900 seconds (15 minutes) + $remainder = $totalSeconds % 900; + + if ($remainder > 450) { // If remainder is more than 7.5 minutes (450 seconds), round up + $totalSeconds += (900 - $remainder); + } else { // Else round down + $totalSeconds -= $remainder; + } + + // Convert total seconds to decimal hours + $decimalHours = $totalSeconds / 3600; + + // Return the decimal hours + return number_format($decimalHours, 2); +} + +function getSettingValue($mysqli, $setting_name) +{ + //if starts with config_ then get from config table + if (substr($setting_name, 0, 7) == "config_") { + $sql = mysqli_query($mysqli, "SELECT $setting_name FROM settings"); + $row = mysqli_fetch_array($sql); + return $row[$setting_name]; + } elseif (substr($setting_name, 0, 7) == "company") { + $sql = mysqli_query($mysqli, "SELECT $setting_name FROM companies"); + $row = mysqli_fetch_array($sql); + return $row[$setting_name]; + } else { + return "Cannot Find Setting Name"; + } +} + +function generateReadablePassword($security_level) +{ + // Cap security level at 5 + $security_level = intval($security_level); + $security_level = min($security_level, 5); + + // Arrays of words + $articles = ['The', 'A']; + $adjectives = ['Smart', 'Swift', 'Secure', 'Stable', 'Digital', 'Virtual', 'Active', 'Dynamic', 'Innovative', 'Efficient', 'Portable', 'Wireless', 'Rapid', 'Intuitive', 'Automated', 'Robust', 'Reliable', 'Sleek', 'Modern', 'Happy', 'Funny', 'Quick', 'Bright', 'Clever', 'Gentle', 'Brave', 'Calm', 'Eager', 'Fierce', 'Kind', 'Lucky', 'Proud', 'Silly', 'Witty', 'Bold', 'Curious', 'Elated', 'Gracious', 'Honest', 'Jolly', 'Merry', 'Noble', 'Optimistic', 'Playful', 'Quirky', 'Rustic', 'Steady', 'Tranquil', 'Upbeat']; + $nouns = ['Computer', 'Laptop', 'Tablet', 'Server', 'Router', 'Software', 'Hardware', 'Pixel', 'Byte', 'App', 'Network', 'Cloud', 'Firewall', 'Email', 'Database', 'Folder', 'Document', 'Interface', 'Program', 'Gadget', 'Dinosaur', 'Tiger', 'Elephant', 'Kangaroo', 'Monkey', 'Unicorn', 'Dragon', 'Puppy', 'Kitten', 'Parrot', 'Lion', 'Bear', 'Fox', 'Wolf', 'Rabbit', 'Deer', 'Owl', 'Hedgehog', 'Turtle', 'Frog', 'Butterfly', 'Panda', 'Giraffe', 'Zebra', 'Peacock', 'Koala', 'Raccoon', 'Squirrel', 'Hippo', 'Rhino', 'Book', "Monitor"]; + $verbs = ['Connects', 'Runs', 'Processes', 'Secures', 'Encrypts', 'Saves', 'Updates', 'Boots', 'Scans', 'Compiles', 'Executes', 'Restores', 'Installs', 'Configures', 'Downloads', 'Streams', 'BacksUp', 'Syncs', 'Browses', 'Navigates', 'Runs', 'Jumps', 'Flies', 'Swims', 'Dances', 'Sings', 'Hops', 'Skips', 'Races', 'Climbs', 'Crawls', 'Glides', 'Twirls', 'Swings', 'Sprints', 'Gallops', 'Trots', 'Wanders', 'Strolls', 'Marches']; + $adverbs = ['Quickly', 'Slowly', 'Gracefully', 'Wildly', 'Loudly', 'Silently', 'Cheerfully', 'Eagerly', 'Gently', 'Happily', 'Jovially', 'Kindly', 'Lazily', 'Merrily', 'Neatly', 'Politely', 'Quietly', 'Rapidly', 'Smoothly', 'Tightly', 'Swiftly', 'Securely', 'Efficiently', 'Rapidly', 'Smoothly', 'Reliably', 'Safely', 'Wirelessly', 'Instantly', 'Silently', 'Automatically', 'Seamlessly', 'Digitally', 'Virtually', 'Continuously', 'Regularly', 'Intelligently', 'Logically']; + + // Randomly select words from arrays + $adj = $adjectives[array_rand($adjectives)]; + $noun = $nouns[array_rand($nouns)]; + $verb = $verbs[array_rand($verbs)]; + $adv = $adverbs[array_rand($adverbs)]; + + // Combine to create a base password + $password = $adj . $noun . $verb . $adv; + + // Select an article randomly + $article = $articles[array_rand($articles)]; + + // Determine if we should use 'An' instead of 'A' + if ($article == 'A' && preg_match('/^[aeiouAEIOU]/', $adj)) { + $article = 'An'; + } + + // Add the article to the password + $password = $article . $password; + + // Mapping of letters to special characters and numbers + $mappings = [ + 'A' => '@', 'a' => '@', + 'E' => '3', 'e' => '3', + 'I' => '!', 'i' => '!', + 'O' => '0', 'o' => '0', + 'S' => '$', 's' => '$', + 'T' => '+', 't' => '+', + 'B' => '8', 'b' => '8' + ]; + + // Generate an array of indices based on the password length + $indices = range(0, strlen($password) - 1); + // Randomly shuffle the indices + shuffle($indices); + + // Iterate through the shuffled indices and replace characters based on the security level + for ($i = 0; $i < min($security_level, strlen($password)); $i++) { + $index = $indices[$i]; // Get a random index + $currentChar = $password[$index]; // Get the character at this index + // Check if the current character has a mapping and replace it + if (array_key_exists($currentChar, $mappings)) { + $password[$index] = $mappings[$currentChar]; + } + } + + // Add as many random numbers as the security level + $password .= rand(pow(10, $security_level - 1), pow(10, $security_level) - 1); + + return $password; +} + +function createiCalStr($datetime, $title, $description, $location) +{ + require_once "plugins/zapcal/zapcallib.php"; + + // Create the iCal object + $cal_event = new ZCiCal(); + $event = new ZCiCalNode("VEVENT", $cal_event->curnode); + + + // Set the method to REQUEST to indicate an invite + $event->addNode(new ZCiCalDataNode("METHOD:REQUEST")); + $event->addNode(new ZCiCalDataNode("SUMMARY:" . $title)); + $event->addNode(new ZCiCalDataNode("DTSTART:" . ZCiCal::fromSqlDateTime($datetime))); + // Assuming the end time is the same as start time. + // Todo: adjust this for actual duration + $event->addNode(new ZCiCalDataNode("DTEND:" . ZCiCal::fromSqlDateTime($datetime))); + $event->addNode(new ZCiCalDataNode("DTSTAMP:" . ZCiCal::fromSqlDateTime())); + $uid = date('Y-m-d-H-i-s') . "@" . $_SERVER['SERVER_NAME']; + $event->addNode(new ZCiCalDataNode("UID:" . $uid)); + $event->addNode(new ZCiCalDataNode("LOCATION:" . $location)); + $event->addNode(new ZCiCalDataNode("DESCRIPTION:" . $description)); + // Todo: add organizer details + // $event->addNode(new ZCiCalDataNode("ORGANIZER;CN=Organizer Name:MAILTO:organizer@example.com")); + + // Return the iCal string + return $cal_event->export(); +} + +function createiCalStrCancel($originaliCalStr) { + require_once "plugins/zapcal/zapcallib.php"; + + // Import the original iCal string + $cal_event = new ZCiCal($originaliCalStr); + + // Iterate through the iCalendar object to find VEVENT nodes + foreach($cal_event->tree->child as $node) { + if($node->getName() == "VEVENT") { + // Check if STATUS node exists, update it, or add a new one + $statusFound = false; + foreach($node->data as $key => $value) { + if($key == "STATUS") { + $value->setValue("CANCELLED"); + $statusFound = true; + break; // Exit the loop once the STATUS is updated + } + } + // If STATUS node is not found, add a new STATUS node + if (!$statusFound) { + $node->addNode(new ZCiCalDataNode("STATUS:CANCELLED")); + } + } + } + + // Return the modified iCal string + return $cal_event->export(); +} + +function getTicketStatusColor($status) { + switch ($status) { + + case 'New': + return 'danger'; + + case 'Assigned': + return 'danger'; + + case 'Open': + return 'warning'; + + case 'On Hold': + return 'success'; + + case 'Closed': + return 'dark'; + + case 'Auto Close': + return 'dark'; + + case 'In-Progress': + return 'primary'; + + default: + return 'secondary'; + } +} + +function referWithAlert( + $alert, $type = "danger", + $url = $_SERVER["HTTP_REFERER"] +) { + $_SESSION['alert_message'] = $alert; + $_SESSION['alert_type'] = $type; + + header("Location: " . $url); + exit(); +} \ No newline at end of file diff --git a/functions/payment_functions.php b/functions/payment_functions.php new file mode 100644 index 000000000..0e02ef750 --- /dev/null +++ b/functions/payment_functions.php @@ -0,0 +1,384 @@ + $balance) { + $payment_is_credit = true; + + // Calculate the overpayment amount + $credit_amount = $amount - $balance; + + // Set the payment amount to the invoice balance + $amount = $balance; + } else { + $payment_is_credit = false; + } + + + mysqli_query($mysqli,"INSERT INTO payments SET payment_date = '$date', payment_amount = $amount, payment_currency_code = '$currency_code', payment_account_id = $account, payment_method = '$payment_method', payment_reference = '$reference', payment_invoice_id = $invoice_id"); + + // Get payment ID for reference + $payment_id = mysqli_insert_id($mysqli); + + if($payment_is_credit) { + //Create a credit for the overpayment + mysqli_query($mysqli,"INSERT INTO credits SET credit_amount = $credit_amount, credit_currency_code = '$currency_code', credit_date = '$date', credit_reference = 'Overpayment: $reference', credit_client_id = (SELECT invoice_client_id FROM invoices WHERE invoice_id = $invoice_id), credit_payment_id = $payment_id, credit_account_id = $account"); + // Get credit ID for reference + $credit_id = mysqli_insert_id($mysqli); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Credit', log_action = 'Create', log_description = 'Credit for Overpayment', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + } + + //Add up all the payments for the invoice and get the total amount paid to the invoice + $sql_total_payments_amount = mysqli_query($mysqli,"SELECT SUM(payment_amount) AS payments_amount FROM payments WHERE payment_invoice_id = $invoice_id"); + $row = mysqli_fetch_array($sql_total_payments_amount); + $total_payments_amount = floatval($row['payments_amount']); + + //Get the invoice total + $sql = mysqli_query($mysqli,"SELECT * FROM invoices + LEFT JOIN clients ON invoice_client_id = client_id + LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1 + WHERE invoice_id = $invoice_id" + ); + + $row = mysqli_fetch_array($sql); + $invoice_amount = floatval($row['invoice_amount']); + $invoice_prefix = sanitizeInput($row['invoice_prefix']); + $invoice_number = intval($row['invoice_number']); + $invoice_url_key = sanitizeInput($row['invoice_url_key']); + $invoice_currency_code = sanitizeInput($row['invoice_currency_code']); + $client_id = intval($row['client_id']); + $client_name = sanitizeInput($row['client_name']); + $contact_name = sanitizeInput($row['contact_name']); + $contact_email = sanitizeInput($row['contact_email']); + $contact_phone = sanitizeInput(formatPhoneNumber($row['contact_phone'])); + $contact_extension = preg_replace("/[^0-9]/", '',$row['contact_extension']); + $contact_mobile = sanitizeInput(formatPhoneNumber($row['contact_mobile'])); + + $sql = mysqli_query($mysqli,"SELECT * FROM companies WHERE company_id = 1"); + $row = mysqli_fetch_array($sql); + + $company_name = sanitizeInput($row['company_name']); + $company_country = sanitizeInput($row['company_country']); + $company_address = sanitizeInput($row['company_address']); + $company_city = sanitizeInput($row['company_city']); + $company_state = sanitizeInput($row['company_state']); + $company_zip = sanitizeInput($row['company_zip']); + $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); + $company_email = sanitizeInput($row['company_email']); + $company_website = sanitizeInput($row['company_website']); + $company_logo = sanitizeInput($row['company_logo']); + + // Sanitize Config vars from get_settings.php + $config_invoice_from_name = sanitizeInput($config_invoice_from_name); + $config_invoice_from_email = sanitizeInput($config_invoice_from_email); + + //Calculate the Invoice balance + $invoice_balance = $invoice_amount - $total_payments_amount; + + $email_data = []; + + //Determine if invoice has been paid then set the status accordingly + if ($invoice_balance == 0) { + + + $invoice_status = "Paid"; + + + + if ($email_receipt == 1) { + + $subject = "$company_name Payment Received - Invoice $invoice_prefix$invoice_number"; + $body = "Hello $contact_name,

We have received your payment in the amount of " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . " for invoice $invoice_prefix$invoice_number. Please keep this email as a receipt for your records.

Amount: " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . "
Balance: " . numfmt_format_currency($currency_format, $invoice_balance, $invoice_currency_code) . "

Thank you for your business!


--
$company_name - Billing Department
$config_invoice_from_email
$company_phone"; + // Queue Mail + $email = [ + 'from' => $config_invoice_from_email, + 'from_name' => $config_invoice_from_name, + 'recipient' => $contact_email, + 'recipient_name' => $contact_name, + 'subject' => $subject, + 'body' => $body + ]; + + $email_data[] = $email; + + // Get Email ID for reference + $email_id = mysqli_insert_id($mysqli); + + // Email Logging + + $_SESSION['alert_message'] = "Email receipt sent "; + + mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Emailed Receipt!', history_invoice_id = $invoice_id"); + + } + + } else { + + + $invoice_status = "Partial"; + + if ($email_receipt == 1) { + + $subject = "$company_name Partial Payment Received - Invoice $invoice_prefix$invoice_number"; + $body = "Hello $contact_name,

We have received partial payment in the amount of " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . " and it has been applied to invoice $invoice_prefix$invoice_number. Please keep this email as a receipt for your records.

Amount: " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . "
Balance: " . numfmt_format_currency($currency_format, $invoice_balance, $invoice_currency_code) . "

Thank you for your business!


~
$company_name - Billing
$config_invoice_from_email
$company_phone"; + + + // Queue Mail + $email = [ + 'from' => $config_invoice_from_email, + 'from_name' => $config_invoice_from_name, + 'recipient' => $contact_email, + 'recipient_name' => $contact_name, + 'subject' => $subject, + 'body' => $body + ]; + + $email_data[] = $email; + + // Get Email ID for reference + $email_id = mysqli_insert_id($mysqli); + + // Email Logging + + $_SESSION['alert_message'] .= "Email receipt sent "; + + mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Payment Receipt sent to mail queue ID: $email_id!', history_invoice_id = $invoice_id"); + + } + + } + + // Add emails to queue + if (!empty($email)) { + addToMailQueue($mysqli, $email_data); + } + + //Update Invoice Status + mysqli_query($mysqli,"UPDATE invoices SET invoice_status = '$invoice_status' WHERE invoice_id = $invoice_id"); + + //Add Payment to History + mysqli_query($mysqli,"INSERT INTO history SET history_status = '$invoice_status', history_description = 'Payment added', history_invoice_id = $invoice_id"); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Create', log_description = 'Payment created for $amount', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); + + if ($email_receipt == 1) { + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Email', log_description = 'Payment receipt for invoice $invoice_prefix$invoice_number queued to $contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); + } +} + +function createBulkPayment( + $bulk_payment +){ + + // Access global variables + global $mysqli, $session_user_id, $session_ip, $session_user_agent, $config_base_url, $config_invoice_from_name, $config_invoice_from_email, $currency_format; + + $date = $bulk_payment['date']; + $bulk_payment_amount = floatval($bulk_payment['amount']); + $bulk_payment_amount_static = $bulk_payment_amount; + $currency_code = sanitizeInput($bulk_payment['currency_code']); + $account = intval($bulk_payment['account']); + $payment_method = sanitizeInput($bulk_payment['method']); + $reference = sanitizeInput($bulk_payment['reference']); + $client_id = intval($bulk_payment['client_id']); + $email_receipt = intval($bulk_payment['email_receipt']); + $total_client_balance = floatval($bulk_payment['total_client_balance']); + + $email_body_invoices = ""; + + + // Check if bulk_payment_amount exceeds total_account_balance + if ($bulk_payment_amount > $total_client_balance) { + // Create new credit for the overpayment + $credit_amount = $bulk_payment_amount - $total_client_balance; + $bulk_payment_amount = $total_client_balance; + + // Add Credit + $credit_query = "INSERT INTO credits SET credit_amount = $credit_amount, credit_currency_code = '$currency_code', credit_date = '$date', credit_reference = 'Overpayment: $reference', credit_client_id = $client_id, credit_account_id = $account"; + mysqli_query($mysqli, $credit_query); + $credit_id = mysqli_insert_id($mysqli); + } + + // Get Invoices + $sql_invoices = "SELECT * FROM invoices + WHERE invoice_status != 'Draft' + AND invoice_status != 'Paid' + AND invoice_status != 'Cancelled' + AND invoice_client_id = $client_id + ORDER BY invoice_number ASC"; + $result_invoices = mysqli_query($mysqli, $sql_invoices); + + // Loop Through Each Invoice + while ($row = mysqli_fetch_array($result_invoices)) { + $invoice_id = intval($row['invoice_id']); + $invoice_prefix = sanitizeInput($row['invoice_prefix']); + $invoice_number = intval($row['invoice_number']); + $invoice_amount = floatval($row['invoice_amount']); + $invoice_url_key = sanitizeInput($row['invoice_url_key']); + $invoice_balance_query = "SELECT SUM(payment_amount) AS amount_paid FROM payments WHERE payment_invoice_id = $invoice_id"; + $result_amount_paid = mysqli_query($mysqli, $invoice_balance_query); + $row_amount_paid = mysqli_fetch_array($result_amount_paid); + $amount_paid = floatval($row_amount_paid['amount_paid']); + $invoice_balance = $invoice_amount - $amount_paid; + + if ($bulk_payment_amount <= 0) { + break; // Exit the loop if no payment amount is left + } + + if ($bulk_payment_amount >= $invoice_balance) { + $payment_amount = $invoice_balance; + $invoice_status = "Paid"; + } else { + $payment_amount = $bulk_payment_amount; + $invoice_status = "Partial"; + } + + // Subtract the payment amount from the bulk payment amount + $bulk_payment_amount -= $payment_amount; + + // Get Invoice Remain Balance + $remaining_invoice_balance = $invoice_balance - $payment_amount; + + // Add Payment + $payment_query = "INSERT INTO payments (payment_date, payment_amount, payment_currency_code, payment_account_id, payment_method, payment_reference, payment_invoice_id) VALUES ('{$date}', {$payment_amount}, '{$currency_code}', {$account}, '{$payment_method}', '{$reference}', {$invoice_id})"; + mysqli_query($mysqli, $payment_query); + $payment_id = mysqli_insert_id($mysqli); + + // Update Invoice Status + $update_invoice_query = "UPDATE invoices SET invoice_status = '{$invoice_status}' WHERE invoice_id = {$invoice_id}"; + mysqli_query($mysqli, $update_invoice_query); + + // Add Payment to History + $history_description = "Payment added"; + $add_history_query = "INSERT INTO history (history_status, history_description, history_invoice_id) VALUES ('{$invoice_status}', '{$history_description}', {$invoice_id})"; + mysqli_query($mysqli, $add_history_query); + + // Add to Email Body Invoice Portion + $email_body_invoices .= "
Invoice $invoice_prefix$invoice_number - Outstanding Amount: " . numfmt_format_currency($currency_format, $invoice_balance, $currency_code) . " - Payment Applied: " . numfmt_format_currency($currency_format, $payment_amount, $currency_code) . " - New Balance: " . numfmt_format_currency($currency_format, $remaining_invoice_balance, $currency_code); + + + } // End Invoice Loop + + // Send Email + if ($email_receipt == 1) { + + // Get Client / Contact Info + $sql_client = mysqli_query($mysqli,"SELECT * FROM clients + LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id + AND contact_primary = 1 + WHERE client_id = $client_id" + ); + + $row = mysqli_fetch_array($sql_client); + $client_name = sanitizeInput($row['client_name']); + $contact_name = sanitizeInput($row['contact_name']); + $contact_email = sanitizeInput($row['contact_email']); + + $sql_company = mysqli_query($mysqli,"SELECT company_name, company_phone FROM companies WHERE company_id = 1"); + $row = mysqli_fetch_array($sql_company); + + $company_name = sanitizeInput($row['company_name']); + $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); + + // Sanitize Config vars from get_settings.php + $config_invoice_from_name = sanitizeInput($config_invoice_from_name); + $config_invoice_from_email = sanitizeInput($config_invoice_from_email); + + $subject = "Payment Received - Multiple Invoices"; + $body = "Hello $contact_name,

Thank you for your payment of " . numfmt_format_currency($currency_format, $bulk_payment_amount_static, $currency_code) . " We\'ve applied your payment to the following invoices, updating their balances accordingly:

$email_body_invoices


We appreciate your continued business!

Sincerely,
$company_name - Billing
$config_invoice_from_email
$company_phone"; + + // Queue Mail + mysqli_query($mysqli, "INSERT INTO email_queue SET email_recipient = '$contact_email', email_recipient_name = '$contact_name', email_from = '$config_invoice_from_email', email_from_name = '$config_invoice_from_name', email_subject = '$subject', email_content = '$body'"); + + // Get Email ID for reference + $email_id = mysqli_insert_id($mysqli); + + // Email Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Email', log_description = 'Bulk Payment receipt for multiple Invoices queued to $contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); + + $_SESSION['alert_message'] .= "Email receipt sent and "; + + } // End Email + + // Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Create', log_description = 'Bulk Payment of $bulk_payment_amount_static', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); + +} + +function readPayment( + $payment_id +) { + // Access global variables + global $mysqli; + + $sql = mysqli_query($mysqli,"SELECT * FROM payments WHERE payment_id = $payment_id"); + $row = mysqli_fetch_array($sql); + + return $row; +} + +function deletePayment( + $payment_id +){ + // Access global variables + global $mysqli, $session_user_id, $session_ip, $session_user_agent; + + $sql = mysqli_query($mysqli,"SELECT * FROM payments WHERE payment_id = $payment_id"); + $row = mysqli_fetch_array($sql); + $invoice_id = intval($row['payment_invoice_id']); + $deleted_payment_amount = floatval($row['payment_amount']); + + //Add up all the payments for the invoice and get the total amount paid to the invoice + $sql_total_payments_amount = mysqli_query($mysqli,"SELECT SUM(payment_amount) AS total_payments_amount FROM payments WHERE payment_invoice_id = $invoice_id"); + $row = mysqli_fetch_array($sql_total_payments_amount); + $total_payments_amount = floatval($row['total_payments_amount']); + + //Get the invoice total + $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id"); + $row = mysqli_fetch_array($sql); + $invoice_amount = floatval($row['invoice_amount']); + + //Calculate the Invoice balance + $invoice_balance = $invoice_amount - $total_payments_amount + $deleted_payment_amount; + + //Determine if invoice has been paid + if ($invoice_balance == 0) { + $invoice_status = "Paid"; + } else { + $invoice_status = "Partial"; + } + + //Update Invoice Status + mysqli_query($mysqli,"UPDATE invoices SET invoice_status = '$invoice_status' WHERE invoice_id = $invoice_id"); + + //Add Payment to History + mysqli_query($mysqli,"INSERT INTO history SET history_status = '$invoice_status', history_description = 'Payment deleted', history_invoice_id = $invoice_id"); + + mysqli_query($mysqli,"DELETE FROM payments WHERE payment_id = $payment_id"); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Delete', log_description = '$payment_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); +} diff --git a/functions/security_functions.php b/functions/security_functions.php new file mode 100644 index 000000000..091fa8485 --- /dev/null +++ b/functions/security_functions.php @@ -0,0 +1,346 @@ + " Internet Explorer", + '/firefox/i' => " Firefox", + '/safari/i' => " Safari", + '/chrome/i' => " Chrome", + '/edge/i' => " Edge", + '/opera/i' => " Opera" + ); + foreach ($browser_array as $regex => $value) { + if (preg_match($regex, $user_browser)) { + $browser = $value; + } + } + return $browser; +} + +function getOS($user_os) +{ + $os_platform = "Unknown OS"; + $os_array = array( + '/windows nt 10/i' => " Windows 10", + '/windows nt 6.3/i' => " Windows 8.1", + '/windows nt 6.2/i' => " Windows 8", + '/windows nt 6.1/i' => " Windows 7", + '/windows nt 6.0/i' => " Windows Vista", + '/windows nt 5.2/i' => " Windows Server 2003/XP x64", + '/windows nt 5.1/i' => " Windows XP", + '/windows xp/i' => " Windows XP", + '/macintosh|mac os x/i' => " MacOS", + '/linux/i' => " Linux", + '/ubuntu/i' => " Ubuntu", + '/iphone/i' => " iPhone", + '/ipod/i' => " iPod", + '/ipad/i' => " iPad", + '/android/i' => " Android" + ); + foreach ($os_array as $regex => $value) { + if (preg_match($regex, $user_os)) { + $os_platform = $value; + } + } + return $os_platform; +} + +function getDevice() +{ + $tablet_browser = 0; + $mobile_browser = 0; + if (preg_match('/(tablet|ipad|playbook)|(android(?!.*(mobi|opera mini)))/i', strtolower($_SERVER['HTTP_USER_AGENT']))) { + $tablet_browser++; + } + if (preg_match('/(up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone|android|iemobile)/i', strtolower($_SERVER['HTTP_USER_AGENT']))) { + $mobile_browser++; + } + if ((strpos(strtolower($_SERVER['HTTP_ACCEPT']), 'application/vnd.wap.xhtml+xml') > 0) || ((isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_PROFILE'])))) { + $mobile_browser++; + } + $mobile_ua = strtolower(substr(getUserAgent(), 0, 4)); + $mobile_agents = array( + 'w3c ', 'acs-', 'alav', 'alca', 'amoi', 'audi', 'avan', 'benq', 'bird', 'blac', + 'blaz', 'brew', 'cell', 'cldc', 'cmd-', 'dang', 'doco', 'eric', 'hipt', 'inno', + 'ipaq', 'java', 'jigs', 'kddi', 'keji', 'leno', 'lg-c', 'lg-d', 'lg-g', 'lge-', + 'maui', 'maxo', 'midp', 'mits', 'mmef', 'mobi', 'mot-', 'moto', 'mwbp', 'nec-', + 'newt', 'noki', 'palm', 'pana', 'pant', 'phil', 'play', 'port', 'prox', + 'qwap', 'sage', 'sams', 'sany', 'sch-', 'sec-', 'send', 'seri', 'sgh-', 'shar', + 'sie-', 'siem', 'smal', 'smar', 'sony', 'sph-', 'symb', 't-mo', 'teli', 'tim-', + 'tosh', 'tsm-', 'upg1', 'upsi', 'vk-v', 'voda', 'wap-', 'wapa', 'wapi', 'wapp', + 'wapr', 'webc', 'winw', 'winw', 'xda ', 'xda-' + ); + if (in_array($mobile_ua, $mobile_agents)) { + $mobile_browser++; + } + if (strpos(strtolower(getUserAgent()), 'opera mini') > 0) { + $mobile_browser++; + //Check for tablets on Opera Mini alternative headers + $stock_ua = strtolower(isset($_SERVER['HTTP_X_OPERAMINI_PHONE_UA']) ? $_SERVER['HTTP_X_OPERAMINI_PHONE_UA'] : (isset($_SERVER['HTTP_DEVICE_STOCK_UA']) ? $_SERVER['HTTP_DEVICE_STOCK_UA'] : '')); + if (preg_match('/(tablet|ipad|playbook)|(android(?!.*mobile))/i', $stock_ua)) { + $tablet_browser++; + } + } + if ($tablet_browser > 0) { + //do something for tablet devices + return 'Tablet'; + } else if ($mobile_browser > 0) { + //do something for mobile devices + return 'Mobile'; + } else { + //do something for everything else + return 'Computer'; + } +} + +// Called during initial setup +// Encrypts the master key with the user's password +function setupFirstUserSpecificKey($user_password, $site_encryption_master_key) +{ + $iv = randomString(); + $salt = randomString(); + + //Generate 128-bit (16 byte/char) kdhash of the users password + $user_password_kdhash = hash_pbkdf2('sha256', $user_password, $salt, 100000, 16); + + //Encrypt the master key with the users kdf'd hash and the IV + $ciphertext = openssl_encrypt($site_encryption_master_key, 'aes-128-cbc', $user_password_kdhash, 0, $iv); + + return $salt . $iv . $ciphertext; +} + +/* + * For additional users / password changes + * New Users: Requires the admin setting up their account have a Specific/Session key configured + * Password Changes: Will use the current info in the session. +*/ +function encryptUserSpecificKey($user_password) +{ + $iv = randomString(); + $salt = randomString(); + + // Get the session info. + $user_encryption_session_ciphertext = $_SESSION['user_encryption_session_ciphertext']; + $user_encryption_session_iv = $_SESSION['user_encryption_session_iv']; + $user_encryption_session_key = $_COOKIE['user_encryption_session_key']; + + // Decrypt the session key to get the master key + $site_encryption_master_key = openssl_decrypt($user_encryption_session_ciphertext, 'aes-128-cbc', $user_encryption_session_key, 0, $user_encryption_session_iv); + + // Generate 128-bit (16 byte/char) kdhash of the users (new) password + $user_password_kdhash = hash_pbkdf2('sha256', $user_password, $salt, 100000, 16); + + // Encrypt the master key with the users kdf'd hash and the IV + $ciphertext = openssl_encrypt($site_encryption_master_key, 'aes-128-cbc', $user_password_kdhash, 0, $iv); + + return $salt . $iv . $ciphertext; +} + +// Given a ciphertext (incl. IV) and the user's password, returns the site master key +// Ran at login, to facilitate generateUserSessionKey +function decryptUserSpecificKey($user_encryption_ciphertext, $user_password) +{ + //Get the IV, salt and ciphertext + $salt = substr($user_encryption_ciphertext, 0, 16); + $iv = substr($user_encryption_ciphertext, 16, 16); + $ciphertext = substr($user_encryption_ciphertext, 32); + + //Generate 128-bit (16 byte/char) kdhash of the users password + $user_password_kdhash = hash_pbkdf2('sha256', $user_password, $salt, 100000, 16); + + //Use this hash to get the original/master key + return openssl_decrypt($ciphertext, 'aes-128-cbc', $user_password_kdhash, 0, $iv); +} + +/* +Generates what is probably best described as a session key (ephemeral-ish) +- Allows us to store the master key on the server whilst the user is using the application, without prompting to type their password everytime they want to decrypt a credential +- Ciphertext/IV is stored on the server in the users' session, encryption key is controlled/provided by the user as a cookie +- Only the user can decrypt their session ciphertext to get the master key +- Encryption key never hits the disk in cleartext +*/ +function generateUserSessionKey($site_encryption_master_key) +{ + $user_encryption_session_key = randomString(); + $user_encryption_session_iv = randomString(); + $user_encryption_session_ciphertext = openssl_encrypt($site_encryption_master_key, 'aes-128-cbc', $user_encryption_session_key, 0, $user_encryption_session_iv); + + // Store ciphertext in the user's session + $_SESSION['user_encryption_session_ciphertext'] = $user_encryption_session_ciphertext; + $_SESSION['user_encryption_session_iv'] = $user_encryption_session_iv; + + // Give the user "their" key as a cookie + include 'config.php'; + + if ($config_https_only) { + setcookie("user_encryption_session_key", "$user_encryption_session_key", ['path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'None']); + } else { + setcookie("user_encryption_session_key", $user_encryption_session_key, 0, "/"); + $_SESSION['alert_message'] = "Unencrypted connection flag set: Using non-secure cookies."; + } +} + +// Decrypts an encrypted password (website/asset login), returns it as a string +function decryptLoginEntry($login_password_ciphertext) +{ + + // Split the login into IV and Ciphertext + $login_iv = substr($login_password_ciphertext, 0, 16); + $login_ciphertext = $salt = substr($login_password_ciphertext, 16); + + // Get the user session info. + $user_encryption_session_ciphertext = $_SESSION['user_encryption_session_ciphertext']; + $user_encryption_session_iv = $_SESSION['user_encryption_session_iv']; + $user_encryption_session_key = $_COOKIE['user_encryption_session_key']; + + // Decrypt the session key to get the master key + $site_encryption_master_key = openssl_decrypt($user_encryption_session_ciphertext, 'aes-128-cbc', $user_encryption_session_key, 0, $user_encryption_session_iv); + + // Decrypt the login password using the master key + return openssl_decrypt($login_ciphertext, 'aes-128-cbc', $site_encryption_master_key, 0, $login_iv); +} + +// Encrypts a website/asset login password +function encryptLoginEntry($login_password_cleartext) +{ + $iv = randomString(); + + // Get the user session info. + $user_encryption_session_ciphertext = $_SESSION['user_encryption_session_ciphertext']; + $user_encryption_session_iv = $_SESSION['user_encryption_session_iv']; + $user_encryption_session_key = $_COOKIE['user_encryption_session_key']; + + //Decrypt the session key to get the master key + $site_encryption_master_key = openssl_decrypt($user_encryption_session_ciphertext, 'aes-128-cbc', $user_encryption_session_key, 0, $user_encryption_session_iv); + + //Encrypt the website/asset login using the master key + $ciphertext = openssl_encrypt($login_password_cleartext, 'aes-128-cbc', $site_encryption_master_key, 0, $iv); + + return $iv . $ciphertext; +} + +function strtoAZaz09($string) +{ + + // Gets rid of non-alphanumerics + return preg_replace('/[^A-Za-z0-9_-]/', '', $string); +} + +// Cross-Site Request Forgery check for sensitive functions +// Validates the CSRF token provided matches the one in the users session +function validateCSRFToken($token) +{ + if (hash_equals($token, $_SESSION['csrf_token'])) { + return true; + } else { + $_SESSION['alert_type'] = "warning"; + $_SESSION['alert_message'] = "CSRF token verification failed. Try again, or log out to refresh your token."; + header("Location: index.php"); + exit(); + } +} + +/* + * Role validation + * Admin - 3 + * Tech - 2 + * Accountant - 1 + */ + +function validateAdminRole() +{ + if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] != 3) { + $_SESSION['alert_type'] = "danger"; + $_SESSION['alert_message'] = WORDING_ROLECHECK_FAILED; + header("Location: " . $_SERVER["HTTP_REFERER"]); + exit(); + } +} + +// Validates a user is a tech (or admin). Stops page load and attempts to direct away from the page if not (i.e. user is an accountant) +function validateTechRole() +{ + if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] == 1) { + $_SESSION['alert_type'] = "danger"; + $_SESSION['alert_message'] = WORDING_ROLECHECK_FAILED; + header("Location: " . $_SERVER["HTTP_REFERER"]); + exit(); + } +} + +// Validates a user is an accountant (or admin). Stops page load and attempts to direct away from the page if not (i.e. user is a tech) +function validateAccountantRole() +{ + if (!isset($_SESSION['user_role']) || $_SESSION['user_role'] == 2) { + $_SESSION['alert_type'] = "danger"; + $_SESSION['alert_message'] = WORDING_ROLECHECK_FAILED; + header("Location: " . $_SERVER["HTTP_REFERER"]); + exit(); + } +} + +function sanitizeInput($input) +{ + global $mysqli; + + // Remove HTML and PHP tags + $input = strip_tags((string) $input); + + // Remove white space from beginning and end of input + $input = trim($input); + + // Escape special characters + $input = mysqli_real_escape_string($mysqli, $input); + + // Return sanitized input + return $input; +} + +function sanitizeForEmail($data) +{ + $sanitized = htmlspecialchars($data); + $sanitized = strip_tags($sanitized); + $sanitized = trim($sanitized); + return $sanitized; +} + +function isMobile() +{ + // Check if the user agent is a mobile device + return preg_match('/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|opera mini|palm|phone|pie|tablet|up.browser|up.link|webos|wos)/i', $_SERVER['HTTP_USER_AGENT']); +} + + diff --git a/post/account.php b/post/account.php index 1a5b3a6c1..ca722592f 100644 --- a/post/account.php +++ b/post/account.php @@ -5,67 +5,65 @@ */ if (isset($_POST['add_account'])) { + // Check if the user is an accountant + validateAccountantRole(); + // Sanitize the input $name = sanitizeInput($_POST['name']); $opening_balance = floatval($_POST['opening_balance']); $currency_code = sanitizeInput($_POST['currency_code']); $notes = sanitizeInput($_POST['notes']); $type = intval($_POST['type']); - mysqli_query($mysqli,"INSERT INTO accounts SET account_name = '$name', opening_balance = $opening_balance, account_currency_code = '$currency_code', account_type ='$type', account_notes = '$notes'"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Account', log_action = 'Create', log_description = '$name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Account added"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + // Create the account + createAccount($name, $opening_balance, $currency_code, $notes, $type); + // Redirect to the accounts page with a success message + referWithAlert("Account created", "success", "accounts.php"); } if (isset($_POST['edit_account'])) { + // Check if the user is an accountant + validateAccountantRole(); + // Sanitize the input $account_id = intval($_POST['account_id']); $name = sanitizeInput($_POST['name']); $type = intval($_POST['type']); $notes = sanitizeInput($_POST['notes']); - mysqli_query($mysqli,"UPDATE accounts SET account_name = '$name',account_type = '$type', account_notes = '$notes' WHERE account_id = $account_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Account', log_action = 'Modify', log_description = '$name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Account modified"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + // Edit the account + editAccount($account_id, $name, $type, $notes); + // Redirect to the referring page with a success message + referWithAlert("Account edited", "success"); } if (isset($_GET['archive_account'])) { - $account_id = intval($_GET['archive_account']); + // Check if the user is an accountant + validateAccountantRole(); - mysqli_query($mysqli,"UPDATE accounts SET account_archived_at = NOW() WHERE account_id = $account_id"); - - //logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Account', log_action = 'Archive', log_description = '$account_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); - - $_SESSION['alert_message'] = "Account Archived"; + // Sanitize the input + $account_id = intval($_GET['archive_account']); - header("Location: " . $_SERVER["HTTP_REFERER"]); + // Archive the account + archiveAccount($account_id); + // Redirect to the accounts page with a success message + referWithAlert("Account archived", "success", "accounts.php"); } if (isset($_GET['delete_account'])) { - $account_id = intval($_GET['delete_account']); + // Check if the user is an accountant + validateAccountantRole(); - mysqli_query($mysqli,"DELETE FROM accounts WHERE account_id = $account_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Account', log_action = 'Delete', log_description = '$account_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Account deleted"; + // Sanitize the input + $account_id = intval($_GET['delete_account']); - header("Location: " . $_SERVER["HTTP_REFERER"]); + // Delete the account + deleteAccount($account_id); + // Redirect to the accounts page with a success message + referWithAlert("Account deleted", "success", "accounts.php"); } diff --git a/post/account_type.php b/post/account_type.php index 07fc39bd4..382fad56a 100644 --- a/post/account_type.php +++ b/post/account_type.php @@ -5,64 +5,62 @@ */ if (isset($_POST['add_account_type'])) { + // Check if the user is an accountant + validateAccountantRole(); + // Sanitize the input $name = sanitizeInput($_POST['name']); $type = intval($_POST['type']); $description = sanitizeInput($_POST['description']); - mysqli_query($mysqli,"INSERT INTO account_types SET account_type_parent = $type, account_type_name = '$name', account_type_description = '$description'"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Account Type', log_action = 'Create', log_description = '$name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Account added"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + // Create the account type + createAccountType($name, $type, $description); + // Redirect to the referring page with a success message + referWithAlert("Account added", "success"); } if (isset($_POST['edit_account_type'])) { + // Check if the user is an accountant + validateAccountantRole(); + // Sanitize the input $account_type_id = intval($_POST['account_type_id']); $name = sanitizeInput($_POST['name']); $type = intval($_POST['type']); $description = sanitizeInput($_POST['description']); - mysqli_query($mysqli,"UPDATE account_types SET account_type_parent = $type, account_type_name = '$name', account_type_description = '$description' WHERE account_type_id = $account_type_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Account Type', log_action = 'Edit', log_description = '$name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Account edited"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + // Edit the account type + editAccountType($account_type_id, $name, $type, $description); + // Redirect to the referring page with a success message + referWithAlert("Account edited", "success"); } if (isset($_GET['archive_account_type'])) { - $account_type_id = intval($_GET['archive_account_type']); + // Check if the user is an accountant + validateAccountantRole(); - mysqli_query($mysqli,"UPDATE account_types SET account_type_archived_at = NOW() WHERE account_type_id = $account_type_id"); - - //logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Account Type', log_action = 'Archive', log_description = '$account_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); - - $_SESSION['alert_message'] = "Account Archived"; + // Sanitize the input + $account_type_id = intval($_GET['archive_account_type']); - header("Location: " . $_SERVER["HTTP_REFERER"]); + // Archive the account type + archiveAccountType($account_type_id); + // Redirect to the referring page with a success message + referWithAlert("Account Archived", "success"); } if (isset($_GET['unarchive_account_type'])) { - $account_type_id = intval($_GET['unarchive_account_type']); + // Check if the user is an accountant + validateAccountantRole(); - mysqli_query($mysqli,"UPDATE account_types SET account_type_archived_at = NULL WHERE account_type_id = $account_type_id"); - - //logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Account Type', log_action = 'Unarchive', log_description = '$account_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); - - $_SESSION['alert_message'] = "Account Unarchived"; + // Sanitize the input + $account_type_id = intval($_GET['unarchive_account_type']); - header("Location: " . $_SERVER["HTTP_REFERER"]); + // Unarchive the account type + unarchiveAccountType($account_type_id); + // Redirect to the referring page with a success message + referWithAlert("Account Unarchived", "success"); } \ No newline at end of file diff --git a/post/api.php b/post/api.php index 4ea3c394b..6f961a96c 100644 --- a/post/api.php +++ b/post/api.php @@ -16,16 +16,9 @@ $expire = sanitizeInput($_POST['expire']); $client = intval($_POST['client']); - mysqli_query($mysqli,"INSERT INTO api_keys SET api_key_name = '$name', api_key_secret = '$secret', api_key_expire = '$expire', api_key_client_id = $client"); + $api_key_ID = createAPIKey($secret, $name, $expire, $client); - $api_key_id = mysqli_insert_id($mysqli); - - // Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'API', log_action = 'Create', log_description = '$session_name created API Key $name set to expire on $expire', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client, log_user_id = $session_user_id, log_entity_id = $api_key_id"); - - $_SESSION['alert_message'] = "API Key $name created"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + referWithAlert("API Key $api_key_id created", "success"); } @@ -38,19 +31,9 @@ $api_key_id = intval($_GET['delete_api_key']); - // Get API Key Name - $row = mysqli_fetch_array(mysqli_query($mysqli,"SELECT * FROM api_keys WHERE api_key_id = $api_key_id")); - $name = sanitizeInput($row['api_key_name']); - - mysqli_query($mysqli,"DELETE FROM api_keys WHERE api_key_id = $api_key_id"); + deleteAPIKey($api_key_id); - // Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'API Key', log_action = 'Delete', log_description = '$session_name deleted API key $name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $api_key_id"); - - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "API Key $name deleted"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + referWithAlert("API Key $api_key_id deleted", "success"); } @@ -61,24 +44,9 @@ $count = 0; // Default 0 $api_key_ids = $_POST['api_key_ids']; // Get array of API key IDs to be deleted - if (!empty($api_key_ids)) { - - // Cycle through array and delete each scheduled ticket - foreach ($api_key_ids as $api_key_id) { - - $api_key_id = intval($api_key_id); - mysqli_query($mysqli, "DELETE FROM api_keys WHERE api_key_id = $api_key_id"); - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'API Key', log_action = 'Delete', log_description = '$session_name deleted API key (bulk)', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $api_key_id"); - - $count++; - } - - // Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'API Key', log_action = 'Delete', log_description = '$session_name bulk deleted $count keys', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Deleted $count keys(s)"; - + foreach ($api_key_ids as $api_key_id) { + deleteAPIKey($api_key_id); + $count++; } - - header("Location: " . $_SERVER["HTTP_REFERER"]); + referWithAlert("$count API Keys deleted"); } diff --git a/post/invoice.php b/post/invoice.php index b1f4ec6ce..2ffb969bc 100644 --- a/post/invoice.php +++ b/post/invoice.php @@ -9,31 +9,12 @@ require_once 'post/invoice_model.php'; $client = intval($_POST['client']); + $due = sanitizeInput($_POST['due']); + $date = sanitizeInput($_POST['date']); + $category = intval($_POST['category']); - //Get Net Terms - $sql = mysqli_query($mysqli,"SELECT client_net_terms FROM clients WHERE client_id = $client"); - $row = mysqli_fetch_array($sql); - $client_net_terms = intval($row['client_net_terms']); - - //Get the last Invoice Number and add 1 for the new invoice number - $invoice_number = $config_invoice_next_number; - $new_config_invoice_next_number = $config_invoice_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = 1"); - - //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 = $invoice_number, invoice_scope = '$scope', invoice_date = '$date', invoice_due = DATE_ADD('$date', INTERVAL $client_net_terms day), invoice_currency_code = '$session_company_currency', invoice_category_id = $category, invoice_status = 'Draft', invoice_url_key = '$url_key', invoice_client_id = $client"); - $invoice_id = mysqli_insert_id($mysqli); - - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Draft', history_description = 'INVOICE added!', history_invoice_id = $invoice_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = '$config_invoice_prefix$invoice_number', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Invoice added"; - - header("Location: invoice.php?invoice_id=$invoice_id"); + createInvoice($client, $due, $date, $category); + referWithAlert("Invoice added", "success"); } if (isset($_POST['edit_invoice'])) { @@ -43,26 +24,20 @@ $invoice_id = intval($_POST['invoice_id']); $due = sanitizeInput($_POST['due']); - //Calculate new total - $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_invoice_id = $invoice_id"); - $invoice_amount = 0; - while($row = mysqli_fetch_array($sql)) { - $item_total = floatval($row['item_total']); - $invoice_amount = $invoice_amount + $item_total; - } - $invoice_amount = $invoice_amount - $invoice_discount; - - - mysqli_query($mysqli,"UPDATE invoices SET invoice_scope = '$scope', invoice_date = '$date', invoice_due = '$due', invoice_category_id = $category, invoice_discount_amount = '$invoice_discount', invoice_amount = '$invoice_amount' WHERE invoice_id = $invoice_id"); - - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Modify', log_description = '$invoice_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Invoice modified"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + updateInvoice( + $invoice_id, + $due, + $status, + $prefix, + $number, + $scope, + $date, + $currency_code, + $category_id, + $url_key, + $client_id + ); + referWithAlert("Invoice edited", "success"); } if (isset($_POST['add_invoice_copy'])) { @@ -70,58 +45,8 @@ $invoice_id = intval($_POST['invoice_id']); $date = sanitizeInput($_POST['date']); - //Get Net Terms - $sql = mysqli_query($mysqli,"SELECT client_net_terms FROM clients, invoices WHERE client_id = invoice_client_id AND invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql); - $client_net_terms = intval($row['client_net_terms']); - - $invoice_number = $config_invoice_next_number; - $new_config_invoice_next_number = $config_invoice_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = 1"); - - $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql); - $invoice_scope = sanitizeInput($row['invoice_scope']); - $invoice_discount_amount = floatval($row['invoice_discount_amount']); - $invoice_amount = floatval($row['invoice_amount']); - $invoice_currency_code = sanitizeInput($row['invoice_currency_code']); - $invoice_note = sanitizeInput($row['invoice_note']); - $client_id = intval($row['invoice_client_id']); - $category_id = intval($row['invoice_category_id']); - - //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 = $invoice_number, invoice_scope = '$invoice_scope', invoice_date = '$date', invoice_due = DATE_ADD('$date', INTERVAL $client_net_terms day), invoice_category_id = $category_id, invoice_status = 'Draft', invoice_discount_amount = $invoice_discount_amount, invoice_amount = $invoice_amount, invoice_currency_code = '$invoice_currency_code', invoice_note = '$invoice_note', invoice_url_key = '$url_key', invoice_client_id = $client_id") or die(mysql_error()); - - $new_invoice_id = mysqli_insert_id($mysqli); - - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Draft', history_description = 'Copied INVOICE!', history_invoice_id = $new_invoice_id"); - - $sql_items = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_invoice_id = $invoice_id"); - while($row = mysqli_fetch_array($sql_items)) { - $item_id = intval($row['item_id']); - $item_name = sanitizeInput($row['item_name']); - $item_description = sanitizeInput($row['item_description']); - $item_quantity = floatval($row['item_quantity']); - $item_price = floatval($row['item_price']); - $item_subtotal = floatval($row['item_subtotal']); - $item_tax = floatval($row['item_tax']); - $item_total = floatval($row['item_total']); - $item_order = intval($row['item_order']); - $tax_id = intval($row['item_tax_id']); - - mysqli_query($mysqli,"INSERT INTO invoice_items SET item_name = '$item_name', item_description = '$item_description', item_quantity = $item_quantity, item_price = $item_price, item_subtotal = $item_subtotal, item_tax = $item_tax, item_total = $item_total, item_order = $item_order, item_tax_id = $tax_id, item_invoice_id = $new_invoice_id"); - } - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = 'Copied Invoice', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Invoice copied"; - - header("Location: invoice.php?invoice_id=$new_invoice_id"); - + copyInvoice($invoice_id, $date); + referWithAlert("Invoice copied", "success"); } if (isset($_POST['add_invoice_recurring'])) { @@ -129,49 +54,8 @@ $invoice_id = intval($_POST['invoice_id']); $recurring_frequency = sanitizeInput($_POST['frequency']); - $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql); - $invoice_date = sanitizeInput($row['invoice_date']); - $invoice_amount = floatval($row['invoice_amount']); - $invoice_currency_code = sanitizeInput($row['invoice_currency_code']); - $invoice_scope = sanitizeInput($row['invoice_scope']); - $invoice_note = sanitizeInput($row['invoice_note']); //SQL Escape in case notes have , them - $client_id = intval($row['invoice_client_id']); - $category_id = intval($row['invoice_category_id']); - - //Get the last Recurring Number and add 1 for the new Recurring number - $recurring_number = $config_recurring_next_number; - $new_config_recurring_next_number = $config_recurring_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_recurring_next_number = $new_config_recurring_next_number WHERE company_id = 1"); - - mysqli_query($mysqli,"INSERT INTO recurring SET recurring_prefix = '$config_recurring_prefix', recurring_number = $recurring_number, recurring_scope = '$invoice_scope', recurring_frequency = '$recurring_frequency', recurring_next_date = DATE_ADD('$invoice_date', INTERVAL 1 $recurring_frequency), recurring_status = 1, recurring_amount = $invoice_amount, recurring_currency_code = '$invoice_currency_code', recurring_note = '$invoice_note', recurring_category_id = $category_id, recurring_client_id = $client_id"); - - $recurring_id = mysqli_insert_id($mysqli); - - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Draft', history_description = 'Recurring Created from INVOICE!', history_recurring_id = $recurring_id"); - - $sql_items = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_invoice_id = $invoice_id"); - while($row = mysqli_fetch_array($sql_items)) { - $item_id = intval($row['item_id']); - $item_name = sanitizeInput($row['item_name']); - $item_description = sanitizeInput($row['item_description']); - $item_quantity = floatval($row['item_quantity']); - $item_price = floatval($row['item_price']); - $item_subtotal = floatval($row['item_subtotal']); - $item_tax = floatval($row['item_tax']); - $item_total = floatval($row['item_total']); - $item_order = intval($row['item_order']); - $tax_id = intval($row['item_tax_id']); - - mysqli_query($mysqli,"INSERT INTO invoice_items SET item_name = '$item_name', item_description = '$item_description', item_quantity = $item_quantity, item_price = $item_price, item_subtotal = $item_subtotal, item_tax = $item_tax, item_total = $item_total, item_order = $item_order, item_tax_id = $tax_id, item_recurring_id = $recurring_id"); - } - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = 'From recurring invoice', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Created recurring Invoice from this Invoice"; - - header("Location: recurring_invoice.php?recurring_id=$recurring_id"); + createInvoiceFromRecurring($invoice_id, $recurring_frequency); + referWithAlert("Recurring Invoice added from this invoice", "success", "recurring_invoice.php?recurring_id=$recurring_id"); } @@ -183,24 +67,14 @@ $category = intval($_POST['category']); $scope = sanitizeInput($_POST['scope']); - //Get the last Recurring Number and add 1 for the new Recurring number - $recurring_number = $config_recurring_next_number; - $new_config_recurring_next_number = $config_recurring_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_recurring_next_number = $new_config_recurring_next_number WHERE company_id = 1"); - - mysqli_query($mysqli,"INSERT INTO recurring SET recurring_prefix = '$config_recurring_prefix', recurring_number = $recurring_number, recurring_scope = '$scope', recurring_frequency = '$frequency', recurring_next_date = '$start_date', recurring_category_id = $category, recurring_status = 1, recurring_currency_code = '$session_company_currency', recurring_client_id = $client"); - - $recurring_id = mysqli_insert_id($mysqli); - - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Active', history_description = 'Recurring Invoice created!', history_recurring_id = $recurring_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Recurring', log_action = 'Create', log_description = '$start_date - $category', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Recurring Invoice added"; - - header("Location: recurring_invoice.php?recurring_id=$recurring_id"); - + createRecurringInvoice( + $client, + $frequency, + $start_date, + $category, + $scope + ); + referWithAlert("Recurring Invoice added", "success"); } if (isset($_POST['edit_recurring'])) { @@ -213,55 +87,23 @@ $status = intval($_POST['status']); $recurring_discount = floatval($_POST['recurring_discount']); - //Calculate new total - $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_recurring_id = $recurring_id"); - $recurring_amount = 0; - while($row = mysqli_fetch_array($sql)) { - $item_total = floatval($row['item_total']); - $recurring_amount = $recurring_amount + $item_total; - } - $recurring_amount = $recurring_amount - $recurring_discount; - - mysqli_query($mysqli,"UPDATE recurring SET recurring_scope = '$scope', recurring_frequency = '$frequency', recurring_next_date = '$next_date', recurring_category_id = $category, recurring_discount_amount = $recurring_discount, recurring_amount = $recurring_amount, recurring_status = $status WHERE recurring_id = $recurring_id"); - - mysqli_query($mysqli,"INSERT INTO history SET history_status = '$status', history_description = 'Recurring modified', history_recurring_id = $recurring_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Recurring', log_action = 'Modify', log_description = '$recurring_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Recurring Invoice modified"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + updateRecurringInvoice( + $recurring_id, + $frequency, + $next_date, + $category, + $scope, + $status, + $recurring_discount + ); + referWithAlert("Recurring Invoice edited", "success"); } if (isset($_GET['delete_recurring'])) { $recurring_id = intval($_GET['delete_recurring']); - mysqli_query($mysqli,"DELETE FROM recurring WHERE recurring_id = $recurring_id"); - - //Delete Items Associated with the Recurring - $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_recurring_id = $recurring_id"); - while($row = mysqli_fetch_array($sql)) { - $item_id = intval($row['item_id']); - mysqli_query($mysqli,"DELETE FROM invoice_items WHERE item_id = $item_id"); - } - - //Delete History Associated with the Invoice - $sql = mysqli_query($mysqli,"SELECT * FROM history WHERE history_recurring_id = $recurring_id"); - while($row = mysqli_fetch_array($sql)) { - $history_id = intval($row['history_id']); - mysqli_query($mysqli,"DELETE FROM history WHERE history_id = $history_id"); - } - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Recurring', log_action = 'Delete', log_description = '$recurring_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "Recurring Invoice deleted"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + deleteRecurringInvoice($recurring_id); + referWithAlert("Recurring Invoice deleted", "success"); } if (isset($_POST['add_recurring_item'])) { @@ -274,43 +116,17 @@ $tax_id = intval($_POST['tax_id']); $item_order = intval($_POST['item_order']); - $subtotal = $price * $qty; - - if ($tax_id > 0) { - $sql = mysqli_query($mysqli,"SELECT * FROM taxes WHERE tax_id = $tax_id"); - $row = mysqli_fetch_array($sql); - $tax_percent = floatval($row['tax_percent']); - $tax_amount = $subtotal * $tax_percent / 100; - } else { - $tax_amount = 0; - } - - $total = $subtotal + $tax_amount; - - mysqli_query($mysqli,"INSERT INTO invoice_items SET item_name = '$name', item_description = '$description', item_quantity = $qty, item_price = $price, item_subtotal = $subtotal, item_tax = $tax_amount, item_total = $total, item_tax_id = $tax_id, item_order = $item_order, item_recurring_id = $recurring_id"); - - //Get Discount - - $sql = mysqli_query($mysqli,"SELECT * FROM recurring WHERE recurring_id = $recurring_id"); - $row = mysqli_fetch_array($sql); - $recurring_discount = floatval($row['recurring_discount_amount']); - - - //add up all the items - $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_recurring_id = $recurring_id"); - $recurring_amount = 0; - while($row = mysqli_fetch_array($sql)) { - $item_total = floatval($row['item_total']); - $recurring_amount = $recurring_amount + $item_total; - } - $recurring_amount = $recurring_amount - $recurring_discount; - - mysqli_query($mysqli,"UPDATE recurring SET recurring_amount = $recurring_amount WHERE recurring_id = $recurring_id"); - - $_SESSION['alert_message'] = "Recurring Invoice Updated"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + $item = []; + $item['invoice_id'] = $recurring_id; + $item['name'] = $name; + $item['description'] = $description; + $item['qty'] = $qty; + $item['price'] = $price; + $item['tax_id'] = $tax_id; + $item['item_order'] = $item_order; + + createInvoiceItem("recurring", $item); + referWithAlert("Item added", "success"); } if (isset($_POST['recurring_note'])) { @@ -319,113 +135,37 @@ $note = sanitizeInput($_POST['note']); mysqli_query($mysqli,"UPDATE recurring SET recurring_note = '$note' WHERE recurring_id = $recurring_id"); - - $_SESSION['alert_message'] = "Notes added"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + referWithAlert("Notes added", "success"); } if (isset($_GET['delete_recurring_item'])) { $item_id = intval($_GET['delete_recurring_item']); - $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_id = $item_id"); - $row = mysqli_fetch_array($sql); - $recurring_id = intval($row['item_recurring_id']); - $item_subtotal = floatval($row['item_subtotal']); - $item_tax = floatval($row['item_tax']); - $item_total = floatval($row['item_total']); - - $sql = mysqli_query($mysqli,"SELECT * FROM recurring WHERE recurring_id = $recurring_id"); - $row = mysqli_fetch_array($sql); - - $new_recurring_amount = floatval($row['recurring_amount']) - $item_total; - - mysqli_query($mysqli,"UPDATE recurring SET recurring_amount = $new_recurring_amount WHERE recurring_id = $recurring_id"); - - mysqli_query($mysqli,"DELETE FROM invoice_items WHERE item_id = $item_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Recurring Item', log_action = 'Delete', log_description = 'Item ID $item_id from Recurring ID $recurring_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "Item deleted"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + deleteInvoiceItem("recurring", $item_id); + referWithAlert("Item deleted"); } if (isset($_GET['mark_invoice_sent'])) { $invoice_id = intval($_GET['mark_invoice_sent']); - mysqli_query($mysqli,"UPDATE invoices SET invoice_status = 'Sent' WHERE invoice_id = $invoice_id"); - - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'INVOICE marked sent', history_invoice_id = $invoice_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Update', log_description = '$invoice_id marked sent', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Invoice marked sent"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + updateInvoiceStatus($invoice_id, "Sent"); + referWithAlert("Invoice marked as sent", "success"); } if (isset($_GET['cancel_invoice'])) { $invoice_id = intval($_GET['cancel_invoice']); - mysqli_query($mysqli,"UPDATE invoices SET invoice_status = 'Cancelled' WHERE invoice_id = $invoice_id"); - - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Cancelled', history_description = 'INVOICE cancelled!', history_invoice_id = $invoice_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Modify', log_description = 'Cancelled', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Invoice cancelled"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + updateInvoiceStatus($invoice_id, "Cancelled"); + referWithAlert("Invoice cancelled", "success"); } if (isset($_GET['delete_invoice'])) { $invoice_id = intval($_GET['delete_invoice']); - mysqli_query($mysqli,"DELETE FROM invoices WHERE invoice_id = $invoice_id"); - - //Delete Items Associated with the Invoice - $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_invoice_id = $invoice_id"); - while($row = mysqli_fetch_array($sql)) { - $item_id = intval($row['item_id']); - mysqli_query($mysqli,"DELETE FROM invoice_items WHERE item_id = $item_id"); - } - - //Delete History Associated with the Invoice - $sql = mysqli_query($mysqli,"SELECT * FROM history WHERE history_invoice_id = $invoice_id"); - while($row = mysqli_fetch_array($sql)) { - $history_id = intval($row['history_id']); - mysqli_query($mysqli,"DELETE FROM history WHERE history_id = $history_id"); - } - - //Delete Payments Associated with the Invoice - $sql = mysqli_query($mysqli,"SELECT * FROM payments WHERE payment_invoice_id = $invoice_id"); - while($row = mysqli_fetch_array($sql)) { - $payment_id = intval($row['payment_id']); - mysqli_query($mysqli,"DELETE FROM payments WHERE payment_id = $payment_id"); - } - - //unlink tickets from invoice - mysqli_query($mysqli,"UPDATE tickets SET ticket_invoice_id = 0 WHERE ticket_invoice_id = $invoice_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Delete', log_description = '$invoice_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "Invoice deleted"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + deleteInvoice($invoice_id); + referWithAlert("Invoice deleted"); } if (isset($_POST['add_invoice_item'])) { @@ -438,47 +178,17 @@ $tax_id = intval($_POST['tax_id']); $item_order = intval($_POST['item_order']); - $subtotal = $price * $qty; - - if ($tax_id > 0) { - $sql = mysqli_query($mysqli,"SELECT * FROM taxes WHERE tax_id = $tax_id"); - $row = mysqli_fetch_array($sql); - $tax_percent = floatval($row['tax_percent']); - $tax_amount = $subtotal * $tax_percent / 100; - } else { - $tax_amount = 0; - } - - $total = $subtotal + $tax_amount; - - mysqli_query($mysqli,"INSERT INTO invoice_items SET item_name = '$name', item_description = '$description', item_quantity = $qty, item_price = $price, item_subtotal = $subtotal, item_tax = $tax_amount, item_total = $total, item_order = $item_order, item_tax_id = $tax_id, item_invoice_id = $invoice_id"); - - //Get Discount - - $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql); - if($invoice_id > 0){ - $invoice_discount = floatval($row['invoice_discount_amount']); - } else { - $invoice_discount = 0; - } - - //add up all line items - $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_invoice_id = $invoice_id"); - $invoice_total = 0; - while($row = mysqli_fetch_array($sql)) { - $item_total = floatval($row['item_total']); - $invoice_total = $invoice_total + $item_total; - } - $new_invoice_amount = $invoice_total - $invoice_discount; - - mysqli_query($mysqli,"UPDATE invoices SET invoice_amount = $new_invoice_amount WHERE invoice_id = $invoice_id"); - - $_SESSION['alert_message'] = "Item added"; - - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + $item = []; + $item['invoice_id'] = $invoice_id; + $item['name'] = $name; + $item['description'] = $description; + $item['qty'] = $qty; + $item['price'] = $price; + $item['tax_id'] = $tax_id; + $item['item_order'] = $item_order; + + createInvoiceItem("invoice", $item); + referWithAlert("Item added", "success"); } if (isset($_POST['invoice_note'])) { @@ -487,11 +197,7 @@ $note = sanitizeInput($_POST['note']); mysqli_query($mysqli,"UPDATE invoices SET invoice_note = '$note' WHERE invoice_id = $invoice_id"); - - $_SESSION['alert_message'] = "Notes added"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + referWithAlert("Notes added", "success"); } if (isset($_POST['edit_item'])) { @@ -506,95 +212,26 @@ $price = floatval($_POST['price']); $tax_id = intval($_POST['tax_id']); - $subtotal = $price * $qty; - - if ($tax_id > 0) { - $sql = mysqli_query($mysqli,"SELECT * FROM taxes WHERE tax_id = $tax_id"); - $row = mysqli_fetch_array($sql); - $tax_percent = floatval($row['tax_percent']); - $tax_amount = $subtotal * $tax_percent / 100; - } else { - $tax_amount = 0; - } - - $total = $subtotal + $tax_amount; - - mysqli_query($mysqli,"UPDATE invoice_items SET item_name = '$name', item_description = '$description', item_quantity = $qty, item_price = $price, item_subtotal = $subtotal, item_tax = $tax_amount, item_total = $total, item_tax_id = $tax_id WHERE item_id = $item_id"); - - if ($invoice_id > 0) { - //Get Discount Amount - $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql); - $invoice_discount = floatval($row['invoice_discount_amount']); - - //Update Invoice Balances by tallying up invoice items - $sql_invoice_total = mysqli_query($mysqli,"SELECT SUM(item_total) AS invoice_total FROM invoice_items WHERE item_invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql_invoice_total); - $new_invoice_amount = floatval($row['invoice_total']) - $invoice_discount; - - mysqli_query($mysqli,"UPDATE invoices SET invoice_amount = $new_invoice_amount WHERE invoice_id = $invoice_id"); - - }elseif ($quote_id > 0) { - //Get Discount Amount - $sql = mysqli_query($mysqli,"SELECT * FROM quotes WHERE quote_id = $quote_id"); - $row = mysqli_fetch_array($sql); - $quote_discount = floatval($row['quote_discount_amount']); - - //Update Quote Balances by tallying up items - $sql_quote_total = mysqli_query($mysqli,"SELECT SUM(item_total) AS quote_total FROM invoice_items WHERE item_quote_id = $quote_id"); - $row = mysqli_fetch_array($sql_quote_total); - $new_quote_amount = floatval($row['quote_total']) - $quote_discount; - - mysqli_query($mysqli,"UPDATE quotes SET quote_amount = $new_quote_amount WHERE quote_id = $quote_id"); - - } else { - //Get Discount Amount - $sql = mysqli_query($mysqli,"SELECT * FROM recurring WHERE recurring_id = $recurring_id"); - $row = mysqli_fetch_array($sql); - $recurring_discount = floatval($row['recurring_discount_amount']); - - //Update Invoice Balances by tallying up invoice items - $sql_recurring_total = mysqli_query($mysqli,"SELECT SUM(item_total) AS recurring_total FROM invoice_items WHERE item_recurring_id = $recurring_id"); - $row = mysqli_fetch_array($sql_recurring_total); - $new_recurring_amount = floatval($row['recurring_total']) - $recurring_discount; - - mysqli_query($mysqli,"UPDATE recurring SET recurring_amount = $new_recurring_amount WHERE recurring_id = $recurring_id"); - - } - - $_SESSION['alert_message'] = "Item updated"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + $item = []; + $item['name'] = $name; + $item['description'] = $description; + $item['qty'] = $qty; + $item['price'] = $price; + $item['tax_id'] = $tax_id; + $item['item_id'] = $item_id; + $item['invoice_id'] = $invoice_id; + $item['quote_id'] = $quote_id; + $item['recurring_id'] = $recurring_id; + + updateInvoiceItem($item); + referWithAlert("Item edited", "success"); } if (isset($_GET['delete_invoice_item'])) { $item_id = intval($_GET['delete_invoice_item']); - $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_id = $item_id"); - $row = mysqli_fetch_array($sql); - $invoice_id = intval($row['item_invoice_id']); - $item_subtotal = floatval($row['item_subtotal']); - $item_tax = floatval($row['item_tax']); - $item_total = floatval($row['item_total']); - - $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql); - - $new_invoice_amount = floatval($row['invoice_amount']) - $item_total; - - mysqli_query($mysqli,"UPDATE invoices SET invoice_amount = $new_invoice_amount WHERE invoice_id = $invoice_id"); - - mysqli_query($mysqli,"DELETE FROM invoice_items WHERE item_id = $item_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice Item', log_action = 'Delete', log_description = '$item_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "Item deleted"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + deleteInvoiceItem("invoice", $item_id); + referWithAlert("Item deleted"); } if (isset($_POST['add_payment'])) { @@ -609,179 +246,21 @@ $reference = sanitizeInput($_POST['reference']); $email_receipt = intval($_POST['email_receipt']); - //Check to see if amount entered is greater than the balance of the invoice - if ($amount > $balance) { - $payment_is_credit = true; - - // Calculate the overpayment amount - $credit_amount = $amount - $balance; - - // Set the payment amount to the invoice balance - $amount = $balance; - } else { - $payment_is_credit = false; - } - - - mysqli_query($mysqli,"INSERT INTO payments SET payment_date = '$date', payment_amount = $amount, payment_currency_code = '$currency_code', payment_account_id = $account, payment_method = '$payment_method', payment_reference = '$reference', payment_invoice_id = $invoice_id"); - - // Get payment ID for reference - $payment_id = mysqli_insert_id($mysqli); - - if($payment_is_credit) { - //Create a credit for the overpayment - mysqli_query($mysqli,"INSERT INTO credits SET credit_amount = $credit_amount, credit_currency_code = '$currency_code', credit_date = '$date', credit_reference = 'Overpayment: $reference', credit_client_id = (SELECT invoice_client_id FROM invoices WHERE invoice_id = $invoice_id), credit_payment_id = $payment_id, credit_account_id = $account"); - // Get credit ID for reference - $credit_id = mysqli_insert_id($mysqli); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Credit', log_action = 'Create', log_description = 'Credit for Overpayment', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - } - - //Add up all the payments for the invoice and get the total amount paid to the invoice - $sql_total_payments_amount = mysqli_query($mysqli,"SELECT SUM(payment_amount) AS payments_amount FROM payments WHERE payment_invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql_total_payments_amount); - $total_payments_amount = floatval($row['payments_amount']); - - //Get the invoice total - $sql = mysqli_query($mysqli,"SELECT * FROM invoices - LEFT JOIN clients ON invoice_client_id = client_id - LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1 - WHERE invoice_id = $invoice_id" - ); - - $row = mysqli_fetch_array($sql); - $invoice_amount = floatval($row['invoice_amount']); - $invoice_prefix = sanitizeInput($row['invoice_prefix']); - $invoice_number = intval($row['invoice_number']); - $invoice_url_key = sanitizeInput($row['invoice_url_key']); - $invoice_currency_code = sanitizeInput($row['invoice_currency_code']); - $client_id = intval($row['client_id']); - $client_name = sanitizeInput($row['client_name']); - $contact_name = sanitizeInput($row['contact_name']); - $contact_email = sanitizeInput($row['contact_email']); - $contact_phone = sanitizeInput(formatPhoneNumber($row['contact_phone'])); - $contact_extension = preg_replace("/[^0-9]/", '',$row['contact_extension']); - $contact_mobile = sanitizeInput(formatPhoneNumber($row['contact_mobile'])); - - $sql = mysqli_query($mysqli,"SELECT * FROM companies WHERE company_id = 1"); - $row = mysqli_fetch_array($sql); - - $company_name = sanitizeInput($row['company_name']); - $company_country = sanitizeInput($row['company_country']); - $company_address = sanitizeInput($row['company_address']); - $company_city = sanitizeInput($row['company_city']); - $company_state = sanitizeInput($row['company_state']); - $company_zip = sanitizeInput($row['company_zip']); - $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); - $company_email = sanitizeInput($row['company_email']); - $company_website = sanitizeInput($row['company_website']); - $company_logo = sanitizeInput($row['company_logo']); - - // Sanitize Config vars from get_settings.php - $config_invoice_from_name = sanitizeInput($config_invoice_from_name); - $config_invoice_from_email = sanitizeInput($config_invoice_from_email); - - //Calculate the Invoice balance - $invoice_balance = $invoice_amount - $total_payments_amount; - - $email_data = []; - - //Determine if invoice has been paid then set the status accordingly - if ($invoice_balance == 0) { - - - $invoice_status = "Paid"; - - - - if ($email_receipt == 1) { - - $subject = "$company_name Payment Received - Invoice $invoice_prefix$invoice_number"; - $body = "Hello $contact_name,

We have received your payment in the amount of " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . " for invoice $invoice_prefix$invoice_number. Please keep this email as a receipt for your records.

Amount: " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . "
Balance: " . numfmt_format_currency($currency_format, $invoice_balance, $invoice_currency_code) . "

Thank you for your business!


--
$company_name - Billing Department
$config_invoice_from_email
$company_phone"; - // Queue Mail - $email = [ - 'from' => $config_invoice_from_email, - 'from_name' => $config_invoice_from_name, - 'recipient' => $contact_email, - 'recipient_name' => $contact_name, - 'subject' => $subject, - 'body' => $body - ]; - - $email_data[] = $email; - - // Get Email ID for reference - $email_id = mysqli_insert_id($mysqli); - - // Email Logging - - $_SESSION['alert_message'] = "Email receipt sent "; - - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Emailed Receipt!', history_invoice_id = $invoice_id"); - - } - - } else { - - - $invoice_status = "Partial"; - - if ($email_receipt == 1) { - - $subject = "$company_name Partial Payment Received - Invoice $invoice_prefix$invoice_number"; - $body = "Hello $contact_name,

We have received partial payment in the amount of " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . " and it has been applied to invoice $invoice_prefix$invoice_number. Please keep this email as a receipt for your records.

Amount: " . numfmt_format_currency($currency_format, $amount, $invoice_currency_code) . "
Balance: " . numfmt_format_currency($currency_format, $invoice_balance, $invoice_currency_code) . "

Thank you for your business!


~
$company_name - Billing
$config_invoice_from_email
$company_phone"; - - - // Queue Mail - $email = [ - 'from' => $config_invoice_from_email, - 'from_name' => $config_invoice_from_name, - 'recipient' => $contact_email, - 'recipient_name' => $contact_name, - 'subject' => $subject, - 'body' => $body - ]; - - $email_data[] = $email; - - // Get Email ID for reference - $email_id = mysqli_insert_id($mysqli); - - // Email Logging - - $_SESSION['alert_message'] .= "Email receipt sent "; - - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Payment Receipt sent to mail queue ID: $email_id!', history_invoice_id = $invoice_id"); - - } - - } - - // Add emails to queue - if (!empty($email)) { - addToMailQueue($mysqli, $email_data); - } - - //Update Invoice Status - mysqli_query($mysqli,"UPDATE invoices SET invoice_status = '$invoice_status' WHERE invoice_id = $invoice_id"); - - //Add Payment to History - mysqli_query($mysqli,"INSERT INTO history SET history_status = '$invoice_status', history_description = 'Payment added', history_invoice_id = $invoice_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Create', log_description = '$payment_amount', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); - - if ($email_receipt == 1) { - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Email', log_description = 'Payment receipt for invoice $invoice_prefix$invoice_number queued to $contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); - } - - $_SESSION['alert_message'] .= "Payment added"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + $payment = []; + $payment['invoice_id'] = $invoice_id; + $payment['balance'] = $balance; + $payment['date'] = $date; + $payment['amount'] = $amount; + $payment['account'] = $account; + $payment['currency_code'] = $currency_code; + $payment['payment_method'] = $payment_method; + $payment['reference'] = $reference; + $payment['email_receipt'] = $email_receipt; + + createPayment($payment); + referWithAlert("Payment added", "success"); } - if (isset($_POST['add_bulk_payment'])) { $client_id = intval($_POST['client_id']); @@ -795,449 +274,41 @@ $reference = sanitizeInput($_POST['reference']); $email_receipt = intval($_POST['email_receipt']); - // Check if bulk_payment_amount exceeds total_account_balance - if ($bulk_payment_amount > $total_client_balance) { - // Create new credit for the overpayment - $credit_amount = $bulk_payment_amount - $total_client_balance; - $bulk_payment_amount = $total_client_balance; - - // Add Credit - $credit_query = "INSERT INTO credits SET credit_amount = $credit_amount, credit_currency_code = '$currency_code', credit_date = '$date', credit_reference = 'Overpayment: $reference', credit_client_id = $client_id, credit_account_id = $account"; - mysqli_query($mysqli, $credit_query); - $credit_id = mysqli_insert_id($mysqli); - } - - // Get Invoices - $sql_invoices = "SELECT * FROM invoices - WHERE invoice_status != 'Draft' - AND invoice_status != 'Paid' - AND invoice_status != 'Cancelled' - AND invoice_client_id = $client_id - ORDER BY invoice_number ASC"; - $result_invoices = mysqli_query($mysqli, $sql_invoices); - - // Loop Through Each Invoice - while ($row = mysqli_fetch_array($result_invoices)) { - $invoice_id = intval($row['invoice_id']); - $invoice_prefix = sanitizeInput($row['invoice_prefix']); - $invoice_number = intval($row['invoice_number']); - $invoice_amount = floatval($row['invoice_amount']); - $invoice_url_key = sanitizeInput($row['invoice_url_key']); - $invoice_balance_query = "SELECT SUM(payment_amount) AS amount_paid FROM payments WHERE payment_invoice_id = $invoice_id"; - $result_amount_paid = mysqli_query($mysqli, $invoice_balance_query); - $row_amount_paid = mysqli_fetch_array($result_amount_paid); - $amount_paid = floatval($row_amount_paid['amount_paid']); - $invoice_balance = $invoice_amount - $amount_paid; - - if ($bulk_payment_amount <= 0) { - break; // Exit the loop if no payment amount is left - } - - if ($bulk_payment_amount >= $invoice_balance) { - $payment_amount = $invoice_balance; - $invoice_status = "Paid"; - } else { - $payment_amount = $bulk_payment_amount; - $invoice_status = "Partial"; - } - - // Subtract the payment amount from the bulk payment amount - $bulk_payment_amount -= $payment_amount; - - // Get Invoice Remain Balance - $remaining_invoice_balance = $invoice_balance - $payment_amount; - - // Add Payment - $payment_query = "INSERT INTO payments (payment_date, payment_amount, payment_currency_code, payment_account_id, payment_method, payment_reference, payment_invoice_id) VALUES ('{$date}', {$payment_amount}, '{$currency_code}', {$account}, '{$payment_method}', '{$reference}', {$invoice_id})"; - mysqli_query($mysqli, $payment_query); - $payment_id = mysqli_insert_id($mysqli); - - // Update Invoice Status - $update_invoice_query = "UPDATE invoices SET invoice_status = '{$invoice_status}' WHERE invoice_id = {$invoice_id}"; - mysqli_query($mysqli, $update_invoice_query); - - // Add Payment to History - $history_description = "Payment added"; - $add_history_query = "INSERT INTO history (history_status, history_description, history_invoice_id) VALUES ('{$invoice_status}', '{$history_description}', {$invoice_id})"; - mysqli_query($mysqli, $add_history_query); - - // Add to Email Body Invoice Portion - - $email_body_invoices .= "
Invoice $invoice_prefix$invoice_number - Outstanding Amount: " . numfmt_format_currency($currency_format, $invoice_balance, $currency_code) . " - Payment Applied: " . numfmt_format_currency($currency_format, $payment_amount, $currency_code) . " - New Balance: " . numfmt_format_currency($currency_format, $remaining_invoice_balance, $currency_code); - - - } // End Invoice Loop - - // Send Email - if ($email_receipt == 1) { - - // Get Client / Contact Info - $sql_client = mysqli_query($mysqli,"SELECT * FROM clients - LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id - AND contact_primary = 1 - WHERE client_id = $client_id" - ); - - $row = mysqli_fetch_array($sql_client); - $client_name = sanitizeInput($row['client_name']); - $contact_name = sanitizeInput($row['contact_name']); - $contact_email = sanitizeInput($row['contact_email']); - - $sql_company = mysqli_query($mysqli,"SELECT company_name, company_phone FROM companies WHERE company_id = 1"); - $row = mysqli_fetch_array($sql_company); - - $company_name = sanitizeInput($row['company_name']); - $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); - - // Sanitize Config vars from get_settings.php - $config_invoice_from_name = sanitizeInput($config_invoice_from_name); - $config_invoice_from_email = sanitizeInput($config_invoice_from_email); - - $subject = "Payment Received - Multiple Invoices"; - $body = "Hello $contact_name,

Thank you for your payment of " . numfmt_format_currency($currency_format, $bulk_payment_amount_static, $currency_code) . " We\'ve applied your payment to the following invoices, updating their balances accordingly:

$email_body_invoices


We appreciate your continued business!

Sincerely,
$company_name - Billing
$config_invoice_from_email
$company_phone"; - - // Queue Mail - mysqli_query($mysqli, "INSERT INTO email_queue SET email_recipient = '$contact_email', email_recipient_name = '$contact_name', email_from = '$config_invoice_from_email', email_from_name = '$config_invoice_from_name', email_subject = '$subject', email_content = '$body'"); - - // Get Email ID for reference - $email_id = mysqli_insert_id($mysqli); - - // Email Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Email', log_description = 'Bulk Payment receipt for multiple Invoices queued to $contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); - - $_SESSION['alert_message'] .= "Email receipt sent and "; - - } // End Email - - // Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Create', log_description = 'Bulk Payment of $bulk_payment_amount_static', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $payment_id"); - - $_SESSION['alert_message'] .= "Bulk Payment added"; - - // Redirect Back - header("Location: " . $_SERVER["HTTP_REFERER"]); + $bulk_payment = []; + $bulk_payment['client_id'] = $client_id; + $bulk_payment['date'] = $date; + $bulk_payment['bulk_payment_amount'] = $bulk_payment_amount; + $bulk_payment['bulk_payment_amount_static'] = $bulk_payment_amount_static; + $bulk_payment['total_client_balance'] = $total_client_balance; + $bulk_payment['account'] = $account; + $bulk_payment['currency_code'] = $currency_code; + $bulk_payment['payment_method'] = $payment_method; + $bulk_payment['reference'] = $reference; + $bulk_payment['email_receipt'] = $email_receipt; + + createBulkPayment($bulk_payment); + referWithAlert("Bulk Payment added", "success"); } if (isset($_GET['delete_payment'])) { $payment_id = intval($_GET['delete_payment']); - $sql = mysqli_query($mysqli,"SELECT * FROM payments WHERE payment_id = $payment_id"); - $row = mysqli_fetch_array($sql); - $invoice_id = intval($row['payment_invoice_id']); - $deleted_payment_amount = floatval($row['payment_amount']); - - //Add up all the payments for the invoice and get the total amount paid to the invoice - $sql_total_payments_amount = mysqli_query($mysqli,"SELECT SUM(payment_amount) AS total_payments_amount FROM payments WHERE payment_invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql_total_payments_amount); - $total_payments_amount = floatval($row['total_payments_amount']); - - //Get the invoice total - $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql); - $invoice_amount = floatval($row['invoice_amount']); - - //Calculate the Invoice balance - $invoice_balance = $invoice_amount - $total_payments_amount + $deleted_payment_amount; - - //Determine if invoice has been paid - if ($invoice_balance == 0) { - $invoice_status = "Paid"; - } else { - $invoice_status = "Partial"; - } - - //Update Invoice Status - mysqli_query($mysqli,"UPDATE invoices SET invoice_status = '$invoice_status' WHERE invoice_id = $invoice_id"); - - //Add Payment to History - mysqli_query($mysqli,"INSERT INTO history SET history_status = '$invoice_status', history_description = 'Payment deleted', history_invoice_id = $invoice_id"); - - mysqli_query($mysqli,"DELETE FROM payments WHERE payment_id = $payment_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Delete', log_description = '$payment_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "Payment deleted"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + deletePayment($payment_id); + referWithAlert("Payment deleted"); } if (isset($_GET['email_invoice'])) { $invoice_id = intval($_GET['email_invoice']); - $sql = mysqli_query($mysqli,"SELECT * FROM invoices - LEFT JOIN clients ON invoice_client_id = client_id - LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1 - WHERE invoice_id = $invoice_id" - ); - $row = mysqli_fetch_array($sql); - - $invoice_id = intval($row['invoice_id']); - $invoice_prefix = sanitizeInput($row['invoice_prefix']); - $invoice_number = intval($row['invoice_number']); - $invoice_scope = sanitizeInput($row['invoice_scope']); - $invoice_status = sanitizeInput($row['invoice_status']); - $invoice_date = sanitizeInput($row['invoice_date']); - $invoice_due = sanitizeInput($row['invoice_due']); - $invoice_amount = floatval($row['invoice_amount']); - $invoice_url_key = sanitizeInput($row['invoice_url_key']); - $invoice_currency_code = sanitizeInput($row['invoice_currency_code']); - $client_id = intval($row['client_id']); - $client_name = sanitizeInput($row['client_name']); - $contact_name = sanitizeInput($row['contact_name']); - $contact_email = sanitizeInput($row['contact_email']); - - $sql = mysqli_query($mysqli,"SELECT * FROM companies WHERE company_id = 1"); - $row = mysqli_fetch_array($sql); - - $company_name = sanitizeInput($row['company_name']); - $company_country = sanitizeInput($row['company_country']); - $company_address = sanitizeInput($row['company_address']); - $company_city = sanitizeInput($row['company_city']); - $company_state = sanitizeInput($row['company_state']); - $company_zip = sanitizeInput($row['company_zip']); - $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); - $company_email = sanitizeInput($row['company_email']); - $company_website = sanitizeInput($row['company_website']); - $company_logo = sanitizeInput($row['company_logo']); - - // Sanitize Config vars from get_settings.php - $config_invoice_from_name = sanitizeInput($config_invoice_from_name); - $config_invoice_from_email = sanitizeInput($config_invoice_from_email); - - $sql_payments = mysqli_query($mysqli,"SELECT * FROM payments, accounts WHERE payment_account_id = account_id AND payment_invoice_id = $invoice_id ORDER BY payment_id DESC"); - - // Add up all the payments for the invoice and get the total amount paid to the invoice - $sql_amount_paid = mysqli_query($mysqli,"SELECT SUM(payment_amount) AS amount_paid FROM payments WHERE payment_invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql_amount_paid); - $amount_paid = floatval($row['amount_paid']); - - $balance = $invoice_amount - $amount_paid; - - if ($invoice_status == 'Paid') { - $subject = "$company_name Invoice $invoice_prefix$invoice_number Receipt"; - $body = "Hello $contact_name,

Please click on the link below to see your invoice regarding \"$invoice_scope\" marked paid.

Invoice Link


--
$company_name - Billing
$config_invoice_from_email
$company_phone"; - } else { - $subject = "$company_name Invoice $invoice_prefix$invoice_number"; - $body = "Hello $contact_name,

Please view the details of your invoice regarding \"$invoice_scope\" below.

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


To view your invoice, please click here.


--
$company_name - Billing
$config_invoice_from_email
$company_phone"; - } - - // Queue Mail - $data = [ - [ - 'from' => $config_invoice_from_email, - 'from_name' => $config_invoice_from_name, - 'recipient' => $contact_email, - 'recipient_name' => $contact_name, - 'subject' => $subject, - 'body' => $body - ] - ]; - - addToMailQueue($mysqli, $data); - - // Get Email ID for reference - $email_id = mysqli_insert_id($mysqli); - - $_SESSION['alert_message'] = "Invoice has been sent"; - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Invoice sent to the mail queue ID: $email_id', history_invoice_id = $invoice_id"); - - // Don't change the status to sent if the status is anything but draft - if ($invoice_status == 'Draft') { - mysqli_query($mysqli,"UPDATE invoices SET invoice_status = 'Sent' WHERE invoice_id = $invoice_id"); - } - - // Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Email', log_description = 'Invoice $invoice_prefix$invoice_number queued to $contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $invoice_id"); - - // Send copies of the invoice to any additional billing contacts - $sql_billing_contacts = mysqli_query( - $mysqli, - "SELECT contact_name, contact_email FROM contacts - WHERE contact_billing = 1 - AND contact_email != '$contact_email' - AND contact_email != '' - AND contact_client_id = $client_id" - ); - - $data = []; - - while ($billing_contact = mysqli_fetch_array($sql_billing_contacts)) { - $billing_contact_name = sanitizeInput($billing_contact['contact_name']); - $billing_contact_email = sanitizeInput($billing_contact['contact_email']); - - $data = [ - [ - 'from' => $config_invoice_from_email, - 'from_name' => $config_invoice_from_name, - 'recipient' => $billing_contact_email, - 'recipient_name' => $billing_contact_name, - 'subject' => $subject, - 'body' => $body - ] - ]; - - // Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Email', log_description = 'Invoice $invoice_prefix$invoice_number queued to $billing_contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $invoice_id"); - } - - addToMailQueue($mysqli, $data); - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + emailInvoice($invoice_id); + referWithAlert("Invoice sent", "success"); } if (isset($_GET['force_recurring'])) { $recurring_id = intval($_GET['force_recurring']); - $sql_recurring = mysqli_query($mysqli,"SELECT * FROM recurring, clients WHERE client_id = recurring_client_id AND recurring_id = $recurring_id"); - - $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']); - $category_id = intval($row['recurring_category_id']); - $client_id = intval($row['recurring_client_id']); - $client_net_terms = intval($row['client_net_terms']); - - //Get the last Invoice Number and add 1 for the new invoice number - $new_invoice_number = $config_invoice_next_number; - $new_config_invoice_next_number = $config_invoice_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = 1"); - - //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"); - - $new_invoice_id = mysqli_insert_id($mysqli); - - //Copy Items from original 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"); - - while($row = mysqli_fetch_array($sql_invoice_items)) { - $item_id = intval($row['item_id']); - $item_name = sanitizeInput($row['item_name']); - $item_description = sanitizeInput($row['item_description']); - $item_quantity = floatval($row['item_quantity']); - $item_price = floatval($row['item_price']); - $item_subtotal = floatval($row['item_subtotal']); - $item_order = intval($row['item_order']); - $tax_id = intval($row['item_tax_id']); - - //Recalculate Item Tax since Tax percents can change. - if ($tax_id > 0) { - $sql = mysqli_query($mysqli,"SELECT * FROM taxes WHERE tax_id = $tax_id"); - $row = mysqli_fetch_array($sql); - $tax_percent = floatval($row['tax_percent']); - $item_tax_amount = $item_subtotal * $tax_percent / 100; - } else { - $item_tax_amount = 0; - } - - $item_total = $item_subtotal + $item_tax_amount; - - //Update Recurring Items with new tax - mysqli_query($mysqli,"UPDATE invoice_items SET item_tax = $item_tax_amount, item_total = $item_total, item_tax_id = $tax_id, item_order = $item_order WHERE item_id = $item_id"); - - mysqli_query($mysqli,"INSERT INTO invoice_items SET item_name = '$item_name', item_description = '$item_description', item_quantity = $item_quantity, item_price = $item_price, item_subtotal = $item_subtotal, item_tax = $item_tax_amount, item_total = $item_total, item_tax_id = $tax_id, item_invoice_id = $new_invoice_id"); - } - - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Invoice Generated from Recurring!', history_invoice_id = $new_invoice_id"); - - //Update Recurring Balances by tallying up recurring items also update recurring dates - $sql_recurring_total = mysqli_query($mysqli,"SELECT SUM(item_total) AS recurring_total FROM invoice_items WHERE item_recurring_id = $recurring_id"); - $row = mysqli_fetch_array($sql_recurring_total); - $new_recurring_amount = floatval($row['recurring_total']) - $recurring_discount_amount; - - mysqli_query($mysqli,"UPDATE recurring SET recurring_amount = $new_recurring_amount, recurring_last_sent = CURDATE(), recurring_next_date = DATE_ADD(CURDATE(), INTERVAL 1 $recurring_frequency) WHERE recurring_id = $recurring_id"); - - //Also update the newly created invoice with the new amounts - mysqli_query($mysqli,"UPDATE invoices SET invoice_amount = $new_recurring_amount WHERE invoice_id = $new_invoice_id"); - - if ($config_recurring_auto_send_invoice == 1) { - $sql = mysqli_query($mysqli,"SELECT * FROM invoices - LEFT JOIN clients ON invoice_client_id = client_id - LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1 - WHERE invoice_id = $new_invoice_id" - ); - $row = mysqli_fetch_array($sql); - - $invoice_prefix = sanitizeInput($row['invoice_prefix']); - $invoice_number = intval($row['invoice_number']); - $invoice_scope = sanitizeInput($row['invoice_scope']); - $invoice_date = sanitizeInput($row['invoice_date']); - $invoice_due = sanitizeInput($row['invoice_due']); - $invoice_amount = floatval($row['invoice_amount']); - $invoice_url_key = sanitizeInput($row['invoice_url_key']); - $client_id = intval($row['client_id']); - $client_name = sanitizeInput($row['client_name']); - $contact_name = sanitizeInput($row['contact_name']); - $contact_email = sanitizeInput($row['contact_email']); - $contact_phone = sanitizeInput(formatPhoneNumber($row['contact_phone'])); - $contact_extension = intval($row['contact_extension']); - $contact_mobile = sanitizeInput(formatPhoneNumber($row['contact_mobile'])); - - $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_email = sanitizeInput($row['company_email']); - $company_website = sanitizeInput($row['company_website']); - - // Sanitize Config Vars - $config_invoice_from_email = sanitizeInput($config_invoice_from_email); - $config_invoice_from_name = sanitizeInput($config_invoice_from_name); - - // Email to client - - $subject = "$company_name 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: $$invoice_amount
Due Date: $invoice_due


To view your invoice, please click here.


--
$company_name - Billing
$company_phone"; - - - $data = [ - [ - 'from' => $config_invoice_from_email, - 'from_name' => $config_invoice_from_name, - 'recipient' => $contact_email, - 'recipient_name' => $contact_name, - 'subject' => $subject, - 'body' => $body - ] - ]; - $mail = addToMailQueue($mysqli, $data); - - if ($mail === true) { - // Add send history - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Force Emailed Invoice!', history_invoice_id = $new_invoice_id"); - - // Update Invoice Status to Sent - mysqli_query($mysqli,"UPDATE invoices SET invoice_status = 'Sent', invoice_client_id = $client_id WHERE invoice_id = $new_invoice_id"); - - } else { - // Error reporting - mysqli_query($mysqli,"INSERT INTO notifications SET notification_type = 'Mail', notification = 'Failed to send email to $contact_email', notification_client_id = $client_id"); - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Mail', log_action = 'Error', log_description = 'Failed to send email to $contact_email regarding $subject. $mail', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - } - - } //End Recurring Invoices Loop - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = '$session_name forced recurring invoice into an invoice', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $new_invoice_id"); - - $_SESSION['alert_message'] = "Recurring Invoice Forced"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + forceRecurring($recurring_id); + referWithAlert("Recurring Invoice forced", "success"); } if (isset($_POST['export_client_invoices_csv'])) { @@ -1363,252 +434,52 @@ } - - if (isset($_POST['update_recurring_item_order'])) { $item_id = intval($_POST['item_id']); $item_recurring_id = intval($_POST['item_recurring_id']); + $item_direction = sanitizeInput($_POST['item_direction']); - $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_id = $item_id"); - $row = mysqli_fetch_array($sql); - $current_order = intval($row['item_order']); - $update_direction = sanitizeInput($_POST['update_recurring_item_order']); - - switch ($update_direction) - { - case 'up': - $new_order = $current_order - 1; - break; - case 'down': - $new_order = $current_order + 1; - break; - } - - //Find item_id of current item in $new_order - $other_sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_recurring_id = $item_recurring_id AND item_order = $new_order"); - $other_row = mysqli_fetch_array($other_sql); - $other_item_id = intval($other_row['item_id']); - $other_row_str = strval($other_row['item_name']); - - mysqli_query($mysqli,"UPDATE invoice_items SET item_order = $new_order WHERE item_id = $item_id"); - - mysqli_query($mysqli,"UPDATE invoice_items SET item_order = $current_order WHERE item_id = $other_item_id"); - - $_SESSION['alert_message'] = "recurring Item Order Updated"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + updateItemOrder("recurring", $item_id, $item_recurring_id, $item_direction); + referWithAlert("Recurring Item Order Updated", "success"); } if (isset($_POST['update_invoice_item_order'])) { $item_id = intval($_POST['item_id']); $item_invoice_id = intval($_POST['item_invoice_id']); + $item_direction = sanitizeInput($_POST['item_direction']); - $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_id = $item_id"); - $row = mysqli_fetch_array($sql); - $current_order = intval($row['item_order']); - $update_direction = sanitizeInput($_POST['update_invoice_item_order']); - - switch ($update_direction) - { - case 'up': - $new_order = $current_order - 1; - break; - case 'down': - $new_order = $current_order + 1; - break; - } - - //Find item_id of current item in $new_order - $other_sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_invoice_id = $item_invoice_id AND item_order = $new_order"); - $other_row = mysqli_fetch_array($other_sql); - $other_item_id = intval($other_row['item_id']); - $other_row_str = strval($other_row['item_name']); - - mysqli_query($mysqli,"UPDATE invoice_items SET item_order = $new_order WHERE item_id = $item_id"); - - mysqli_query($mysqli,"UPDATE invoice_items SET item_order = $current_order WHERE item_id = $other_item_id"); - - $_SESSION['alert_message'] = "Invoice Item Order Updated"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + updateItemOrder("invoice", $item_id, $item_invoice_id, $item_direction); + referWithAlert("Invoice Item Order Updated", "success"); } if (isset($_POST['link_invoice_to_ticket'])) { $invoice_id = intval($_POST['invoice_id']); $ticket_id = intval($_POST['ticket_id']); - mysqli_query($mysqli,"UPDATE invoices SET invoice_ticket_id = $ticket_id WHERE invoice_id = $invoice_id"); - - $_SESSION['alert_message'] = "Invoice linked to ticket"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + addInvoiceToTicket($invoice_id, $ticket_id); + referWithAlert("Invoice linked to ticket", "success"); } if (isset($_POST['add_ticket_to_invoice'])) { $invoice_id = intval($_POST['invoice_id']); $ticket_id = intval($_POST['ticket_id']); - mysqli_query($mysqli,"UPDATE tickets SET ticket_invoice_id = $invoice_id WHERE ticket_id = $ticket_id"); - - $_SESSION['alert_message'] = "Ticket linked to invoice"; - - header("Location: post.php?add_ticket_to_invoice=$invoice_id"); + addInvoiceToTicket($invoice_id, $ticket_id); + referWithAlert("Ticket linked to invoice", "success"); } if (isset($_GET['apply_credit'])) { $credit_id = intval($_GET['apply_credit']); - $credit_sql = mysqli_query($mysqli,"SELECT * FROM credits WHERE credit_id = $credit_id"); - $credit_row = mysqli_fetch_array($credit_sql); - - $client_id = intval($credit_row['credit_client_id']); - $credit_amount = floatval($credit_row['credit_amount']); - $credit_currency_code = sanitizeInput($credit_row['credit_currency_code']); - - $client_balance = getClientBalance($mysqli, $client_id); - - if ($client_balance < $credit_amount) { - //create a new credit for the remaining amount - $new_credit_amount = $credit_amount - $client_balance; - $new_credit_query = "INSERT INTO credits credit_date = CURDATE(), credit_amount = $new_credit_amount, credit_client_id = $client_id, credit_currency_code = '$credit_currency_code', credit_reference = 'Credit Applied'"; - mysqli_query($mysqli, $new_credit_query); - $new_credit_id = mysqli_insert_id($mysqli); - } - // Delete the original credit - mysqli_query($mysqli,"DELETE FROM credits WHERE credit_id = $credit_id"); - - // Apply payments similar to add bulk payment - - // Get Invoices - $sql_invoices = "SELECT * FROM invoices - WHERE invoice_status != 'Draft' - AND invoice_status != 'Paid' - AND invoice_status != 'Cancelled' - AND invoice_client_id = $client_id - ORDER BY invoice_number ASC"; - $result_invoices = mysqli_query($mysqli, $sql_invoices); - $invoice_applied_count = 0; - - // Loop Through Each Invoice - while ($row = mysqli_fetch_array($result_invoices)) { - $invoice_id = intval($row['invoice_id']); - $invoice_prefix = sanitizeInput($row['invoice_prefix']); - $invoice_number = intval($row['invoice_number']); - $invoice_amount = floatval($row['invoice_amount']); - $invoice_url_key = sanitizeInput($row['invoice_url_key']); - $invoice_balance_query = "SELECT SUM(payment_amount) AS amount_paid FROM payments WHERE payment_invoice_id = $invoice_id"; - $result_amount_paid = mysqli_query($mysqli, $invoice_balance_query); - $row_amount_paid = mysqli_fetch_array($result_amount_paid); - $amount_paid = floatval($row_amount_paid['amount_paid']); - $invoice_balance = $invoice_amount - $amount_paid; - - - if ($credit_amount <= 0) { - break; // Exit the loop if no credit amount is left - } - - if ($invoice_balance <= 0) { - continue; // Skip the invoice if it's already paid - } - - if ($credit_amount >= $invoice_balance) { - $payment_amount = $invoice_balance; - $invoice_status = "Paid"; - } else { - $payment_amount = $credit_amount; - $invoice_status = "Partial"; - } - - $invoice_applied_count++; - - // Subtract the payment amount from the credit amount - $credit_amount -= $payment_amount; - - // Get Invoice Remain Balance - $remaining_invoice_balance = $invoice_balance - $payment_amount; - - // Add Payment - $payment_query = "INSERT INTO payments SET payment_date = CURDATE(), payment_amount = $payment_amount, payment_invoice_id = $invoice_id, payment_account_id = 1, payment_currency_code = '{$credit_row['credit_currency_code']}', payment_reference = 'Credit Applied'"; - mysqli_query($mysqli, $payment_query); - $payment_id = mysqli_insert_id($mysqli); - - // Update Invoice Status - $update_invoice_query = "UPDATE invoices SET invoice_status = '{$invoice_status}' WHERE invoice_id = {$invoice_id}"; - mysqli_query($mysqli, $update_invoice_query); - - // Add Payment to History - $history_description = "Payment added"; - $add_history_query = "INSERT INTO history (history_status, history_description, history_invoice_id) VALUES ('{$invoice_status}', '{$history_description}', {$invoice_id})"; - mysqli_query($mysqli, $add_history_query); - - // Add to Email Body Invoice Portion - - $email_body_invoices .= "
Invoice $invoice_prefix$invoice_number - Outstanding Amount: " . numfmt_format_currency($currency_format, $invoice_balance, $currency_code) . " - Payment Applied: " . numfmt_format_currency($currency_format, $payment_amount, $currency_code) . " - New Balance: " . numfmt_format_currency($currency_format, $remaining_invoice_balance, $currency_code); - - } // End Invoice Loop - - //Todo add option to send receipts - $email_receipt = 1; - - // Send Email - if ($email_receipt == 1) { - - // Get Client / Contact Info - $sql_client = mysqli_query($mysqli,"SELECT * FROM clients - LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id - AND contact_primary = 1 - WHERE client_id = $client_id" - ); - - $row = mysqli_fetch_array($sql_client); - $client_name = sanitizeInput($row['client_name']); - $contact_name = sanitizeInput($row['contact_name']); - $contact_email = sanitizeInput($row['contact_email']); - - $sql_company = mysqli_query($mysqli,"SELECT company_name, company_phone FROM companies WHERE company_id = 1"); - $row = mysqli_fetch_array($sql_company); - - $company_name = sanitizeInput($row['company_name']); - $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); - - // Sanitize Config vars from get_settings.php - $config_invoice_from_name = sanitizeInput($config_invoice_from_name); - $config_invoice_from_email = sanitizeInput($config_invoice_from_email); - - $subject = "Payment Received - Multiple Invoices"; - $body = "Hello $contact_name,

Thank you for your payment of " . numfmt_format_currency($currency_format, $bulk_payment_amount_static, $currency_code) . " We\'ve applied your payment to the following invoices, updating their balances accordingly:

$email_body_invoices


We appreciate your continued business!

Sincerely,
$company_name - Billing
$config_invoice_from_email
$company_phone"; - - // Queue Mail - mysqli_query($mysqli, "INSERT INTO email_queue SET email_recipient = '$contact_email', email_recipient_name = '$contact_name', email_from = '$config_invoice_from_email', email_from_name = '$config_invoice_from_name', email_subject = '$subject', email_content = '$body'"); - - // Get Email ID for reference - $email_id = mysqli_insert_id($mysqli); - - // Email Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Email', log_description = 'Bulk Payment receipt for multiple Invoices queued to $contact_email Email ID: $email_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session"); - - $_SESSION['alert_message'] .= "Email receipt sent and "; - - } // End Email - - // Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Payment', log_action = 'Create', log_description = 'Bulk Payment of $bulk_payment_amount_static', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id"); - - $_SESSION['alert_message'] .= "Credit applied to $credit_applied_count invoices"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + applyCredit($credit_id); + referWithAlert("Credit applied", "success"); } if (isset($_GET['delete_credit'])) { $credit_id = intval($_GET['delete_credit']); - mysqli_query($mysqli,"DELETE FROM credits WHERE credit_id = $credit_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Credit', log_action = 'Delete', log_description = 'Credit $credit_id deleted', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_message'] = "Credit deleted"; - header("Location: " . $_SERVER["HTTP_REFERER"]); + deleteCredit($credit_id); + referWithAlert("Credit deleted", "success"); } \ No newline at end of file From 1a80e5b7165fd0e3ae50ba2f2a82f9f196ca5141 Mon Sep 17 00:00:00 2001 From: o-psi Date: Sun, 17 Mar 2024 00:37:35 +0000 Subject: [PATCH 18/32] Functionify the post handler This draft PR shows the work to attempt to make all actions in post.php occur via functions. This enables easier management of a CRUD API --- functions/api_functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/functions/api_functions.php b/functions/api_functions.php index 4f274a718..95a4ccaa1 100644 --- a/functions/api_functions.php +++ b/functions/api_functions.php @@ -6,12 +6,12 @@ function createAPIKey($secret, $name, $expire, $client) { global $mysqli, $session_name, $session_ip, $session_user_agent, $session_user_id; mysqli_query($mysqli,"INSERT INTO api_keys SET api_key_name = '$name', api_key_secret = '$secret', api_key_expire = '$expire', api_key_client_id = $client"); - $api_key = mysqli_insert_id($mysqli); + $api_key_id = mysqli_insert_id($mysqli); // Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'API', log_action = 'Create', log_description = '$session_name created API Key $name set to expire on $expire', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client, log_user_id = $session_user_id, log_entity_id = $api_key_id"); - return $api_key; + return $api_key_id; } function deleteAPIKey($api_key_id) { From 1b74584b5d25fce5a81269394d115752d6c60ede Mon Sep 17 00:00:00 2001 From: o-psi Date: Sun, 17 Mar 2024 00:45:38 +0000 Subject: [PATCH 19/32] Sanitize --- post/api.php | 1 + 1 file changed, 1 insertion(+) diff --git a/post/api.php b/post/api.php index 6f961a96c..d7a661864 100644 --- a/post/api.php +++ b/post/api.php @@ -45,6 +45,7 @@ $api_key_ids = $_POST['api_key_ids']; // Get array of API key IDs to be deleted foreach ($api_key_ids as $api_key_id) { + $api_key_ids = intval($api_key_id); deleteAPIKey($api_key_id); $count++; } From 3a0a1051b573843430b9f85b174192082c44fac4 Mon Sep 17 00:00:00 2001 From: o-psi Date: Sun, 17 Mar 2024 05:43:19 +0000 Subject: [PATCH 20/32] Testing API fixes --- api/v2/api.php | 105 ++++++++++++++++++ api/v2/objects.php | 9 ++ functions.php | 4 +- functions/asset_functions.php | 200 ++++++++++++++++++++++++++++++++++ functions/email_functions.php | 6 +- functions/misc_functions.php | 10 +- post/asset.php | 130 +++++++++++----------- 7 files changed, 389 insertions(+), 75 deletions(-) create mode 100644 api/v2/api.php create mode 100644 api/v2/objects.php create mode 100644 functions/asset_functions.php diff --git a/api/v2/api.php b/api/v2/api.php new file mode 100644 index 000000000..6f2034d7b --- /dev/null +++ b/api/v2/api.php @@ -0,0 +1,105 @@ + 'Invalid request method']); + exit; +} + +// Check if action is CRUD +if (!in_array($action, ['create', 'read', 'update', 'delete'])) { + echo json_encode(['error' => 'Invalid action in request']); + exit; +} + +if (is_string($parameters)) { + $parameters = json_decode($parameters, true); + if (json_last_error() !== JSON_ERROR_NONE) { + // Handle JSON decode error (e.g., invalid JSON format) + echo json_encode(['error' => 'Invalid JSON format in parameters']); + exit; + } +} +// Sanitize the parameters +$sanitized_parameters = []; +foreach ($parameters as $key => $value) { + $sanitized_parameters[sanitizeInput($key)] = sanitizeInput($value); +} +// Replace the parameters with the sanitized parameters +$parameters = $sanitized_parameters; + +// Check if the object is valid +if (!in_array($object, $valid_objects)) { + echo json_encode(['error' => 'Invalid object in request']); + exit; +} +//Uppercase every first letter of the object +$object = ucwords($object); + +// Remove spaces in object +$object = str_replace(' ', '', $object); + +// Create function +$function = $action . $object; + +if (!function_exists($function)) { + echo json_encode(['error' => 'Invalid function in request']); + exit; +} +if ($action == 'read') { + // Call the function and return the result + echo json_encode($function($parameters)); + exit; +} else { + // Call the function and return the result + echo json_encode($function($parameters)['status']); + exit; +} diff --git a/api/v2/objects.php b/api/v2/objects.php new file mode 100644 index 000000000..7e66dd878 --- /dev/null +++ b/api/v2/objects.php @@ -0,0 +1,9 @@ + 'error', 'message' => 'Asset not found']; + } + } + + $assets = []; + while ($row = mysqli_fetch_assoc($result)) { + foreach ($row as $key => $value) { + $row[$key] = sanitizeInput($value); + } + $assets[] = $row; + } + return $assets; +} + +function updateAsset( + $parameters, + $archive = -1 +) { + if ($archive == 1) { + // Archive + archiveAsset($parameters['asset_id']); + exit; + } elseif ($archive == 0) { + // Unarchive + unarchiveAsset($parameters['asset_id']); + exit; + } + + $name = $parameters['name']; + $description = $parameters['description']; + $type = $parameters['type']; + $make = $parameters['make']; + $model = $parameters['model']; + $serial = $parameters['serial']; + $os = $parameters['os']; + $ip = $parameters['ip']; + $nat_ip = $parameters['nat_ip']; + $mac = $parameters['mac']; + $uri = $parameters['uri']; + $uri_2 = $parameters['uri_2']; + $status = $parameters['status']; + $location = $parameters['location']; + $vendor = $parameters['vendor']; + $contact = $parameters['contact']; + $network = $parameters['network']; + $purchase_date = $parameters['purchase_date']; + $warranty_expire = $parameters['warranty_expire']; + $install_date = $parameters['install_date']; + $notes = $parameters['notes']; + $asset_id = $parameters['asset_id']; + $client_id = $parameters['client_id']; + + global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; + + if (!empty($dhcp)) { + $dhcp = 1; + } else { + $dhcp = 0; + } + + mysqli_query($mysqli,"UPDATE assets SET asset_name = '$name', asset_description = '$description', asset_type = '$type', asset_make = '$make', asset_model = '$model', asset_serial = '$serial', asset_os = '$os', asset_ip = '$ip', asset_nat_ip = '$nat_ip', asset_mac = '$mac', asset_uri = '$uri', asset_uri_2 = '$uri_2', asset_location_id = $location, asset_vendor_id = $vendor, asset_contact_id = $contact, asset_status = '$status', asset_purchase_date = $purchase_date, asset_warranty_expire = $warranty_expire, asset_install_date = $install_date, asset_notes = '$notes', asset_network_id = $network WHERE asset_id = $asset_id"); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Modify', log_description = '$session_name modified asset $name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); + + return ['status' => 'success']; +} + +function archiveAsset( + $asset_id +) { + global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; + + // Get Asset Name and Client ID for logging and alert message + $sql = mysqli_query($mysqli,"SELECT asset_name, asset_client_id FROM assets WHERE asset_id = $asset_id"); + $row = mysqli_fetch_array($sql); + $asset_name = sanitizeInput($row['asset_name']); + $client_id = intval($row['asset_client_id']); + + mysqli_query($mysqli,"UPDATE assets SET asset_archived_at = NOW() WHERE asset_id = $asset_id"); + + //logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Archive', log_description = '$session_name archived asset $asset_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); + + return ['status' => 'success']; +} + +function unarchiveAsset( + $asset_id +) { + global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; + + // Get Asset Name and Client ID for logging and alert message + $sql = mysqli_query($mysqli,"SELECT asset_name, asset_client_id FROM assets WHERE asset_id = $asset_id"); + $row = mysqli_fetch_array($sql); + $asset_name = sanitizeInput($row['asset_name']); + $client_id = intval($row['asset_client_id']); + + mysqli_query($mysqli,"UPDATE assets SET asset_archived_at = NULL WHERE asset_id = $asset_id"); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Unarchive', log_description = '$session_name unarchived asset $asset_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); + + return ['status' => 'success']; +} + +function deleteAsset( + $parameters +) { + $asset_id = $parameters['asset_id']; + + global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; + + // Get Asset Name and Client ID for logging and alert message + $sql = mysqli_query($mysqli,"SELECT asset_name, asset_client_id FROM assets WHERE asset_id = $asset_id"); + $row = mysqli_fetch_array($sql); + $asset_name = sanitizeInput($row['asset_name']); + $client_id = intval($row['asset_client_id']); + + mysqli_query($mysqli,"DELETE FROM assets WHERE asset_id = $asset_id"); + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Delete', log_description = '$session_name deleted asset $asset_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); + + return ['status' => 'success']; +} \ No newline at end of file diff --git a/functions/email_functions.php b/functions/email_functions.php index fae5fb3e8..98a1645a3 100644 --- a/functions/email_functions.php +++ b/functions/email_functions.php @@ -3,11 +3,11 @@ // Email related functions // PHP Mailer Libs -require_once "plugins/PHPMailer/src/Exception.php"; +require_once "/var/www/develop.twe.tech/plugins/PHPMailer/src/Exception.php"; -require_once "plugins/PHPMailer/src/PHPMailer.php"; +require_once "/var/www/develop.twe.tech/plugins/PHPMailer/src/PHPMailer.php"; -require_once "plugins/PHPMailer/src/SMTP.php"; +require_once "/var/www/develop.twe.tech/plugins/PHPMailer/src/SMTP.php"; // Initiate PHPMailer use PHPMailer\PHPMailer\PHPMailer; diff --git a/functions/misc_functions.php b/functions/misc_functions.php index debb5bf08..5504c7941 100644 --- a/functions/misc_functions.php +++ b/functions/misc_functions.php @@ -386,12 +386,16 @@ function getTicketStatusColor($status) { } function referWithAlert( - $alert, $type = "danger", - $url = $_SERVER["HTTP_REFERER"] + $alert, + $type = "alert", + $url = null ) { + if ($url == null) { + $url = $_SERVER["HTTP_REFERER"]; + } + $_SESSION['alert_message'] = $alert; $_SESSION['alert_type'] = $type; - header("Location: " . $url); exit(); } \ No newline at end of file diff --git a/post/asset.php b/post/asset.php index acd7efd48..17177aca7 100644 --- a/post/asset.php +++ b/post/asset.php @@ -49,33 +49,36 @@ } $notes = sanitizeInput($_POST['notes']); - $alert_extended = ""; - - mysqli_query($mysqli,"INSERT INTO assets SET asset_name = '$name', asset_description = '$description', asset_type = '$type', asset_make = '$make', asset_model = '$model', asset_serial = '$serial', asset_os = '$os', asset_ip = '$ip', asset_nat_ip = '$nat_ip', asset_mac = '$mac', asset_uri = '$uri', asset_uri_2 = '$uri_2', asset_location_id = $location, asset_vendor_id = $vendor, asset_contact_id = $contact, asset_status = '$status', asset_purchase_date = $purchase_date, asset_warranty_expire = $warranty_expire, asset_install_date = $install_date, asset_notes = '$notes', asset_network_id = $network, asset_client_id = $client_id"); - - $asset_id = mysqli_insert_id($mysqli); - - if (!empty($_POST['username'])) { - $username = trim(mysqli_real_escape_string($mysqli, encryptLoginEntry($_POST['username']))); - $password = trim(mysqli_real_escape_string($mysqli, encryptLoginEntry($_POST['password']))); - - mysqli_query($mysqli,"INSERT INTO logins SET login_name = '$name', login_username = '$username', login_password = '$password', login_asset_id = $asset_id, login_client_id = $client_id"); - - $login_id = mysqli_insert_id($mysqli); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Login', log_action = 'Create', log_description = '$session_name created login credentials for asset $name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $login_id"); - - $alert_extended = " along with login credentials"; - - } - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Create', log_description = '$session_name created asset $name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); - - $_SESSION['alert_message'] = "Asset $name created $alert_extended"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + $return_data = createAsset( + [ + 'client_id' => $client_id, + 'name' => $name, + 'description' => $description, + 'type' => $type, + 'make' => $make, + 'model' => $model, + 'serial' => $serial, + 'os' => $os, + 'ip' => $ip, + 'nat_ip' => $nat_ip, + 'mac' => $mac, + 'uri' => $uri, + 'uri_2' => $uri_2, + 'status' => $status, + 'location' => $location, + 'vendor' => $vendor, + 'contact' => $contact, + 'network' => $network, + 'purchase_date' => $purchase_date, + 'warranty_expire' => $warranty_expire, + 'install_date' => $install_date, + 'notes' => $notes + ] + ); + + $alert_extended = $return_data['alert_extended']; + + referWithAlert("Asset $name created $alert_extended", "success", "client_assets.php?client_id=$client_id"); } @@ -125,15 +128,34 @@ } $notes = sanitizeInput($_POST['notes']); - mysqli_query($mysqli,"UPDATE assets SET asset_name = '$name', asset_description = '$description', asset_type = '$type', asset_make = '$make', asset_model = '$model', asset_serial = '$serial', asset_os = '$os', asset_ip = '$ip', asset_nat_ip = '$nat_ip', asset_mac = '$mac', asset_uri = '$uri', asset_uri_2 = '$uri_2', asset_location_id = $location, asset_vendor_id = $vendor, asset_contact_id = $contact, asset_status = '$status', asset_purchase_date = $purchase_date, asset_warranty_expire = $warranty_expire, asset_install_date = $install_date, asset_notes = '$notes', asset_network_id = $network WHERE asset_id = $asset_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Modify', log_description = '$session_name modified asset $name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); - - $_SESSION['alert_message'] = "Asset $name updated"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); - + updateAsset( + [ + 'asset_id' => $asset_id, + 'client_id' => $client_id, + 'name' => $name, + 'description' => $description, + 'type' => $type, + 'make' => $make, + 'model' => $model, + 'serial' => $serial, + 'os' => $os, + 'ip' => $ip, + 'nat_ip' => $nat_ip, + 'mac' => $mac, + 'uri' => $uri, + 'uri_2' => $uri_2, + 'status' => $status, + 'location' => $location, + 'vendor' => $vendor, + 'contact' => $contact, + 'network' => $network, + 'purchase_date' => $purchase_date, + 'warranty_expire' => $warranty_expire, + 'install_date' => $install_date, + 'notes' => $notes + ] + ); + referWithAlert("Asset $name updated", "success", "client_assets.php?client_id=$client_id"); } if (isset($_GET['archive_asset'])) { @@ -142,21 +164,8 @@ $asset_id = intval($_GET['archive_asset']); - // Get Asset Name and Client ID for logging and alert message - $sql = mysqli_query($mysqli,"SELECT asset_name, asset_client_id FROM assets WHERE asset_id = $asset_id"); - $row = mysqli_fetch_array($sql); - $asset_name = sanitizeInput($row['asset_name']); - $client_id = intval($row['asset_client_id']); - - mysqli_query($mysqli,"UPDATE assets SET asset_archived_at = NOW() WHERE asset_id = $asset_id"); - - //logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Archive', log_description = '$session_name archived asset $asset_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); - - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "Asset $asset_name archived"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + archiveAsset($asset_id); + referWithAlert("Asset $asset_name archived", "error", "client_assets.php?client_id=$client_id"); } @@ -164,23 +173,10 @@ validateAdminRole(); - $asset_id = intval($_GET['delete_asset']); - - // Get Asset Name and Client ID for logging and alert message - $sql = mysqli_query($mysqli,"SELECT asset_name, asset_client_id FROM assets WHERE asset_id = $asset_id"); - $row = mysqli_fetch_array($sql); - $asset_name = sanitizeInput($row['asset_name']); - $client_id = intval($row['asset_client_id']); - - mysqli_query($mysqli,"DELETE FROM assets WHERE asset_id = $asset_id"); + $parameters['asset_id'] = intval($_GET['delete_asset']); - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Delete', log_description = '$session_name deleted asset $asset_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); - - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "Asset $asset_name deleted"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + deleteAsset($parameters); + referWithAlert("Asset $asset_name deleted", "error", "client_assets.php?client_id=$client_id"); } From ce0d8a94ba5d8652e2c1f86c725a69e363579a2b Mon Sep 17 00:00:00 2001 From: o-psi Date: Sun, 17 Mar 2024 05:49:24 +0000 Subject: [PATCH 21/32] fix for bulk delete --- post/api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/post/api.php b/post/api.php index d7a661864..0b7a676da 100644 --- a/post/api.php +++ b/post/api.php @@ -46,7 +46,7 @@ foreach ($api_key_ids as $api_key_id) { $api_key_ids = intval($api_key_id); - deleteAPIKey($api_key_id); + deleteAPIKey(intval($api_key_id)); $count++; } referWithAlert("$count API Keys deleted"); From 669d54f9f4b2f5d1dec384a5fb48dd6d567a4350 Mon Sep 17 00:00:00 2001 From: o-psi Date: Sun, 17 Mar 2024 06:34:14 +0000 Subject: [PATCH 22/32] API key checking --- api/v2/api.php | 13 ++++++++++++ functions/api_functions.php | 38 +++++++++++++++++++++++++++++++++++ functions/asset_functions.php | 30 ++++++++++++++++++++++----- 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/api/v2/api.php b/api/v2/api.php index 6f2034d7b..8698a25c7 100644 --- a/api/v2/api.php +++ b/api/v2/api.php @@ -31,6 +31,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') { $object = strtolower(sanitizeInput($_POST['object'])); $parameters = $_POST['parameters']; + $api_key = $_POST['api_key']; if (!isset($_POST['action'])) { // Default to create if no action is specified $action = 'create'; @@ -41,6 +42,7 @@ } elseif ($_SERVER['REQUEST_METHOD'] == 'GET') { $object = strtolower(sanitizeInput($_GET['object'])); $parameters = $_GET['parameters']; + $api_key = $_GET['api_key']; if (!isset($_GET['action'])) { // Default to read if no action is specified $action = 'read'; @@ -54,12 +56,19 @@ exit; } +// Check if the API key is valid +$api_key_data = tryAPIKey($api_key); +if (isset($api_key_data['api_key_client_id'])) { + $api_client_id = $api_key_data['api_key_client_id']; +} + // Check if action is CRUD if (!in_array($action, ['create', 'read', 'update', 'delete'])) { echo json_encode(['error' => 'Invalid action in request']); exit; } +// Check the parameters if (is_string($parameters)) { $parameters = json_decode($parameters, true); if (json_last_error() !== JSON_ERROR_NONE) { @@ -68,6 +77,7 @@ exit; } } + // Sanitize the parameters $sanitized_parameters = []; foreach ($parameters as $key => $value) { @@ -76,6 +86,9 @@ // Replace the parameters with the sanitized parameters $parameters = $sanitized_parameters; +if (!isset($parameters['api_key_client_id'])) { + $parameters['api_key_client_id'] = $api_client_id; +} // Check if the object is valid if (!in_array($object, $valid_objects)) { echo json_encode(['error' => 'Invalid object in request']); diff --git a/functions/api_functions.php b/functions/api_functions.php index 95a4ccaa1..427adc8d1 100644 --- a/functions/api_functions.php +++ b/functions/api_functions.php @@ -25,4 +25,42 @@ function deleteAPIKey($api_key_id) { // Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'API Key', log_action = 'Delete', log_description = '$session_name deleted API key $name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id, log_entity_id = $api_key_id"); +} + +function getAPIKey($api_key_id) { + global $mysqli; + + $row = mysqli_fetch_array(mysqli_query($mysqli,"SELECT * FROM api_keys WHERE api_key_id = $api_key_id")); + return $row; +} + +function tryAPIKey($api_key_secret) { + global $mysqli, $session_ip, $session_user_agent; + + $row = mysqli_fetch_array(mysqli_query($mysqli,"SELECT * FROM api_keys WHERE api_key_secret = '$api_key_secret'")); + + if($row) { + $api_key_id = intval($row['api_key_id']); + $api_key_client_id = intval($row['api_key_client_id']); + $api_key_expire = sanitizeInput($row['api_key_expire']); + + // Check if the key has expired + if(strtotime($api_key_expire) < time()) { + // Log expired Key + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'API', log_action = 'Failed', log_description = 'Expired key: ', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); + echo json_encode(['status' => 'error', 'message' => 'Expired API Key']); + exit; + } + + return [ + 'api_key_id' => $api_key_id, + 'api_key_client_id' => $api_key_client_id, + 'api_key_expire' => $api_key_expire, + ]; + } else { + // Log invalid Key + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'API', log_action = 'Failed', log_description = 'Incorrect or expired key: ', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); + echo json_encode(['status' => 'error', 'message' => 'Invalid API Key']); + exit; + } } \ No newline at end of file diff --git a/functions/asset_functions.php b/functions/asset_functions.php index a266b0fa4..823337aa6 100644 --- a/functions/asset_functions.php +++ b/functions/asset_functions.php @@ -65,14 +65,29 @@ function readAsset( $parameters ) { $asset_id = sanitizeInput($parameters['asset_id']); - + + if (isset($parameters['api_key_client_id'])) { + $client_id = sanitizeInput($parameters['api_key_client_id']); + } else { + $client_id = 'all'; + } global $mysqli; + if ($asset_id == 'all') { - $result = mysqli_query($mysqli,"SELECT * FROM assets"); + if ($client_id == 'all') { + $result = mysqli_query($mysqli,"SELECT * FROM assets"); + } else { + $result = mysqli_query($mysqli,"SELECT * FROM assets WHERE asset_client_id = $client_id"); + } } else { - $result = mysqli_query($mysqli,"SELECT * FROM assets WHERE asset_id = $asset_id"); + if ($client_id == 'all') { + $result = mysqli_query($mysqli,"SELECT * FROM assets WHERE asset_id = $asset_id"); + } else { + $result = mysqli_query($mysqli,"SELECT * FROM assets WHERE asset_id = $asset_id AND asset_client_id = $client_id"); + } if (mysqli_num_rows($result) == 0) { return ['status' => 'error', 'message' => 'Asset not found']; + exit; } } @@ -123,16 +138,21 @@ function updateAsset( $notes = $parameters['notes']; $asset_id = $parameters['asset_id']; $client_id = $parameters['client_id']; + $api_client_id = $parameters['api_client_id']; global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; - if (!empty($dhcp)) { + if ($ip == "DCHP") { $dhcp = 1; } else { $dhcp = 0; } - mysqli_query($mysqli,"UPDATE assets SET asset_name = '$name', asset_description = '$description', asset_type = '$type', asset_make = '$make', asset_model = '$model', asset_serial = '$serial', asset_os = '$os', asset_ip = '$ip', asset_nat_ip = '$nat_ip', asset_mac = '$mac', asset_uri = '$uri', asset_uri_2 = '$uri_2', asset_location_id = $location, asset_vendor_id = $vendor, asset_contact_id = $contact, asset_status = '$status', asset_purchase_date = $purchase_date, asset_warranty_expire = $warranty_expire, asset_install_date = $install_date, asset_notes = '$notes', asset_network_id = $network WHERE asset_id = $asset_id"); + if ($api_client_id != $client_id) { + return ['status' => 'error', 'message' => 'You are not permitted to do that!']; + } + + mysqli_query($mysqli,"UPDATE assets SET asset_name = '$name', asset_description = '$description', asset_type = '$type', asset_make = '$make', asset_model = '$model', asset_serial = '$serial', asset_os = '$os', asset_ip = '$ip', asset_dchp = $dhcp asset_nat_ip = '$nat_ip', asset_mac = '$mac', asset_uri = '$uri', asset_uri_2 = '$uri_2', asset_location_id = $location, asset_vendor_id = $vendor, asset_contact_id = $contact, asset_status = '$status', asset_purchase_date = $purchase_date, asset_warranty_expire = $warranty_expire, asset_install_date = $install_date, asset_notes = '$notes', asset_network_id = $network WHERE asset_id = $asset_id"); //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Modify', log_description = '$session_name modified asset $name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); From 4616fa8f2a146aed481b82e4b0185f05c8bb9060 Mon Sep 17 00:00:00 2001 From: o-psi Date: Sun, 17 Mar 2024 06:42:29 +0000 Subject: [PATCH 23/32] Sanitizing --- api/v2/api.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/v2/api.php b/api/v2/api.php index 8698a25c7..c52a9c7ec 100644 --- a/api/v2/api.php +++ b/api/v2/api.php @@ -31,7 +31,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') { $object = strtolower(sanitizeInput($_POST['object'])); $parameters = $_POST['parameters']; - $api_key = $_POST['api_key']; + $api_key = sanitizeInput($_POST['api_key']); if (!isset($_POST['action'])) { // Default to create if no action is specified $action = 'create'; @@ -42,7 +42,7 @@ } elseif ($_SERVER['REQUEST_METHOD'] == 'GET') { $object = strtolower(sanitizeInput($_GET['object'])); $parameters = $_GET['parameters']; - $api_key = $_GET['api_key']; + $api_key = sanitizeInput($_GET['api_key']); if (!isset($_GET['action'])) { // Default to read if no action is specified $action = 'read'; From 35b20b37765917b1cabbf37f822d95847c4c8357 Mon Sep 17 00:00:00 2001 From: o-psi Date: Sun, 17 Mar 2024 16:36:08 +0000 Subject: [PATCH 24/32] Add clients to API V2 --- api/v2/objects.php | 4 +- functions/api_functions.php | 36 +++- functions/asset_functions.php | 42 ++-- functions/client_functions.php | 369 +++++++++++++++++++++++++++++++++ post/client.php | 319 +++++++--------------------- 5 files changed, 489 insertions(+), 281 deletions(-) create mode 100644 functions/client_functions.php diff --git a/api/v2/objects.php b/api/v2/objects.php index 7e66dd878..b6e4977d8 100644 --- a/api/v2/objects.php +++ b/api/v2/objects.php @@ -4,6 +4,6 @@ $valid_objects = [ 'asset', - 'account', - 'account_type', + 'client', + 'clients' ]; \ No newline at end of file diff --git a/functions/api_functions.php b/functions/api_functions.php index 427adc8d1..c2a6f75aa 100644 --- a/functions/api_functions.php +++ b/functions/api_functions.php @@ -35,14 +35,15 @@ function getAPIKey($api_key_id) { } function tryAPIKey($api_key_secret) { + global $mysqli, $session_ip, $session_user_agent; $row = mysqli_fetch_array(mysqli_query($mysqli,"SELECT * FROM api_keys WHERE api_key_secret = '$api_key_secret'")); if($row) { $api_key_id = intval($row['api_key_id']); - $api_key_client_id = intval($row['api_key_client_id']); $api_key_expire = sanitizeInput($row['api_key_expire']); + $api_client_id = intval($row['api_key_client_id']); // Check if the key has expired if(strtotime($api_key_expire) < time()) { @@ -52,15 +53,44 @@ function tryAPIKey($api_key_secret) { exit; } - return [ + $return_data = [ 'api_key_id' => $api_key_id, - 'api_key_client_id' => $api_key_client_id, 'api_key_expire' => $api_key_expire, ]; + + if ($api_client_id) { + $return_data['api_client_id'] = $api_client_id; + } + + return $return_data; } else { // Log invalid Key mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'API', log_action = 'Failed', log_description = 'Incorrect or expired key: ', log_ip = '$session_ip', log_user_agent = '$session_user_agent'"); echo json_encode(['status' => 'error', 'message' => 'Invalid API Key']); exit; } +} + +function getAPIWhereClause( + $var, + $var_id, + $api_client_id +){ + $where_clause = ""; + // If asset_id is all, check if client_id is set + if ($var_id == 'all') { + if ($api_client_id != 'all') { + // If client_id is set, get all assets for that client + $where_clause = "WHERE " . $var . "_client_id = $api_client_id"; + } // If client_id is not set, get all assets + } else { + if ($api_client_id != 'all') { + // If client_id is set, get the asset only if the client matches + $where_clause = "WHERE " . $var . "_id = $var_id AND asset_client_id = $api_client_id"; + } else { + // If client_id is not set, get the asset + $where_clause = "WHERE " . $var . "_id = $var_id"; + } + } + return $where_clause; } \ No newline at end of file diff --git a/functions/asset_functions.php b/functions/asset_functions.php index 823337aa6..096a3010e 100644 --- a/functions/asset_functions.php +++ b/functions/asset_functions.php @@ -61,43 +61,25 @@ function createAsset( return $return_data; } -function readAsset( - $parameters -) { +function readAsset($parameters) { $asset_id = sanitizeInput($parameters['asset_id']); - - if (isset($parameters['api_key_client_id'])) { - $client_id = sanitizeInput($parameters['api_key_client_id']); - } else { - $client_id = 'all'; - } + global $mysqli; - if ($asset_id == 'all') { - if ($client_id == 'all') { - $result = mysqli_query($mysqli,"SELECT * FROM assets"); - } else { - $result = mysqli_query($mysqli,"SELECT * FROM assets WHERE asset_client_id = $client_id"); - } - } else { - if ($client_id == 'all') { - $result = mysqli_query($mysqli,"SELECT * FROM assets WHERE asset_id = $asset_id"); - } else { - $result = mysqli_query($mysqli,"SELECT * FROM assets WHERE asset_id = $asset_id AND asset_client_id = $client_id"); - } - if (mysqli_num_rows($result) == 0) { - return ['status' => 'error', 'message' => 'Asset not found']; - exit; - } - } + // Check if there is an API Key Client ID parameter, if so, use it. Otherwise, default to 'all' + $api_client_id = isset($parameters['api_key_client_id']) ? sanitizeInput($parameters['api_key_client_id']) : 'all'; + // Get the where clause for the query + $where_clause = getAPIWhereClause("asset", $asset_id, $api_client_id); + + $query = "SELECT * FROM assets $where_clause"; + $result = mysqli_query($mysqli, $query); $assets = []; + while ($row = mysqli_fetch_assoc($result)) { - foreach ($row as $key => $value) { - $row[$key] = sanitizeInput($value); - } - $assets[] = $row; + $assets[$row['asset_id']] = $row; } + return $assets; } diff --git a/functions/client_functions.php b/functions/client_functions.php new file mode 100644 index 000000000..113181b69 --- /dev/null +++ b/functions/client_functions.php @@ -0,0 +1,369 @@ + $client_id, + 'status' => 'success' + ]; + + return $return_data; +} + +function readClient( + $parameters +) { + $client_id = $parameters['client_id']; + + global $mysqli; + + if (isset($parameters['api_key_client_id'])) { + $api_client_id = $parameters['api_key_client_id']; + $where_clause = getAPIWhereClause('client', $client_id, $api_client_id); + } else { + $where_clause = "WHERE client_id = $client_id"; + } + + $query = "SELECT * FROM clients $where_clause"; + + $result = mysqli_fetch_assoc(mysqli_query($mysqli, $query)); + + return $result; +} + +function readClients( +) +{ + global $mysqli; + + $sql = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_archived_at IS NULL AND client_lead = 0 ORDER BY client_accessed_at ASC"); + $clients = []; + while ($row = mysqli_fetch_array($sql)) { + $clients[intval($row['client_id'])] = $row; + } + return $clients; +} + +function updateClient( + $parameters +) { + $client_id = $parameters['client_id']; + $name = $parameters['name']; + $type = $parameters['type']; + $website = $parameters['website']; + $referral = $parameters['referral']; + $rate = $parameters['rate']; + $currency_code = $parameters['currency_code']; + $net_terms = $parameters['net_terms']; + $tax_id_number = $parameters['tax_id_number']; + $lead = $parameters['lead']; + $notes = $parameters['notes']; + + global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; + + mysqli_query($mysqli, "UPDATE clients SET client_name = '$name', client_type = '$type', client_website = '$website', client_referral = '$referral', client_rate = $rate, client_currency_code = '$currency_code', client_net_terms = $net_terms, client_tax_id_number = '$tax_id_number', client_lead = $lead, client_notes = '$notes' WHERE client_id = $client_id"); + + // Create Referral if it doesn't exist + $sql = mysqli_query($mysqli, "SELECT category_name FROM categories WHERE category_type = 'Referral' AND category_archived_at IS NULL AND category_name = '$referral'"); + if(mysqli_num_rows($sql) == 0) { + mysqli_query($mysqli, "INSERT INTO categories SET category_name = '$referral', category_type = 'Referral'"); + // Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Category', log_action = 'Create', log_description = '$name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + } + + // Tags + // Delete existing tags + mysqli_query($mysqli, "DELETE FROM client_tags WHERE client_tag_client_id = $client_id"); + + // Add new tags + foreach($_POST['tags'] as $tag) { + $tag = intval($tag); + mysqli_query($mysqli, "INSERT INTO client_tags SET client_tag_client_id = $client_id, client_tag_tag_id = $tag"); + } + + // Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Client', log_action = 'Modify', log_description = '$session_name modified client $name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $client_id"); + + return ['status' => 'success']; +} + +function archiveClient( + $parameters +) { + $client_id = $parameters['client_id']; + + global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; + + // Get Client Name + $sql = mysqli_query($mysqli, "SELECT * 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"); + + //Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Client', log_action = 'Archive', log_description = '$session_name archived client $client_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $client_id"); + + return ['status' => 'success']; +} + +function unarchiveClient( + $parameters +) { + $client_id = $parameters['client_id']; + + global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; + + // Get Client Name + $sql = mysqli_query($mysqli, "SELECT * 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"); + + //Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Client', log_action = 'Unarchive', log_description = '$session_name unarchived client $client_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $client_id"); + + return ['status' => 'success']; +} + +function deleteClient( + $parameters +) { + $client_id = $parameters['client_id']; + + global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; + + //Get Client Name + $sql = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_id = $client_id"); + $row = mysqli_fetch_array($sql); + $client_name = sanitizeInput($row['client_name']); + + // Delete Client Data + mysqli_query($mysqli, "DELETE FROM api_keys WHERE api_key_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM assets WHERE asset_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM certificates WHERE certificate_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM client_tags WHERE client_tag_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM contacts WHERE contact_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM documents WHERE document_client_id = $client_id"); + + // Delete Domains and associated records + $sql = mysqli_query($mysqli, "SELECT domain_id FROM domains WHERE domain_client_id = $client_id"); + while($row = mysqli_fetch_array($sql)) { + $domain_id = $row['domain_id']; + mysqli_query($mysqli, "DELETE FROM records WHERE record_domain_id = $domain_id"); + } + mysqli_query($mysqli, "DELETE FROM domains WHERE domain_client_id = $client_id"); + + mysqli_query($mysqli, "DELETE FROM events WHERE event_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM files WHERE file_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM folders WHERE folder_client_id = $client_id"); + + //Delete Invoices and Invoice Referencing data + $sql = mysqli_query($mysqli, "SELECT invoice_id FROM invoices WHERE invoice_client_id = $client_id"); + while($row = mysqli_fetch_array($sql)) { + $invoice_id = $row['invoice_id']; + mysqli_query($mysqli, "DELETE FROM invoice_items WHERE item_invoice_id = $invoice_id"); + mysqli_query($mysqli, "DELETE FROM payments WHERE payment_invoice_id = $invoice_id"); + mysqli_query($mysqli, "DELETE FROM history WHERE history_invoice_id = $invoice_id"); + } + mysqli_query($mysqli, "DELETE FROM invoices WHERE invoice_client_id = $client_id"); + + mysqli_query($mysqli, "DELETE FROM locations WHERE location_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM logins WHERE login_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM logs WHERE log_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM networks WHERE network_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM notifications WHERE notification_client_id = $client_id"); + + //Delete Quote and related items + $sql = mysqli_query($mysqli, "SELECT quote_id FROM quotes WHERE quote_client_id = $client_id"); + while($row = mysqli_fetch_array($sql)) { + $quote_id = $row['quote_id']; + + mysqli_query($mysqli, "DELETE FROM invoice_items WHERE item_quote_id = $quote_id"); + } + mysqli_query($mysqli, "DELETE FROM quotes WHERE quote_client_id = $client_id"); + + // Delete Recurring Invoices and associated items + $sql = mysqli_query($mysqli, "SELECT recurring_id FROM recurring WHERE recurring_client_id = $client_id"); + while($row = mysqli_fetch_array($sql)) { + $recurring_id = $row['recurring_id']; + mysqli_query($mysqli, "DELETE FROM invoice_items WHERE item_recurring_id = $recurring_id"); + } + mysqli_query($mysqli, "DELETE FROM recurring WHERE recurring_client_id = $client_id"); + + mysqli_query($mysqli, "DELETE FROM revenues WHERE revenue_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM scheduled_tickets WHERE scheduled_ticket_client_id = $client_id"); + + // Delete Services and items associated with services + $sql = mysqli_query($mysqli, "SELECT service_id FROM services WHERE service_client_id = $client_id"); + while($row = mysqli_fetch_array($sql)) { + $service_id = $row['service_id']; + mysqli_query($mysqli, "DELETE FROM service_assets WHERE service_id = $service_id"); + mysqli_query($mysqli, "DELETE FROM service_certificates WHERE service_id = $service_id"); + mysqli_query($mysqli, "DELETE FROM service_contacts WHERE service_id = $service_id"); + mysqli_query($mysqli, "DELETE FROM service_documents WHERE service_id = $service_id"); + mysqli_query($mysqli, "DELETE FROM service_domains WHERE service_id = $service_id"); + mysqli_query($mysqli, "DELETE FROM service_logins WHERE service_id = $service_id"); + mysqli_query($mysqli, "DELETE FROM service_vendors WHERE service_id = $service_id"); + } + mysqli_query($mysqli, "DELETE FROM services WHERE service_client_id = $client_id"); + + mysqli_query($mysqli, "DELETE FROM shared_items WHERE item_client_id = $client_id"); + + $sql = mysqli_query($mysqli, "SELECT software_id FROM software WHERE software_client_id = $client_id"); + while($row = mysqli_fetch_array($sql)) { + $software_id = $row['software_id']; + mysqli_query($mysqli, "DELETE FROM software_assets WHERE software_id = $software_id"); + mysqli_query($mysqli, "DELETE FROM software_contacts WHERE software_id = $software_id"); + } + mysqli_query($mysqli, "DELETE FROM software WHERE software_client_id = $client_id"); + + // Delete tickets and related data + $sql = mysqli_query($mysqli, "SELECT ticket_id FROM tickets WHERE ticket_client_id = $client_id"); + while($row = mysqli_fetch_array($sql)) { + $ticket_id = $row['ticket_id']; + mysqli_query($mysqli, "DELETE FROM ticket_replies WHERE ticket_reply_ticket_id = $ticket_id"); + mysqli_query($mysqli, "DELETE FROM ticket_views WHERE view_ticket_id = $ticket_id"); + } + mysqli_query($mysqli, "DELETE FROM tickets WHERE ticket_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM trips WHERE trip_client_id = $client_id"); + mysqli_query($mysqli, "DELETE FROM vendors WHERE vendor_client_id = $client_id"); + + //Delete Client Files + removeDirectory('uploads/clients/$client_id'); + + //Finally Remove the Client + mysqli_query($mysqli, "DELETE FROM clients WHERE client_id = $client_id"); + + //Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Client', log_action = 'Delete', log_description = '$session_name deleted client $client_name and all associated data', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + + + return ['status' => 'success']; +} diff --git a/post/client.php b/post/client.php index 7c2e998f0..cce502762 100644 --- a/post/client.php +++ b/post/client.php @@ -26,93 +26,36 @@ $extended_log_description = ''; - // Create client - mysqli_query($mysqli, "INSERT INTO clients SET client_name = '$name', client_type = '$type', client_website = '$website', client_referral = '$referral', client_rate = $rate, client_currency_code = '$currency_code', client_net_terms = $net_terms, client_tax_id_number = '$tax_id_number', client_lead = $lead, client_notes = '$notes', client_accessed_at = NOW()"); - - $client_id = mysqli_insert_id($mysqli); - - if (!file_exists("uploads/clients/$client_id")) { - mkdir("uploads/clients/$client_id"); - file_put_contents("uploads/clients/$client_id/index.php", ""); - } - - // Create Referral if it doesn't exist - $sql = mysqli_query($mysqli, "SELECT category_name FROM categories WHERE category_type = 'Referral' AND category_archived_at IS NULL AND category_name = '$referral'"); - if(mysqli_num_rows($sql) == 0) { - mysqli_query($mysqli, "INSERT INTO categories SET category_name = '$referral', category_type = 'Referral'"); - // Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Category', log_action = 'Create', log_description = '$name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - } - - // Create Location - if (!empty($location_phone) || !empty($address) || !empty($city) || !empty($state) || !empty($zip)) { - mysqli_query($mysqli, "INSERT INTO locations SET location_name = 'Primary', location_address = '$address', location_city = '$city', location_state = '$state', location_zip = '$zip', location_phone = '$location_phone', location_country = '$country', location_primary = 1, location_client_id = $client_id"); - - //Extended Logging - $extended_log_description .= ", primary location $address added"; - } - - - // Create Contact - if (!empty($contact) || !empty($title) || !empty($contact_phone) || !empty($contact_mobile) || !empty($contact_email)) { - mysqli_query($mysqli, "INSERT INTO contacts SET contact_name = '$contact', contact_title = '$title', contact_phone = '$contact_phone', contact_extension = '$contact_extension', contact_mobile = '$contact_mobile', contact_email = '$contact_email', contact_primary = 1, contact_important = 1, contact_client_id = $client_id"); - - //Extended Logging - $extended_log_description .= ", primary contact $contact added"; - } - - // Add Tags - if (isset($_POST['tags'])) { - foreach($_POST['tags'] as $tag) { - $tag = intval($tag); - mysqli_query($mysqli, "INSERT INTO client_tags SET client_tag_client_id = $client_id, client_tag_tag_id = $tag"); - } - } - - // Create domain in domains/certificates - if (!empty($website) && filter_var($website, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)) { - // Get domain expiry date - $expire = getDomainExpirationDate($website); - - // NS, MX, A and WHOIS records/data - $records = getDomainRecords($website); - $a = sanitizeInput($records['a']); - $ns = sanitizeInput($records['ns']); - $mx = sanitizeInput($records['mx']); - $whois = sanitizeInput($records['whois']); - - // Add domain record - mysqli_query($mysqli, "INSERT INTO domains SET domain_name = '$website', domain_registrar = 0, domain_webhost = 0, domain_expire = '$expire', domain_ip = '$a', domain_name_servers = '$ns', domain_mail_servers = '$mx', domain_raw_whois = '$whois', domain_client_id = $client_id"); - - //Extended Logging - $extended_log_description .= ", domain added"; - - // Get inserted ID (for linking certificate, if exists) - $domain_id = mysqli_insert_id($mysqli); - - // Get SSL cert for domain (if exists) - $certificate = getSSL($website); - if ($certificate['success'] == "TRUE") { - $expire = sanitizeInput($certificate['expire']); - $issued_by = sanitizeInput($certificate['issued_by']); - $public_key = sanitizeInput($certificate['public_key']); - - mysqli_query($mysqli, "INSERT INTO certificates SET certificate_name = '$website', certificate_domain = '$website', certificate_issued_by = '$issued_by', certificate_expire = '$expire', certificate_public_key = '$public_key', certificate_domain_id = $domain_id, certificate_client_id = $client_id"); - - //Extended Logging - $extended_log_description .= ", SSL certificate added"; - } - - } - - // Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Client', log_action = 'Create', log_description = '$session_name created client $name$extended_log_description', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $client_id"); - - $_SESSION['alert_message'] = "Client $name created"; - - header("Location: clients.php"); - exit; - + $return_data = createClient( + [ + 'name' => $name, + 'type' => $type, + 'website' => $website, + 'referral' => $referral, + 'rate' => $rate, + 'currency_code' => $currency_code, + 'net_terms' => $net_terms, + 'tax_id_number' => $tax_id_number, + 'lead' => $lead, + 'notes' => $notes, + 'location_phone' => $location_phone, + 'address' => $address, + 'city' => $city, + 'state' => $state, + 'zip' => $zip, + 'country' => $country, + 'contact' => $contact, + 'title' => $title, + 'contact_phone' => $contact_phone, + 'contact_extension' => $contact_extension, + 'contact_mobile' => $contact_mobile, + 'contact_email' => $contact_email + ] + ); + $client_id = $return_data['client_id']; + referWithAlert( + 'Client ' . $name . ' added', 'success', 'clients.php?client_id=' . $client_id + ); } if (isset($_POST['edit_client'])) { @@ -123,32 +66,24 @@ $client_id = intval($_POST['client_id']); - mysqli_query($mysqli, "UPDATE clients SET client_name = '$name', client_type = '$type', client_website = '$website', client_referral = '$referral', client_rate = $rate, client_currency_code = '$currency_code', client_net_terms = $net_terms, client_tax_id_number = '$tax_id_number', client_lead = $lead, client_notes = '$notes' WHERE client_id = $client_id"); - - // Create Referral if it doesn't exist - $sql = mysqli_query($mysqli, "SELECT category_name FROM categories WHERE category_type = 'Referral' AND category_archived_at IS NULL AND category_name = '$referral'"); - if(mysqli_num_rows($sql) == 0) { - mysqli_query($mysqli, "INSERT INTO categories SET category_name = '$referral', category_type = 'Referral'"); - // Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Category', log_action = 'Create', log_description = '$name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - } - - // Tags - // Delete existing tags - mysqli_query($mysqli, "DELETE FROM client_tags WHERE client_tag_client_id = $client_id"); - - // Add new tags - foreach($_POST['tags'] as $tag) { - $tag = intval($tag); - mysqli_query($mysqli, "INSERT INTO client_tags SET client_tag_client_id = $client_id, client_tag_tag_id = $tag"); - } - - // Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Client', log_action = 'Modify', log_description = '$session_name modified client $name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $client_id"); - - $_SESSION['alert_message'] = "Client $client_name updated"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + $return_data = updateClient( + [ + 'client_id' => $client_id, + 'name' => $name, + 'type' => $type, + 'website' => $website, + 'referral' => $referral, + 'rate' => $rate, + 'currency_code' => $currency_code, + 'net_terms' => $net_terms, + 'tax_id_number' => $tax_id_number, + 'lead' => $lead, + 'notes' => $notes + ] + ); + referWithAlert( + 'Client ' . $name . ' updated', 'success', 'clients.php?client_id=' . $client_id + ); } if (isset($_GET['archive_client'])) { @@ -157,39 +92,23 @@ $client_id = intval($_GET['archive_client']); - // Get Client Name - $sql = mysqli_query($mysqli, "SELECT * 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"); - - //Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Client', log_action = 'Archive', log_description = '$session_name archived client $client_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $client_id"); - - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "Client $client_name archived"; - - header("Location: " . $_SERVER["HTTP_REFERER"]); + $return_data = archiveClient([ + 'client_id' => $client_id + ]); + referWithAlert( + 'Client ' . $return_data['client_name'] . ' archived' + ); } if (isset($_GET['undo_archive_client'])) { - $client_id = intval($_GET['undo_archive_client']); - - // Get Client Name - $sql = mysqli_query($mysqli, "SELECT * 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"); - - //Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Client', log_action = 'Undo Archive', log_description = '$session_name unarchived client $client_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $client_id"); - - $_SESSION['alert_message'] = "Client $client_name unarchived"; + validateAdminRole(); - header("Location: " . $_SERVER["HTTP_REFERER"]); + $client_id = intval($_GET['undo_archive_client']); + $return_data = unarchiveClient($client_id); + referWithAlert( + 'Client ' . $return_data['client_name'] . ' unarchived' + ); } if (isset($_GET['delete_client'])) { @@ -201,115 +120,23 @@ $client_id = intval($_GET['delete_client']); - //Get Client Name - $sql = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_id = $client_id"); - $row = mysqli_fetch_array($sql); - $client_name = sanitizeInput($row['client_name']); - - // Delete Client Data - mysqli_query($mysqli, "DELETE FROM api_keys WHERE api_key_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM assets WHERE asset_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM certificates WHERE certificate_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM client_tags WHERE client_tag_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM contacts WHERE contact_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM documents WHERE document_client_id = $client_id"); - - // Delete Domains and associated records - $sql = mysqli_query($mysqli, "SELECT domain_id FROM domains WHERE domain_client_id = $client_id"); - while($row = mysqli_fetch_array($sql)) { - $domain_id = $row['domain_id']; - mysqli_query($mysqli, "DELETE FROM records WHERE record_domain_id = $domain_id"); - } - mysqli_query($mysqli, "DELETE FROM domains WHERE domain_client_id = $client_id"); - - mysqli_query($mysqli, "DELETE FROM events WHERE event_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM files WHERE file_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM folders WHERE folder_client_id = $client_id"); - - //Delete Invoices and Invoice Referencing data - $sql = mysqli_query($mysqli, "SELECT invoice_id FROM invoices WHERE invoice_client_id = $client_id"); - while($row = mysqli_fetch_array($sql)) { - $invoice_id = $row['invoice_id']; - mysqli_query($mysqli, "DELETE FROM invoice_items WHERE item_invoice_id = $invoice_id"); - mysqli_query($mysqli, "DELETE FROM payments WHERE payment_invoice_id = $invoice_id"); - mysqli_query($mysqli, "DELETE FROM history WHERE history_invoice_id = $invoice_id"); - } - mysqli_query($mysqli, "DELETE FROM invoices WHERE invoice_client_id = $client_id"); - - mysqli_query($mysqli, "DELETE FROM locations WHERE location_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM logins WHERE login_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM logs WHERE log_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM networks WHERE network_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM notifications WHERE notification_client_id = $client_id"); - - //Delete Quote and related items - $sql = mysqli_query($mysqli, "SELECT quote_id FROM quotes WHERE quote_client_id = $client_id"); - while($row = mysqli_fetch_array($sql)) { - $quote_id = $row['quote_id']; - - mysqli_query($mysqli, "DELETE FROM invoice_items WHERE item_quote_id = $quote_id"); - } - mysqli_query($mysqli, "DELETE FROM quotes WHERE quote_client_id = $client_id"); - - // Delete Recurring Invoices and associated items - $sql = mysqli_query($mysqli, "SELECT recurring_id FROM recurring WHERE recurring_client_id = $client_id"); - while($row = mysqli_fetch_array($sql)) { - $recurring_id = $row['recurring_id']; - mysqli_query($mysqli, "DELETE FROM invoice_items WHERE item_recurring_id = $recurring_id"); - } - mysqli_query($mysqli, "DELETE FROM recurring WHERE recurring_client_id = $client_id"); - - mysqli_query($mysqli, "DELETE FROM revenues WHERE revenue_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM scheduled_tickets WHERE scheduled_ticket_client_id = $client_id"); - - // Delete Services and items associated with services - $sql = mysqli_query($mysqli, "SELECT service_id FROM services WHERE service_client_id = $client_id"); - while($row = mysqli_fetch_array($sql)) { - $service_id = $row['service_id']; - mysqli_query($mysqli, "DELETE FROM service_assets WHERE service_id = $service_id"); - mysqli_query($mysqli, "DELETE FROM service_certificates WHERE service_id = $service_id"); - mysqli_query($mysqli, "DELETE FROM service_contacts WHERE service_id = $service_id"); - mysqli_query($mysqli, "DELETE FROM service_documents WHERE service_id = $service_id"); - mysqli_query($mysqli, "DELETE FROM service_domains WHERE service_id = $service_id"); - mysqli_query($mysqli, "DELETE FROM service_logins WHERE service_id = $service_id"); - mysqli_query($mysqli, "DELETE FROM service_vendors WHERE service_id = $service_id"); - } - mysqli_query($mysqli, "DELETE FROM services WHERE service_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM shared_items WHERE item_client_id = $client_id"); - $sql = mysqli_query($mysqli, "SELECT software_id FROM software WHERE software_client_id = $client_id"); - while($row = mysqli_fetch_array($sql)) { - $software_id = $row['software_id']; - mysqli_query($mysqli, "DELETE FROM software_assets WHERE software_id = $software_id"); - mysqli_query($mysqli, "DELETE FROM software_contacts WHERE software_id = $software_id"); - } - mysqli_query($mysqli, "DELETE FROM software WHERE software_client_id = $client_id"); - - // Delete tickets and related data - $sql = mysqli_query($mysqli, "SELECT ticket_id FROM tickets WHERE ticket_client_id = $client_id"); - while($row = mysqli_fetch_array($sql)) { - $ticket_id = $row['ticket_id']; - mysqli_query($mysqli, "DELETE FROM ticket_replies WHERE ticket_reply_ticket_id = $ticket_id"); - mysqli_query($mysqli, "DELETE FROM ticket_views WHERE view_ticket_id = $ticket_id"); + $return_data = deleteClient( + [ + 'client_id' => $client_id + ] + ); + if ($return_data['status'] == 'success') { + referWithAlert( + 'Client deleted along with all associated data' + ); + } else { + referWithAlert( + 'Client ' . $return_data['client_name'] . ' could not be deleted' + ); } - mysqli_query($mysqli, "DELETE FROM tickets WHERE ticket_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM trips WHERE trip_client_id = $client_id"); - mysqli_query($mysqli, "DELETE FROM vendors WHERE vendor_client_id = $client_id"); - - //Delete Client Files - removeDirectory('uploads/clients/$client_id'); - - //Finally Remove the Client - mysqli_query($mysqli, "DELETE FROM clients WHERE client_id = $client_id"); - - //Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Client', log_action = 'Delete', log_description = '$session_name deleted client $client_name and all associated data', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "Client $client_name deleted along with all associated data"; - header("Location: clients.php"); } if (isset($_POST['export_clients_csv'])) { From 0928ab44a3ad484e87bf12a45fb5eb0dc14fac85 Mon Sep 17 00:00:00 2001 From: o-psi Date: Sun, 17 Mar 2024 19:30:43 +0000 Subject: [PATCH 25/32] API V2 Documentation --- api/v2/api.php | 70 ++++++++--- api/v2/objects.php | 227 +++++++++++++++++++++++++++++++++- functions/api_functions.php | 3 + functions/asset_functions.php | 198 ++++++++++++++++++++--------- post/asset.php | 42 +++---- 5 files changed, 438 insertions(+), 102 deletions(-) diff --git a/api/v2/api.php b/api/v2/api.php index c52a9c7ec..39694e72a 100644 --- a/api/v2/api.php +++ b/api/v2/api.php @@ -7,20 +7,33 @@ It receives requests from the client, validates them, and then calls the appropriate function to handle the request. It then returns the result to the client. - example usage: - - GET /api/v2/api.php - { - "object": "asset", - "parameters": { - "asset_id": "all" - }, - } + Only the functions with objects in the objects.php file are permitted to be called by the API. + They must be in the format of actionObject($parameters) where action is one of create, read, update, or delete. - example response: - { + The API key is required for all requests and is used to authenticate the client. - } + The parameters are passed as an associative array and are validated and sanitized before being passed to the function. + + The API returns the result of the function as a JSON object. + + Example usage: + + --- Create a new client --- + + POST /api/v2/api.php?object=client&action=create¶meters[See objects.php for valid parameters]&api_key=[API Key + + + --- Read a client --- + + GET /api/v2/api.php?object=client&action=read¶meters[See objects.php for valid parameters]&api_key=[API Key] + + + --- Expected Result --- + { + "status": "success", + ... + } + */ require '/var/www/develop.twe.tech/config.php'; @@ -29,19 +42,31 @@ // Check the request method and get the object, parameters, and action from the request if ($_SERVER['REQUEST_METHOD'] == 'POST') { + // Sanitize the object $object = strtolower(sanitizeInput($_POST['object'])); + // Decode the parameters $parameters = $_POST['parameters']; + // Sanitize the API key $api_key = sanitizeInput($_POST['api_key']); + // Check if the action is set if (!isset($_POST['action'])) { // Default to create if no action is specified $action = 'create'; } else { // Sanitize the action $action = strtolower(sanitizeInput($_POST['action'])); + // Check if the action is read + if ($action == 'read') { + // Read requests should use the GET method + echo json_encode(['error' => 'Invalid action in request. Use GET method for read requests']); + } } } elseif ($_SERVER['REQUEST_METHOD'] == 'GET') { + // Sanitize the object $object = strtolower(sanitizeInput($_GET['object'])); + // Decode the parameters $parameters = $_GET['parameters']; + // Sanitize the API key $api_key = sanitizeInput($_GET['api_key']); if (!isset($_GET['action'])) { // Default to read if no action is specified @@ -49,10 +74,14 @@ } else { // Sanitize the action $action = strtolower(sanitizeInput($_GET['action'])); + // Check if the action is not read + if ($action != 'read') { + echo json_encode(['error' => 'Invalid action in request. Use POST method for create, update, and delete requests']); + } } } else { // Invalid request method - echo json_encode(['error' => 'Invalid request method']); + echo json_encode(['error' => 'Invalid request method. Use GET for read requests and POST for create, update, and delete requests']); exit; } @@ -64,7 +93,7 @@ // Check if action is CRUD if (!in_array($action, ['create', 'read', 'update', 'delete'])) { - echo json_encode(['error' => 'Invalid action in request']); + echo json_encode(['error' => 'Invalid action in request. ' . $action . ' must be one of create, read, update, or delete']); exit; } @@ -73,7 +102,7 @@ $parameters = json_decode($parameters, true); if (json_last_error() !== JSON_ERROR_NONE) { // Handle JSON decode error (e.g., invalid JSON format) - echo json_encode(['error' => 'Invalid JSON format in parameters']); + echo json_encode(['error' => 'Invalid JSON format in parameters. ' . json_last_error_msg() . ' in ' . $parameters]); exit; } } @@ -86,12 +115,15 @@ // Replace the parameters with the sanitized parameters $parameters = $sanitized_parameters; -if (!isset($parameters['api_key_client_id'])) { - $parameters['api_key_client_id'] = $api_client_id; +// Add the api_key_name to the parameters if it is not already set +if (!isset($parameters['api_key_name'])) { + $parameters['api_key_name'] = $api_key_data['api_key_name']; + // Add IP address to the parameters + $parameters['api_ip'] = $_SERVER['REMOTE_ADDR']; } // Check if the object is valid if (!in_array($object, $valid_objects)) { - echo json_encode(['error' => 'Invalid object in request']); + echo json_encode(['error' => 'Invalid object in request. ' . $object . ' is not an object to be manipulated via the API. \n\n Valid objects are: ' . implode(', \n', $valid_objects) . '.']); exit; } //Uppercase every first letter of the object @@ -113,6 +145,6 @@ exit; } else { // Call the function and return the result - echo json_encode($function($parameters)['status']); + echo json_encode($function($parameters)); exit; } diff --git a/api/v2/objects.php b/api/v2/objects.php index b6e4977d8..5615a9564 100644 --- a/api/v2/objects.php +++ b/api/v2/objects.php @@ -2,8 +2,233 @@ // Objects are to be the objects manipulated via the API +// Valid objects + $valid_objects = [ 'asset', 'client', 'clients' - ]; \ No newline at end of file + ]; + +/* +------------------------ +Object Parameters: +------------------------ + +* Denotes required field + +Asset: + Create: + Parameters: + *client_id = client_id(int) + *asset_name = name(str) + asset_description = description(str) + *asset_type = Valid types are: 'Server', 'Desktop', 'Laptop', 'Tablet', 'Phone', 'Printer', 'Switch', 'Router', 'Firewall', 'Access Point', 'Other' + asset_make = make(str) + asset_model = model(str) + asset_serial = serial(str) + asset_os = os(str) + asset_ip = ip(str) + asset_nat_ip = nat_ip(str) + asset_mac = mac(str) + asset_uri = uri(str) + asset_uri_2 = uri_2(str) + asset_status = status(str) + asset_location = location(str) + asset_vendor = vendor_id(int) + asset_contact = contact_id(int) + asset_network = network_id(int) + asset_purchase_date = purchase_date(date) + asset_warranty_expire = warranty_expire(date) + asset_install_date = install_date(date) + asset_notes = notes(str) + + Returns: + status = success or error + message = error message if status is error + asset_id = asset_id(int) + + Read: + Parameters: + *asset_id = asset_id(int) or all(str) + Returns: + status = success or error + message = error message if status is error + asset_id = asset_id(int) + client_id = client_id(int) + asset_name = name(str) + asset_description = description(str) + asset_type = (str) + asset_make = make(str) + asset_model = model(str) + asset_serial = serial(str) + asset_os = os(str) + asset_ip = ip(str) + asset_nat_ip = nat_ip(str) + asset_mac = mac(str) + asset_uri = uri(str) + asset_uri_2 = uri_2(str) + asset_status = status(str) + asset_location = location(str) + asset_vendor = vendor_id(int) + asset_contact = contact_id(int) + asset_network = network_id(int) + asset_purchase_date = purchase_date(date) + asset_warranty_expire = warranty_expire(date) + asset_install_date = install_date(date) + asset_notes = notes(str) + + Update: + Parameters: + *asset_id = asset_id(int) + *client_id = client_id(int) + asset_name = name(str) + asset_description = description(str) + asset_type = Valid types are: 'Server', 'Desktop', 'Laptop', 'Tablet', 'Phone', 'Printer', 'Switch', 'Router', 'Firewall', 'Access Point', 'Other' + asset_make = make(str) + asset_model = model(str) + asset_serial = serial(str) + asset_os = os(str) + asset_ip = ip(str) + asset_nat_ip = nat_ip(str) + asset_mac = mac(str) + asset_uri = uri(str) + asset_uri_2 = uri_2(str) + asset_status = status(str) + asset_location = location(str) + asset_vendor = vendor_id(int) + asset_contact = contact_id(int) + asset_network = network_id(int) + asset_purchase_date = purchase_date(date) + asset_warranty_expire = warranty_expire(date) + asset_install_date = install_date(date) + asset_notes = notes(str) + Returns: + status = success or error + message = error message if status is error + asset_id(int) = [ + client_id(int) + asset_name(str) + asset_description(str) + asset_type(str) + asset_make(str) + asset_model(str) + asset_serial(str) + asset_os(str) + asset_ip(str) + asset_nat_ip(str) + asset_mac(str) + asset_uri(str) + asset_uri_2(str) + asset_status(str) + asset_location(str) + asset_vendor(int) + asset_contact(int) + asset_network(int) + asset_purchase_date(date) + asset_warranty_expire(date) + asset_install_date(date) + asset_notes(str) + ] + + Delete: + Parameters: + *asset_id = asset_id(int) + Returns: + status = success or error + message = error message if status is error + asset_id = asset_id(int) + +clients: + Read: + No parameters + Returns: + status = success or error + message = error message if status is error + client_id = [ + client_id(int) + client_name(str) + client_type(str) + client_website(str) + client_website(str) + client_referral(str) + client_rate(int) + client_currency_code(str) + client_net_terms(int) + client_tax_id_number(int) + client_lead(tinyint) + cleint_notes(str) + ] + +client: + Create: + Parameters: + *client_name = name(str) + client_type = type(str) + client_website = website(str) + client_referral = referral(str) + client_rate = rate(int) + client_currency_code = currency_code(str) + client_net_terms = net_terms(int) + client_tax_id_number = tax_id_number(int) + client_lead = lead(tinyint) + client_notes = notes(str) + Returns: + status = success or error + message = error message if status is error + client_id = client_id(int) + Read: + Parameters: + *client_id = client_id(int) + Returns: + status = success or error + message = error message if status is error + client_id = client_id(int) + client_name = name(str) + client_type = type(str) + client_website = website(str) + client_referral = referral(str) + client_rate = rate(int) + client_currency_code = currency_code(str) + client_net_terms = net_terms(int) + client_tax_id_number = tax_id_number(int) + client_lead = lead(tinyint) + client_notes = notes(str) + + Update: + Parameters: + *client_id = client_id(int) + client_name = name(str) + client_type = type(str) + client_website = website(str) + client_referral = referral(str) + client_rate = rate(int) + client_currency_code = currency_code(str) + client_net_terms = net_terms(int) + client_tax_id_number = tax_id_number(int) + client_lead = lead(tinyint) + client_notes = notes(str) + Returns: + status = success or error + message = error message if status is error + client_id = client_id(int) + client_name = name(str) + client_type = type(str) + client_website = website(str) + client_referral = referral(str) + client_rate = rate(int) + client_currency_code = currency_code(str) + client_net_terms = net_terms(int) + client_tax_id_number = tax_id_number(int) + client_lead = lead(tinyint) + client_notes = notes(str) + + Delete: + Parameters: + *client_id = client_id(int) + Returns: + status = success or error + message = error message if status is error + client_id = client_id(int) + +*/ \ No newline at end of file diff --git a/functions/api_functions.php b/functions/api_functions.php index c2a6f75aa..913cac57e 100644 --- a/functions/api_functions.php +++ b/functions/api_functions.php @@ -44,6 +44,7 @@ function tryAPIKey($api_key_secret) { $api_key_id = intval($row['api_key_id']); $api_key_expire = sanitizeInput($row['api_key_expire']); $api_client_id = intval($row['api_key_client_id']); + $api_key_name = sanitizeInput($row['api_key_name']); // Check if the key has expired if(strtotime($api_key_expire) < time()) { @@ -55,6 +56,8 @@ function tryAPIKey($api_key_secret) { $return_data = [ 'api_key_id' => $api_key_id, + 'api_key_client_id' => $api_client_id, + 'api_key_name' => $api_key_name, 'api_key_expire' => $api_key_expire, ]; diff --git a/functions/asset_functions.php b/functions/asset_functions.php index 096a3010e..e61197b13 100644 --- a/functions/asset_functions.php +++ b/functions/asset_functions.php @@ -1,8 +1,8 @@ $asset_id])[$asset_id]; + + // Set parameters to the new values or the old values if not set + $name = $parameters['asset_name']??$asset_data['asset_name']; + $description = $parameters['description']??$asset_data['asset_description']; + $type = $parameters['asset_type']??$asset_data['asset_type']; + $make = $parameters['asset_make']??$asset_data['asset_make']; + $model = $parameters['asset_model']??$asset_data['asset_model']; + $serial = $parameters['asset_serial']??$asset_data['asset_serial']; + $os = $parameters['asset_os']??$asset_data['asset_os']; + $ip = $parameters['asset_ip']??$asset_data['asset_ip']; + $nat_ip = $parameters['asset_nat_ip']??$asset_data['asset_nat_ip']; + $mac = $parameters['asset_mac']??$asset_data['asset_mac']; + $uri = $parameters['asset_uri']??$asset_data['asset_uri']; + $uri_2 = $parameters['asset_uri_2']??$asset_data['asset_uri_2']; + $location = $parameters['location']??$asset_data['asset_location_id']; + $vendor = $parameters['vendor']??$asset_data['asset_vendor_id']; + $contact = $parameters['contact']??$asset_data['asset_contact_id']; + $network = $parameters['network']??$asset_data['asset_network_id']; + $status = $parameters['status']??$asset_data['asset_status']; + $notes = $parameters['notes']??$asset_data['asset_notes']; + + + + // if null, output 'NULL' for SQL, else output the date + $purchase_date = $parameters['purchase_date']??($asset_data['asset_purchase_date'] == NULL ? 'NULL' : $asset_data['asset_purchase_date']); + $warranty_expire = $parameters['warranty_expire']??($asset_data['asset_warranty_expire'] == NULL ? 'NULL' : $asset_data['asset_warranty_expire']); + $install_date = $parameters['install_date']??($asset_data['asset_install_date'] == NULL ? 'NULL' : $asset_data['asset_install_date']); + + $notes = $parameters['notes']??$asset_data['asset_notes']; + $client_id = $parameters['client_id']??$asset_data['asset_client_id']; + + if (isset($parameters['api_client_id']) && $parameters['api_client_id'] != 0){ + + $api_client_id = $parameters['api_client_id']; + + if ($api_client_id != $client_id) { + return ['status' => 'error', 'message' => 'You are not permitted to do that!']; + } + } global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; - if ($ip == "DCHP") { - $dhcp = 1; - } else { - $dhcp = 0; - } - - if ($api_client_id != $client_id) { - return ['status' => 'error', 'message' => 'You are not permitted to do that!']; + if (!isset($session_ip)) { + //Assume API is making changes + $session_ip = "API"; + $session_user_agent = "API"; + $session_user_id = 0; + $session_name = "API"; } - mysqli_query($mysqli,"UPDATE assets SET asset_name = '$name', asset_description = '$description', asset_type = '$type', asset_make = '$make', asset_model = '$model', asset_serial = '$serial', asset_os = '$os', asset_ip = '$ip', asset_dchp = $dhcp asset_nat_ip = '$nat_ip', asset_mac = '$mac', asset_uri = '$uri', asset_uri_2 = '$uri_2', asset_location_id = $location, asset_vendor_id = $vendor, asset_contact_id = $contact, asset_status = '$status', asset_purchase_date = $purchase_date, asset_warranty_expire = $warranty_expire, asset_install_date = $install_date, asset_notes = '$notes', asset_network_id = $network WHERE asset_id = $asset_id"); + mysqli_query($mysqli,"UPDATE assets SET asset_name = '$name', asset_description = '$description', asset_type = '$type', asset_make = '$make', asset_model = '$model', asset_serial = '$serial', asset_os = '$os', asset_ip = '$ip', asset_nat_ip = '$nat_ip', asset_mac = '$mac', asset_uri = '$uri', asset_uri_2 = '$uri_2', asset_location_id = $location, asset_vendor_id = $vendor, asset_contact_id = $contact, asset_status = '$status', asset_purchase_date = $purchase_date, asset_warranty_expire = $warranty_expire, asset_install_date = $install_date, asset_notes = '$notes', asset_network_id = $network WHERE asset_id = $asset_id"); //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Modify', log_description = '$session_name modified asset $name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); - return ['status' => 'success']; + $return_data = [ + 'status' => 'success', + 'message' => "Asset $name has been updated", + 'asset' => readAsset(['asset_id' => $asset_id]) + ]; + + return $return_data; } -function archiveAsset( - $asset_id +function deleteAsset( + $parameters ) { + $asset_id = $parameters['asset_id']; + + if (!isset($session_ip)) { + //Assume API is making changes + $session_ip = "API"; + $session_user_agent = "API"; + $session_user_id = 0; + $session_name = "API"; + } + global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; // Get Asset Name and Client ID for logging and alert message @@ -153,50 +197,82 @@ function archiveAsset( $asset_name = sanitizeInput($row['asset_name']); $client_id = intval($row['asset_client_id']); - mysqli_query($mysqli,"UPDATE assets SET asset_archived_at = NOW() WHERE asset_id = $asset_id"); + mysqli_query($mysqli,"DELETE FROM assets WHERE asset_id = $asset_id"); - //logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Archive', log_description = '$session_name archived asset $asset_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Delete', log_description = '$session_name deleted asset $asset_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); - return ['status' => 'success']; + $return_data = [ + 'status' => 'success', + 'message' => "Asset $asset_name has been deleted" + ]; + return $return_data; } -function unarchiveAsset( + + +//Functions below this are not used by the API, but are used by the web interface. (They are not compatible with C.R.U.D.) +function archiveAsset( $asset_id ) { global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; + if (!isset($session_ip)) { + //Assume API is making changes + $session_ip = "API"; + $session_user_agent = "API"; + $session_user_id = 0; + $session_name = "API"; + } + // Get Asset Name and Client ID for logging and alert message $sql = mysqli_query($mysqli,"SELECT asset_name, asset_client_id FROM assets WHERE asset_id = $asset_id"); $row = mysqli_fetch_array($sql); $asset_name = sanitizeInput($row['asset_name']); $client_id = intval($row['asset_client_id']); - mysqli_query($mysqli,"UPDATE assets SET asset_archived_at = NULL WHERE asset_id = $asset_id"); + mysqli_query($mysqli,"UPDATE assets SET asset_archived_at = NOW() WHERE asset_id = $asset_id"); - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Unarchive', log_description = '$session_name unarchived asset $asset_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); + //logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Archive', log_description = '$session_name archived asset $asset_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); - return ['status' => 'success']; + $return_data = [ + 'status' => 'success', + 'message' => "Asset $asset_name has been archived", + 'asset' => readAsset(['asset_id' => $asset_id]) + ]; + + return $return_data; } -function deleteAsset( - $parameters +function unarchiveAsset( + $asset_id ) { - $asset_id = $parameters['asset_id']; - global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; + if (!isset($session_ip)) { + //Assume API is making changes + $session_ip = "API"; + $session_user_agent = "API"; + $session_user_id = 0; + $session_name = "API"; + } + // Get Asset Name and Client ID for logging and alert message $sql = mysqli_query($mysqli,"SELECT asset_name, asset_client_id FROM assets WHERE asset_id = $asset_id"); $row = mysqli_fetch_array($sql); $asset_name = sanitizeInput($row['asset_name']); $client_id = intval($row['asset_client_id']); - mysqli_query($mysqli,"DELETE FROM assets WHERE asset_id = $asset_id"); + mysqli_query($mysqli,"UPDATE assets SET asset_archived_at = NULL WHERE asset_id = $asset_id"); //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Delete', log_description = '$session_name deleted asset $asset_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Asset', log_action = 'Unarchive', log_description = '$session_name unarchived asset $asset_name', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $asset_id"); - return ['status' => 'success']; -} \ No newline at end of file + $return_data = [ + 'status' => 'success', + 'message' => "Asset $asset_name has been unarchived", + 'asset' => readAsset(['asset_id' => $asset_id]) + ]; + return $return_data; +} diff --git a/post/asset.php b/post/asset.php index 17177aca7..1181bdb5c 100644 --- a/post/asset.php +++ b/post/asset.php @@ -132,27 +132,27 @@ [ 'asset_id' => $asset_id, 'client_id' => $client_id, - 'name' => $name, - 'description' => $description, - 'type' => $type, - 'make' => $make, - 'model' => $model, - 'serial' => $serial, - 'os' => $os, - 'ip' => $ip, - 'nat_ip' => $nat_ip, - 'mac' => $mac, - 'uri' => $uri, - 'uri_2' => $uri_2, - 'status' => $status, - 'location' => $location, - 'vendor' => $vendor, - 'contact' => $contact, - 'network' => $network, - 'purchase_date' => $purchase_date, - 'warranty_expire' => $warranty_expire, - 'install_date' => $install_date, - 'notes' => $notes + 'asset_name' => $name, + 'asset_description' => $description, + 'asset_type' => $type, + 'asset_make' => $make, + 'asset_model' => $model, + 'asset_serial' => $serial, + 'asset_os' => $os, + 'asset_ip' => $ip, + 'asset_nat_ip' => $nat_ip, + 'asset_mac' => $mac, + 'asset_uri' => $uri, + 'asset_uri_2' => $uri_2, + 'asset_status' => $status, + 'asset_location_id' => $location, + 'asset_vendor_id' => $vendor, + 'asset_contact_id' => $contact, + 'asset_network_id' => $network, + 'asset_purchase_date' => $purchase_date, + 'asset_warranty_expire' => $warranty_expire, + 'asset_install_date' => $install_date, + 'asset_notes' => $notes ] ); referWithAlert("Asset $name updated", "success", "client_assets.php?client_id=$client_id"); From bae20ebc11e30916afe0655179933557acc422bc Mon Sep 17 00:00:00 2001 From: o-psi Date: Sun, 17 Mar 2024 19:32:59 +0000 Subject: [PATCH 26/32] Fix Sonar Bug (Unrelated in cron email parser) --- cron_ticket_email_parser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cron_ticket_email_parser.php b/cron_ticket_email_parser.php index 2a2f6e48a..dd46cfc5d 100644 --- a/cron_ticket_email_parser.php +++ b/cron_ticket_email_parser.php @@ -359,7 +359,7 @@ function addReply($from_email, $date, $subject, $ticket_number, $message, $attac mysqli_query($mysqli, "UPDATE tickets SET ticket_status = 'Open' WHERE ticket_id = $ticket_id AND ticket_client_id = $client_id LIMIT 1"); // App Notification - mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Ticket', notification = 'Ticket $ticket_prefix$ticket_number - Subject: $subject - has been updated via email', notification_action = 'ticket.php?ticket_id=$ticket_id', notification_client_id = $client_id, notification_user_id = $ticket_assigned_to"); + mysqli_query($mysqli, "INSERT INTO notifications SET notification_type = 'Ticket', notification = 'Ticket $config_ticket_prefix$ticket_number - Subject: $subject - has been updated via email', notification_action = 'ticket.php?ticket_id=$ticket_id', notification_client_id = $client_id, notification_user_id = $ticket_assigned_to"); echo "Updated existing ticket.
"; mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Update', log_description = 'Email parser: Client contact $from_email updated ticket $config_ticket_prefix$ticket_number ($subject)', log_client_id = $client_id"); From 2b4ac872a6187eda8d22f441e25d8c01ea810fe9 Mon Sep 17 00:00:00 2001 From: o-psi Date: Sun, 17 Mar 2024 19:36:46 +0000 Subject: [PATCH 27/32] Another sonar bug in email ticket parser --- cron_ticket_email_parser.php | 1 + 1 file changed, 1 insertion(+) diff --git a/cron_ticket_email_parser.php b/cron_ticket_email_parser.php index dd46cfc5d..c70bf8047 100644 --- a/cron_ticket_email_parser.php +++ b/cron_ticket_email_parser.php @@ -183,6 +183,7 @@ function addTicket($contact_id, $contact_name, $contact_email, $client_id, $date $client_sql = mysqli_query($mysqli, "SELECT client_name FROM clients WHERE client_id = $client_id"); $client_row = mysqli_fetch_array($client_sql); $client_name = sanitizeInput($client_row['client_name']); + $details = sanitizeInput($message); $email_subject = "$config_app_name - New Ticket - $client_name: $subject"; $email_body = "Hello,

This is a notification that a new ticket has been raised in ITFlow.
Client: $client_name
Priority: Low (email parsed)
Link: https://$config_base_url/ticket.php?ticket_id=$id

--------------------------------

$subject
$details"; From 5f10b0660d8fd858f731ae4b3de3de86fb937411 Mon Sep 17 00:00:00 2001 From: o-psi Date: Sun, 17 Mar 2024 19:47:28 +0000 Subject: [PATCH 28/32] Adjust error handling --- api/v2/api.php | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/api/v2/api.php b/api/v2/api.php index 39694e72a..47682fa98 100644 --- a/api/v2/api.php +++ b/api/v2/api.php @@ -135,16 +135,29 @@ // Create function $function = $action . $object; +// Check if the function exists if (!function_exists($function)) { - echo json_encode(['error' => 'Invalid function in request']); + echo json_encode(['error' => 'Invalid function in request. This is probably a bug. Please report this to the developer. ' . $function . ' does not exist.']); exit; } -if ($action == 'read') { - // Call the function and return the result - echo json_encode($function($parameters)); +// Call the function +try{ + $function_result = $function($parameters); +} +// Catch any exceptions +catch (Exception $e) { + echo json_encode(['error' => 'Invalid function result. This is probably a bug. Please report this to the developer. ' . $e->getMessage()]); exit; +} + +// Return the result as a JSON object +if ($function_result) { + if (is_array($function_result)) { + echo json_encode($function_result); + } else { + echo json_encode(['error' => 'Invalid function result. This is probably a bug. Please report this to the developer.']); + } } else { - // Call the function and return the result - echo json_encode($function($parameters)); - exit; + echo json_encode(['error' => 'Invalid function result. This is probably a bug. Please report this to the developer.']); } +exit; From 8b0c5c23266150727c3a92b496beed8c809f57f8 Mon Sep 17 00:00:00 2001 From: o-psi Date: Mon, 18 Mar 2024 02:59:37 +0000 Subject: [PATCH 29/32] Error checking --- api/v2/api.php | 7 +++-- functions/asset_functions.php | 56 ++++++++++++++++++++++------------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/api/v2/api.php b/api/v2/api.php index 47682fa98..a8dffb0b2 100644 --- a/api/v2/api.php +++ b/api/v2/api.php @@ -58,7 +58,7 @@ // Check if the action is read if ($action == 'read') { // Read requests should use the GET method - echo json_encode(['error' => 'Invalid action in request. Use GET method for read requests']); + echo json_encode(['WARN' => 'Invalid action in request. Use GET method for read requests']); } } } elseif ($_SERVER['REQUEST_METHOD'] == 'GET') { @@ -76,12 +76,13 @@ $action = strtolower(sanitizeInput($_GET['action'])); // Check if the action is not read if ($action != 'read') { - echo json_encode(['error' => 'Invalid action in request. Use POST method for create, update, and delete requests']); + echo json_encode(['WARN' => 'Invalid action in request. Use POST method for create, update, and delete requests. Action '. $action . ' is not valid.']); + exit; } } } else { // Invalid request method - echo json_encode(['error' => 'Invalid request method. Use GET for read requests and POST for create, update, and delete requests']); + echo json_encode(['ERROR' => 'Invalid request method. Use GET for read requests and POST for create, update, and delete requests']); exit; } diff --git a/functions/asset_functions.php b/functions/asset_functions.php index e61197b13..68c8b3f3c 100644 --- a/functions/asset_functions.php +++ b/functions/asset_functions.php @@ -7,27 +7,41 @@ function createAsset( $parameters ) { $client_id = $parameters['client_id']; - $name = $parameters['name']; - $description = $parameters['description']; - $type = $parameters['type']; - $make = $parameters['make']; - $model = $parameters['model']; - $serial = $parameters['serial']; - $os = $parameters['os']; - $ip = $parameters['ip']; - $nat_ip = $parameters['nat_ip']; - $mac = $parameters['mac']; - $uri = $parameters['uri']; - $uri_2 = $parameters['uri_2']; - $status = $parameters['status']; - $location = $parameters['location']; - $vendor = $parameters['vendor']; - $contact = $parameters['contact']; - $network = $parameters['network']; - $purchase_date = $parameters['purchase_date']; - $warranty_expire = $parameters['warranty_expire']; - $install_date = $parameters['install_date']; - $notes = $parameters['notes']; + $name = $parameters['asset_name']; + $description = $parameters['asset_description']??''; + $type = $parameters['asset_type']; + $make = $parameters['asset_make']??''; + $model = $parameters['asset_model']??''; + $serial = $parameters['asset_serial']??''; + $os = $parameters['asset_os']??''; + $ip = $parameters['asset_ip']??''; + $nat_ip = $parameters['asset_nat_ip']??''; + $mac = $parameters['asset_mac']??''; + $uri = $parameters['asset_uri']??''; + $uri_2 = $parameters['asset_uri_2']??''; + $status = $parameters['asset_status']??''; + $location = $parameters['asset_location']??'NULL'; + $vendor = $parameters['asset_vendor']??'NULL'; + $contact = $parameters['asset_contact']??'NULL'; + $network = $parameters['asset_network']??'NULL'; + $purchase_date = $parameters['asset_purchase_date']??'NULL'; + $warranty_expire = $parameters['asset_warranty_expire']??'NULL'; + $install_date = $parameters['asset_install_date']??'NULL'; + $notes = $parameters['asset_notes']??''; + + if (empty($name)) { + return ['status' => 'error', 'message' => 'Asset Name is required']; + } + if (empty($client_id)) { + return ['status' => 'error', 'message' => 'Client ID is required']; + } + + if (empty($type)) { + return ['status' => 'error', 'message' => 'Asset Type is required']; + }elseif (!in_array($type, ['Server', 'Desktop', 'Laptop', 'Tablet', 'Phone', 'Printer', 'Switch', 'Router', 'Firewall', 'Access Point', 'Other'])) { + return ['status' => 'error', 'message' => 'Invalid Asset Type']; + } + global $mysqli, $session_ip, $session_user_agent, $session_user_id, $session_name; From 6c84751194844ff5c977872bdc6f346ea3943a55 Mon Sep 17 00:00:00 2001 From: o-psi Date: Mon, 18 Mar 2024 17:01:19 +0000 Subject: [PATCH 30/32] Added Invoices and Tickets to API --- api/v2/objects.php | 134 +++++++- functions/asset_functions.php | 2 +- functions/invoice_functions.php | 375 ++++------------------ functions/recurring_invoice_functions.php | 293 +++++++++++++++++ functions/ticket_functions.php | 272 ++++++++++++++++ post/invoice.php | 24 +- post/ticket.php | 252 +++------------ 7 files changed, 818 insertions(+), 534 deletions(-) create mode 100644 functions/recurring_invoice_functions.php create mode 100644 functions/ticket_functions.php diff --git a/api/v2/objects.php b/api/v2/objects.php index 5615a9564..204973f4b 100644 --- a/api/v2/objects.php +++ b/api/v2/objects.php @@ -1,13 +1,17 @@ $invoice_id]); + + $invoice_due = $parameters['invoice_due']??$invoice_data['invoice_due']; + $invoice_prefix = $parameters['invoice_prefix']??$invoice_data['invoice_prefix']; + $invoice_number = $parameters['invoice_number']??$invoice_data['invoice_number']; + $invoice_scope = $parameters['invoice_scope']??$invoice_data['invoice_scope']; + $invoice_currency_code = $parameters['invoice_currency_code']??$invoice_data['invoice_currency_code']; + $invoice_category_id = $parameters['invoice_category_id']??$invoice_data['invoice_category_id']; + $invoice_url_key = $parameters['invoice_url_key']??$invoice_data['invoice_url_key']; + $invoice_client_id = $parameters['invoice_client_id']??$invoice_data['invoice_client_id']; + + // if invoice status is a parameter, use update invoice status function for that part + // else, use the current status + if (isset($parameters['invoice_status']) || isset($parameters['invoice_archived'])) { + + $invoice_status = $parameters['invoice_status']; + $invoice_archived = $parameters['invoice_archived']; + + if ($invoice_archived == 1) { + $invoice_status = 'Archived'; + } + + updateInvoiceStatus($invoice_id, $invoice_status); + + // check if thats the only parameter, if so, return + if (count($parameters) == 1) { return ['status' => 'success']; } + + } else { $invoice_status = $invoice_data['invoice_status']; } + + // if null, output 'NULL' for SQL, else output the data + $invoice_date = $parameters['invoice_date']??($invoice_data['invoice_date'] == NULL ? 'NULL' : $invoice_data['invoice_date']); // Access global variables global $mysqli, $session_user_id, $session_ip, $session_user_agent; - mysqli_query($mysqli,"UPDATE invoices SET invoice_prefix = '$invoice_prefix', invoice_number = $invoice_number, invoice_scope = '$invoice_scope', invoice_date = '$invoice_date', invoice_due = '$invoice_due', invoice_currency_code = '$invoice_currency_code', invoice_category_id = $invoice_category_id, invoice_status = '$invoice_status', invoice_url_key = '$invoice_url_key', invoice_client_id = $invoice_client_id WHERE invoice_id = $invoice_id"); + mysqli_query($mysqli,"UPDATE invoices SET invoice_prefix = '$invoice_prefix', invoice_number = $invoice_number, invoice_scope = '$invoice_scope', invoice_date = '$invoice_date', invoice_due = '$invoice_due', invoice_currency_code = '$invoice_currency_code', invoice_category_id = $invoice_category_id, invoice_url_key = '$invoice_url_key', invoice_client_id = $invoice_client_id WHERE invoice_id = $invoice_id"); mysqli_query($mysqli,"INSERT INTO history SET history_status = '$invoice_status', history_description = 'INVOICE updated!', history_invoice_id = $invoice_id"); //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Update', log_description = '$invoice_prefix$invoice_number', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + $return_data = [ + 'status' => 'success', + 'message' => "Invoice $invoice_number has been updated", + 'asset' => readInvoice(['invoice_id' => $invoice_id]) + ]; + + return $return_data; } function deleteInvoice( - $invoice_id + $parameters ) { + $invoice_id = intval($parameters['invoice_id']); // Access global variables global $mysqli, $session_user_id, $session_ip, $session_user_agent; @@ -112,6 +147,7 @@ function deleteInvoice( mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Delete', log_description = '$invoice_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); } +// Below this are functions not called directly by the API function copyInvoice( $invoice_id, $date @@ -170,162 +206,17 @@ function copyInvoice( } -function createRecurringInvoice( - $client, - $frequency, - $start_date, - $category, - $scope -) { - // Access global variables - global $mysqli, $session_company_currency, $session_user_id, $session_ip, $session_user_agent, $config_recurring_prefix, $config_recurring_next_number; - - //Get the last Recurring Number and add 1 for the new Recurring number - $recurring_number = $config_recurring_next_number; - $new_config_recurring_next_number = $config_recurring_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_recurring_next_number = $new_config_recurring_next_number WHERE company_id = 1"); - - mysqli_query($mysqli,"INSERT INTO recurring SET recurring_prefix = '$config_recurring_prefix', recurring_number = $recurring_number, recurring_scope = '$scope', recurring_frequency = '$frequency', recurring_next_date = '$start_date', recurring_category_id = $category, recurring_status = 1, recurring_currency_code = '$session_company_currency', recurring_client_id = $client"); - - $recurring_id = mysqli_insert_id($mysqli); - - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Active', history_description = 'Recurring Invoice created!', history_recurring_id = $recurring_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Recurring', log_action = 'Create', log_description = '$start_date - $category', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); -} - -function createInvoiceFromRecurring( +function updateInvoiceStatus( $invoice_id, - $recurring_frequency -){ - // Access global variables - global $mysqli, $session_user_id, $session_ip, $session_user_agent, $config_recurring_prefix, $config_recurring_next_number; - - $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id"); - $row = mysqli_fetch_array($sql); - $invoice_date = sanitizeInput($row['invoice_date']); - $invoice_amount = floatval($row['invoice_amount']); - $invoice_currency_code = sanitizeInput($row['invoice_currency_code']); - $invoice_scope = sanitizeInput($row['invoice_scope']); - $invoice_note = sanitizeInput($row['invoice_note']); //SQL Escape in case notes have , them - $client_id = intval($row['invoice_client_id']); - $category_id = intval($row['invoice_category_id']); - - //Get the last Recurring Number and add 1 for the new Recurring number - $recurring_number = $config_recurring_next_number; - $new_config_recurring_next_number = $config_recurring_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_recurring_next_number = $new_config_recurring_next_number WHERE company_id = 1"); - - mysqli_query($mysqli,"INSERT INTO recurring SET recurring_prefix = '$config_recurring_prefix', recurring_number = $recurring_number, recurring_scope = '$invoice_scope', recurring_frequency = '$recurring_frequency', recurring_next_date = DATE_ADD('$invoice_date', INTERVAL 1 $recurring_frequency), recurring_status = 1, recurring_amount = $invoice_amount, recurring_currency_code = '$invoice_currency_code', recurring_note = '$invoice_note', recurring_category_id = $category_id, recurring_client_id = $client_id"); - - $recurring_id = mysqli_insert_id($mysqli); - - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Draft', history_description = 'Recurring Created from INVOICE!', history_recurring_id = $recurring_id"); - - $sql_items = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_invoice_id = $invoice_id"); - while($row = mysqli_fetch_array($sql_items)) { - $item_id = intval($row['item_id']); - $item_name = sanitizeInput($row['item_name']); - $item_description = sanitizeInput($row['item_description']); - $item_quantity = floatval($row['item_quantity']); - $item_price = floatval($row['item_price']); - $item_subtotal = floatval($row['item_subtotal']); - $item_tax = floatval($row['item_tax']); - $item_total = floatval($row['item_total']); - $item_order = intval($row['item_order']); - $tax_id = intval($row['item_tax_id']); - - mysqli_query($mysqli,"INSERT INTO invoice_items SET item_name = '$item_name', item_description = '$item_description', item_quantity = $item_quantity, item_price = $item_price, item_subtotal = $item_subtotal, item_tax = $item_tax, item_total = $item_total, item_order = $item_order, item_tax_id = $tax_id, item_recurring_id = $recurring_id"); - } - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = 'From recurring invoice', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - - -} - -function readRecurringInvoice( - $recurring_id -) { - // Access global variables - global $mysqli; - - $sql = mysqli_query($mysqli,"SELECT * FROM recurring WHERE recurring_id = $recurring_id"); - $row = mysqli_fetch_array($sql); - - return $row; -} - -function updateRecurringInvoice( - $recurring_id, - $frequency, - $next_date, - $category, - $scope, - $status, - $recurring_discount -) { - // Access global variables - global $mysqli, $session_user_id, $session_ip, $session_user_agent; - - //Calculate new total - $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_recurring_id = $recurring_id"); - $recurring_amount = 0; - while($row = mysqli_fetch_array($sql)) { - $item_total = floatval($row['item_total']); - $recurring_amount = $recurring_amount + $item_total; - } - $recurring_amount = $recurring_amount - $recurring_discount; - - mysqli_query($mysqli,"UPDATE recurring SET recurring_scope = '$scope', recurring_frequency = '$frequency', recurring_next_date = '$next_date', recurring_category_id = $category, recurring_discount_amount = $recurring_discount, recurring_amount = $recurring_amount, recurring_status = $status WHERE recurring_id = $recurring_id"); - - mysqli_query($mysqli,"INSERT INTO history SET history_status = '$status', history_description = 'Recurring modified', history_recurring_id = $recurring_id"); - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Recurring', log_action = 'Modify', log_description = '$recurring_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - -} - -function deleteRecurringInvoice( - $recurring_id + $status ) { - // Access global variables - global $mysqli, $session_user_id, $session_ip, $session_user_agent; - mysqli_query($mysqli,"DELETE FROM recurring WHERE recurring_id = $recurring_id"); - - //Delete Items Associated with the Recurring - $sql = mysqli_query($mysqli,"SELECT * FROM invoice_items WHERE item_recurring_id = $recurring_id"); - while($row = mysqli_fetch_array($sql)) { - $item_id = intval($row['item_id']); - mysqli_query($mysqli,"DELETE FROM invoice_items WHERE item_id = $item_id"); + // Check if invoice needs to be unarchived (status updated other than archive) + $invoice_data = readInvoice(['invoice_id' => $invoice_id]); + if ($invoice_data['status'] == 'Archived' && $invoice_data['status'] != $status) { + $archived_query = ", invoice_archived_at = NULL"; } - //Delete History Associated with the Invoice - $sql = mysqli_query($mysqli,"SELECT * FROM history WHERE history_recurring_id = $recurring_id"); - while($row = mysqli_fetch_array($sql)) { - $history_id = intval($row['history_id']); - mysqli_query($mysqli,"DELETE FROM history WHERE history_id = $history_id"); - } - - //Logging - mysqli_query($mysqli, - "INSERT INTO logs SET - log_type = 'Recurring', - log_action = 'Delete', - log_description = '$recurring_id', - log_ip = '$session_ip', - log_user_agent = '$session_user_agent', - log_user_id = $session_user_id - "); -} - -function updateInvoiceStatus( - $invoice_id, - $status -) { - $archived_query = ""; switch($status): case "Draft": $history_description = "Invoice set to Draft"; @@ -364,148 +255,6 @@ function updateInvoiceStatus( //Logging mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Status', log_description = 'Invoice ID $invoice_id', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - -} - -function forceRecurring( - $recurring_id -) { - // Access global variables - global $mysqli, $session_user_id, $session_ip, $session_user_agent, $config_recurring_auto_send_invoice, $config_invoice_next_number, $config_invoice_prefix, $config_invoice_from_email, $config_invoice_from_name, $config_base_url, $session_name; - - - $sql_recurring = mysqli_query($mysqli,"SELECT * FROM recurring, clients WHERE client_id = recurring_client_id AND recurring_id = $recurring_id"); - - $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_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']); - $category_id = intval($row['recurring_category_id']); - $client_id = intval($row['recurring_client_id']); - $client_net_terms = intval($row['client_net_terms']); - - //Get the last Invoice Number and add 1 for the new invoice number - $new_invoice_number = $config_invoice_next_number; - $new_config_invoice_next_number = $config_invoice_next_number + 1; - mysqli_query($mysqli,"UPDATE settings SET config_invoice_next_number = $new_config_invoice_next_number WHERE company_id = 1"); - - //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"); - - $new_invoice_id = mysqli_insert_id($mysqli); - - //Copy Items from original 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"); - - while($row = mysqli_fetch_array($sql_invoice_items)) { - $item_id = intval($row['item_id']); - $item_name = sanitizeInput($row['item_name']); - $item_description = sanitizeInput($row['item_description']); - $item_quantity = floatval($row['item_quantity']); - $item_price = floatval($row['item_price']); - $item_subtotal = floatval($row['item_subtotal']); - $item_order = intval($row['item_order']); - $tax_id = intval($row['item_tax_id']); - - //Recalculate Item Tax since Tax percents can change. - if ($tax_id > 0) { - $sql = mysqli_query($mysqli,"SELECT * FROM taxes WHERE tax_id = $tax_id"); - $row = mysqli_fetch_array($sql); - $tax_percent = floatval($row['tax_percent']); - $item_tax_amount = $item_subtotal * $tax_percent / 100; - } else { - $item_tax_amount = 0; - } - - $item_total = $item_subtotal + $item_tax_amount; - - //Update Recurring Items with new tax - mysqli_query($mysqli,"UPDATE invoice_items SET item_tax = $item_tax_amount, item_total = $item_total, item_tax_id = $tax_id, item_order = $item_order WHERE item_id = $item_id"); - - mysqli_query($mysqli,"INSERT INTO invoice_items SET item_name = '$item_name', item_description = '$item_description', item_quantity = $item_quantity, item_price = $item_price, item_subtotal = $item_subtotal, item_tax = $item_tax_amount, item_total = $item_total, item_tax_id = $tax_id, item_invoice_id = $new_invoice_id"); - } - - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Invoice Generated from Recurring!', history_invoice_id = $new_invoice_id"); - - //Update Recurring Balances by tallying up recurring items also update recurring dates - $sql_recurring_total = mysqli_query($mysqli,"SELECT SUM(item_total) AS recurring_total FROM invoice_items WHERE item_recurring_id = $recurring_id"); - $row = mysqli_fetch_array($sql_recurring_total); - $new_recurring_amount = floatval($row['recurring_total']) - $recurring_discount_amount; - - mysqli_query($mysqli,"UPDATE recurring SET recurring_amount = $new_recurring_amount, recurring_last_sent = CURDATE(), recurring_next_date = DATE_ADD(CURDATE(), INTERVAL 1 $recurring_frequency) WHERE recurring_id = $recurring_id"); - - //Also update the newly created invoice with the new amounts - mysqli_query($mysqli,"UPDATE invoices SET invoice_amount = $new_recurring_amount WHERE invoice_id = $new_invoice_id"); - - if ($config_recurring_auto_send_invoice == 1) { - $sql = mysqli_query($mysqli,"SELECT * FROM invoices - LEFT JOIN clients ON invoice_client_id = client_id - LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1 - WHERE invoice_id = $new_invoice_id" - ); - $row = mysqli_fetch_array($sql); - - $invoice_prefix = sanitizeInput($row['invoice_prefix']); - $invoice_number = intval($row['invoice_number']); - $invoice_scope = sanitizeInput($row['invoice_scope']); - $invoice_date = sanitizeInput($row['invoice_date']); - $invoice_due = sanitizeInput($row['invoice_due']); - $invoice_amount = floatval($row['invoice_amount']); - $invoice_url_key = sanitizeInput($row['invoice_url_key']); - $client_id = intval($row['client_id']); - $contact_name = sanitizeInput($row['contact_name']); - $contact_email = sanitizeInput($row['contact_email']); - - $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'])); - - // Sanitize Config Vars - $config_invoice_from_email = sanitizeInput($config_invoice_from_email); - $config_invoice_from_name = sanitizeInput($config_invoice_from_name); - - // Email to client - - $subject = "$company_name 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: $$invoice_amount
Due Date: $invoice_due


To view your invoice, please click here.


--
$company_name - Billing
$company_phone"; - - - $data = [ - [ - 'from' => $config_invoice_from_email, - 'from_name' => $config_invoice_from_name, - 'recipient' => $contact_email, - 'recipient_name' => $contact_name, - 'subject' => $subject, - 'body' => $body - ] - ]; - $mail = addToMailQueue($mysqli, $data); - - if ($mail === true) { - // Add send history - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Force Emailed Invoice!', history_invoice_id = $new_invoice_id"); - - // Update Invoice Status to Sent - mysqli_query($mysqli,"UPDATE invoices SET invoice_status = 'Sent', invoice_client_id = $client_id WHERE invoice_id = $new_invoice_id"); - - } else { - // Error reporting - mysqli_query($mysqli,"INSERT INTO notifications SET notification_type = 'Mail', notification = 'Failed to send email to $contact_email', notification_client_id = $client_id"); - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Mail', log_action = 'Error', log_description = 'Failed to send email to $contact_email regarding $subject. $mail', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); - } - - } //End Recurring Invoices Loop - - //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = '$session_name forced recurring invoice into an invoice', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $new_invoice_id"); } function addInvoiceToTicket( diff --git a/functions/recurring_invoice_functions.php b/functions/recurring_invoice_functions.php new file mode 100644 index 000000000..a57667f40 --- /dev/null +++ b/functions/recurring_invoice_functions.php @@ -0,0 +1,293 @@ + 0) { + $sql = mysqli_query($mysqli,"SELECT * FROM taxes WHERE tax_id = $tax_id"); + $row = mysqli_fetch_array($sql); + $tax_percent = floatval($row['tax_percent']); + $item_tax_amount = $item_subtotal * $tax_percent / 100; + } else { + $item_tax_amount = 0; + } + + $item_total = $item_subtotal + $item_tax_amount; + + //Update Recurring Items with new tax + mysqli_query($mysqli,"UPDATE invoice_items SET item_tax = $item_tax_amount, item_total = $item_total, item_tax_id = $tax_id, item_order = $item_order WHERE item_id = $item_id"); + + mysqli_query($mysqli,"INSERT INTO invoice_items SET item_name = '$item_name', item_description = '$item_description', item_quantity = $item_quantity, item_price = $item_price, item_subtotal = $item_subtotal, item_tax = $item_tax_amount, item_total = $item_total, item_tax_id = $tax_id, item_invoice_id = $new_invoice_id"); + } + + mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Invoice Generated from Recurring!', history_invoice_id = $new_invoice_id"); + + //Update Recurring Balances by tallying up recurring items also update recurring dates + $sql_recurring_total = mysqli_query($mysqli,"SELECT SUM(item_total) AS recurring_total FROM invoice_items WHERE item_recurring_id = $recurring_id"); + $row = mysqli_fetch_array($sql_recurring_total); + $new_recurring_amount = floatval($row['recurring_total']) - $recurring_discount_amount; + + mysqli_query($mysqli,"UPDATE recurring SET recurring_amount = $new_recurring_amount, recurring_last_sent = CURDATE(), recurring_next_date = DATE_ADD(CURDATE(), INTERVAL 1 $recurring_frequency) WHERE recurring_id = $recurring_id"); + + //Also update the newly created invoice with the new amounts + mysqli_query($mysqli,"UPDATE invoices SET invoice_amount = $new_recurring_amount WHERE invoice_id = $new_invoice_id"); + + if ($config_recurring_auto_send_invoice == 1) { + $sql = mysqli_query($mysqli,"SELECT * FROM invoices + LEFT JOIN clients ON invoice_client_id = client_id + LEFT JOIN contacts ON clients.client_id = contacts.contact_client_id AND contact_primary = 1 + WHERE invoice_id = $new_invoice_id" + ); + $row = mysqli_fetch_array($sql); + + $invoice_prefix = sanitizeInput($row['invoice_prefix']); + $invoice_number = intval($row['invoice_number']); + $invoice_scope = sanitizeInput($row['invoice_scope']); + $invoice_date = sanitizeInput($row['invoice_date']); + $invoice_due = sanitizeInput($row['invoice_due']); + $invoice_amount = floatval($row['invoice_amount']); + $invoice_url_key = sanitizeInput($row['invoice_url_key']); + $client_id = intval($row['client_id']); + $contact_name = sanitizeInput($row['contact_name']); + $contact_email = sanitizeInput($row['contact_email']); + + $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'])); + + // Sanitize Config Vars + $config_invoice_from_email = sanitizeInput($config_invoice_from_email); + $config_invoice_from_name = sanitizeInput($config_invoice_from_name); + + // Email to client + + $subject = "$company_name 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: $$invoice_amount
Due Date: $invoice_due


To view your invoice, please click here.


--
$company_name - Billing
$company_phone"; + + + $data = [ + [ + 'from' => $config_invoice_from_email, + 'from_name' => $config_invoice_from_name, + 'recipient' => $contact_email, + 'recipient_name' => $contact_name, + 'subject' => $subject, + 'body' => $body + ] + ]; + $mail = addToMailQueue($mysqli, $data); + + if ($mail === true) { + // Add send history + mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Force Emailed Invoice!', history_invoice_id = $new_invoice_id"); + + // Update Invoice Status to Sent + mysqli_query($mysqli,"UPDATE invoices SET invoice_status = 'Sent', invoice_client_id = $client_id WHERE invoice_id = $new_invoice_id"); + + } else { + // Error reporting + mysqli_query($mysqli,"INSERT INTO notifications SET notification_type = 'Mail', notification = 'Failed to send email to $contact_email', notification_client_id = $client_id"); + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Mail', log_action = 'Error', log_description = 'Failed to send email to $contact_email regarding $subject. $mail', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + } + + } //End Recurring Invoices Loop + + //Logging + mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = '$session_name forced recurring invoice into an invoice', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $new_invoice_id"); +} diff --git a/functions/ticket_functions.php b/functions/ticket_functions.php new file mode 100644 index 000000000..9ffa84e81 --- /dev/null +++ b/functions/ticket_functions.php @@ -0,0 +1,272 @@ +##- Please type your reply above this line -##

Hello $contact_name,

A ticket regarding \"$ticket_subject\" has been created for you.

--------------------------------
$ticket_details--------------------------------

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Status: Open
Portal: https://$config_base_url/portal/ticket.php?id=$ticket_id

--
$company_name - Support
$config_ticket_from_email
$company_phone"; + + // Email Ticket Contact + // Queue Mail + $data = []; + + $data[] = [ + 'from' => $config_ticket_from_email, + 'from_name' => $config_ticket_from_name, + 'recipient' => $contact_email, + 'recipient_name' => $contact_name, + 'subject' => $subject, + 'body' => $body + ]; + + // Also Email all the watchers + $sql_watchers = mysqli_query($mysqli, "SELECT watcher_email FROM ticket_watchers WHERE watcher_ticket_id = $ticket_id"); + $body .= "

----------------------------------------
DO NOT REPLY - YOU ARE RECEIVING THIS EMAIL BECAUSE YOU ARE A WATCHER"; + while ($row = mysqli_fetch_array($sql_watchers)) { + $watcher_email = sanitizeInput($row['watcher_email']); + + // Queue Mail + $data[] = [ + 'from' => $config_ticket_from_email, + 'from_name' => $config_ticket_from_name, + 'recipient' => $watcher_email, + 'recipient_name' => $watcher_email, + 'subject' => $subject, + 'body' => $body + ]; + } + addToMailQueue($mysqli, $data); + } + } + + // Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Create', log_description = '$session_name created ticket $config_ticket_prefix$ticket_number - $ticket_subject', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $ticket_id"); + + $return_data = [ + 'ticket_id' => $ticket_id, + 'ticket_number' => $ticket_number, + 'ticket_prefix' => $config_ticket_prefix, + 'ticket_subject' => $subject, + 'status' => 'success', + 'message' => "Ticket $config_ticket_prefix$ticket_number has been created" + ]; + + return $return_data; +} + +function readTicket( + $parameters +) { + + global $mysqli; + + $ticket_id = intval($parameters['ticket_id']); + + $sql = mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_id = $ticket_id"); + $row = mysqli_fetch_array($sql); + + return $row; +} + +function updateTicket( + $parameters +) { + + global $mysqli, $session_user_id, $session_name, $session_ip, $session_user_agent; + + $ticket_id = intval($parameters['ticket_id']); + $ticket_data = readTicket(['ticket_id' => $parameters['ticket_id']]); + + //if in parameters, set the new value, else keep the old value + $prefix = isset($parameters['ticket_prefix']) ? sanitizeInput($parameters['ticket_prefix']) : $ticket_data['ticket_prefix']; + $number = isset($parameters['ticket_number']) ? intval($parameters['ticket_number']) : $ticket_data['ticket_number']; + $subject = isset($parameters['ticket_subject']) ? sanitizeInput($parameters['ticket_subject']) : $ticket_data['ticket_subject']; + $priority = isset($parameters['ticket_priority']) ? sanitizeInput($parameters['ticket_priority']) : $ticket_data['ticket_priority']; + $details = isset($parameters['ticket_details']) ? mysqli_real_escape_string($mysqli, $parameters['ticket_details']) : $ticket_data['ticket_details']; + $billable = isset($parameters['ticket_billable']) ? intval($parameters['ticket_billable']) : $ticket_data['ticket_billable']; + $vendor_ticket_number = isset($parameters['ticket_vendor_ticket_number']) ? sanitizeInput($parameters['ticket_vendor_ticket_number']) : $ticket_data['ticket_vendor_ticket_number']; + $contact_id = isset($parameters['ticket_contact']) ? intval($parameters['ticket_contact']) : $ticket_data['ticket_contact_id']; + $vendor_id = isset($parameters['ticket_vendor']) ? intval($parameters['ticket_vendor']) : $ticket_data['ticket_vendor_id']; + $asset_id = isset($parameters['ticket_asset']) ? intval($parameters['ticket_asset']) : $ticket_data['ticket_asset_id']; + $ticket_number = isset($parameters['ticket_number']) ? intval($parameters['ticket_number']) : $ticket_data['ticket_number']; + $client_id = isset($parameters['ticket_client']) ? intval($parameters['ticket_client']) : $ticket_data['ticket_client_id']; + $source = isset($parameters['ticket_source']) ? sanitizeInput($parameters['ticket_source']) : $ticket_data['ticket_source']; + $category = isset($parameters['ticket_category']) ? sanitizeInput($parameters['ticket_category']) : $ticket_data['ticket_category']; + $status = isset($parameters['ticket_status']) ? sanitizeInput($parameters['ticket_status']) : $ticket_data['ticket_status']; + $schedule = isset($parameters['ticket_schedule']) ? sanitizeInput($parameters['ticket_schedule']) : $ticket_data['ticket_schedule']; + $on_site = isset($parameters['ticket_on_site']) ? sanitizeInput($parameters['ticket_on_site']) : $ticket_data['ticket_on_site']; + $feedback = isset($parameters['ticket_feedback']) ? mysqli_real_escape_string($mysqli, $parameters['ticket_feedback']) : $ticket_data['ticket_feedback']; + $assigned_to = isset($parameters['ticket_assigned_to']) ? intval($parameters['ticket_assigned_to']) : $ticket_data['ticket_assigned_to']; + $invoice_id = isset($parameters['ticket_invoice']) ? intval($parameters['ticket_invoice']) : $ticket_data['ticket_invoice_id']; + $location = isset($parameters['ticket_location']) ? sanitizeInput($parameters['ticket_location']) : $ticket_data['ticket_location_id']; + $closed_by = isset($parameters['ticket_closed_by']) ? intval($parameters['ticket_closed_by']) : $ticket_data['ticket_closed_by']; + + + mysqli_query($mysqli, "UPDATE tickets SET + ticket_prefix = '$prefix', + ticket_number = $number, + ticket_subject = '$subject', + ticket_priority = '$priority', + ticket_details = '$details', + ticket_billable = $billable, + ticket_vendor_ticket_number = '$vendor_ticket_number', + ticket_contact_id = $contact_id, + ticket_vendor_id = $vendor_id, + ticket_asset_id = $asset_id, + ticket_number = $ticket_number, + ticket_client_id = $client_id, + ticket_source = '$source', + ticket_category = '$category', + ticket_status = '$status', + ticket_schedule = '$schedule', + ticket_on_site = '$on_site', + ticket_feedback = '$feedback', + ticket_assigned_to = $assigned_to, + ticket_invoice_id = $invoice_id, + ticket_location_id = $location, + ticket_closed_by = $closed_by + WHERE ticket_id = $ticket_id"); + + // Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Modify', log_description = '$session_name modified ticket $ticket_number - $subject', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $ticket_id"); + + $return_data = [ + 'status' => 'success', + 'message' => 'Ticket $ticket_number has been updated. \n Updated:'. implode(", ", array_keys($parameters)), + 'ticket' => readTicket(['ticket_id' => $asset_id]) + ]; + + return $return_data; +} + +function deleteTicket( + $parameters +) { + + global $mysqli, $session_user_id, $session_name, $session_ip, $session_user_agent; + + $ticket_id = intval($parameters['ticket_id']); + + // Get Ticket and Client ID for logging and alert message + $sql = mysqli_query($mysqli, "SELECT ticket_prefix, ticket_number, ticket_subject, ticket_status, ticket_client_id FROM tickets WHERE ticket_id = $ticket_id"); + $row = mysqli_fetch_array($sql); + $ticket_prefix = sanitizeInput($row['ticket_prefix']); + $ticket_number = sanitizeInput($row['ticket_number']); + $ticket_subject = sanitizeInput($row['ticket_subject']); + $ticket_status = sanitizeInput($row['ticket_status']); + $client_id = intval($row['ticket_client_id']); + + if ($ticket_status !== 'Closed') { + mysqli_query($mysqli, "DELETE FROM tickets WHERE ticket_id = $ticket_id"); + + // Delete all ticket replies + mysqli_query($mysqli, "DELETE FROM ticket_replies WHERE ticket_reply_ticket_id = $ticket_id"); + + // Delete all ticket views + mysqli_query($mysqli, "DELETE FROM ticket_views WHERE view_ticket_id = $ticket_id"); + + // Logging + mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Delete', log_description = '$session_name deleted ticket $ticket_prefix$ticket_number - $ticket_subject along with all replies', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $ticket_id"); + + $return_data = [ + 'status' => 'success', + 'message' => "Ticket $ticket_prefix$ticket_number has been deleted" + ]; + } else { + $return_data = [ + 'status' => 'error', + 'message' => "Ticket $ticket_prefix$ticket_number is closed and cannot be deleted" + ]; + } + + return $return_data; +} \ No newline at end of file diff --git a/post/invoice.php b/post/invoice.php index 2ffb969bc..0cc889775 100644 --- a/post/invoice.php +++ b/post/invoice.php @@ -8,12 +8,12 @@ require_once 'post/invoice_model.php'; - $client = intval($_POST['client']); - $due = sanitizeInput($_POST['due']); - $date = sanitizeInput($_POST['date']); - $category = intval($_POST['category']); + $parameters['invoice_client'] = intval($_POST['client']); + $parameters['due'] = sanitizeInput($_POST['due']); + $parameters['date'] = sanitizeInput($_POST['date']); + $parameters['category'] = intval($_POST['category']); - createInvoice($client, $due, $date, $category); + createInvoice($parameters); referWithAlert("Invoice added", "success"); } @@ -24,19 +24,7 @@ $invoice_id = intval($_POST['invoice_id']); $due = sanitizeInput($_POST['due']); - updateInvoice( - $invoice_id, - $due, - $status, - $prefix, - $number, - $scope, - $date, - $currency_code, - $category_id, - $url_key, - $client_id - ); + updateInvoice($parameters); referWithAlert("Invoice edited", "success"); } diff --git a/post/ticket.php b/post/ticket.php index 1176e3cf4..fd64b1453 100644 --- a/post/ticket.php +++ b/post/ticket.php @@ -8,134 +8,23 @@ validateTechRole(); - $client_id = intval($_POST['client']); - $assigned_to = intval($_POST['assigned_to']); - if ($assigned_to == 0) { - $ticket_status = 'New'; - } else { - $ticket_status = 'Open'; - } - $contact = intval($_POST['contact']); - $subject = sanitizeInput($_POST['subject']); - $priority = sanitizeInput($_POST['priority']); - $details = mysqli_real_escape_string($mysqli, $_POST['details']); - $vendor_ticket_number = sanitizeInput($_POST['vendor_ticket_number']); - $vendor_id = intval($_POST['vendor']); - $asset_id = intval($_POST['asset']); - $use_primary_contact = intval($_POST['use_primary_contact']); - - - // Add the primary contact as the ticket contact if "Use primary contact" is checked - if ($use_primary_contact == 1) { - $sql = mysqli_query($mysqli, "SELECT contact_id FROM contacts WHERE contact_client_id = $client_id AND contact_primary = 1"); - $row = mysqli_fetch_array($sql); - $contact = intval($row['contact_id']); - } - - if (!isset($_POST['billable'])) { - $billable = 1; - } else { - $billable = intval($_POST['billable']); - } - - //Get the next Ticket Number and add 1 for the new ticket number - $ticket_number = $config_ticket_next_number; - $new_config_ticket_next_number = $config_ticket_next_number + 1; - - // Sanitize Config Vars from get_settings.php and Session Vars from check_login.php - $config_ticket_prefix = sanitizeInput($config_ticket_prefix); - $config_ticket_from_name = sanitizeInput($config_ticket_from_name); - $config_ticket_from_email = sanitizeInput($config_ticket_from_email); - $config_base_url = sanitizeInput($config_base_url); - - mysqli_query($mysqli, "UPDATE settings SET config_ticket_next_number = $new_config_ticket_next_number WHERE company_id = 1"); - - 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_billable = '$billable', ticket_status = '$ticket_status', ticket_vendor_ticket_number = '$vendor_ticket_number', ticket_vendor_id = $vendor_id, ticket_asset_id = $asset_id, ticket_created_by = $session_user_id, ticket_assigned_to = $assigned_to, ticket_contact_id = $contact, ticket_client_id = $client_id, ticket_invoice_id = 0"); - - $ticket_id = mysqli_insert_id($mysqli); - - // Add Watchers - if (!empty($_POST['watchers'])) { - foreach ($_POST['watchers'] as $watcher) { - $watcher_email = sanitizeInput($watcher); - mysqli_query($mysqli, "INSERT INTO ticket_watchers SET watcher_email = '$watcher_email', watcher_ticket_id = $ticket_id"); - } - } - - // E-mail client - if (!empty($config_smtp_host) && $config_ticket_client_general_notifications == 1) { - - // Get contact/ticket details - $sql = mysqli_query($mysqli, "SELECT contact_name, contact_email, ticket_prefix, ticket_number, ticket_category, ticket_subject, ticket_details, ticket_priority, ticket_status, ticket_created_by, ticket_assigned_to, ticket_client_id FROM tickets - LEFT JOIN clients ON ticket_client_id = client_id - LEFT JOIN contacts ON ticket_contact_id = contact_id - WHERE ticket_id = $ticket_id"); - $row = mysqli_fetch_array($sql); - - $contact_name = sanitizeInput($row['contact_name']); - $contact_email = sanitizeInput($row['contact_email']); - $ticket_prefix = sanitizeInput($row['ticket_prefix']); - $ticket_number = intval($row['ticket_number']); - $ticket_category = sanitizeInput($row['ticket_category']); - $ticket_subject = sanitizeInput($row['ticket_subject']); - $ticket_details = mysqli_escape_string($mysqli, $row['ticket_details']); - $ticket_priority = sanitizeInput($row['ticket_priority']); - $ticket_status = sanitizeInput($row['ticket_status']); - $client_id = intval($row['ticket_client_id']); - $ticket_created_by = intval($row['ticket_created_by']); - $ticket_assigned_to = intval($row['ticket_assigned_to']); - - // Get Company Phone Number - $sql = mysqli_query($mysqli, "SELECT company_name, company_phone FROM companies WHERE company_id = 1"); - $row = mysqli_fetch_array($sql); - $company_name = sanitizeInput($row['company_name']); - $company_phone = sanitizeInput(formatPhoneNumber($row['company_phone'])); - - // Verify contact email is valid - if (filter_var($contact_email, FILTER_VALIDATE_EMAIL)) { - - $subject = "Ticket created [$ticket_prefix$ticket_number] - $ticket_subject"; - $body = "##- Please type your reply above this line -##

Hello $contact_name,

A ticket regarding \"$ticket_subject\" has been created for you.

--------------------------------
$ticket_details--------------------------------

Ticket: $ticket_prefix$ticket_number
Subject: $ticket_subject
Status: Open
Portal: https://$config_base_url/portal/ticket.php?id=$ticket_id

--
$company_name - Support
$config_ticket_from_email
$company_phone"; - - // Email Ticket Contact - // Queue Mail - $data = []; - - $data[] = [ - 'from' => $config_ticket_from_email, - 'from_name' => $config_ticket_from_name, - 'recipient' => $contact_email, - 'recipient_name' => $contact_name, - 'subject' => $subject, - 'body' => $body - ]; - - // Also Email all the watchers - $sql_watchers = mysqli_query($mysqli, "SELECT watcher_email FROM ticket_watchers WHERE watcher_ticket_id = $ticket_id"); - $body .= "

----------------------------------------
DO NOT REPLY - YOU ARE RECEIVING THIS EMAIL BECAUSE YOU ARE A WATCHER"; - while ($row = mysqli_fetch_array($sql_watchers)) { - $watcher_email = sanitizeInput($row['watcher_email']); - - // Queue Mail - $data[] = [ - 'from' => $config_ticket_from_email, - 'from_name' => $config_ticket_from_name, - 'recipient' => $watcher_email, - 'recipient_name' => $watcher_email, - 'subject' => $subject, - 'body' => $body - ]; - } - addToMailQueue($mysqli, $data); - } - } - - // Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Create', log_description = '$session_name created ticket $config_ticket_prefix$ticket_number - $ticket_subject', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $ticket_id"); - - $_SESSION['alert_message'] = "You created Ticket $ticket_subject $config_ticket_prefix$ticket_number"; + $parameters = [ + 'ticket_client_id' => intval($_POST['client_id']), + 'ticket_assigned_to' => intval($_POST['assigned_to']), + 'ticket_contact' => intval($_POST['contact']), + 'ticket_subject' => sanitizeInput($_POST['subject']), + 'ticket_priority' => sanitizeInput($_POST['priority']), + 'ticket_details' => mysqli_real_escape_string($mysqli, $_POST['details']), + 'ticket_vendor_ticket_number' => sanitizeInput($_POST['vendor_ticket_number']), + 'ticket_vendor' => intval($_POST['vendor_id']), + 'ticket_asset' => intval($_POST['asset_id']), + 'ticket_billable' => intval($_POST['billable']), + 'ticket_use_primary_contact' => intval($_POST['use_primary_contact']), + ]; - header("Location: ticket.php?ticket_id=" . $ticket_id); + $return_data = createTicket($parameters); + $ticket_id = $return_data['ticket_id']; + referWithAlert($return_data['message'], $return_data['status'], "tickets.php?ticket_id=$ticket_id"); } if (isset($_POST['edit_ticket'])) { @@ -152,54 +41,48 @@ $vendor_id = intval($_POST['vendor']); $asset_id = intval($_POST['asset']); - $client_id = intval($_POST['client_id']); - $ticket_number = intval($_POST['ticket_number']); - - mysqli_query($mysqli, "UPDATE tickets SET ticket_subject = '$subject', ticket_priority = '$priority', ticket_billable = $billable, ticket_details = '$details', ticket_vendor_ticket_number = '$vendor_ticket_number', ticket_contact_id = $contact_id, ticket_vendor_id = $vendor_id, ticket_asset_id = $asset_id WHERE ticket_id = $ticket_id"); - - //Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Modify', log_description = '$session_name modified ticket $ticket_number - $subject', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $ticket_id"); - - $_SESSION['alert_message'] = "Ticket $ticket_number updated"; + $parameters = [ + 'ticket_id' => intval($_POST['ticket_id']), + 'ticket_client_id' => intval($_POST['client_id']), + 'ticket_assigned_to' => intval($_POST['assigned_to']), + 'ticket_contact' => intval($_POST['contact']), + 'ticket_subject' => sanitizeInput($_POST['subject']), + 'ticket_priority' => sanitizeInput($_POST['priority']), + 'ticket_details' => mysqli_real_escape_string($mysqli, $_POST['details']), + 'ticket_vendor_ticket_number' => sanitizeInput($_POST['vendor_ticket_number']), + 'ticket_vendor' => intval($_POST['vendor_id']), + 'ticket_asset' => intval($_POST['asset_id']), + 'ticket_number' => intval($_POST['ticket_number']), + ]; - header("Location: " . $_SERVER["HTTP_REFERER"]); + $return_data = updateTicket($parameters); + referWithAlert($return_data['message'], $return_data['status']); } if (isset($_POST['edit_ticket_priority'])) { validateTechRole(); - $ticket_id = intval($_POST['ticket_id']); - $priority = sanitizeInput($_POST['priority']); - $client_id = intval($_POST['client_id']); - - mysqli_query($mysqli, "UPDATE tickets SET ticket_priority = '$priority' WHERE ticket_id = $ticket_id"); - - //Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Modify', log_description = '$session_name edited ticket priority', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $ticket_id"); - - $_SESSION['alert_message'] = "Ticket priority updated"; + $parameters = [ + 'ticket_id' => intval($_POST['ticket_id']), + 'ticket_priority' => sanitizeInput($_POST['priority']) + ]; - header("Location: " . $_SERVER["HTTP_REFERER"]); + $return_data = updateTicket($parameters); + referWithAlert($return_data['message'], $return_data['status']); } if (isset($_POST['edit_ticket_contact'])) { validateTechRole(); - $ticket_id = intval($_POST['ticket_id']); - $contact_id = intval($_POST['contact']); - $client_id = intval($_POST['client_id']); - $ticket_number = sanitizeInput($_POST['ticket_number']); - - mysqli_query($mysqli, "UPDATE tickets SET ticket_contact_id = $contact_id WHERE ticket_id = $ticket_id"); - - //Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Modify', log_description = '$session_name changed contact for ticket $ticket_number', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $ticket_id"); - - $_SESSION['alert_message'] = "Ticket $ticket_number contact updated"; + $parameters = [ + 'ticket_id' => intval($_POST['ticket_id']), + 'ticket_contact' => intval($_POST['contact']) + ]; - header("Location: " . $_SERVER["HTTP_REFERER"]); + $return_data = updateTicket($parameters); + referWithAlert($return_data['message'], $return_data['status']); } if (isset($_POST['add_ticket_watcher'])) { @@ -268,19 +151,13 @@ validateTechRole(); - $ticket_id = intval($_POST['ticket_id']); - $asset_id = intval($_POST['asset']); - $client_id = intval($_POST['client_id']); - $ticket_number = sanitizeInput($_POST['ticket_number']); - - mysqli_query($mysqli, "UPDATE tickets SET ticket_asset_id = $asset_id WHERE ticket_id = $ticket_id"); - - //Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Edit', log_description = '$session_name edited asset for ticket $ticket_number', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $ticket_id"); - - $_SESSION['alert_message'] = "Ticket $ticket_number asset updated"; + $parameters = [ + 'ticket_id' => intval($_POST['ticket_id']), + 'ticket_asset' => intval($_POST['asset_id']) + ]; - header("Location: " . $_SERVER["HTTP_REFERER"]); + $return_data = updateTicket($parameters); + referWithAlert($return_data['message'], $return_data['status']); } if (isset($_POST['edit_ticket_vendor'])) { @@ -419,37 +296,10 @@ } if (isset($_GET['delete_ticket'])) { - validateAdminRole(); - $ticket_id = intval($_GET['delete_ticket']); - - // Get Ticket and Client ID for logging and alert message - $sql = mysqli_query($mysqli, "SELECT ticket_prefix, ticket_number, ticket_subject, ticket_status, ticket_client_id FROM tickets WHERE ticket_id = $ticket_id"); - $row = mysqli_fetch_array($sql); - $ticket_prefix = sanitizeInput($row['ticket_prefix']); - $ticket_number = sanitizeInput($row['ticket_number']); - $ticket_subject = sanitizeInput($row['ticket_subject']); - $ticket_status = sanitizeInput($row['ticket_status']); - $client_id = intval($row['ticket_client_id']); - - if ($ticket_status !== 'Closed') { - mysqli_query($mysqli, "DELETE FROM tickets WHERE ticket_id = $ticket_id"); - - // Delete all ticket replies - mysqli_query($mysqli, "DELETE FROM ticket_replies WHERE ticket_reply_ticket_id = $ticket_id"); - - // Delete all ticket views - mysqli_query($mysqli, "DELETE FROM ticket_views WHERE view_ticket_id = $ticket_id"); - - // Logging - mysqli_query($mysqli, "INSERT INTO logs SET log_type = 'Ticket', log_action = 'Delete', log_description = '$session_name deleted ticket $ticket_prefix$ticket_number - $ticket_subject along with all replies', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_client_id = $client_id, log_user_id = $session_user_id, log_entity_id = $ticket_id"); - - $_SESSION['alert_type'] = "error"; - $_SESSION['alert_message'] = "Ticket $ticket_prefix$ticket_number along with all replies deleted"; - - header("Location: tickets.php"); - } + $return_data = deleteTicket(['ticket_id' => $ticket_id]); + referWithAlert($return_data['message'], $return_data['alert_type'], "tickets.php"); } if (isset($_POST['bulk_assign_ticket'])) { From ba5bdfce3b41050e1a515910f332958a29628138 Mon Sep 17 00:00:00 2001 From: o-psi Date: Tue, 19 Mar 2024 23:02:35 +0000 Subject: [PATCH 31/32] Additional API, Fix get setting value --- api/v2/api.php | 30 +++--- api/v2/objects.php | 24 +---- client_statement.php | 2 +- functions/api_functions.php | 30 ++++-- functions/asset_functions.php | 19 ++-- functions/client_functions.php | 30 ++---- functions/invoice_functions.php | 171 ++++++++++++++++++++++++++------ functions/misc_functions.php | 3 +- functions/ticket_functions.php | 24 ++++- post/invoice.php | 2 +- report_collections.php | 2 +- report_tax_summary.php | 2 +- 12 files changed, 221 insertions(+), 118 deletions(-) diff --git a/api/v2/api.php b/api/v2/api.php index a8dffb0b2..67820b621 100644 --- a/api/v2/api.php +++ b/api/v2/api.php @@ -55,11 +55,6 @@ } else { // Sanitize the action $action = strtolower(sanitizeInput($_POST['action'])); - // Check if the action is read - if ($action == 'read') { - // Read requests should use the GET method - echo json_encode(['WARN' => 'Invalid action in request. Use GET method for read requests']); - } } } elseif ($_SERVER['REQUEST_METHOD'] == 'GET') { // Sanitize the object @@ -74,11 +69,6 @@ } else { // Sanitize the action $action = strtolower(sanitizeInput($_GET['action'])); - // Check if the action is not read - if ($action != 'read') { - echo json_encode(['WARN' => 'Invalid action in request. Use POST method for create, update, and delete requests. Action '. $action . ' is not valid.']); - exit; - } } } else { // Invalid request method @@ -110,9 +100,15 @@ // Sanitize the parameters $sanitized_parameters = []; -foreach ($parameters as $key => $value) { - $sanitized_parameters[sanitizeInput($key)] = sanitizeInput($value); +if (is_array($parameters)) { + foreach ($parameters as $key => $value) { + $sanitized_parameters[sanitizeInput($key)] = sanitizeInput($value); + } +} else{ + echo json_encode(['WARN' => 'Invalid parameters in request. Parameters must be included as an associative array. is not an array.']); + exit; } + // Replace the parameters with the sanitized parameters $parameters = $sanitized_parameters; @@ -124,7 +120,7 @@ } // Check if the object is valid if (!in_array($object, $valid_objects)) { - echo json_encode(['error' => 'Invalid object in request. ' . $object . ' is not an object to be manipulated via the API. \n\n Valid objects are: ' . implode(', \n', $valid_objects) . '.']); + echo json_encode(['error' => 'Invalid object in request. ' . $object . ' is not an object to be manipulated via the API. Valid objects are: ' . implode(',', $valid_objects) . '.']); exit; } //Uppercase every first letter of the object @@ -138,7 +134,7 @@ // Check if the function exists if (!function_exists($function)) { - echo json_encode(['error' => 'Invalid function in request. This is probably a bug. Please report this to the developer. ' . $function . ' does not exist.']); + echo json_encode(['error' => 'Invalid function in request. This is probably a bug. Please report this to the developer. ' . $function . '() does not exist.']); exit; } // Call the function @@ -147,7 +143,7 @@ } // Catch any exceptions catch (Exception $e) { - echo json_encode(['error' => 'Invalid function result. This is probably a bug. Please report this to the developer. ' . $e->getMessage()]); + echo json_encode(['error' => 'Invalid function result. Function returned exception. This is probably a bug. Please report this to the developer. ' . $e->getMessage()]); exit; } @@ -156,9 +152,9 @@ if (is_array($function_result)) { echo json_encode($function_result); } else { - echo json_encode(['error' => 'Invalid function result. This is probably a bug. Please report this to the developer.']); + echo json_encode(['error' => 'Invalid function result. This is probably a bug. Please report this to the developer. ERR: ' . json_encode($function_result) . ' is not an array.']); } } else { - echo json_encode(['error' => 'Invalid function result. This is probably a bug. Please report this to the developer.']); + echo json_encode(['error' => 'Invalid function result. This is probably a bug. Please report this to the developer. ERR: ' . json_encode($function_result) . ' is not a valid result.']); } exit; diff --git a/api/v2/objects.php b/api/v2/objects.php index 204973f4b..2c6589bf0 100644 --- a/api/v2/objects.php +++ b/api/v2/objects.php @@ -8,11 +8,10 @@ $valid_objects = [ 'asset', 'client', - 'clients', 'invoice', 'ticket', 'location' - ]; +]; /* ------------------------ @@ -143,27 +142,6 @@ message = error message if status is error asset_id = asset_id(int) -clients: - Read: - No parameters - Returns: - status = success or error - message = error message if status is error - client_id = [ - client_id(int) - client_name(str) - client_type(str) - client_website(str) - client_website(str) - client_referral(str) - client_rate(int) - client_currency_code(str) - client_net_terms(int) - client_tax_id_number(int) - client_lead(tinyint) - cleint_notes(str) - ] - client: Create: Parameters: diff --git a/client_statement.php b/client_statement.php index 94c4ef91f..96e4cdf02 100644 --- a/client_statement.php +++ b/client_statement.php @@ -45,7 +45,7 @@ $result_client_unpaid_invoices = mysqli_query($mysqli, $sql_client_unpaid_invoices); - $currency_code = getSettingValue($mysqli, "company_currency"); + $currency_code = getSettingValue("company_currency"); ?> diff --git a/functions/api_functions.php b/functions/api_functions.php index 913cac57e..0d9053617 100644 --- a/functions/api_functions.php +++ b/functions/api_functions.php @@ -75,24 +75,36 @@ function tryAPIKey($api_key_secret) { } function getAPIWhereClause( - $var, - $var_id, + $obj, + $obj_id, $api_client_id ){ + + //if object is not integer, exit + if (!is_numeric($obj_id)) { + echo json_encode(['status' => 'error', 'message' => 'Invalid Object ID']); + exit; + } + $where_clause = ""; // If asset_id is all, check if client_id is set - if ($var_id == 'all') { - if ($api_client_id != 'all') { + + + if ($obj_id == '0') { + if ($api_client_id != '0') { // If client_id is set, get all assets for that client - $where_clause = "WHERE " . $var . "_client_id = $api_client_id"; - } // If client_id is not set, get all assets + $where_clause = "WHERE " . $obj . "_client_id = $api_client_id"; + }else { // If client_id is not set, get all assets + $where_clause = ""; + } + } else { - if ($api_client_id != 'all') { + if ($api_client_id != '0') { // If client_id is set, get the asset only if the client matches - $where_clause = "WHERE " . $var . "_id = $var_id AND asset_client_id = $api_client_id"; + $where_clause = "WHERE " . $obj . "_id = $obj_id AND asset_client_id = $api_client_id"; } else { // If client_id is not set, get the asset - $where_clause = "WHERE " . $var . "_id = $var_id"; + $where_clause = "WHERE " . $obj . "_id = $obj_id"; } } return $where_clause; diff --git a/functions/asset_functions.php b/functions/asset_functions.php index 5c98a88ac..dad35bcce 100644 --- a/functions/asset_functions.php +++ b/functions/asset_functions.php @@ -29,17 +29,18 @@ function createAsset( $install_date = $parameters['asset_install_date']??'NULL'; $notes = $parameters['asset_notes']??''; + + $return_message = ""; if (empty($name)) { - return ['status' => 'error', 'message' => 'Asset Name is required']; + $return_message .= "Asset Name is required. "; } if (empty($client_id)) { - return ['status' => 'error', 'message' => 'Client ID is required']; + $return_message .= "Client ID is required. "; } - if (empty($type)) { - return ['status' => 'error', 'message' => 'Asset Type is required']; + $return_message .= "Asset Type is required. "; }elseif (!in_array($type, ['Server', 'Desktop', 'Laptop', 'Tablet', 'Phone', 'Printer', 'Switch', 'Router', 'Firewall', 'Access Point', 'Other'])) { - return ['status' => 'error', 'message' => 'Invalid Asset Type']; + $return_message .= "Invalid Asset Type. "; } @@ -91,7 +92,7 @@ function readAsset( global $mysqli; // Check if there is an API Key Client ID parameter, if so, use it. Otherwise, default to 'all' - $api_client_id = isset($parameters['api_key_client_id']) ? sanitizeInput($parameters['api_key_client_id']) : 'all'; + $api_client_id = isset($parameters['api_key_client_id']) ? sanitizeInput($parameters['api_key_client_id']) : 0; // Get the where clause for the query $where_clause = getAPIWhereClause("asset", $asset_id, $api_client_id); @@ -101,7 +102,11 @@ function readAsset( $assets = []; while ($row = mysqli_fetch_assoc($result)) { - $assets[$row['asset_id']] = $row; + $assets[] = $row; + } + + if (empty($assets)) { + return ['status' => 'error', 'message' => 'No assets found']; } return $assets; diff --git a/functions/client_functions.php b/functions/client_functions.php index 113181b69..365decae7 100644 --- a/functions/client_functions.php +++ b/functions/client_functions.php @@ -138,34 +138,24 @@ function createClient( function readClient( $parameters ) { - $client_id = $parameters['client_id']; + $client_id = sanitizeInput($parameters['client_id']); global $mysqli; - if (isset($parameters['api_key_client_id'])) { - $api_client_id = $parameters['api_key_client_id']; - $where_clause = getAPIWhereClause('client', $client_id, $api_client_id); - } else { - $where_clause = "WHERE client_id = $client_id"; - } + // Check if there is an API Key Client ID parameter, if so, use it. Otherwise, default to 'all' + $api_client_id = isset($parameters['api_key_client_id']) ? sanitizeInput($parameters['api_key_client_id']) : 0; + // Get the where clause for the query + $where_clause = getAPIWhereClause("client", $client_id, $api_client_id); $query = "SELECT * FROM clients $where_clause"; + $result = mysqli_query($mysqli, $query); - $result = mysqli_fetch_assoc(mysqli_query($mysqli, $query)); - - return $result; -} - -function readClients( -) -{ - global $mysqli; - - $sql = mysqli_query($mysqli, "SELECT * FROM clients WHERE client_archived_at IS NULL AND client_lead = 0 ORDER BY client_accessed_at ASC"); $clients = []; - while ($row = mysqli_fetch_array($sql)) { - $clients[intval($row['client_id'])] = $row; + + while ($row = mysqli_fetch_assoc($result)) { + $clients[$row['client_id']] = $row; } + return $clients; } diff --git a/functions/invoice_functions.php b/functions/invoice_functions.php index fc8afd899..3814c5e36 100644 --- a/functions/invoice_functions.php +++ b/functions/invoice_functions.php @@ -7,13 +7,45 @@ function createInvoice( $parameters ) { - $client = intval($parameters['client']); - $scope = sanitizeInput($parameters['scope']); - $date = date($parameters['date']); - $category = intval($parameters['category']); + $client = intval($parameters['invoice_client_id']); + $scope = sanitizeInput($parameters['invoice_scope']); + $category = intval($parameters['invoice_category']); + + $return_message = ""; + + $dateObject = DateTime::createFromFormat('Y-m-d', $parameters['invoice_date']); + if (!$dateObject) { + $return_message .= "Invalid date format. "; + } else { + $date = $dateObject->format('Y-m-d'); + } + + // If any parameters are empty, error. + if (empty($client)) { + $return_message .= "Client ID is required. "; + } + if (empty($scope)) { + $return_message .= "Scope is required. "; + } + if (empty($category)) { + $return_message .= "Category is required. "; + } + if ($return_message != "") { + return ['status' => 'error', 'message' => $return_message]; + } // Access global variables - global $mysqli, $session_company_currency, $session_user_id, $session_ip, $session_user_agent, $config_invoice_prefix, $config_invoice_next_number; + global $mysqli, $session_user_id, $session_ip, $session_user_agent; + + if (!empty($parameters['api_key_name'])) { + $session_user_id = 0; + $session_ip = $parameters['api_key_ip']??''; + $session_user_agent = $parameters['api_key_name']??''; + } + + $config_invoice_next_number = getSettingValue('config_invoice_next_number'); + $config_invoice_prefix = getSettingValue('config_invoice_prefix'); + $config_currency_code = getSettingValue('company_currency'); //Get Net Terms $sql = mysqli_query($mysqli,"SELECT client_net_terms FROM clients WHERE client_id = $client"); @@ -28,29 +60,59 @@ function createInvoice( //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 = $invoice_number, invoice_scope = '$scope', invoice_date = '$date', invoice_due = DATE_ADD('$date', INTERVAL $client_net_terms day), invoice_currency_code = '$session_company_currency', invoice_category_id = $category, invoice_status = 'Draft', invoice_url_key = '$url_key', invoice_client_id = $client"); - $invoice_id = mysqli_insert_id($mysqli); + //Insert the new invoice + $sql_query = "INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $invoice_number, invoice_scope = '$scope', invoice_date = '$date', invoice_due = DATE_ADD('$date', INTERVAL $client_net_terms day), invoice_currency_code = '$config_currency_code', invoice_category_id = $category, invoice_status = 'Draft', invoice_url_key = '$url_key', invoice_client_id = $client"; + mysqli_query($mysqli, $sql_query); + + // Check for SQL errors + $inv_sql_error = mysqli_error($mysqli); + error_log("SQL Error: " . $inv_sql_error . " in query:" . $sql_query); - mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Draft', history_description = 'INVOICE added!', history_invoice_id = $invoice_id"); + // Get the invoice ID + $invoice_id = mysqli_insert_id($mysqli); + + // Insert the history + $sql_query = "INSERT INTO history SET history_status = 'Draft', history_description = 'INVOICE added!', history_invoice_id = $invoice_id"; + mysqli_query($mysqli, $sql_query); + $hist_sql_error = mysqli_error($mysqli); + error_log("SQL Error: " . $hist_sql_error . " in query:" . $sql_query); //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = '$config_invoice_prefix$invoice_number', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + $sql_query = "INSERT INTO logs SET log_type = 'Invoice', log_action = 'Create', log_description = '$config_invoice_prefix$invoice_number', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"; + mysqli_query($mysqli, $sql_query); + $log_sql_error = mysqli_error($mysqli); + error_log("SQL Error: " . $log_sql_error . " in query:" . $sql_query); + + + $return_data = [ + 'status' => 'success', + 'message' => "Invoice $invoice_number has been created", + 'invoice' => readInvoice(['invoice_id' => $invoice_id]) + ]; + return $return_data; } function readInvoice( $parameters ) { + $invoice_id = sanitizeInput($parameters['invoice_id']); - $invoice_id = intval($parameters['invoice_id']); - - // Access global variables global $mysqli; - $sql = mysqli_query($mysqli,"SELECT * FROM invoices WHERE invoice_id = $invoice_id LIMIT 1"); - $row = mysqli_fetch_assoc($sql); + // Check if there is an API Key Client ID parameter, if so, use it. Otherwise, default to 'all' + $api_client_id = isset($parameters['api_key_client_id']) ? sanitizeInput($parameters['api_key_client_id']) : 0; + // Get the where clause for the query + $where_clause = getAPIWhereClause('invoice',$invoice_id, $api_client_id); + + $sql = mysqli_query($mysqli,"SELECT * FROM invoices $where_clause"); + $invoices = []; - return $row; + while($row = mysqli_fetch_assoc($sql)) { + $invoices[$row['invoice_id']] = $row; + } + + return $invoices; } @@ -58,28 +120,37 @@ function updateInvoice( $parameters ) { $invoice_id = $parameters['invoice_id']; - $invoice_data = readInvoice(['invoice_id' => $invoice_id]); - $invoice_due = $parameters['invoice_due']??$invoice_data['invoice_due']; - $invoice_prefix = $parameters['invoice_prefix']??$invoice_data['invoice_prefix']; - $invoice_number = $parameters['invoice_number']??$invoice_data['invoice_number']; - $invoice_scope = $parameters['invoice_scope']??$invoice_data['invoice_scope']; - $invoice_currency_code = $parameters['invoice_currency_code']??$invoice_data['invoice_currency_code']; - $invoice_category_id = $parameters['invoice_category_id']??$invoice_data['invoice_category_id']; - $invoice_url_key = $parameters['invoice_url_key']??$invoice_data['invoice_url_key']; - $invoice_client_id = $parameters['invoice_client_id']??$invoice_data['invoice_client_id']; + if (!empty($invoice_id)) { + $invoice_id = intval($invoice_id); + } else { + return ['status' => 'error', 'message' => 'Invoice ID is required']; + } + + $invoice_data = readInvoice(['invoice_id' => $invoice_id])[$invoice_id]; + + + //if in parameters, set the new value, else keep the old value + $invoice_due = isset($parameters['invoice_due']) ? $parameters['invoice_due'] : $invoice_data['invoice_due']; + $invoice_prefix = isset($parameters['invoice_prefix']) ? $parameters['invoice_prefix'] : $invoice_data['invoice_prefix']; + $invoice_number = isset($parameters['invoice_number']) ? $parameters['invoice_number'] : $invoice_data['invoice_number']; + $invoice_scope = isset($parameters['invoice_scope']) ? $parameters['invoice_scope'] : $invoice_data['invoice_scope']; + $invoice_currency_code = isset($parameters['invoice_currency_code']) ? $parameters['invoice_currency_code'] : $invoice_data['invoice_currency_code']; + $invoice_category_id = isset($parameters['invoice_category_id']) ? $parameters['invoice_category_id'] : $invoice_data['invoice_category_id']; + $invoice_url_key = isset($parameters['invoice_url_key']) ? $parameters['invoice_url_key'] : $invoice_data['invoice_url_key']; + $invoice_client_id = isset($parameters['invoice_client_id']) ? $parameters['invoice_client_id'] : $invoice_data['invoice_client_id']; + // if invoice status is a parameter, use update invoice status function for that part // else, use the current status if (isset($parameters['invoice_status']) || isset($parameters['invoice_archived'])) { - $invoice_status = $parameters['invoice_status']; $invoice_archived = $parameters['invoice_archived']; - - if ($invoice_archived == 1) { + if ($invoice_archived) { $invoice_status = 'Archived'; } + // update the invoice status updateInvoiceStatus($invoice_id, $invoice_status); // check if thats the only parameter, if so, return @@ -88,22 +159,58 @@ function updateInvoice( } else { $invoice_status = $invoice_data['invoice_status']; } // if null, output 'NULL' for SQL, else output the data - $invoice_date = $parameters['invoice_date']??($invoice_data['invoice_date'] == NULL ? 'NULL' : $invoice_data['invoice_date']); + if (isset($parameters['invoice_date'])){ + $invoice_date = $parameters['invoice_date']; + } else { + $invoice_date = $invoice_data['invoice_date']; + } + + if (empty($invoice_date)) { + $invoice_date == 'null'; + } // Access global variables global $mysqli, $session_user_id, $session_ip, $session_user_agent; - mysqli_query($mysqli,"UPDATE invoices SET invoice_prefix = '$invoice_prefix', invoice_number = $invoice_number, invoice_scope = '$invoice_scope', invoice_date = '$invoice_date', invoice_due = '$invoice_due', invoice_currency_code = '$invoice_currency_code', invoice_category_id = $invoice_category_id, invoice_url_key = '$invoice_url_key', invoice_client_id = $invoice_client_id WHERE invoice_id = $invoice_id"); + if (!empty($parameters['api_key_name'])) { + $session_user_id = 0; + $session_ip = $parameters['api_key_ip']??''; + $session_user_agent = $parameters['api_key_name']??''; + } - mysqli_query($mysqli,"INSERT INTO history SET history_status = '$invoice_status', history_description = 'INVOICE updated!', history_invoice_id = $invoice_id"); + mysqli_query($mysqli,"UPDATE invoices SET + invoice_prefix = '$invoice_prefix', + invoice_number = $invoice_number, + invoice_scope = '$invoice_scope', + invoice_date = '$invoice_date', + invoice_due = '$invoice_due', + invoice_currency_code = '$invoice_currency_code', + invoice_category_id = $invoice_category_id, + invoice_url_key = '$invoice_url_key', + invoice_client_id = $invoice_client_id + WHERE invoice_id = $invoice_id + "); + + mysqli_query($mysqli,"INSERT INTO history SET + history_status = '$invoice_status', + history_description = 'INVOICE updated!', + history_invoice_id = $invoice_id + "); //Logging - mysqli_query($mysqli,"INSERT INTO logs SET log_type = 'Invoice', log_action = 'Update', log_description = '$invoice_prefix$invoice_number', log_ip = '$session_ip', log_user_agent = '$session_user_agent', log_user_id = $session_user_id"); + mysqli_query($mysqli,"INSERT INTO logs SET + log_type = 'Invoice', + log_action = 'Update', + log_description = '$invoice_prefix$invoice_number', + log_ip = '$session_ip', + log_user_agent = '$session_user_agent', + log_user_id = $session_user_id + "); $return_data = [ 'status' => 'success', 'message' => "Invoice $invoice_number has been updated", - 'asset' => readInvoice(['invoice_id' => $invoice_id]) + 'invoice' => readInvoice(['invoice_id' => $invoice_id]) ]; return $return_data; diff --git a/functions/misc_functions.php b/functions/misc_functions.php index 5504c7941..c12749e1c 100644 --- a/functions/misc_functions.php +++ b/functions/misc_functions.php @@ -218,8 +218,9 @@ function roundToNearest15($time) return number_format($decimalHours, 2); } -function getSettingValue($mysqli, $setting_name) +function getSettingValue($setting_name) { + global $mysqli; //if starts with config_ then get from config table if (substr($setting_name, 0, 7) == "config_") { $sql = mysqli_query($mysqli, "SELECT $setting_name FROM settings"); diff --git a/functions/ticket_functions.php b/functions/ticket_functions.php index 9ffa84e81..764ca68f3 100644 --- a/functions/ticket_functions.php +++ b/functions/ticket_functions.php @@ -146,17 +146,31 @@ function createTicket( function readTicket( $parameters ) { - global $mysqli; + if (empty($parameters['ticket_id'])) { + return [ + 'status' => 'error', + 'message' => 'Ticket ID is required' + ];} + $ticket_id = intval($parameters['ticket_id']); - $sql = mysqli_query($mysqli, "SELECT * FROM tickets WHERE ticket_id = $ticket_id"); - $row = mysqli_fetch_array($sql); + // Check if there is an API Key Client ID parameter, if so, use it. Otherwise, default to 'all' + $api_client_id = isset($parameters['api_key_client_id']) ? sanitizeInput($parameters['api_key_client_id']) : 0; - return $row; -} + // Get the where clause for the query + $where_clause = getAPIWhereClause("ticket", $ticket_id, $api_client_id); + $sql = mysqli_query($mysqli, "SELECT * FROM tickets $where_clause"); + $assets = []; + + while ($row = mysqli_fetch_assoc($sql)) { + $assets[$row['ticket_id']] = $row; + } + + return $assets; +} function updateTicket( $parameters ) { diff --git a/post/invoice.php b/post/invoice.php index 0cc889775..c8c835e87 100644 --- a/post/invoice.php +++ b/post/invoice.php @@ -8,7 +8,7 @@ require_once 'post/invoice_model.php'; - $parameters['invoice_client'] = intval($_POST['client']); + $parameters['client_id'] = intval($_POST['client']); $parameters['due'] = sanitizeInput($_POST['due']); $parameters['date'] = sanitizeInput($_POST['date']); $parameters['category'] = intval($_POST['category']); diff --git a/report_collections.php b/report_collections.php index 0321db7f1..54fe51fee 100644 --- a/report_collections.php +++ b/report_collections.php @@ -59,7 +59,7 @@ $result_client_balance_report = mysqli_query($mysqli, $sql_client_balance_report); //get currency format from settings - $config_currency_code = getSettingValue($mysqli, "company_currency"); + $config_currency_code = getSettingValue("company_currency"); ?>
diff --git a/report_tax_summary.php b/report_tax_summary.php index ea041602b..9418565d7 100644 --- a/report_tax_summary.php +++ b/report_tax_summary.php @@ -7,7 +7,7 @@ $year = isset($_GET['year']) ? intval($_GET['year']) : date('Y'); $view = isset($_GET['view']) ? $_GET['view'] : 'quarterly'; -$company_currency = getSettingValue($mysqli, 'company_currency'); +$company_currency = getSettingValue('company_currency'); //GET unique years from expenses, payments and revenues From 19f62ee960026846b09346fdb1aad55665239f56 Mon Sep 17 00:00:00 2001 From: o-psi Date: Tue, 19 Mar 2024 23:47:41 +0000 Subject: [PATCH 32/32] Fix Database name --- admin_inventory_locations.php | 48 ++++++++++++------------ admin_inventory_locations_add_modal.php | 4 +- admin_inventory_locations_edit_modal.php | 24 ++++++------ database_updates.php | 26 ++++++------- db.sql | 26 ++++++------- inventory.php | 6 +-- inventory_edit_item_location_modal.php | 4 +- inventory_locations.php | 18 ++++----- inventory_locations_manage.php | 10 ++--- inventory_manage.php | 10 ++--- post/inventory.php | 6 +-- post/ticket.php | 8 ++-- ticket_add_product_modal.php | 4 +- 13 files changed, 97 insertions(+), 97 deletions(-) diff --git a/admin_inventory_locations.php b/admin_inventory_locations.php index c02272a85..9ee2521ff 100644 --- a/admin_inventory_locations.php +++ b/admin_inventory_locations.php @@ -1,7 +1,7 @@ "> - Name - Description - User Assigned - City + Name + Description + User Assigned + City Action @@ -44,38 +44,38 @@ - - - - + + + +