4 Commits

Author SHA1 Message Date
Johnny
dbf143d771 Merge pull request #1301 from itflow-org/develop
Develop to Master
2026-09-04 11:47:17 -04:00
Johnny
5dcc99d880 Merge pull request #1300 from itflow-org/develop
Develop to Master
2026-09-02 19:19:24 -04:00
Johnny
811b05e2d6 Merge pull request #1299 from itflow-org/develop
Develop to Master for release
2026-09-02 15:42:00 -04:00
Johnny
61bb9dd7cb Merge pull request #1298 from itflow-org/develop
Develop to Master for Release
2026-09-01 13:01:18 -04:00
45 changed files with 113 additions and 1210 deletions

View File

@@ -1,14 +0,0 @@
<?php
/*
* ITFlow - Database update to version 2.7.9 (from 2.7.8)
* Included by admin/database_updates.php - do not access directly
*/
defined('FROM_DB_UPDATER') || die("Direct file access is not allowed");
// Add auto-send option to recurring invoices (default to enabled) or whether they should be generated as drafts for manual review & sending
mysqli_query($mysqli, "ALTER TABLE `recurring_invoices`
ADD COLUMN `recurring_invoice_auto_send` tinyint(1) NOT NULL DEFAULT 1 AFTER `recurring_invoice_note`
");

View File

@@ -1,8 +0,0 @@
/* Custom syling for scaling the brand text (MSP name) in the sidebar */
.app-sidebar .brand-text {
display: inline-block;
vertical-align: middle;
white-space: nowrap;
transform-origin: left center;
}

View File

@@ -1,4 +1,3 @@
<!-- Do not modify - copy if you need to edit -->
<!-- Main Sidebar Container --> <!-- Main Sidebar Container -->
<aside class="app-sidebar shadow d-print-none" data-bs-theme="dark"> <aside class="app-sidebar shadow d-print-none" data-bs-theme="dark">

View File

@@ -1,5 +1,4 @@
<?php <?php
// Do not modify - copy if you need to edit
require_once "../../config.php"; require_once "../../config.php";
require_once "../../functions.php"; require_once "../../functions.php";

View File

@@ -1,7 +1,3 @@
<!-- Custom fix for scaling the brand text (MSP name) in the sidebar -->
<link rel="stylesheet" href="css/sidebar_brand_fix.css">
<script src="js/sidebar_brand_fix.js" defer></script>
<!-- Main Sidebar Container --> <!-- Main Sidebar Container -->
<aside class="app-sidebar shadow d-print-none" data-bs-theme="dark"> <aside class="app-sidebar shadow d-print-none" data-bs-theme="dark">
@@ -20,7 +16,7 @@
<div class="sidebar-brand"> <div class="sidebar-brand">
<a class="brand-link" href="<?= $brand_link ?>"> <a class="brand-link" href="<?= $brand_link ?>">
<span class="brand-text h4 mb-0" id="sidebar-brand-text"><?= escapeHtml($session_company_name) ?></span> <span class="brand-text h4 mb-0"><?= escapeHtml($session_company_name) ?></span>
</a> </a>
</div> </div>

View File

@@ -141,7 +141,7 @@ if (isset($_GET['invoice_id'])) {
//Get billable, and unbilled tickets to add to invoice //Get billable, and unbilled tickets to add to invoice
$sql_tickets_billable = mysqli_query( $sql_tickets_billable = mysqli_query(
$mysqli, " $mysqli, "
SELECT ticket_id, ticket_subject, ticket_number, ticket_prefix, ticket_status SELECT 1
FROM FROM
tickets tickets
WHERE WHERE
@@ -151,9 +151,10 @@ if (isset($_GET['invoice_id'])) {
AND AND
ticket_invoice_id = 0 ticket_invoice_id = 0
AND AND
ticket_status IN (4, 5); ticket_status = 5;
"); ");
//Add up all the payments for the invoice and get the total amount paid to the invoice //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"); $sql_amount_paid = mysqli_query($mysqli, "SELECT SUM(payment_amount) AS amount_paid FROM payments WHERE payment_invoice_id = $invoice_id");
$row = mysqli_fetch_assoc($sql_amount_paid); $row = mysqli_fetch_assoc($sql_amount_paid);
@@ -696,7 +697,7 @@ if (isset($_GET['invoice_id'])) {
</div> </div>
</div> </div>
</div> </div>
<div class="col-sm d-print-none <?php if (mysqli_num_rows($sql_tickets) == 0 && mysqli_num_rows($sql_tickets_billable) == 0) { echo "d-none"; } ?>"> <div class="col-sm d-print-none <?php if (mysqli_num_rows($sql_tickets) == 0) { echo "d-none"; } ?>">
<div class="card"> <div class="card">
<div class="card-header text-bold"> <div class="card-header text-bold">
<i class="fa fa-life-ring me-2"></i>Tickets <i class="fa fa-life-ring me-2"></i>Tickets
@@ -727,19 +728,15 @@ if (isset($_GET['invoice_id'])) {
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th>#</th>
<th>Date</th> <th>Date</th>
<th>Subject</th> <th>Subject</th>
<th class="text-end">Time Worked</th> <th class="text-end">Time Worked</th>
<th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<?php <?php
while ($row = mysqli_fetch_assoc($sql_tickets)) { while ($row = mysqli_fetch_assoc($sql_tickets)) {
$ticket_prefix = escapeHtml($row['ticket_prefix']);
$ticket_number = escapeHtml($row['ticket_number']);
$ticket_id = intval($row['ticket_id']); $ticket_id = intval($row['ticket_id']);
$ticket_created_at = escapeHtml($row['ticket_created_at']); $ticket_created_at = escapeHtml($row['ticket_created_at']);
$ticket_subject = escapeHtml($row['ticket_subject']); $ticket_subject = escapeHtml($row['ticket_subject']);
@@ -747,12 +744,9 @@ if (isset($_GET['invoice_id'])) {
?> ?>
<tr> <tr>
<td><a href="ticket.php?ticket_id=<?= $ticket_id ?>"><?= "$ticket_prefix$ticket_number" ?></a></td>
<td><?= $ticket_created_at ?></td> <td><?= $ticket_created_at ?></td>
<td><?= $ticket_subject ?></td> <td><?= $ticket_subject ?></td>
<td class="text-end"><?= $ticket_total_time_worked ?></td> <td class="text-end"><?= $ticket_total_time_worked ?></td>
<td align="right"><a class="btn btn-light text-danger confirm-link" title="Remove" href="post.php?remove_ticket_from_invoice&invoice_id=<?= $invoice_id ?>&ticket_id=<?= $ticket_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>"><i class="fa fa-times"></i></a></td>
</tr> </tr>
<?php <?php
} }

View File

@@ -1,19 +0,0 @@
function fitBrandText() {
text.style.transform = 'none';
// getBoundingClientRect gives real on-screen edges, unlike
// offsetLeft which is unreliable for inline text
var available = link.getBoundingClientRect().right - text.getBoundingClientRect().left - 8;
var natural = text.scrollWidth;
// Scale rather than step font-size down.
var scale = natural > available ? available / natural : 1;
text.style.transform = 'scale(' + scale.toFixed(4) + ')';
}
var text = document.getElementById('sidebar-brand-text');
if (text) {
var link = text.closest('.brand-link');
// Fires once on observe as well as any later width change
new ResizeObserver(fitBrandText).observe(link);
}

View File

@@ -76,7 +76,7 @@ ob_start();
<label>Shortened Name</label> <label>Shortened Name</label>
<div class="input-group"> <div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-id-badge"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-id-badge"></i></span>
<input type="text" class="form-control" name="abbreviation" placeholder="Shortened name for client - Max chars 6" maxlength="6" oninput="this.value = this.value.toUpperCase()"> <input type="text" class="form-control" name="abbreviation" placeholder="Shortned name for client - Max chars 6" maxlength="6" oninput="this.value = this.value.toUpperCase()">
</div> </div>
</div> </div>

View File

@@ -101,7 +101,7 @@ ob_start();
<label>Shortened Name</label> <label>Shortened Name</label>
<div class="input-group"> <div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-id-badge"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-id-badge"></i></span>
<input type="text" class="form-control" name="abbreviation" placeholder="Shortened name for client - Max chars 6" value="<?= $client_abbreviation ?>" maxlength="6" oninput="this.value = this.value.toUpperCase()"> <input type="text" class="form-control" name="abbreviation" placeholder="Shortned name for client - Max chars 6" value="<?= $client_abbreviation ?>" maxlength="6" oninput="this.value = this.value.toUpperCase()">
</div> </div>
</div> </div>

View File

@@ -44,16 +44,8 @@
</a> </a>
</td> </td>
<td><?= $ticket_subject ?></td> <td><?= $ticket_subject ?></td>
<td> <td><a href='ticket.php?ticket_id=<?= $ticket_id ?>&invoice_id=<?= $invoice_id ?>#addInvoiceFromTicketModal'>
<form action="post.php" method="post"> <i class="fas fa-fw fa-plus-circle"></i></td>
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="hidden" name="invoice_id" value="<?= $invoice_id ?>">
<input type="hidden" name="ticket_id" value="<?= $ticket_id ?>">
<button class="btn btn-link p-0" type="submit" name="add_ticket_to_invoice" title="Add ticket to invoice">
<i class="fas fa-fw fa-plus-circle"></i>
</button>
</form>
</td>
</tr> </tr>
<?php } ?> <?php } ?>
</table> </table>

View File

@@ -9,8 +9,7 @@ $recurring_invoice_id = intval($_GET['id']);
$sql = mysqli_query($mysqli, "SELECT recurring_invoice_category_id, recurring_invoice_client_id, recurring_invoice_created_at, $sql = mysqli_query($mysqli, "SELECT recurring_invoice_category_id, recurring_invoice_client_id, recurring_invoice_created_at,
recurring_invoice_discount_amount, recurring_invoice_frequency, recurring_invoice_discount_amount, recurring_invoice_frequency,
recurring_invoice_next_date, recurring_invoice_number, recurring_invoice_prefix, recurring_invoice_next_date, recurring_invoice_number, recurring_invoice_prefix,
recurring_invoice_auto_send, recurring_invoice_scope, recurring_invoice_status recurring_invoice_scope, recurring_invoice_status FROM recurring_invoices WHERE recurring_invoice_id = $recurring_invoice_id LIMIT 1");
FROM recurring_invoices WHERE recurring_invoice_id = $recurring_invoice_id LIMIT 1");
$row = mysqli_fetch_assoc($sql); $row = mysqli_fetch_assoc($sql);
$recurring_invoice_prefix = escapeHtml($row['recurring_invoice_prefix']); $recurring_invoice_prefix = escapeHtml($row['recurring_invoice_prefix']);
@@ -20,7 +19,6 @@ $recurring_invoice_frequency = escapeHtml($row['recurring_invoice_frequency']);
$recurring_invoice_status = escapeHtml($row['recurring_invoice_status']); $recurring_invoice_status = escapeHtml($row['recurring_invoice_status']);
$recurring_invoice_created_at = date('Y-m-d', strtotime($row['recurring_invoice_created_at'])); $recurring_invoice_created_at = date('Y-m-d', strtotime($row['recurring_invoice_created_at']));
$recurring_invoice_next_date = escapeHtml($row['recurring_invoice_next_date']); $recurring_invoice_next_date = escapeHtml($row['recurring_invoice_next_date']);
$recurring_invoice_auto_send = intval($row['recurring_invoice_auto_send']);
$recurring_invoice_discount = floatval($row['recurring_invoice_discount_amount']); $recurring_invoice_discount = floatval($row['recurring_invoice_discount_amount']);
$category_id = intval($row['recurring_invoice_category_id']); $category_id = intval($row['recurring_invoice_category_id']);
$client_id = intval($row['recurring_invoice_client_id']); $client_id = intval($row['recurring_invoice_client_id']);
@@ -103,22 +101,11 @@ ob_start();
</div> </div>
</div> </div>
<div class="mb-3">
<label>Action <strong class="text-danger">*</strong></label>
<div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-paper-plane"></i></span>
<select class="form-select select" name="auto_send" required>
<option <?php if ($recurring_invoice_auto_send == 1) { echo "selected"; } ?> value="1">Send Automatically</option>
<option <?php if ($recurring_invoice_auto_send == 0) { echo "selected"; } ?> value="0">Generate Draft for Review</option>
</select>
</div>
</div>
<div class="mb-3"> <div class="mb-3">
<label>Status <strong class="text-danger">*</strong></label> <label>Status <strong class="text-danger">*</strong></label>
<div class="input-group"> <div class="input-group">
<span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span> <span class="input-group-text"><i class="fa fa-fw fa-clock"></i></span>
<select class="form-select select" name="status" required> <select class="form-select select2" name="status" required>
<option <?php if ($recurring_invoice_status == 1) { <option <?php if ($recurring_invoice_status == 1) {
echo "selected"; echo "selected";
} ?> value="1">Active</option> } ?> value="1">Active</option>

View File

@@ -24,7 +24,7 @@ $sql = mysqli_query(
$mysqli, $mysqli,
"SELECT client_id, client_name, location_name, network, network_client_id, network_description, "SELECT client_id, client_name, location_name, network, network_client_id, network_description,
network_dhcp_range, network_gateway, network_id, network_name, network_notes, network_dhcp_range, network_gateway, network_id, network_name, network_notes,
network_primary_dns, network_secondary_dns, network_vlan, network_archived_at FROM networks network_primary_dns, network_secondary_dns, network_vlan FROM networks
LEFT JOIN clients ON client_id = network_client_id LEFT JOIN clients ON client_id = network_client_id
LEFT JOIN locations ON location_id = network_location_id LEFT JOIN locations ON location_id = network_location_id
WHERE network_id = $network_id WHERE network_id = $network_id
@@ -50,7 +50,6 @@ if (mysqli_num_rows($sql) == 0) {
$network_primary_dns = escapeHtml($row['network_primary_dns']); $network_primary_dns = escapeHtml($row['network_primary_dns']);
$network_secondary_dns = escapeHtml($row['network_secondary_dns']); $network_secondary_dns = escapeHtml($row['network_secondary_dns']);
$network_dhcp_range = escapeHtml($row['network_dhcp_range']); $network_dhcp_range = escapeHtml($row['network_dhcp_range']);
$network_archived_at = escapeHtml($row['network_archived_at']);
$network_notes = escapeHtml($row['network_notes']); $network_notes = escapeHtml($row['network_notes']);
$location_name = escapeHtml($row['location_name']); $location_name = escapeHtml($row['location_name']);
@@ -92,9 +91,6 @@ if (mysqli_num_rows($sql) == 0) {
</li> </li>
<li class="breadcrumb-item active"> <li class="breadcrumb-item active">
<i class="fas fa-fw fa-network-wired"></i> <?= $network_name ?> <i class="fas fa-fw fa-network-wired"></i> <?= $network_name ?>
<?php if ($network_archived_at) { ?>
<span class="text-secondary"> (Archived)</span>
<?php } ?>
</li> </li>
</ol> </ol>

View File

@@ -1003,7 +1003,6 @@ if (isExportRequest('export_invoices')) {
} }
// TODO: This should probably be removed as we now allow multiple invoices to be linked to a single ticket
if (isset($_POST['link_invoice_to_ticket'])) { if (isset($_POST['link_invoice_to_ticket'])) {
validateCSRFToken(); validateCSRFToken();
@@ -1042,28 +1041,7 @@ if (isset($_POST['add_ticket_to_invoice'])) {
flashAlert("Ticket linked to invoice"); flashAlert("Ticket linked to invoice");
redirect("invoice.php?invoice_id=$invoice_id"); redirect("post.php?add_ticket_to_invoice=$invoice_id");
}
if (isset($_GET['remove_ticket_from_invoice'])) {
validateCSRFToken();
enforceUserPermission('module_sales', 2);
$invoice_id = intval($_GET['invoice_id']);
$ticket_id = intval($_GET['ticket_id']);
$client_id = intval(getFieldById('tickets', $ticket_id, 'ticket_client_id'));
enforceClientAccess();
mysqli_query($mysqli,"UPDATE tickets SET ticket_invoice_id = 0 WHERE ticket_id = $ticket_id");
flashAlert("Ticket unlinked from invoice");
redirect("invoice.php?invoice_id=$invoice_id");
} }

View File

@@ -124,7 +124,6 @@ if (isset($_POST['edit_recurring_invoice'])) {
$scope = escapeSql($_POST['scope']); $scope = escapeSql($_POST['scope']);
$status = intval($_POST['status']); $status = intval($_POST['status']);
$recurring_invoice_discount = floatval($_POST['recurring_invoice_discount']); $recurring_invoice_discount = floatval($_POST['recurring_invoice_discount']);
$auto_send = intval($_POST['auto_send']);
// Get Recurring Invoice Details and Client ID for Logging // Get Recurring Invoice Details and Client ID for Logging
$sql = mysqli_query($mysqli,"SELECT recurring_invoice_prefix, recurring_invoice_number, recurring_invoice_client_id FROM recurring_invoices WHERE recurring_invoice_id = $recurring_invoice_id"); $sql = mysqli_query($mysqli,"SELECT recurring_invoice_prefix, recurring_invoice_number, recurring_invoice_client_id FROM recurring_invoices WHERE recurring_invoice_id = $recurring_invoice_id");
@@ -144,7 +143,7 @@ if (isset($_POST['edit_recurring_invoice'])) {
} }
$recurring_invoice_amount = $recurring_invoice_amount - $recurring_invoice_discount; $recurring_invoice_amount = $recurring_invoice_amount - $recurring_invoice_discount;
mysqli_query($mysqli,"UPDATE recurring_invoices SET recurring_invoice_scope = '$scope', recurring_invoice_frequency = '$frequency', recurring_invoice_next_date = '$next_date', recurring_invoice_category_id = $category, recurring_invoice_discount_amount = $recurring_invoice_discount, recurring_invoice_amount = $recurring_invoice_amount, recurring_invoice_auto_send = $auto_send, recurring_invoice_status = $status WHERE recurring_invoice_id = $recurring_invoice_id"); mysqli_query($mysqli,"UPDATE recurring_invoices SET recurring_invoice_scope = '$scope', recurring_invoice_frequency = '$frequency', recurring_invoice_next_date = '$next_date', recurring_invoice_category_id = $category, recurring_invoice_discount_amount = $recurring_invoice_discount, recurring_invoice_amount = $recurring_invoice_amount, recurring_invoice_status = $status WHERE recurring_invoice_id = $recurring_invoice_id");
mysqli_query($mysqli,"INSERT INTO history SET history_status = '$status', history_description = 'Recurring Invoice edited', history_recurring_invoice_id = $recurring_invoice_id"); mysqli_query($mysqli,"INSERT INTO history SET history_status = '$status', history_description = 'Recurring Invoice edited', history_recurring_invoice_id = $recurring_invoice_id");
@@ -371,13 +370,12 @@ if (isset($_GET['force_recurring'])) {
recurring_invoice_client_id, recurring_invoice_currency_code, recurring_invoice_client_id, recurring_invoice_currency_code,
recurring_invoice_discount_amount, recurring_invoice_frequency, recurring_invoice_id, recurring_invoice_discount_amount, recurring_invoice_frequency, recurring_invoice_id,
recurring_invoice_last_sent, recurring_invoice_next_date, recurring_invoice_note, recurring_invoice_last_sent, recurring_invoice_next_date, recurring_invoice_note,
recurring_invoice_scope, recurring_invoice_auto_send, recurring_invoice_status FROM recurring_invoices, clients WHERE client_id = recurring_invoice_client_id AND recurring_invoice_id = $recurring_invoice_id"); recurring_invoice_scope, recurring_invoice_status FROM recurring_invoices, clients WHERE client_id = recurring_invoice_client_id AND recurring_invoice_id = $recurring_invoice_id");
$row = mysqli_fetch_assoc($sql_recurring_invoices); $row = mysqli_fetch_assoc($sql_recurring_invoices);
$recurring_invoice_id = intval($row['recurring_invoice_id']); $recurring_invoice_id = intval($row['recurring_invoice_id']);
$recurring_invoice_scope = escapeSql($row['recurring_invoice_scope']); $recurring_invoice_scope = escapeSql($row['recurring_invoice_scope']);
$recurring_invoice_frequency = validateRecurringFrequency($row['recurring_invoice_frequency']); $recurring_invoice_frequency = validateRecurringFrequency($row['recurring_invoice_frequency']);
$recurring_invoice_auto_send = intval($row['recurring_invoice_auto_send']);
$recurring_invoice_status = escapeSql($row['recurring_invoice_status']); $recurring_invoice_status = escapeSql($row['recurring_invoice_status']);
$recurring_invoice_last_sent = escapeSql($row['recurring_invoice_last_sent']); $recurring_invoice_last_sent = escapeSql($row['recurring_invoice_last_sent']);
$recurring_invoice_next_date = escapeSql($row['recurring_invoice_next_date']); $recurring_invoice_next_date = escapeSql($row['recurring_invoice_next_date']);
@@ -405,12 +403,10 @@ if (isset($_GET['force_recurring'])) {
//Generate a unique URL key for clients to access //Generate a unique URL key for clients to access
$url_key = randomString(32); $url_key = randomString(32);
mysqli_query($mysqli,"INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $new_invoice_number, invoice_scope = '$recurring_invoice_scope', invoice_date = CURDATE(), invoice_due = DATE_ADD(CURDATE(), INTERVAL $client_net_terms day), invoice_discount_amount = $recurring_invoice_discount_amount, invoice_amount = $recurring_invoice_amount, invoice_currency_code = '$recurring_invoice_currency_code', invoice_note = '$recurring_invoice_note', invoice_category_id = $category_id, invoice_status = 'Draft', invoice_url_key = '$url_key', invoice_recurring_invoice_id = $recurring_invoice_id, invoice_client_id = $client_id"); mysqli_query($mysqli,"INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $new_invoice_number, invoice_scope = '$recurring_invoice_scope', invoice_date = CURDATE(), invoice_due = DATE_ADD(CURDATE(), INTERVAL $client_net_terms day), invoice_discount_amount = $recurring_invoice_discount_amount, invoice_amount = $recurring_invoice_amount, invoice_currency_code = '$recurring_invoice_currency_code', invoice_note = '$recurring_invoice_note', invoice_category_id = $category_id, invoice_status = 'Sent', invoice_url_key = '$url_key', invoice_recurring_invoice_id = $recurring_invoice_id, invoice_client_id = $client_id");
$new_invoice_id = mysqli_insert_id($mysqli); $new_invoice_id = mysqli_insert_id($mysqli);
mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Draft', history_description = 'Invoice Generated from Recurring!', history_invoice_id = $new_invoice_id");
//Copy Items from original invoice to new invoice //Copy Items from original invoice to new invoice
$sql_invoice_items = mysqli_query($mysqli,"SELECT item_description, item_id, item_name, item_order, item_price, item_quantity, item_subtotal, $sql_invoice_items = mysqli_query($mysqli,"SELECT item_description, item_id, item_name, item_order, item_price, item_quantity, item_subtotal,
item_tax_id FROM recurring_invoice_items WHERE item_recurring_invoice_id = $recurring_invoice_id ORDER BY item_id ASC"); item_tax_id FROM recurring_invoice_items WHERE item_recurring_invoice_id = $recurring_invoice_id ORDER BY item_id ASC");
@@ -443,10 +439,7 @@ if (isset($_GET['force_recurring'])) {
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 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");
} }
// Only send the invoice if the recurring invoice is set to auto-send, otherwise just leave as draft for manual sending mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Invoice Generated from Recurring!', history_invoice_id = $new_invoice_id");
if ($recurring_invoice_auto_send == 1) {
mysqli_query($mysqli,"INSERT INTO history SET history_status = 'Sent', history_description = 'Invoice Sent from Recurring!', history_invoice_id = $new_invoice_id");
//Update Recurring Balances by tallying up recurring items also update recurring dates //Update Recurring Balances by tallying up recurring items also update recurring dates
$sql_recurring_invoice_total = mysqli_query($mysqli,"SELECT SUM(item_total) AS recurring_invoice_total FROM recurring_invoice_items WHERE item_recurring_invoice_id = $recurring_invoice_id"); $sql_recurring_invoice_total = mysqli_query($mysqli,"SELECT SUM(item_total) AS recurring_invoice_total FROM recurring_invoice_items WHERE item_recurring_invoice_id = $recurring_invoice_id");
@@ -525,8 +518,7 @@ if (isset($_GET['force_recurring'])) {
} }
} //End Recurring Invoices Mail Loop } //End Recurring Invoices Loop
}
logAudit("Invoice", "Create", "$session_name forced recurring invoice into an invoice", $client_id, $new_invoice_id); logAudit("Invoice", "Create", "$session_name forced recurring invoice into an invoice", $client_id, $new_invoice_id);

View File

@@ -22,7 +22,7 @@ if (isset($_GET['recurring_invoice_id'])) {
recurring_invoice_category_id, recurring_invoice_created_at, recurring_invoice_category_id, recurring_invoice_created_at,
recurring_invoice_currency_code, recurring_invoice_discount_amount, recurring_invoice_currency_code, recurring_invoice_discount_amount,
recurring_invoice_email_notify, recurring_invoice_frequency, recurring_invoice_last_sent, recurring_invoice_email_notify, recurring_invoice_frequency, recurring_invoice_last_sent,
recurring_invoice_next_date, recurring_invoice_note, recurring_invoice_auto_send, recurring_invoice_number, recurring_invoice_next_date, recurring_invoice_note, recurring_invoice_number,
recurring_invoice_prefix, recurring_invoice_scope, recurring_invoice_status, recurring_invoice_prefix, recurring_invoice_scope, recurring_invoice_status,
recurring_payment_id, recurring_payment_method, recurring_payment_recurring_invoice_id, recurring_payment_id, recurring_payment_method, recurring_payment_recurring_invoice_id,
recurring_payment_saved_payment_id FROM recurring_invoices recurring_payment_saved_payment_id FROM recurring_invoices
@@ -61,7 +61,6 @@ if (isset($_GET['recurring_invoice_id'])) {
$recurring_invoice_discount = floatval($row['recurring_invoice_discount_amount']); $recurring_invoice_discount = floatval($row['recurring_invoice_discount_amount']);
$recurring_invoice_currency_code = escapeHtml($row['recurring_invoice_currency_code']); $recurring_invoice_currency_code = escapeHtml($row['recurring_invoice_currency_code']);
$recurring_invoice_note = escapeHtml($row['recurring_invoice_note']); $recurring_invoice_note = escapeHtml($row['recurring_invoice_note']);
$recurring_invoice_auto_send = intval($row['recurring_invoice_auto_send']);
$recurring_invoice_email_notify = intval($row['recurring_invoice_email_notify']); $recurring_invoice_email_notify = intval($row['recurring_invoice_email_notify']);
$category_id = intval($row['recurring_invoice_category_id']); $category_id = intval($row['recurring_invoice_category_id']);
$client_id = intval($row['client_id']); $client_id = intval($row['client_id']);
@@ -149,13 +148,11 @@ if (isset($_GET['recurring_invoice_id'])) {
<div class="row"> <div class="row">
<div class="col-2"> <div class="col-2">
<?php if ($recurring_invoice_auto_send) { ?>
<?php if ($recurring_invoice_email_notify) { ?> <?php if ($recurring_invoice_email_notify) { ?>
<a href="post.php?recurring_invoice_email_notify=0&recurring_invoice_id=<?= $recurring_invoice_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>" class="btn btn-primary"><i class="fas fa-fw fa-bell me-2"></i>Email Notify</a> <a href="post.php?recurring_invoice_email_notify=0&recurring_invoice_id=<?= $recurring_invoice_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>" class="btn btn-primary"><i class="fas fa-fw fa-bell me-2"></i>Email Notify</a>
<?php } else { ?> <?php } else { ?>
<a href="post.php?recurring_invoice_email_notify=1&recurring_invoice_id=<?= $recurring_invoice_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>" class="btn btn-outline-danger"><i class="fas fa-fw fa-bell-slash me-2"></i>Email Notify</a> <a href="post.php?recurring_invoice_email_notify=1&recurring_invoice_id=<?= $recurring_invoice_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>" class="btn btn-outline-danger"><i class="fas fa-fw fa-bell-slash me-2"></i>Email Notify</a>
<?php } ?> <?php } ?>
<?php } ?>
</div> </div>
<div class="col-3"> <div class="col-3">
<?php $sql_saved_payments = mysqli_query($mysqli, "SELECT saved_payment_description, saved_payment_id FROM client_saved_payment_methods WHERE saved_payment_client_id = $client_id"); <?php $sql_saved_payments = mysqli_query($mysqli, "SELECT saved_payment_description, saved_payment_id FROM client_saved_payment_methods WHERE saved_payment_client_id = $client_id");

View File

@@ -1,43 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse ID
$document_id = intval($_POST['document_id'] ?? 0);
// Default
$update_count = false;
if (!empty($document_id)) {
// Fetch document info
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "
SELECT document_name
FROM documents
WHERE document_id = $document_id AND document_client_id = $client_id AND document_archived_at IS NULL
LIMIT 1
"));
if ($row) {
$document_name = escapeSql($row['document_name']);
// Archive document
$update_sql = mysqli_query($mysqli, "
UPDATE documents SET document_archived_at = NOW()
WHERE document_id = $document_id AND document_client_id = $client_id
");
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
// Logging
logAudit("Document", "Archive", "$document_name archived via API ($api_key_name)", $client_id, $document_id);
logAudit("API", "Success", "Archived document $document_name via API ($api_key_name)", $client_id);
}
}
}
// Output
require_once '../update_output.php';

View File

@@ -8,11 +8,11 @@ require_once '../require_get_method.php';
if (isset($_GET['document_id'])) { if (isset($_GET['document_id'])) {
// Document via ID (single) // Document via ID (single)
$id = intval($_GET['document_id']); $id = intval($_GET['document_id']);
$sql = mysqli_query($mysqli, "SELECT * FROM documents WHERE document_id = '$id' AND document_archived_at IS NULL AND 1=1 " . apiClientScopeSql('document_client_id') . ""); $sql = mysqli_query($mysqli, "SELECT * FROM documents WHERE document_id = '$id' AND 1=1 " . apiClientScopeSql('document_client_id') . "");
} else { } else {
// All documents (by client ID if given, or all in general if key permits) // All documents (by client ID if given, or all in general if key permits)
$sql = mysqli_query($mysqli, "SELECT * FROM documents WHERE document_archived_at IS NULL AND 1=1 " . apiClientScopeSql('document_client_id') . " ORDER BY document_id LIMIT $limit OFFSET $offset"); $sql = mysqli_query($mysqli, "SELECT * FROM documents WHERE 1=1 " . apiClientScopeSql('document_client_id') . " ORDER BY document_id LIMIT $limit OFFSET $offset");
} }
// Output // Output

View File

@@ -1,43 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse ID
$document_id = intval($_POST['document_id'] ?? 0);
// Default
$update_count = false;
if (!empty($document_id)) {
// Fetch document info
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "
SELECT document_name
FROM documents
WHERE document_id = $document_id AND document_client_id = $client_id AND document_archived_at IS NOT NULL
LIMIT 1
"));
if ($row) {
$document_name = escapeSql($row['document_name']);
// Unarchive document
$update_sql = mysqli_query($mysqli, "
UPDATE documents SET document_archived_at = NULL
WHERE document_id = $document_id AND document_client_id = $client_id
");
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
// Logging
logAudit("Document", "Unarchive", "$document_name unarchived via API ($api_key_name)", $client_id, $document_id);
logAudit("API", "Success", "Unarchived document $document_name via API ($api_key_name)", $client_id);
}
}
}
// Output
require_once '../update_output.php';

View File

@@ -1,23 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
$domain_id = intval($_POST['domain_id']);
$update_count = false;
if (!empty($domain_id)) {
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT domain_name FROM domains WHERE domain_id = $domain_id AND domain_client_id = $client_id AND domain_archived_at IS NULL LIMIT 1"));
if ($row) {
$domain_name = escapeSql($row['domain_name']);
$update_sql = mysqli_query($mysqli, "UPDATE domains SET domain_archived_at = NOW() WHERE domain_id = $domain_id AND domain_client_id = $client_id AND domain_archived_at IS NULL");
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
logAudit("Domain", "Archive", "$domain_name archived via API ($api_key_name)", $client_id, $domain_id);
}
}
}
require_once '../update_output.php';

View File

@@ -1,37 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
require_once 'domain_model.php';
$insert_id = false;
if (!empty($name) && !empty($client_id)) {
// Use the supplied expiry when valid, otherwise look it up from WHOIS.
if (strtotime($expire)) {
$expire = "'$expire'";
} else {
$expire = getDomainExpirationDate($name);
$expire = strtotime($expire) ? "'$expire'" : 'NULL';
}
// Populate DNS and WHOIS data from the domain rather than accepting it from the API.
$records = getDnsRecords($name);
$ip = escapeSql($records['a']);
$name_servers = escapeSql($records['ns']);
$mail_servers = escapeSql($records['mx']);
$txt = escapeSql($records['txt']);
$raw_whois = escapeSql($records['whois']);
$insert_sql = mysqli_query($mysqli, "INSERT INTO domains SET domain_name = '$name', domain_description = '$description', domain_expire = $expire, domain_ip = '$ip', domain_name_servers = '$name_servers', domain_mail_servers = '$mail_servers', domain_txt = '$txt', domain_raw_whois = '$raw_whois', domain_notes = '$notes', domain_registrar = $registrar, domain_webhost = $webhost, domain_dnshost = $dnshost, domain_mailhost = $mailhost, domain_client_id = $client_id");
if ($insert_sql) {
$insert_id = mysqli_insert_id($mysqli);
logAudit("Domain", "Create", "$name via API ($api_key_name)", $client_id, $insert_id);
logAudit("API", "Success", "Created domain $name via API ($api_key_name)", $client_id);
}
}
require_once '../create_output.php';

View File

@@ -1,67 +0,0 @@
<?php
// Variable assignment from POST (or: blank/from DB is updating)
if (isset($_POST['domain_name'])) {
$name = preg_replace("(^https?://)", "", escapeSql($_POST['domain_name']));
} elseif (isset($domain_row) && isset($domain_row['domain_name'])) {
$name = mysqli_real_escape_string($mysqli, $domain_row['domain_name']);
} else {
$name = '';
}
if (isset($_POST['domain_description'])) {
$description = escapeSql($_POST['domain_description']);
} elseif (isset($domain_row) && isset($domain_row['domain_description'])) {
$description = mysqli_real_escape_string($mysqli, $domain_row['domain_description']);
} else {
$description = '';
}
if (isset($_POST['domain_expire'])) {
$expire = escapeSql($_POST['domain_expire']);
} elseif (isset($domain_row) && !empty($domain_row['domain_expire'])) {
$expire = mysqli_real_escape_string($mysqli, $domain_row['domain_expire']);
} else {
$expire = '';
}
if (isset($_POST['domain_notes'])) {
$notes = escapeSql($_POST['domain_notes']);
} elseif (isset($domain_row) && isset($domain_row['domain_notes'])) {
$notes = mysqli_real_escape_string($mysqli, $domain_row['domain_notes']);
} else {
$notes = '';
}
if (isset($_POST['domain_registrar'])) {
$registrar = intval($_POST['domain_registrar']);
} elseif (isset($domain_row) && isset($domain_row['domain_registrar'])) {
$registrar = $domain_row['domain_registrar'];
} else {
$registrar = 0;
}
if (isset($_POST['domain_webhost'])) {
$webhost = intval($_POST['domain_webhost']);
} elseif (isset($domain_row) && isset($domain_row['domain_webhost'])) {
$webhost = $domain_row['domain_webhost'];
} else {
$webhost = 0;
}
if (isset($_POST['domain_dnshost'])) {
$dnshost = intval($_POST['domain_dnshost']);
} elseif (isset($domain_row) && isset($domain_row['domain_dnshost'])) {
$dnshost = $domain_row['domain_dnshost'];
} else {
$dnshost = 0;
}
if (isset($_POST['domain_mailhost'])) {
$mailhost = intval($_POST['domain_mailhost']);
} elseif (isset($domain_row) && isset($domain_row['domain_mailhost'])) {
$mailhost = $domain_row['domain_mailhost'];
} else {
$mailhost = 0;
}

View File

@@ -8,16 +8,16 @@ require_once '../require_get_method.php';
// Specific domain via ID (single) // Specific domain via ID (single)
if (isset($_GET['domain_id'])) { if (isset($_GET['domain_id'])) {
$id = intval($_GET['domain_id']); $id = intval($_GET['domain_id']);
$sql = mysqli_query($mysqli, "SELECT * FROM domains WHERE domain_id = '$id' AND domain_archived_at IS NULL AND 1=1 " . apiClientScopeSql('domain_client_id') . ""); $sql = mysqli_query($mysqli, "SELECT * FROM domains WHERE domain_id = '$id' AND 1=1 " . apiClientScopeSql('domain_client_id') . "");
} elseif (isset($_GET['domain_name'])) { } elseif (isset($_GET['domain_name'])) {
// Domain by name // Domain by name
$name = mysqli_real_escape_string($mysqli, $_GET['domain_name']); $name = mysqli_real_escape_string($mysqli, $_GET['domain_name']);
$sql = mysqli_query($mysqli, "SELECT * FROM domains WHERE domain_name = '$name' AND domain_archived_at IS NULL AND 1=1 " . apiClientScopeSql('domain_client_id') . " ORDER BY domain_id LIMIT $limit OFFSET $offset"); $sql = mysqli_query($mysqli, "SELECT * FROM domains WHERE domain_name = '$name' AND 1=1 " . apiClientScopeSql('domain_client_id') . " ORDER BY domain_id LIMIT $limit OFFSET $offset");
} else { } else {
// All domains (by client ID or all in general if key permits) // All domains (by client ID or all in general if key permits)
$sql = mysqli_query($mysqli, "SELECT * FROM domains WHERE domain_archived_at IS NULL AND 1=1 " . apiClientScopeSql('domain_client_id') . " ORDER BY domain_id LIMIT $limit OFFSET $offset"); $sql = mysqli_query($mysqli, "SELECT * FROM domains WHERE 1=1 " . apiClientScopeSql('domain_client_id') . " ORDER BY domain_id LIMIT $limit OFFSET $offset");
} }
// Output // Output

View File

@@ -1,23 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
$domain_id = intval($_POST['domain_id']);
$update_count = false;
if (!empty($domain_id)) {
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT domain_name FROM domains WHERE domain_id = $domain_id AND domain_client_id = $client_id AND domain_archived_at IS NOT NULL LIMIT 1"));
if ($row) {
$domain_name = escapeSql($row['domain_name']);
$update_sql = mysqli_query($mysqli, "UPDATE domains SET domain_archived_at = NULL WHERE domain_id = $domain_id AND domain_client_id = $client_id AND domain_archived_at IS NOT NULL");
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
logAudit("Domain", "Unarchive", "$domain_name unarchived via API ($api_key_name)", $client_id, $domain_id);
}
}
}
require_once '../update_output.php';

View File

@@ -1,92 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
$domain_id = intval($_POST['domain_id']);
$update_count = false;
if (!empty($domain_id)) {
$domain_row = mysqli_fetch_assoc(mysqli_query($mysqli, "
SELECT
domains.*,
registrar.vendor_name AS registrar_name,
dnshost.vendor_name AS dnshost_name,
mailhost.vendor_name AS mailhost_name,
webhost.vendor_name AS webhost_name
FROM domains
LEFT JOIN vendors AS registrar ON domains.domain_registrar = registrar.vendor_id
LEFT JOIN vendors AS dnshost ON domains.domain_dnshost = dnshost.vendor_id
LEFT JOIN vendors AS mailhost ON domains.domain_mailhost = mailhost.vendor_id
LEFT JOIN vendors AS webhost ON domains.domain_webhost = webhost.vendor_id
WHERE domain_id = $domain_id AND domain_client_id = $client_id AND domain_archived_at IS NULL
LIMIT 1
"));
$original_domain_info = $domain_row;
require_once 'domain_model.php';
// Use the supplied expiry when valid, otherwise look it up from WHOIS.
if (strtotime($expire)) {
$expire = "'$expire'";
} else {
$expire = getDomainExpirationDate($name);
$expire = strtotime($expire) ? "'$expire'" : 'NULL';
}
// Populate DNS and WHOIS data from the domain rather than accepting it from the API.
$records = getDnsRecords($name);
$ip = escapeSql($records['a']);
$name_servers = escapeSql($records['ns']);
$mail_servers = escapeSql($records['mx']);
$txt = escapeSql($records['txt']);
$raw_whois = escapeSql($records['whois']);
$update_sql = mysqli_query($mysqli, "UPDATE domains SET domain_name = '$name', domain_description = '$description', domain_expire = $expire, domain_ip = '$ip', domain_name_servers = '$name_servers', domain_mail_servers = '$mail_servers', domain_txt = '$txt', domain_raw_whois = '$raw_whois', domain_notes = '$notes', domain_registrar = $registrar, domain_webhost = $webhost, domain_dnshost = $dnshost, domain_mailhost = $mailhost WHERE domain_id = $domain_id AND domain_client_id = $client_id AND domain_archived_at IS NULL LIMIT 1");
if ($update_sql && $domain_row) {
// Capture the update result before any history or audit queries run.
$update_count = mysqli_affected_rows($mysqli);
$new_domain_info = mysqli_fetch_assoc(mysqli_query($mysqli, "
SELECT
domains.*,
registrar.vendor_name AS registrar_name,
dnshost.vendor_name AS dnshost_name,
mailhost.vendor_name AS mailhost_name,
webhost.vendor_name AS webhost_name
FROM domains
LEFT JOIN vendors AS registrar ON domains.domain_registrar = registrar.vendor_id
LEFT JOIN vendors AS dnshost ON domains.domain_dnshost = dnshost.vendor_id
LEFT JOIN vendors AS mailhost ON domains.domain_mailhost = mailhost.vendor_id
LEFT JOIN vendors AS webhost ON domains.domain_webhost = webhost.vendor_id
WHERE domain_id = $domain_id AND domain_client_id = $client_id
LIMIT 1
"));
$ignored_columns = [
'domain_updated_at',
'domain_accessed_at',
'domain_registrar',
'domain_webhost',
'domain_dnshost',
'domain_mailhost'
];
foreach ($original_domain_info as $column => $old_value) {
$new_value = $new_domain_info[$column];
if ($old_value != $new_value && !in_array($column, $ignored_columns)) {
$column = escapeSql($column);
$old_value = escapeSql($old_value);
$new_value = escapeSql($new_value);
mysqli_query($mysqli, "INSERT INTO domain_history SET domain_history_column = '$column', domain_history_old_value = '$old_value', domain_history_new_value = '$new_value', domain_history_domain_id = $domain_id");
}
}
logAudit("Domain", "Edit", "$name via API ($api_key_name)", $client_id, $domain_id);
logAudit("API", "Success", "Updated domain $name via API ($api_key_name)", $client_id);
}
}
require_once '../update_output.php';

View File

@@ -1,45 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse ID
$location_id = intval($_POST['location_id']);
// Default
$update_count = false;
if (!empty($location_id)) {
// Fetch location info
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "
SELECT location_name
FROM locations
WHERE location_id = $location_id AND location_client_id = $client_id AND location_archived_at IS NULL
LIMIT 1
"));
if ($row) {
$location_name = escapeSql($row['location_name']);
// Archive location
$update_sql = mysqli_query($mysqli, "
UPDATE locations SET
location_primary = 0,
location_archived_at = NOW()
WHERE location_id = $location_id AND location_client_id = $client_id
");
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
// Logging
logAudit("Location", "Archive", "$location_name archived via API ($api_key_name)", $client_id, $location_id);
logAudit("API", "Success", "Archived location $location_name via API ($api_key_name)", $client_id);
}
}
}
// Output
require_once '../update_output.php';

View File

@@ -1,30 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse ID
$location_id = intval($_POST['location_id']);
// Default
$delete_count = false;
if (!empty($location_id)) {
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT location_name FROM locations WHERE location_id = $location_id AND location_client_id = $client_id AND location_archived_at IS NOT NULL LIMIT 1"));
$location_name = escapeSql($row['location_name'] ?? '');
$delete_sql = mysqli_query($mysqli, "DELETE FROM locations WHERE location_id = $location_id AND location_client_id = $client_id AND location_archived_at IS NOT NULL LIMIT 1");
// Check delete & get affected rows
if ($delete_sql && !empty($location_name)) {
$delete_count = mysqli_affected_rows($mysqli);
// Logging
logAudit("Location", "Delete", "$location_name via API ($api_key_name)", $client_id, $location_id);
}
}
// Output
require_once '../delete_output.php';

View File

@@ -8,11 +8,11 @@ require_once '../require_get_method.php';
if (isset($_GET['location_id'])) { if (isset($_GET['location_id'])) {
// Location via ID (single) // Location via ID (single)
$id = intval($_GET['location_id']); $id = intval($_GET['location_id']);
$sql = mysqli_query($mysqli, "SELECT * FROM locations WHERE location_id = '$id' AND location_archived_at IS NULL AND 1=1 " . apiClientScopeSql('location_client_id') . ""); $sql = mysqli_query($mysqli, "SELECT * FROM locations WHERE location_id = '$id' AND 1=1 " . apiClientScopeSql('location_client_id') . "");
} else { } else {
// All locations (by client ID if given, or all in general if key permits) // All locations (by client ID if given, or all in general if key permits)
$sql = mysqli_query($mysqli, "SELECT * FROM locations WHERE location_archived_at IS NULL AND 1=1 " . apiClientScopeSql('location_client_id') . " ORDER BY location_id LIMIT $limit OFFSET $offset"); $sql = mysqli_query($mysqli, "SELECT * FROM locations WHERE 1=1 " . apiClientScopeSql('location_client_id') . " ORDER BY location_id LIMIT $limit OFFSET $offset");
} }
// Output // Output

View File

@@ -1,43 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse ID
$location_id = intval($_POST['location_id']);
// Default
$update_count = false;
if (!empty($location_id)) {
// Fetch location info
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "
SELECT location_name
FROM locations
WHERE location_id = $location_id AND location_client_id = $client_id AND location_archived_at IS NOT NULL
LIMIT 1
"));
if ($row) {
$location_name = escapeSql($row['location_name']);
// Unarchive location
$update_sql = mysqli_query($mysqli, "
UPDATE locations SET location_archived_at = NULL
WHERE location_id = $location_id AND location_client_id = $client_id
");
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
// Logging
logAudit("Location", "Unarchive", "$location_name unarchived via API ($api_key_name)", $client_id, $location_id);
logAudit("API", "Success", "Unarchived location $location_name via API ($api_key_name)", $client_id);
}
}
}
// Output
require_once '../update_output.php';

View File

@@ -1,42 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse ID
$location_id = intval($_POST['location_id']);
// Default
$update_count = false;
if (!empty($location_id)) {
$location_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM locations WHERE location_id = '$location_id' AND location_client_id = $client_id AND location_archived_at IS NULL LIMIT 1"));
// Variable assignment from POST - assigning the current database value if a value is not provided
require_once 'location_model.php';
if ($location_row) {
// Reset primary location
if ($primary == 1) {
mysqli_query($mysqli, "UPDATE locations SET location_primary = 0 WHERE location_client_id = $client_id");
}
$update_sql = mysqli_query($mysqli, "UPDATE locations SET location_name = '$name', location_description = '$description', location_country = '$country', location_address = '$address', location_city = '$city', location_state = '$state', location_zip = '$zip', location_hours = '$hours', location_notes = '$notes', location_primary = '$primary' WHERE location_id = $location_id AND location_client_id = $client_id AND location_archived_at IS NULL LIMIT 1");
// Check update & get affected rows
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
// Logging
logAudit("Location", "Edit", "$name via API ($api_key_name)", $client_id, $location_id);
logAudit("API", "Success", "Edited location $name via API ($api_key_name)", $client_id);
}
}
}
// Output
require_once '../update_output.php';

View File

@@ -1,43 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse ID
$network_id = intval($_POST['network_id']);
// Default
$update_count = false;
if (!empty($network_id)) {
// Fetch network info
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "
SELECT network_name
FROM networks
WHERE network_id = $network_id AND network_client_id = $client_id AND network_archived_at IS NULL
LIMIT 1
"));
if ($row) {
$network_name = escapeSql($row['network_name']);
// Archive network
$update_sql = mysqli_query($mysqli, "
UPDATE networks SET network_archived_at = NOW()
WHERE network_id = $network_id AND network_client_id = $client_id AND network_archived_at IS NULL
");
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
// Logging
logAudit("Network", "Archive", "$network_name archived via API ($api_key_name)", $client_id, $network_id);
logAudit("API", "Success", "Archived network $network_name via API ($api_key_name)", $client_id);
}
}
}
// Output
require_once '../update_output.php';

View File

@@ -1,30 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse Info
require_once 'network_model.php';
// Default
$insert_id = false;
if (!empty($name) && !empty($client_id) && !empty($network)) {
// Insert network
$insert_sql = mysqli_query($mysqli, "INSERT INTO networks SET network_name = '$name', network_description = '$description', network_vlan = $vlan, network = '$network', network_gateway = '$gateway', network_primary_dns = '$primary_dns', network_secondary_dns = '$secondary_dns', network_dhcp_range = '$dhcp_range', network_notes = '$notes', network_location_id = $location_id, network_client_id = $client_id");
// Check insert & get insert ID
if ($insert_sql) {
$insert_id = mysqli_insert_id($mysqli);
// Logging
logAudit("Network", "Create", "$name via API ($api_key_name)", $client_id, $insert_id);
logAudit("API", "Success", "Created network $name via API ($api_key_name)", $client_id);
}
}
// Output
require_once '../create_output.php';

View File

@@ -1,83 +0,0 @@
<?php
// Variable assignment from POST (or: blank/from DB is updating)
if (isset($_POST['network_name'])) {
$name = escapeSql($_POST['network_name']);
} elseif ($network_row) {
$name = mysqli_real_escape_string($mysqli, $network_row['network_name']);
} else {
$name = '';
}
if (isset($_POST['network_description'])) {
$description = escapeSql($_POST['network_description']);
} elseif ($network_row) {
$description = mysqli_real_escape_string($mysqli, $network_row['network_description']);
} else {
$description = '';
}
if (isset($_POST['network'])) {
$network = escapeSql($_POST['network']);
} elseif ($network_row) {
$network = mysqli_real_escape_string($mysqli, $network_row['network']);
} else {
$network = '';
}
if (isset($_POST['network_vlan'])) {
$vlan = intval($_POST['network_vlan']);
} elseif ($network_row) {
$vlan = intval($network_row['network_vlan']);
} else {
$vlan = 0;
}
if (isset($_POST['network_gateway'])) {
$gateway = escapeSql($_POST['network_gateway']);
} elseif ($network_row) {
$gateway = mysqli_real_escape_string($mysqli, $network_row['network_gateway']);
} else {
$gateway = '';
}
if (isset($_POST['network_primary_dns'])) {
$primary_dns = escapeSql($_POST['network_primary_dns']);
} elseif ($network_row) {
$primary_dns = mysqli_real_escape_string($mysqli, $network_row['network_primary_dns']);
} else {
$primary_dns = '';
}
if (isset($_POST['network_secondary_dns'])) {
$secondary_dns = escapeSql($_POST['network_secondary_dns']);
} elseif ($network_row) {
$secondary_dns = mysqli_real_escape_string($mysqli, $network_row['network_secondary_dns']);
} else {
$secondary_dns = '';
}
if (isset($_POST['network_dhcp_range'])) {
$dhcp_range = escapeSql($_POST['network_dhcp_range']);
} elseif ($network_row) {
$dhcp_range = mysqli_real_escape_string($mysqli, $network_row['network_dhcp_range']);
} else {
$dhcp_range = '';
}
if (isset($_POST['network_notes'])) {
$notes = escapeSql($_POST['network_notes']);
} elseif ($network_row) {
$notes = mysqli_real_escape_string($mysqli, $network_row['network_notes']);
} else {
$notes = '';
}
if (isset($_POST['network_location_id'])) {
$location_id = intval($_POST['network_location_id']);
} elseif ($network_row) {
$location_id = intval($network_row['network_location_id']);
} else {
$location_id = 0;
}

View File

@@ -8,16 +8,16 @@ require_once '../require_get_method.php';
// Specific network via ID (single) // Specific network via ID (single)
if (isset($_GET['network_id'])) { if (isset($_GET['network_id'])) {
$id = intval($_GET['network_id']); $id = intval($_GET['network_id']);
$sql = mysqli_query($mysqli, "SELECT * FROM networks WHERE network_id = '$id' AND network_archived_at IS NULL AND 1=1 " . apiClientScopeSql('network_client_id') . ""); $sql = mysqli_query($mysqli, "SELECT * FROM networks WHERE network_id = '$id' AND 1=1 " . apiClientScopeSql('network_client_id') . "");
} elseif (isset($_GET['network_name'])) { } elseif (isset($_GET['network_name'])) {
// Network by name // Network by name
$name = mysqli_real_escape_string($mysqli, $_GET['network_name']); $name = mysqli_real_escape_string($mysqli, $_GET['network_name']);
$sql = mysqli_query($mysqli, "SELECT * FROM networks WHERE network_name = '$name' AND network_archived_at IS NULL AND 1=1 " . apiClientScopeSql('network_client_id') . " ORDER BY network_id LIMIT $limit OFFSET $offset"); $sql = mysqli_query($mysqli, "SELECT * FROM networks WHERE network_name = '$name' AND 1=1 " . apiClientScopeSql('network_client_id') . " ORDER BY network_id LIMIT $limit OFFSET $offset");
} else { } else {
// All networks (by client ID or all in general if key permits) // All networks (by client ID or all in general if key permits)
$sql = mysqli_query($mysqli, "SELECT * FROM networks WHERE network_archived_at IS NULL AND 1=1 " . apiClientScopeSql('network_client_id') . " ORDER BY network_id LIMIT $limit OFFSET $offset"); $sql = mysqli_query($mysqli, "SELECT * FROM networks WHERE 1=1 " . apiClientScopeSql('network_client_id') . " ORDER BY network_id LIMIT $limit OFFSET $offset");
} }
// Output // Output

View File

@@ -1,43 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse ID
$network_id = intval($_POST['network_id']);
// Default
$update_count = false;
if (!empty($network_id)) {
// Fetch network info
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "
SELECT network_name
FROM networks
WHERE network_id = $network_id AND network_client_id = $client_id AND network_archived_at IS NOT NULL
LIMIT 1
"));
if ($row) {
$network_name = escapeSql($row['network_name']);
// Unarchive network
$update_sql = mysqli_query($mysqli, "
UPDATE networks SET network_archived_at = NULL
WHERE network_id = $network_id AND network_client_id = $client_id
");
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
// Logging
logAudit("Network", "Unarchive", "$network_name unarchived via API ($api_key_name)", $client_id, $network_id);
logAudit("API", "Success", "Unarchived network $network_name via API ($api_key_name)", $client_id);
}
}
}
// Output
require_once '../update_output.php';

View File

@@ -1,36 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse ID
$network_id = intval($_POST['network_id']);
// Default
$update_count = false;
if (!empty($network_id)) {
$network_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM networks WHERE network_id = '$network_id' AND network_client_id = $client_id AND network_archived_at IS NULL LIMIT 1"));
// Variable assignment from POST - assigning the current database value if a value is not provided
require_once 'network_model.php';
if ($network_row) {
$update_sql = mysqli_query($mysqli, "UPDATE networks SET network_name = '$name', network_description = '$description', network_vlan = $vlan, network = '$network', network_gateway = '$gateway', network_primary_dns = '$primary_dns', network_secondary_dns = '$secondary_dns', network_dhcp_range = '$dhcp_range', network_notes = '$notes', network_location_id = $location_id WHERE network_id = $network_id AND network_client_id = $client_id AND network_archived_at IS NULL LIMIT 1");
// Check update & get affected rows
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
// Logging
logAudit("Network", "Edit", "$name via API ($api_key_name)", $client_id, $network_id);
logAudit("API", "Success", "Edited network $name via API ($api_key_name)", $client_id);
}
}
}
// Output
require_once '../update_output.php';

View File

@@ -1,33 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse ID
$vendor_id = intval($_POST['vendor_id']);
// Default
$update_count = false;
if (!empty($vendor_id)) {
// Fetch vendor info
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT vendor_name FROM vendors WHERE vendor_id = $vendor_id AND vendor_client_id = $client_id AND vendor_archived_at IS NULL LIMIT 1"));
if ($row) {
$vendor_name = escapeSql($row['vendor_name']);
// Archive vendor
$update_sql = mysqli_query($mysqli, "UPDATE vendors SET vendor_archived_at = NOW() WHERE vendor_id = $vendor_id AND vendor_client_id = $client_id AND vendor_archived_at IS NULL");
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
// Logging
logAudit("Vendor", "Archive", "$vendor_name archived via API ($api_key_name)", $client_id, $vendor_id);
}
}
}
// Output
require_once '../update_output.php';

View File

@@ -1,29 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse Info
require_once 'vendor_model.php';
// Default
$insert_id = false;
if (!empty($name)) {
// Insert vendor
$insert_sql = mysqli_query($mysqli, "INSERT INTO vendors SET vendor_name = '$name', vendor_description = '$description', vendor_contact_name = '$contact_name', vendor_phone_country_code = '$phone_country_code', vendor_phone = '$phone', vendor_extension = '$extension', vendor_email = '$email', vendor_website = '$website', vendor_hours = '$hours', vendor_sla = '$sla', vendor_code = '$code', vendor_account_number = '$account_number', vendor_notes = '$notes', vendor_client_id = $client_id");
// Check insert & get insert ID
if ($insert_sql) {
$insert_id = mysqli_insert_id($mysqli);
// Logging
logAudit("Vendor", "Create", "$name via API ($api_key_name)", $client_id, $insert_id);
logAudit("API", "Success", "Created vendor $name via API ($api_key_name)", $client_id, $insert_id);
}
}
// Output
require_once '../create_output.php';

View File

@@ -7,11 +7,11 @@ require_once '../require_get_method.php';
// Specific vendor via their ID (single) // Specific vendor via their ID (single)
if (isset($_GET['vendor_id'])) { if (isset($_GET['vendor_id'])) {
$id = intval($_GET['vendor_id']); $id = intval($_GET['vendor_id']);
$sql = mysqli_query($mysqli, "SELECT * FROM vendors WHERE vendor_id = '$id' AND vendor_archived_at IS NULL AND 1=1 " . apiClientScopeSql('vendor_client_id') . ""); $sql = mysqli_query($mysqli, "SELECT * FROM vendors WHERE vendor_id = '$id' AND 1=1 " . apiClientScopeSql('vendor_client_id') . "");
} else { } else {
// All Vendors (by client ID or all in general if key permits) // All Vendors (by client ID or all in general if key permits)
$sql = mysqli_query($mysqli, "SELECT * FROM vendors WHERE vendor_archived_at IS NULL AND 1=1 " . apiClientScopeSql('vendor_client_id') . " ORDER BY vendor_id LIMIT $limit OFFSET $offset"); $sql = mysqli_query($mysqli, "SELECT * FROM vendors WHERE 1=1 " . apiClientScopeSql('vendor_client_id') . " ORDER BY vendor_id LIMIT $limit OFFSET $offset");
} }
// Output // Output

View File

@@ -1,33 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse ID
$vendor_id = intval($_POST['vendor_id']);
// Default
$update_count = false;
if (!empty($vendor_id)) {
// Fetch vendor info
$row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT vendor_name FROM vendors WHERE vendor_id = $vendor_id AND vendor_client_id = $client_id AND vendor_archived_at IS NOT NULL LIMIT 1"));
if ($row) {
$vendor_name = escapeSql($row['vendor_name']);
// Un-archive vendor
$update_sql = mysqli_query($mysqli, "UPDATE vendors SET vendor_archived_at = NULL WHERE vendor_id = $vendor_id AND vendor_client_id = $client_id AND vendor_archived_at IS NOT NULL");
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
// Logging
logAudit("Vendor", "Unarchive", "$vendor_name unarchived via API ($api_key_name)", $client_id, $vendor_id);
}
}
}
// Output
require_once '../update_output.php';

View File

@@ -1,35 +0,0 @@
<?php
require_once '../validate_api_key.php';
require_once '../require_post_method.php';
// Parse ID
$vendor_id = intval($_POST['vendor_id']);
// Default
$update_count = false;
if (!empty($vendor_id)) {
$vendor_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT * FROM vendors WHERE vendor_id = $vendor_id AND vendor_client_id = $client_id AND vendor_archived_at IS NULL LIMIT 1"));
// Variable assignment from POST - assigning the current database value if a value is not provided
require_once 'vendor_model.php';
if ($vendor_row) {
$update_sql = mysqli_query($mysqli, "UPDATE vendors SET vendor_name = '$name', vendor_description = '$description', vendor_contact_name = '$contact_name', vendor_phone_country_code = '$phone_country_code', vendor_phone = '$phone', vendor_extension = '$extension', vendor_email = '$email', vendor_website = '$website', vendor_hours = '$hours', vendor_sla = '$sla', vendor_code = '$code', vendor_account_number = '$account_number', vendor_notes = '$notes' WHERE vendor_id = $vendor_id AND vendor_client_id = $client_id AND vendor_archived_at IS NULL LIMIT 1");
// Check update & get affected rows
if ($update_sql) {
$update_count = mysqli_affected_rows($mysqli);
// Logging
logAudit("Vendor", "Edit", "$name via API ($api_key_name)", $client_id, $vendor_id);
logAudit("API", "Success", "Edited vendor $name via API ($api_key_name)", $client_id, $vendor_id);
}
}
}
// Output
require_once '../update_output.php';

View File

@@ -1,107 +0,0 @@
<?php
// Variable assignment from POST (or: blank/from DB is updating)
if (isset($_POST['vendor_name'])) {
$name = escapeSql($_POST['vendor_name']);
} elseif ($vendor_row) {
$name = mysqli_real_escape_string($mysqli, $vendor_row['vendor_name']);
} else {
$name = '';
}
if (isset($_POST['vendor_description'])) {
$description = escapeSql($_POST['vendor_description']);
} elseif ($vendor_row) {
$description = mysqli_real_escape_string($mysqli, $vendor_row['vendor_description']);
} else {
$description = '';
}
if (isset($_POST['vendor_account_number'])) {
$account_number = escapeSql($_POST['vendor_account_number']);
} elseif ($vendor_row) {
$account_number = mysqli_real_escape_string($mysqli, $vendor_row['vendor_account_number']);
} else {
$account_number = '';
}
if (isset($_POST['vendor_contact_name'])) {
$contact_name = escapeSql($_POST['vendor_contact_name']);
} elseif ($vendor_row) {
$contact_name = mysqli_real_escape_string($mysqli, $vendor_row['vendor_contact_name']);
} else {
$contact_name = '';
}
if (isset($_POST['vendor_phone_country_code'])) {
$phone_country_code = preg_replace("/[^0-9]/", '', $_POST['vendor_phone_country_code']);
} elseif ($vendor_row) {
$phone_country_code = mysqli_real_escape_string($mysqli, $vendor_row['vendor_phone_country_code']);
} else {
$phone_country_code = '';
}
if (isset($_POST['vendor_phone'])) {
$phone = preg_replace("/[^0-9]/", '', $_POST['vendor_phone']);
} elseif ($vendor_row) {
$phone = mysqli_real_escape_string($mysqli, $vendor_row['vendor_phone']);
} else {
$phone = '';
}
if (isset($_POST['vendor_extension'])) {
$extension = preg_replace("/[^0-9]/", '', $_POST['vendor_extension']);
} elseif ($vendor_row) {
$extension = mysqli_real_escape_string($mysqli, $vendor_row['vendor_extension']);
} else {
$extension = '';
}
if (isset($_POST['vendor_email'])) {
$email = escapeSql($_POST['vendor_email']);
} elseif ($vendor_row) {
$email = mysqli_real_escape_string($mysqli, $vendor_row['vendor_email']);
} else {
$email = '';
}
if (isset($_POST['vendor_website'])) {
$website = preg_replace("(^https?://)", "", escapeSql($_POST['vendor_website']));
} elseif ($vendor_row) {
$website = mysqli_real_escape_string($mysqli, $vendor_row['vendor_website']);
} else {
$website = '';
}
if (isset($_POST['vendor_hours'])) {
$hours = escapeSql($_POST['vendor_hours']);
} elseif ($vendor_row) {
$hours = mysqli_real_escape_string($mysqli, $vendor_row['vendor_hours']);
} else {
$hours = '';
}
if (isset($_POST['vendor_sla'])) {
$sla = escapeSql($_POST['vendor_sla']);
} elseif ($vendor_row) {
$sla = mysqli_real_escape_string($mysqli, $vendor_row['vendor_sla']);
} else {
$sla = '';
}
if (isset($_POST['vendor_code'])) {
$code = escapeSql($_POST['vendor_code']);
} elseif ($vendor_row) {
$code = mysqli_real_escape_string($mysqli, $vendor_row['vendor_code']);
} else {
$code = '';
}
if (isset($_POST['vendor_notes'])) {
$notes = escapeSql($_POST['vendor_notes']);
} elseif ($vendor_row) {
$notes = mysqli_real_escape_string($mysqli, $vendor_row['vendor_notes']);
} else {
$notes = '';
}

View File

@@ -68,7 +68,7 @@ header("X-Frame-Options: DENY"); // Legacy
<div class="container"> <div class="container">
<a class="navbar-brand" href="index.php"><?= escapeHtml($session_company_name) ?></a> <a class="navbar-brand" href="index.php"><?= escapeHtml($session_company_name) ?></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent">
<i class="fas fa-bars text-white"></i> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <div class="collapse navbar-collapse" id="navbarSupportedContent">
@@ -166,9 +166,8 @@ header("X-Frame-Options: DENY"); // Legacy
<!-- Page content container --> <!-- Page content container -->
<div class="container mt-4"> <div class="container mt-4">
<!-- Flex row rather than col-md-1/col-md-11 --> <div class="row mb-3">
<div class="d-flex flex-wrap align-items-center gap-2 mb-3"> <div class="col-md-1 text-center">
<div class="flex-shrink-0">
<?php if (!empty($session_contact_photo)) { ?> <?php if (!empty($session_contact_photo)) { ?>
<img src="/uploads/clients/<?= $session_client_id ?>/<?= $session_contact_photo ?>" alt="..." height="50" width="50" class="rounded-circle img-fluid"> <img src="/uploads/clients/<?= $session_client_id ?>/<?= $session_contact_photo ?>" alt="..." height="50" width="50" class="rounded-circle img-fluid">
@@ -180,11 +179,12 @@ header("X-Frame-Options: DENY"); // Legacy
<?php } ?> <?php } ?>
</div> </div>
<h4 class="mb-0 flex-grow-1">Welcome, <strong><?= stripslashes(escapeHtml($session_contact_name)) ?></strong>!</h4> <div class="col-md-11 p-0">
<?php if ($session_company_logo) { ?> <?php if ($session_company_logo) { ?>
<img height="48" width="142" class="img-fluid flex-shrink-0" src="<?= "/uploads/settings/$session_company_logo" ?>"> <img height="48" width="142" class="img-fluid float-end" src="<?= "/uploads/settings/$session_company_logo" ?>">
<?php } ?> <?php } ?>
<h4>Welcome, <strong><?= stripslashes(escapeHtml($session_contact_name)) ?></strong>!</h4>
</div>
</div> </div>
<hr> <hr>

View File

@@ -674,7 +674,7 @@ if ($config_send_invoice_reminders == 1) {
// Logging // Logging
// logAudit("Cron", "Task", "Cron created notifications for past due invoices and sent out notifications to the primary and billing contacts email"); // logAudit("Cron", "Task", "Cron created notifications for past due invoices and sent out notifications to the primary and billing contacts email");
// Generate & Send Recurring Invoices that match todays date and are active // Send Recurring Invoices that match todays date and are active
//Loop through all recurring that match today's date and is active //Loop through all recurring that match today's date and is active
$sql_recurring_invoices = mysqli_query($mysqli, "SELECT client_name, client_net_terms, recurring_invoice_amount, recurring_invoice_category_id, $sql_recurring_invoices = mysqli_query($mysqli, "SELECT client_name, client_net_terms, recurring_invoice_amount, recurring_invoice_category_id,
@@ -682,7 +682,7 @@ $sql_recurring_invoices = mysqli_query($mysqli, "SELECT client_name, client_net_
recurring_invoice_discount_amount, recurring_invoice_email_notify, recurring_invoice_discount_amount, recurring_invoice_email_notify,
recurring_invoice_frequency, recurring_invoice_id, recurring_invoice_last_sent, recurring_invoice_frequency, recurring_invoice_id, recurring_invoice_last_sent,
recurring_invoice_next_date, recurring_invoice_note, recurring_invoice_scope, recurring_invoice_next_date, recurring_invoice_note, recurring_invoice_scope,
recurring_invoice_status, recurring_invoice_auto_send, recurring_payment_account_id, recurring_payment_currency_code, recurring_invoice_status, recurring_payment_account_id, recurring_payment_currency_code,
recurring_payment_method, recurring_payment_recurring_invoice_id FROM recurring_invoices recurring_payment_method, recurring_payment_recurring_invoice_id FROM recurring_invoices
LEFT JOIN recurring_payments ON recurring_invoice_id = recurring_payment_recurring_invoice_id LEFT JOIN recurring_payments ON recurring_invoice_id = recurring_payment_recurring_invoice_id
LEFT JOIN clients ON client_id = recurring_invoice_client_id LEFT JOIN clients ON client_id = recurring_invoice_client_id
@@ -701,7 +701,6 @@ while ($row = mysqli_fetch_assoc($sql_recurring_invoices)) {
$recurring_invoice_amount = floatval($row['recurring_invoice_amount']); $recurring_invoice_amount = floatval($row['recurring_invoice_amount']);
$recurring_invoice_currency_code = escapeSql($row['recurring_invoice_currency_code']); $recurring_invoice_currency_code = escapeSql($row['recurring_invoice_currency_code']);
$recurring_invoice_note = escapeSql($row['recurring_invoice_note']); $recurring_invoice_note = escapeSql($row['recurring_invoice_note']);
$recurring_invoice_auto_send = intval($row['recurring_invoice_auto_send']);
$recurring_invoice_email_notify = intval($row['recurring_invoice_email_notify']); $recurring_invoice_email_notify = intval($row['recurring_invoice_email_notify']);
$category_id = intval($row['recurring_invoice_category_id']); $category_id = intval($row['recurring_invoice_category_id']);
$client_id = intval($row['recurring_invoice_client_id']); $client_id = intval($row['recurring_invoice_client_id']);
@@ -727,7 +726,7 @@ while ($row = mysqli_fetch_assoc($sql_recurring_invoices)) {
//Generate a unique URL key for clients to access //Generate a unique URL key for clients to access
$url_key = randomString(32); $url_key = randomString(32);
mysqli_query($mysqli, "INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $new_invoice_number, invoice_scope = '$recurring_invoice_scope', invoice_date = CURDATE(), invoice_due = DATE_ADD(CURDATE(), INTERVAL $client_net_terms day), invoice_discount_amount = $recurring_invoice_discount_amount, invoice_amount = $recurring_invoice_amount, invoice_currency_code = '$recurring_invoice_currency_code', invoice_note = '$recurring_invoice_note', invoice_category_id = $category_id, invoice_status = 'Draft', invoice_url_key = '$url_key', invoice_recurring_invoice_id = $recurring_invoice_id, invoice_client_id = $client_id"); mysqli_query($mysqli, "INSERT INTO invoices SET invoice_prefix = '$config_invoice_prefix', invoice_number = $new_invoice_number, invoice_scope = '$recurring_invoice_scope', invoice_date = CURDATE(), invoice_due = DATE_ADD(CURDATE(), INTERVAL $client_net_terms day), invoice_discount_amount = $recurring_invoice_discount_amount, invoice_amount = $recurring_invoice_amount, invoice_currency_code = '$recurring_invoice_currency_code', invoice_note = '$recurring_invoice_note', invoice_category_id = $category_id, invoice_status = 'Sent', invoice_url_key = '$url_key', invoice_recurring_invoice_id = $recurring_invoice_id, invoice_client_id = $client_id");
$new_invoice_id = mysqli_insert_id($mysqli); $new_invoice_id = mysqli_insert_id($mysqli);
@@ -752,17 +751,13 @@ while ($row = mysqli_fetch_assoc($sql_recurring_invoices)) {
} }
mysqli_query($mysqli, "INSERT INTO history SET history_status = 'Draft', history_description = 'Invoice Generated from Recurring!', history_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");
if ($recurring_invoice_auto_send == 1) {
appNotify("Recurring Sent", "Recurring Invoice $config_invoice_prefix$new_invoice_number for $client_name Sent", "/agent/invoice.php?invoice_id=$new_invoice_id", $client_id); appNotify("Recurring Sent", "Recurring Invoice $config_invoice_prefix$new_invoice_number for $client_name Sent", "/agent/invoice.php?invoice_id=$new_invoice_id", $client_id);
} else {
appNotify("Recurring Generated", "Recurring Invoice $config_invoice_prefix$new_invoice_number for $client_name Generated", "/agent/invoice.php?invoice_id=$new_invoice_id", $client_id);
}
triggerCustomAction('invoice_create', $new_invoice_id); triggerCustomAction('invoice_create', $new_invoice_id);
// Update recurring dates //Update recurring dates
mysqli_query($mysqli, "UPDATE recurring_invoices SET recurring_invoice_last_sent = CURDATE(), recurring_invoice_next_date = DATE_ADD(CURDATE(), INTERVAL 1 $recurring_invoice_frequency) WHERE recurring_invoice_id = $recurring_invoice_id"); mysqli_query($mysqli, "UPDATE recurring_invoices SET recurring_invoice_last_sent = CURDATE(), recurring_invoice_next_date = DATE_ADD(CURDATE(), INTERVAL 1 $recurring_invoice_frequency) WHERE recurring_invoice_id = $recurring_invoice_id");
@@ -788,8 +783,7 @@ while ($row = mysqli_fetch_assoc($sql_recurring_invoices)) {
$contact_name = escapeSql($row['contact_name']); $contact_name = escapeSql($row['contact_name']);
$contact_email = escapeSql($row['contact_email']); $contact_email = escapeSql($row['contact_email']);
// Send invoice email if: (1) Global recurring auto-send is on, (2) the recurring invoice is set to auto-send (than just be a draft), and (3) the recurring invoice is opted in for the client to be notified if ($config_recurring_auto_send_invoice == 1 && $recurring_invoice_email_notify == 1) {
if ($config_recurring_auto_send_invoice == 1 && $recurring_invoice_auto_send == 1 && $recurring_invoice_email_notify == 1) {
$subject = "Invoice $invoice_prefix$invoice_number"; $subject = "Invoice $invoice_prefix$invoice_number";
$body = "Hello $contact_name,<br><br>An invoice regarding \"$invoice_scope\" has been generated. Please view the details below.<br><br>Invoice: $invoice_prefix$invoice_number<br>Issue Date: $invoice_date<br>Total: " . numfmt_format_currency($currency_format, $invoice_amount, $recurring_invoice_currency_code) . "<br>Due Date: $invoice_due<br><br><br>To view your invoice, please click <a href=\'https://$config_base_url/guest/guest_view_invoice.php?invoice_id=$new_invoice_id&url_key=$invoice_url_key\'>here</a>.<br><br><br>--<br>$company_name - Billing<br>$config_invoice_from_email<br>$company_phone"; $body = "Hello $contact_name,<br><br>An invoice regarding \"$invoice_scope\" has been generated. Please view the details below.<br><br>Invoice: $invoice_prefix$invoice_number<br>Issue Date: $invoice_date<br>Total: " . numfmt_format_currency($currency_format, $invoice_amount, $recurring_invoice_currency_code) . "<br>Due Date: $invoice_due<br><br><br>To view your invoice, please click <a href=\'https://$config_base_url/guest/guest_view_invoice.php?invoice_id=$new_invoice_id&url_key=$invoice_url_key\'>here</a>.<br><br><br>--<br>$company_name - Billing<br>$config_invoice_from_email<br>$company_phone";
@@ -857,17 +851,6 @@ while ($row = mysqli_fetch_assoc($sql_invalid_recurring_invoices)) {
} }
// End Flag any active recurring "next run" dates that are in the past // End Flag any active recurring "next run" dates that are in the past
// Start Notify draft invoices needing review
$sql_invoices_pending_send = mysqli_query($mysqli,"SELECT invoice_id FROM invoices WHERE invoice_status = 'Draft'");
$invoices_pending_send = mysqli_num_rows($sql_invoices_pending_send);
if ($invoices_pending_send > 0) {
appNotify("Draft Invoices", "There are $invoices_pending_send draft invoices pending review", "/agent/invoices.php?&status=Draft");
}
// End Notify draft invoices needing review
// Start Recurring Payments // Start Recurring Payments
$sql_recurring_payments = mysqli_query($mysqli, " $sql_recurring_payments = mysqli_query($mysqli, "

1
db.sql
View File

@@ -2002,7 +2002,6 @@ CREATE TABLE `recurring_invoices` (
`recurring_invoice_amount` decimal(15,2) NOT NULL DEFAULT 0.00, `recurring_invoice_amount` decimal(15,2) NOT NULL DEFAULT 0.00,
`recurring_invoice_currency_code` varchar(200) NOT NULL, `recurring_invoice_currency_code` varchar(200) NOT NULL,
`recurring_invoice_note` text DEFAULT NULL, `recurring_invoice_note` text DEFAULT NULL,
`recurring_invoice_auto_send` tinyint(1) NOT NULL DEFAULT 1,
`recurring_invoice_email_notify` tinyint(1) NOT NULL DEFAULT 1, `recurring_invoice_email_notify` tinyint(1) NOT NULL DEFAULT 1,
`recurring_invoice_created_at` datetime NOT NULL DEFAULT current_timestamp(), `recurring_invoice_created_at` datetime NOT NULL DEFAULT current_timestamp(),
`recurring_invoice_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(), `recurring_invoice_updated_at` datetime DEFAULT NULL ON UPDATE current_timestamp(),

View File

@@ -653,15 +653,7 @@ function itflowInit() {
// Clipboard // Clipboard
itflowStep('clipboard', function () { itflowStep('clipboard', function () {
if (window.itflowClipboard) { var clipboard = new ClipboardJS('.clipboardjs');
window.itflowClipboard.destroy();
}
var modals = document.querySelectorAll('.modal');
var clipboard = new ClipboardJS('.clipboardjs', {
container: modals.length ? modals[modals.length - 1] : document.body
});
window.itflowClipboard = clipboard;
clipboard.on('success', function(e) { clipboard.on('success', function(e) {
flashTooltip(e.trigger, 'Copied!'); flashTooltip(e.trigger, 'Copied!');