mirror of https://github.com/itflow-org/itflow
FEATURE: Added Batch Payment to multiple invoices, currently works by paying the oldest invoices firest this can be accessed through client invoices and will show as long as the client has a balance
This commit is contained in:
parent
77b431cb61
commit
82ec1408de
|
|
@ -54,6 +54,9 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
|||
|
||||
<div class="col-md-8">
|
||||
<div class="float-right">
|
||||
<?php if($balance > 0) { ?>
|
||||
<button type="button" class="btn btn-secondary" data-toggle="modal" data-target="#addBulkPaymentModal"><i class="fa fa-credit-card mr-2"></i>Batch Payment</button>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -180,6 +183,8 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
|||
<?php
|
||||
require_once "invoice_add_modal.php";
|
||||
|
||||
require_once "invoice_payment_add_bulk_modal.php";
|
||||
|
||||
require_once "client_invoice_export_modal.php";
|
||||
|
||||
require_once "footer.php";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,152 @@
|
|||
<div class="modal" id="addBulkPaymentModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content bg-dark">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fa fa-fw fa-credit-card mr-2"></i>Make Batch Payment</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="post.php" method="post" autocomplete="off">
|
||||
<input type="hidden" name="client_id" value="<?php echo $client_id; ?>">
|
||||
<input type="hidden" name="balance" value="<?php echo $balance; ?>">
|
||||
<input type="hidden" name="currency_code" value="<?php echo $client_currency_code; ?>">
|
||||
<div class="modal-body bg-white">
|
||||
|
||||
<div class="alert alert-info">
|
||||
<h5>Batch Payment Notice</h5>
|
||||
Batch Payment will settle invoices in order from the oldest to the newest.
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="col-md">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Date <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-calendar"></i></span>
|
||||
</div>
|
||||
<input type="date" class="form-control" name="date" max="2999-12-31" value="<?php echo date("Y-m-d"); ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-md">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Amount <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-dollar-sign"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" inputmode="numeric" pattern="[0-9]*\.?[0-9]{0,2}" name="amount" value="<?php echo number_format($balance, 2, '.', ''); ?>" placeholder="0.00" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Account <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-piggy-bank"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="account" required>
|
||||
<option value="">- Select an Account -</option>
|
||||
<?php
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM accounts LEFT JOIN account_types ON account_types.account_type_id = accounts.account_type WHERE account_type_parent = 1 AND account_archived_at IS NULL ORDER BY account_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$account_type = nullable_htmlentities($row['account_type']);
|
||||
$account_id = intval($row['account_id']);
|
||||
$account_name = nullable_htmlentities($row['account_name']);
|
||||
$opening_balance = floatval($row['opening_balance']);
|
||||
|
||||
$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']);
|
||||
|
||||
$account_balance = $opening_balance + $total_payments + $total_revenues - $total_expenses;
|
||||
|
||||
?>
|
||||
<option <?php if ($config_default_payment_account == $account_id) { echo "selected"; } ?>
|
||||
value="<?php echo $account_id; ?>">
|
||||
<?php echo $account_name; ?> [$<?php echo number_format($account_balance, 2); ?>]
|
||||
</option>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Payment Method <strong class="text-danger">*</strong></label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-money-check-alt"></i></span>
|
||||
</div>
|
||||
<select class="form-control select2" name="payment_method" required>
|
||||
<option value="">- Method of Payment -</option>
|
||||
<?php
|
||||
|
||||
$sql = mysqli_query($mysqli, "SELECT * FROM categories WHERE category_type = 'Payment Method' AND category_archived_at IS NULL ORDER BY category_name ASC");
|
||||
while ($row = mysqli_fetch_array($sql)) {
|
||||
$category_name = nullable_htmlentities($row['category_name']);
|
||||
?>
|
||||
<option <?php if ($config_default_payment_method == $category_name) {
|
||||
echo "selected";
|
||||
} ?>><?php echo $category_name; ?></option>
|
||||
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Reference</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="fa fa-fw fa-file-alt"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="reference" placeholder="Check #, Trans #, etc">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($config_smtp_host) && !empty($contact_email)) { ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Email Receipt</label>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="customControlAutosizing" name="email_receipt" value="1" checked>
|
||||
<label class="custom-control-label" for="customControlAutosizing"><?php echo $contact_email; ?></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php } ?>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer bg-white">
|
||||
<button type="submit" name="add_bulk_payment" class="btn btn-primary text-bold"><i class="fas fa-check mr-2"></i>Pay</button>
|
||||
<button type="button" class="btn btn-light" data-dismiss="modal"><i class="fas fa-times mr-2"></i>Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
138
post/invoice.php
138
post/invoice.php
|
|
@ -732,6 +732,142 @@ if (isset($_POST['add_payment'])) {
|
|||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['add_bulk_payment'])) {
|
||||
|
||||
$client_id = intval($_POST['client_id']);
|
||||
$date = sanitizeInput($_POST['date']);
|
||||
$bulk_payment_amount = floatval($_POST['amount']);
|
||||
$bulk_payment_amount_static = floatval($_POST['amount']);
|
||||
$total_account_balance = floatval($_POST['balance']);
|
||||
$account = intval($_POST['account']);
|
||||
$currency_code = sanitizeInput($_POST['currency_code']);
|
||||
$payment_method = sanitizeInput($_POST['payment_method']);
|
||||
$reference = sanitizeInput($_POST['reference']);
|
||||
$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;
|
||||
}
|
||||
|
||||
// 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 .= mysqli_real_escape_string($mysqli, "<br>Invoice <a href='https://$config_base_url/guest_view_invoice.php?invoice_id=$invoice_id&url_key=$invoice_url_key'>$invoice_prefix$invoice_number</a> - 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 = $row['client_name'];
|
||||
$contact_name = $row['contact_name'];
|
||||
$contact_email = $row['contact_email'];
|
||||
|
||||
$invoice_prefix_escaped = sanitizeInput($row['invoice_prefix']);
|
||||
$contact_name_escaped = sanitizeInput($row['contact_name']);
|
||||
$contact_email_escaped = sanitizeInput($row['contact_email']);
|
||||
|
||||
$sql_company = mysqli_query($mysqli,"SELECT * FROM companies WHERE company_id = 1");
|
||||
$row = mysqli_fetch_array($sql_company);
|
||||
|
||||
$company_name = $row['company_name'];
|
||||
$company_phone = formatPhoneNumber($row['company_phone']);
|
||||
|
||||
// Sanitize Config vars from get_settings.php
|
||||
$config_invoice_from_name_escaped = sanitizeInput($config_invoice_from_name);
|
||||
$config_invoice_from_email_escaped = sanitizeInput($config_invoice_from_email);
|
||||
|
||||
$subject = sanitizeInput("Payment Received - Multiple Invoices");
|
||||
$body = mysqli_real_escape_string($mysqli, "Hello $contact_name,<br><br>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:<br><br>$email_body_invoices<br><br><br>We appreciate your continued business!<br><br>Sincerely,<br>$company_name Billing Department<br>$config_invoice_from_email<br>$company_phone");
|
||||
|
||||
// Queue Mail
|
||||
mysqli_query($mysqli, "INSERT INTO email_queue SET email_recipient = '$contact_email_escaped', email_recipient_name = '$contact_name_escaped', email_from = '$config_invoice_from_email_escaped', email_from_name = '$config_invoice_from_name_escaped', 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_escaped 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"]);
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_payment'])) {
|
||||
$payment_id = intval($_GET['delete_payment']);
|
||||
|
||||
|
|
@ -832,7 +968,7 @@ if (isset($_GET['email_invoice'])) {
|
|||
$balance = $invoice_amount - $amount_paid;
|
||||
|
||||
if ($invoice_status == 'Paid') {
|
||||
$subject = sanitizeInput("Invoice $invoice_prefix$invoice_number Copy");
|
||||
$subject = sanitizeInput("Invoice $invoice_prefix$invoice_number Receipt");
|
||||
$body = mysqli_real_escape_string($mysqli, "Hello $contact_name,<br><br>Please click on the link below to see your invoice marked <b>paid</b>.<br><br><a href='https://$config_base_url/guest_view_invoice.php?invoice_id=$invoice_id&url_key=$invoice_url_key'>Invoice Link</a><br><br><br>~<br>$company_name<br>Billing Department<br>$config_invoice_from_email<br>$company_phone");
|
||||
} else {
|
||||
$subject = sanitizeInput("Invoice $invoice_prefix$invoice_number");
|
||||
|
|
|
|||
Loading…
Reference in New Issue