Improvement and Fixes

Add print now features when generating vouchers and print them immediately after generation.

Add multiple voucher code deletion, admin can mark multiple vouchers codes and delete them.

Fix issue of plan names displays in the voucher table even when voucher are empty, very annoying
This commit is contained in:
Focuslinkstech 2024-12-25 14:52:10 +01:00
parent cf6f89b3f2
commit c5fd7c0249
3 changed files with 266 additions and 71 deletions

View File

@ -51,7 +51,7 @@ switch ($action) {
require_once $dvc;
if (method_exists($dvc, 'sync_customer')) {
(new $p['device'])->sync_customer($c, $p);
}else{
} else {
(new $p['device'])->add_customer($c, $p);
}
} else {
@ -106,24 +106,24 @@ switch ($action) {
$plan = ORM::for_table('tbl_plans')->find_one($planId);
list($bills, $add_cost) = User::getBills($id_customer);
// Tax calculation start
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
// Tax calculation start
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
if ($tax_rate_setting === 'custom') {
$tax_rate = $custom_tax_rate;
} else {
$tax_rate = $tax_rate_setting;
}
if ($tax_rate_setting === 'custom') {
$tax_rate = $custom_tax_rate;
} else {
$tax_rate = $tax_rate_setting;
}
if ($tax_enable === 'yes') {
$tax = Package::tax($plan['price'], $tax_rate);
} else {
$tax = 0;
}
// Tax calculation stop
$total_cost = $plan['price'] + $add_cost + $tax;
if ($tax_enable === 'yes') {
$tax = Package::tax($plan['price'], $tax_rate);
} else {
$tax = 0;
}
// Tax calculation stop
$total_cost = $plan['price'] + $add_cost + $tax;
if ($using == 'balance' && $config['enable_balance'] == 'yes') {
if (!$cust) {
@ -146,7 +146,7 @@ switch ($action) {
if (count($usings) == 0) {
$usings[] = Lang::T('Cash');
}
if ($tax_enable === 'yes') {
if ($tax_enable === 'yes') {
$ui->assign('tax', $tax);
}
$ui->assign('usings', $usings);
@ -172,12 +172,12 @@ switch ($action) {
$server = _post('server');
$planId = _post('plan');
$using = _post('using');
$stoken = _post('stoken');
$svoucher = _post('svoucher');
$plan = ORM::for_table('tbl_plans')->find_one($planId);
$plan = ORM::for_table('tbl_plans')->find_one($planId);
if (!empty(App::getTokenValue($stoken))) {
$username = App::getTokenValue($stoken);
if (!empty(App::getVoucherValue($svoucher))) {
$username = App::getVoucherValue($svoucher);
$in = ORM::for_table('tbl_transactions')->where('username', $username)->order_by_desc('id')->find_one();
Package::createInvoice($in);
$ui->display('invoice.tpl');
@ -195,24 +195,24 @@ switch ($action) {
$cust = User::_info($id_customer);
list($bills, $add_cost) = User::getBills($id_customer);
// Tax calculation start
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
// Tax calculation start
$tax_enable = isset($config['enable_tax']) ? $config['enable_tax'] : 'no';
$tax_rate_setting = isset($config['tax_rate']) ? $config['tax_rate'] : null;
$custom_tax_rate = isset($config['custom_tax_rate']) ? (float)$config['custom_tax_rate'] : null;
if ($tax_rate_setting === 'custom') {
$tax_rate = $custom_tax_rate;
} else {
$tax_rate = $tax_rate_setting;
}
if ($tax_rate_setting === 'custom') {
$tax_rate = $custom_tax_rate;
} else {
$tax_rate = $tax_rate_setting;
}
if ($tax_enable === 'yes') {
$tax = Package::tax($plan['price'], $tax_rate);
} else {
$tax = 0;
}
// Tax calculation stop
$total_cost = $plan['price'] + $add_cost + $tax;
if ($tax_enable === 'yes') {
$tax = Package::tax($plan['price'], $tax_rate);
} else {
$tax = 0;
}
// Tax calculation stop
$total_cost = $plan['price'] + $add_cost + $tax;
if ($using == 'balance' && $config['enable_balance'] == 'yes') {
//$plan = ORM::for_table('tbl_plans')->find_one($planId);
@ -238,7 +238,7 @@ switch ($action) {
}
$in = ORM::for_table('tbl_transactions')->where('username', $cust['username'])->order_by_desc('id')->find_one();
Package::createInvoice($in);
App::setToken($stoken, $cust['username']);
App::setVoucher($svoucher, $cust['username']);
$ui->display('invoice.tpl');
_log('[' . $admin['username'] . ']: ' . 'Recharge ' . $cust['username'] . ' [' . $in['plan_name'] . '][' . Lang::moneyFormat($in['price']) . ']', $admin['user_type'], $admin['id']);
} else {
@ -277,7 +277,7 @@ switch ($action) {
$ui->assign('content', $content);
} else {
$id = _post('id');
if(empty($id)) {
if (empty($id)) {
$id = $routes['2'];
}
$d = ORM::for_table('tbl_transactions')->where('id', $id)->find_one();
@ -425,7 +425,7 @@ switch ($action) {
$ui->assign('_system_menu', 'cards');
$query = ORM::for_table('tbl_plans')
->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan'));
->inner_join('tbl_voucher', ['tbl_plans.id', '=', 'tbl_voucher.id_plan']);
if (!empty($router)) {
$query->where('tbl_voucher.routers', $router);
@ -552,7 +552,7 @@ switch ($action) {
$pagebreak = _post('pagebreak');
$limit = _post('limit');
$vpl = _post('vpl');
$selected_datetime = _post('selected_datetime');
$selected_datetime = _post('selected_datetime');
if (empty($vpl)) {
$vpl = 3;
}
@ -617,11 +617,11 @@ switch ($action) {
$v = $v->where_in('generated_by', $sales)->find_many();
$vc = $vc->where_in('generated_by', $sales)->count();
}
if (!empty($selected_datetime)) {
if (!empty($selected_datetime)) {
$v = ORM::for_table('tbl_voucher')
->where('created_at', $selected_datetime)
->find_many();
}
}
$template = file_get_contents("pages/Voucher.html");
$template = str_replace('[[company_name]]', $config['CompanyName'], $template);
@ -634,14 +634,14 @@ switch ($action) {
$ui->assign('plans', $plans);
$ui->assign('limit', $limit);
$ui->assign('planid', $planid);
$createdate = ORM::for_table('tbl_voucher')
$createdate = ORM::for_table('tbl_voucher')
->select_expr('DISTINCT created_at', 'created_datetime')
->where_not_equal('created_at', '0')
->order_by_desc('created_at')
->find_array();
$ui->assign('createdate', $createdate);
$ui->assign('createdate', $createdate);
$voucher = [];
$n = 1;
@ -658,7 +658,7 @@ switch ($action) {
$ui->assign('voucher', $voucher);
$ui->assign('vc', $vc);
$ui->assign('selected_datetime', $selected_datetime);
$ui->assign('selected_datetime', $selected_datetime);
//for counting pagebreak
$ui->assign('jml', 0);
@ -669,6 +669,7 @@ switch ($action) {
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$type = _post('type');
$plan = _post('plan');
$voucher_format = _post('voucher_format');
@ -676,18 +677,21 @@ switch ($action) {
$server = _post('server');
$numbervoucher = _post('numbervoucher');
$lengthcode = _post('lengthcode');
$printNow = _post('print_now', 'no');
$msg = '';
if ($type == '' or $plan == '' or $server == '' or $numbervoucher == '' or $lengthcode == '') {
$msg .= Lang::T('All field is required') . '<br>';
if (empty($type) || empty($plan) || empty($server) || empty($numbervoucher) || empty($lengthcode)) {
$msg .= Lang::T('All fields are required') . '<br>';
}
if (Validator::UnsignedNumber($numbervoucher) == false) {
if (!Validator::UnsignedNumber($numbervoucher)) {
$msg .= 'The Number of Vouchers must be a number' . '<br>';
}
if (Validator::UnsignedNumber($lengthcode) == false) {
if (!Validator::UnsignedNumber($lengthcode)) {
$msg .= 'The Length Code must be a number' . '<br>';
}
if ($msg == '') {
// Update or create voucher prefix
if (!empty($prefix)) {
$d = ORM::for_table('tbl_appconfig')->where('setting', 'voucher_prefix')->find_one();
if ($d) {
@ -700,11 +704,14 @@ switch ($action) {
$d->save();
}
}
run_hook('create_voucher'); #HOOK
run_hook('create_voucher'); // HOOK
$vouchers = [];
$newVoucherIds = [];
if ($voucher_format == 'numbers') {
if (strlen($lengthcode) < 6) {
$msg .= 'The Length Code must be a more than 6 for numbers' . '<br>';
if ($lengthcode < 6) {
$msg .= 'The Length Code must be more than 6 for numbers' . '<br>';
}
$vouchers = generateUniqueNumericVouchers($numbervoucher, $lengthcode);
} else {
@ -724,12 +731,47 @@ switch ($action) {
$d->type = $type;
$d->routers = $server;
$d->id_plan = $plan;
$d->code = $prefix . $code;
$d->code = "$prefix$code";
$d->user = '0';
$d->status = '0';
$d->generated_by = $admin['id'];
$d->save();
$newVoucherIds[] = $d->id();
}
if ($printNow == 'yes' && count($newVoucherIds) > 0) {
$template = file_get_contents("pages/Voucher.html");
$template = str_replace('[[company_name]]', $config['CompanyName'], $template);
$vouchersToPrint = ORM::for_table('tbl_voucher')
->left_outer_join('tbl_plans', ['tbl_plans.id', '=', 'tbl_voucher.id_plan'])
->where_in('tbl_voucher.id', $newVoucherIds)
->find_many();
$voucherHtmls = [];
$n = 1;
foreach ($vouchersToPrint as $vs) {
$temp = $template;
$temp = str_replace('[[qrcode]]', '<img src="qrcode/?data=' . $vs['code'] . '">', $temp);
$temp = str_replace('[[price]]', Lang::moneyFormat($vs['price']), $temp);
$temp = str_replace('[[voucher_code]]', $vs['code'], $temp);
$temp = str_replace('[[plan]]', $vs['name_plan'], $temp);
$temp = str_replace('[[counter]]', $n, $temp);
$voucherHtmls[] = $temp;
$n++;
}
$vc = count($voucherHtmls);
$ui->assign('voucher', $voucherHtmls);
$ui->assign('vc', $vc);
$ui->assign('jml', 0);
$ui->assign('from_id', 0);
$ui->assign('vpl', '3');
$ui->assign('pagebreak', '12');
$ui->display('print-voucher.tpl');
}
if ($numbervoucher == 1) {
r2(U . 'plan/voucher-view/' . $d->id(), 's', Lang::T('Create Vouchers Successfully'));
}
@ -740,6 +782,43 @@ switch ($action) {
}
break;
case 'voucher-delete-many':
header('Content-Type: application/json');
$admin = Admin::_info();
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$voucherIds = json_decode($_POST['voucherIds'], true);
if (is_array($voucherIds) && !empty($voucherIds)) {
$voucherIds = array_map('intval', $voucherIds);
try {
ORM::for_table('tbl_voucher')
->where_in('id', $voucherIds)
->delete_many();
} catch (Exception $e) {
echo json_encode(['status' => 'error', 'message' => Lang::T('Failed to delete vouchers.')]);
exit;
}
// Return success response
echo json_encode(['status' => 'success', 'message' => Lang::T("Vouchers Deleted Successfully.")]);
exit;
} else {
echo json_encode(['status' => 'error', 'message' => Lang::T("Invalid or missing voucher IDs.")]);
exit;
}
} else {
echo json_encode(['status' => 'error', 'message' => Lang::T("Invalid request method.")]);
}
break;
case 'voucher-view':
$id = $routes[2];
if (in_array($admin['user_type'], ['SuperAdmin', 'Admin'])) {
@ -865,17 +944,17 @@ switch ($action) {
$amount = _post('amount');
$plan = _post('id_plan');
$note = _post('note');
$stoken = _req('stoken');
$svoucher = _req('svoucher');
$c = ORM::for_table('tbl_customers')->find_one($user);
if (App::getTokenValue($stoken)) {
$in = ORM::for_table('tbl_transactions')->find_one(App::getTokenValue($stoken));
if (App::getVoucherValue($svoucher)) {
$in = ORM::for_table('tbl_transactions')->find_one(App::getVoucherValue($svoucher));
Package::createInvoice($in);
$ui->display('invoice.tpl');
die();
}
run_hook('deposit_customer'); #HOOK
if (!empty($user) && strlen($amount)>0 && $amount != 0) {
if (!empty($user) && strlen($amount) > 0 && $amount != 0) {
$plan = [];
$plan['name_plan'] = Lang::T('Balance');
$plan['price'] = $amount;
@ -883,21 +962,21 @@ switch ($action) {
if ($trxId > 0) {
$in = ORM::for_table('tbl_transactions')->find_one($trxId);
Package::createInvoice($in);
if (!empty($stoken)) {
App::setToken($stoken, $trxId);
if (!empty($svoucher)) {
App::setVoucher($svoucher, $trxId);
}
$ui->display('invoice.tpl');
} else {
r2(U . 'plan/refill', 'e', "Failed to refill account");
}
}else if (!empty($user) && !empty($plan)) {
} else if (!empty($user) && !empty($plan)) {
$p = ORM::for_table('tbl_plans')->find_one($plan);
$trxId = Package::rechargeBalance($c, $p, "Deposit", $admin['fullname'], $note);
if ($trxId > 0) {
$in = ORM::for_table('tbl_transactions')->find_one($trxId);
Package::createInvoice($in);
if (!empty($stoken)) {
App::setToken($stoken, $trxId);
if (!empty($svoucher)) {
App::setVoucher($svoucher, $trxId);
}
$ui->display('invoice.tpl');
} else {
@ -910,8 +989,8 @@ switch ($action) {
case 'extend':
$id = $routes[2];
$days = $routes[3];
$stoken = $_GET['stoken'];
if (App::getTokenValue($stoken)) {
$svoucher = $_GET['svoucher'];
if (App::getVoucherValue($svoucher)) {
r2(U . 'plan', 's', "Extend already done");
}
$tur = ORM::for_table('tbl_user_recharges')->find_one($id);
@ -924,7 +1003,7 @@ switch ($action) {
//expired
$expiration = date('Y-m-d', strtotime(" +$days day"));
}
App::setToken($stoken, $id);
App::setVoucher($svoucher, $id);
$c = ORM::for_table('tbl_customers')->findOne($tur['customer_id']);
if ($c) {
$p = ORM::for_table('tbl_plans')->find_one($tur['plan_id']);

View File

@ -71,6 +71,14 @@
<input type="text" class="form-control" name="lengthcode" value="12">
</div>
</div>
<div class="form-group">
<label for="inputSkills" class="col-sm-2 control-label">{Lang::T('Print Now')}</label>
<div class="col-sm-10">
<input type="checkbox" id="print_now" name="print_now" class="iCheck" value="yes">
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<button class="btn btn-success" onclick="return ask(this, 'Continue the Voucher creation process?')" type="submit">{Lang::T('Generate')}</button>

View File

@ -90,6 +90,7 @@
<table id="datatable" class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th><input type="checkbox" id="select-all"></th>
<th>ID</th>
<th>{Lang::T('Type')}</th>
<th>{Lang::T('Routers')}</th>
@ -97,7 +98,7 @@
<th>{Lang::T('Code Voucher')}</th>
<th>{Lang::T('Status Voucher')}</th>
<th>{Lang::T('Customer')}</th>
<th>{Lang::T('Create Date')}</th>
<th>{Lang::T('Create Date')}</th>
<th>{Lang::T('Used Date')}</th>
<th>{Lang::T('Generated By')}</th>
<th>{Lang::T('Manage')}</th>
@ -106,6 +107,7 @@
<tbody>
{foreach $d as $ds}
<tr {if $ds['status'] eq '1' }class="danger" {/if}>
<td><input type="checkbox" name="voucher_ids[]" value="{$ds['id']}"></td>
<td>{$ds['id']}</td>
<td>{$ds['type']}</td>
<td>{$ds['routers']}</td>
@ -120,7 +122,7 @@
<td>{if $ds['user'] eq '0'} -
{else}<a href="{$_url}customers/viewu/{$ds['user']}">{$ds['user']}</a>
{/if}</td>
<td>{if $ds['created_at']}{Lang::dateTimeFormat($ds['created_at'])}{/if}</td>
<td>{if $ds['created_at']}{Lang::dateTimeFormat($ds['created_at'])}{/if}</td>
<td>{if $ds['used_date']}{Lang::dateTimeFormat($ds['used_date'])}{/if}</td>
<td>{if $ds['generated_by']}
<a
@ -145,6 +147,112 @@
</table>
</div>
</div>
{include file="pagination.tpl"}
</div>
<div class="row" style="padding: 5px">
<div class="col-lg-3 col-lg-offset-9">
<div class="btn-group btn-group-justified" role="group">
<div class="btn-group" role="group">
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
<button id="deleteSelectedVouchers" class="btn btn-danger">{Lang::T('Delete
Selected')}</button>
{/if}
</div>
</div>
</div>
</div>
{include file="pagination.tpl"}
<script>
function deleteVouchers(voucherIds) {
if (voucherIds.length > 0) {
Swal.fire({
title: 'Are you sure?',
text: 'You won\'t be able to revert this!',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'Cancel'
}).then((result) => {
if (result.isConfirmed) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '{$_url}plan/voucher-delete-many', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function () {
if (xhr.status === 200) {
var response = JSON.parse(xhr.responseText);
if (response.status === 'success') {
Swal.fire({
title: 'Deleted!',
text: response.message,
icon: 'success',
confirmButtonText: 'OK'
}).then(() => {
location.reload(); // Reload the page after confirmation
});
} else {
Swal.fire({
title: 'Error!',
text: response.message,
icon: 'error',
confirmButtonText: 'OK'
});
}
} else {
Swal.fire({
title: 'Error!',
text: 'Failed to delete vouchers. Please try again.',
icon: 'error',
confirmButtonText: 'OK'
});
}
};
xhr.send('voucherIds=' + JSON.stringify(voucherIds));
}
});
} else {
Swal.fire({
title: 'Error!',
text: 'No vouchers selected to delete.',
icon: 'error',
confirmButtonText: 'OK'
});
}
}
// Example usage for selected vouchers
document.getElementById('deleteSelectedVouchers').addEventListener('click', function () {
var selectedVouchers = [];
document.querySelectorAll('input[name="voucher_ids[]"]:checked').forEach(function (checkbox) {
selectedVouchers.push(checkbox.value);
});
if (selectedVouchers.length > 0) {
deleteVouchers(selectedVouchers);
} else {
Swal.fire({
title: 'Error!',
text: 'Please select at least one voucher to delete.',
icon: 'error',
confirmButtonText: 'OK'
});
}
});
document.querySelectorAll('.delete-voucher').forEach(function (button) {
button.addEventListener('click', function () {
var voucherId = this.getAttribute('data-id');
deleteVouchers([voucherId]);
});
});
// Select or deselect all checkboxes
document.getElementById('select-all').addEventListener('change', function () {
var checkboxes = document.querySelectorAll('input[name="voucher_ids[]"]');
for (var checkbox of checkboxes) {
checkbox.checked = this.checked;
}
});
</script>
{include file="sections/footer.tpl"}