mirror of
https://github.com/itflow-org/itflow
synced 2026-08-06 07:37:16 +00:00
Feature: Add Income Bulk Actions (Account, Payment Method and category
This commit is contained in:
@@ -9,6 +9,304 @@ if (!defined('FROM_POST_HANDLER')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// The Income page merges payments and revenues, so its bulk actions have to fan out across both
|
||||
// tables. Selection parsing is shared - see income_model.php.
|
||||
// Gating: these are the multi-row form of the row Edit action on the Income page, so they gate the
|
||||
// same way the payment edit handler does (the strictest of the two row types).
|
||||
|
||||
if (isset($_POST['bulk_edit_income_account'])) {
|
||||
|
||||
validateCSRFToken();
|
||||
|
||||
enforceUserPermission('module_sales', 3);
|
||||
enforceUserPermission('module_financial', 3);
|
||||
|
||||
require_once 'income_model.php';
|
||||
|
||||
$account_id = intval($_POST['bulk_account_id']);
|
||||
|
||||
// Get Account name for logging and Notification - and confirm it is a real, un-archived account
|
||||
$sql_account = mysqli_query($mysqli, "SELECT account_name FROM accounts WHERE account_id = $account_id AND account_archived_at IS NULL LIMIT 1");
|
||||
$row = mysqli_fetch_assoc($sql_account);
|
||||
|
||||
if (!$row || !$income_count) {
|
||||
flashAlert("Nothing to update", 'error');
|
||||
redirect();
|
||||
}
|
||||
|
||||
$account_name = escapeSql($row['account_name']);
|
||||
|
||||
$updated_count = 0;
|
||||
|
||||
// Payments - client comes from the invoice the payment was made against
|
||||
foreach ($payment_ids as $payment_id) {
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT payment_reference, invoice_client_id FROM payments LEFT JOIN invoices ON payment_invoice_id = invoice_id WHERE payment_id = $payment_id AND payment_archived_at IS NULL");
|
||||
$row = mysqli_fetch_assoc($sql);
|
||||
|
||||
if (!$row) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$payment_reference = escapeSql($row['payment_reference']);
|
||||
$client_id = intval($row['invoice_client_id']);
|
||||
|
||||
if ($client_id) {
|
||||
enforceClientAccess($client_id);
|
||||
}
|
||||
|
||||
mysqli_query($mysqli, "UPDATE payments SET payment_account_id = $account_id WHERE payment_id = $payment_id");
|
||||
|
||||
logAudit("Payment", "Edit", "$session_name assigned payment $payment_reference to account $account_name", $client_id, $payment_id);
|
||||
|
||||
$updated_count++;
|
||||
|
||||
}
|
||||
|
||||
// Revenues
|
||||
foreach ($revenue_ids as $revenue_id) {
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT revenue_description, revenue_client_id FROM revenues WHERE revenue_id = $revenue_id AND revenue_archived_at IS NULL");
|
||||
$row = mysqli_fetch_assoc($sql);
|
||||
|
||||
if (!$row) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$revenue_description = escapeSql($row['revenue_description']);
|
||||
$client_id = intval($row['revenue_client_id']);
|
||||
|
||||
if ($client_id) {
|
||||
enforceClientAccess($client_id);
|
||||
}
|
||||
|
||||
mysqli_query($mysqli, "UPDATE revenues SET revenue_account_id = $account_id WHERE revenue_id = $revenue_id");
|
||||
|
||||
logAudit("Revenue", "Edit", "$session_name assigned revenue $revenue_description to account $account_name", $client_id, $revenue_id);
|
||||
|
||||
$updated_count++;
|
||||
|
||||
}
|
||||
|
||||
if ($updated_count) {
|
||||
logAudit("Income", "Bulk Edit", "$session_name assigned $updated_count income record(s) to account $account_name");
|
||||
flashAlert("You assigned account <strong>$account_name</strong> to <strong>$updated_count</strong> income record(s)");
|
||||
} else {
|
||||
flashAlert("No income records were updated", 'error');
|
||||
}
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['bulk_edit_income_category'])) {
|
||||
|
||||
validateCSRFToken();
|
||||
|
||||
enforceUserPermission('module_sales', 3);
|
||||
enforceUserPermission('module_financial', 3);
|
||||
|
||||
require_once 'income_model.php';
|
||||
|
||||
$category_id = intval($_POST['bulk_category_id']);
|
||||
|
||||
// Get Category name for logging and Notification - and confirm it is a live Income category
|
||||
$sql_category = mysqli_query($mysqli, "SELECT category_name FROM categories WHERE category_id = $category_id AND category_type = 'Income' AND category_archived_at IS NULL LIMIT 1");
|
||||
$row = mysqli_fetch_assoc($sql_category);
|
||||
|
||||
if (!$row || !$income_count) {
|
||||
flashAlert("Nothing to update", 'error');
|
||||
redirect();
|
||||
}
|
||||
|
||||
$category_name = escapeSql($row['category_name']);
|
||||
|
||||
$revenue_updated_count = 0;
|
||||
$invoice_updated_count = 0;
|
||||
$skipped_count = 0;
|
||||
|
||||
// Revenues carry their own category
|
||||
foreach ($revenue_ids as $revenue_id) {
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT revenue_description, revenue_client_id FROM revenues WHERE revenue_id = $revenue_id AND revenue_archived_at IS NULL");
|
||||
$row = mysqli_fetch_assoc($sql);
|
||||
|
||||
if (!$row) {
|
||||
$skipped_count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$revenue_description = escapeSql($row['revenue_description']);
|
||||
$client_id = intval($row['revenue_client_id']);
|
||||
|
||||
if ($client_id) {
|
||||
enforceClientAccess($client_id);
|
||||
}
|
||||
|
||||
mysqli_query($mysqli, "UPDATE revenues SET revenue_category_id = $category_id WHERE revenue_id = $revenue_id");
|
||||
|
||||
logAudit("Revenue", "Edit", "$session_name assigned revenue $revenue_description to category $category_name", $client_id, $revenue_id);
|
||||
|
||||
$revenue_updated_count++;
|
||||
|
||||
}
|
||||
|
||||
// A payment has no category of its own - it inherits the one on the invoice it was paid
|
||||
// against, so this writes to the INVOICE. Two selected payments against the same invoice
|
||||
// therefore collapse into a single invoice update, and a payment with no invoice is skipped.
|
||||
$invoice_ids = [];
|
||||
|
||||
foreach ($payment_ids as $payment_id) {
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT payment_invoice_id FROM payments WHERE payment_id = $payment_id AND payment_archived_at IS NULL");
|
||||
$row = mysqli_fetch_assoc($sql);
|
||||
$payment_invoice_id = intval($row['payment_invoice_id'] ?? 0);
|
||||
|
||||
if ($payment_invoice_id) {
|
||||
$invoice_ids[$payment_invoice_id] = $payment_invoice_id;
|
||||
} else {
|
||||
$skipped_count++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
foreach ($invoice_ids as $invoice_id) {
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT invoice_prefix, invoice_number, invoice_client_id FROM invoices WHERE invoice_id = $invoice_id");
|
||||
$row = mysqli_fetch_assoc($sql);
|
||||
|
||||
if (!$row) {
|
||||
$skipped_count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
$invoice_prefix = escapeSql($row['invoice_prefix']);
|
||||
$invoice_number = intval($row['invoice_number']);
|
||||
$client_id = intval($row['invoice_client_id']);
|
||||
|
||||
enforceClientAccess($client_id);
|
||||
|
||||
mysqli_query($mysqli, "UPDATE invoices SET invoice_category_id = $category_id WHERE invoice_id = $invoice_id");
|
||||
|
||||
logAudit("Invoice", "Edit", "$session_name assigned invoice $invoice_prefix$invoice_number to category $category_name", $client_id, $invoice_id);
|
||||
|
||||
$invoice_updated_count++;
|
||||
|
||||
}
|
||||
|
||||
// Spell out the invoice leg - the user selected payments, not invoices
|
||||
$updated_summary = [];
|
||||
if ($revenue_updated_count) {
|
||||
$updated_summary[] = "<strong>$revenue_updated_count</strong> revenue(s)";
|
||||
}
|
||||
if ($invoice_updated_count) {
|
||||
$updated_summary[] = "<strong>$invoice_updated_count</strong> invoice(s) behind the selected payment(s)";
|
||||
}
|
||||
|
||||
if ($updated_summary) {
|
||||
|
||||
logAudit("Income", "Bulk Edit", "$session_name assigned category $category_name to $revenue_updated_count revenue(s) and $invoice_updated_count invoice(s)");
|
||||
|
||||
$skipped_note = '';
|
||||
if ($skipped_count) {
|
||||
$skipped_note = " - <strong>$skipped_count</strong> record(s) skipped";
|
||||
}
|
||||
|
||||
flashAlert("You assigned category <strong>$category_name</strong> to " . implode(' and ', $updated_summary) . $skipped_note);
|
||||
|
||||
} else {
|
||||
flashAlert("No income records were categorised - a payment can only take a category from the invoice it was paid against", 'error');
|
||||
}
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['bulk_edit_income_method'])) {
|
||||
|
||||
validateCSRFToken();
|
||||
|
||||
enforceUserPermission('module_sales', 3);
|
||||
enforceUserPermission('module_financial', 3);
|
||||
|
||||
require_once 'income_model.php';
|
||||
|
||||
// The method is stored by name on both tables, so validate it against the lookup list
|
||||
$payment_method = escapeSql($_POST['bulk_payment_method']);
|
||||
|
||||
$sql_payment_method = mysqli_query($mysqli, "SELECT payment_method_name FROM payment_methods WHERE payment_method_name = '$payment_method' LIMIT 1");
|
||||
$row = mysqli_fetch_assoc($sql_payment_method);
|
||||
|
||||
if (!$row || !$income_count) {
|
||||
flashAlert("Nothing to update", 'error');
|
||||
redirect();
|
||||
}
|
||||
|
||||
$payment_method = escapeSql($row['payment_method_name']);
|
||||
|
||||
$updated_count = 0;
|
||||
|
||||
// Payments - client comes from the invoice the payment was made against
|
||||
foreach ($payment_ids as $payment_id) {
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT payment_reference, invoice_client_id FROM payments LEFT JOIN invoices ON payment_invoice_id = invoice_id WHERE payment_id = $payment_id AND payment_archived_at IS NULL");
|
||||
$row = mysqli_fetch_assoc($sql);
|
||||
|
||||
if (!$row) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$payment_reference = escapeSql($row['payment_reference']);
|
||||
$client_id = intval($row['invoice_client_id']);
|
||||
|
||||
if ($client_id) {
|
||||
enforceClientAccess($client_id);
|
||||
}
|
||||
|
||||
mysqli_query($mysqli, "UPDATE payments SET payment_method = '$payment_method' WHERE payment_id = $payment_id");
|
||||
|
||||
logAudit("Payment", "Edit", "$session_name set payment $payment_reference to payment method $payment_method", $client_id, $payment_id);
|
||||
|
||||
$updated_count++;
|
||||
|
||||
}
|
||||
|
||||
// Revenues
|
||||
foreach ($revenue_ids as $revenue_id) {
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT revenue_description, revenue_client_id FROM revenues WHERE revenue_id = $revenue_id AND revenue_archived_at IS NULL");
|
||||
$row = mysqli_fetch_assoc($sql);
|
||||
|
||||
if (!$row) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$revenue_description = escapeSql($row['revenue_description']);
|
||||
$client_id = intval($row['revenue_client_id']);
|
||||
|
||||
if ($client_id) {
|
||||
enforceClientAccess($client_id);
|
||||
}
|
||||
|
||||
mysqli_query($mysqli, "UPDATE revenues SET revenue_payment_method = '$payment_method' WHERE revenue_id = $revenue_id");
|
||||
|
||||
logAudit("Revenue", "Edit", "$session_name set revenue $revenue_description to payment method $payment_method", $client_id, $revenue_id);
|
||||
|
||||
$updated_count++;
|
||||
|
||||
}
|
||||
|
||||
if ($updated_count) {
|
||||
logAudit("Income", "Bulk Edit", "$session_name set $updated_count income record(s) to payment method $payment_method");
|
||||
flashAlert("You set payment method <strong>$payment_method</strong> on <strong>$updated_count</strong> income record(s)");
|
||||
} else {
|
||||
flashAlert("No income records were updated", 'error');
|
||||
}
|
||||
|
||||
redirect();
|
||||
|
||||
}
|
||||
|
||||
if (isset($_POST['export_income'])) {
|
||||
|
||||
validateCSRFToken();
|
||||
|
||||
Reference in New Issue
Block a user