From 8a36746a0faec6ad48082622383f67a055d9104a Mon Sep 17 00:00:00 2001 From: Focuslinkstech <45756999+Focuslinkstech@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:45:13 +0100 Subject: [PATCH 01/21] New Feature and Bux Fix Lots of changes has been made that i cant recall Added Coupon Fix installation bug Add more nav list Fix this and that --- install/phpnuxbill.sql | 21 +- system/autoload/Message.php | 19 +- system/controllers/coupons.php | 315 +++++++++++++++++++++++++++ system/controllers/order.php | 87 +++++++- system/controllers/plan.php | 3 +- system/updates.json | 71 +++--- ui/ui/coupons-add.tpl | 153 +++++++++++++ ui/ui/coupons-edit.tpl | 150 +++++++++++++ ui/ui/coupons.tpl | 363 +++++++++++++++++++++++++++++++ ui/ui/customer/selectGateway.tpl | 260 +++++++++++----------- ui/ui/sections/header.tpl | 36 ++- 11 files changed, 1299 insertions(+), 179 deletions(-) create mode 100644 system/controllers/coupons.php create mode 100644 ui/ui/coupons-add.tpl create mode 100644 ui/ui/coupons-edit.tpl create mode 100644 ui/ui/coupons.tpl diff --git a/install/phpnuxbill.sql b/install/phpnuxbill.sql index 27f75df1..a76642a9 100644 --- a/install/phpnuxbill.sql +++ b/install/phpnuxbill.sql @@ -72,7 +72,7 @@ DROP TABLE IF EXISTS `tbl_payment_gateway`; CREATE TABLE `tbl_payment_gateway` ( `id` int NOT NULL, `username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `user_id` int(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + `user_id` int(11) NOT NULL, `gateway` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'xendit | midtrans', `gateway_trx_id` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `plan_id` int NOT NULL, @@ -151,7 +151,7 @@ CREATE TABLE `tbl_transactions` ( `id` int NOT NULL, `invoice` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, - `user_id` int(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + `user_id` int(11) NOT NULL, `plan_name` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `price` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `recharged_on` date NOT NULL, @@ -258,6 +258,23 @@ CREATE TABLE IF NOT EXISTS `tbl_meta` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='This Table to add additional data for any table'; +CREATE TABLE `tbl_coupons` ( + `id` INT AUTO_INCREMENT PRIMARY KEY, + `code` VARCHAR(50) NOT NULL UNIQUE, + `type` ENUM('fixed', 'percent') NOT NULL, + `value` DECIMAL(10,2) NOT NULL, + `description` TEXT NOT NULL, + `max_usage` INT NOT NULL DEFAULT 1, + `usage_count` INT NOT NULL DEFAULT 0, + `status` ENUM('active', 'inactive') NOT NULL, + `min_order_amount` DECIMAL(10,2) NOT NULL, + `max_discount_amount` DECIMAL(10,2) NOT NULL, + `start_date` DATE NOT NULL, + `end_date` DATE NOT NULL, + `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + ALTER TABLE `rad_acct` ADD PRIMARY KEY (`id`), ADD KEY `username` (`username`), diff --git a/system/autoload/Message.php b/system/autoload/Message.php index a5bc9449..e4610b4d 100644 --- a/system/autoload/Message.php +++ b/system/autoload/Message.php @@ -17,12 +17,15 @@ require $root_path . 'system/autoload/mail/SMTP.php'; class Message { - public static function sendTelegram($txt) + public static function sendTelegram($txt, $chat_id = null) { global $config; - run_hook('send_telegram', [$txt]); #HOOK - if (!empty($config['telegram_bot']) && !empty($config['telegram_target_id'])) { - return Http::getData('https://api.telegram.org/bot' . $config['telegram_bot'] . '/sendMessage?chat_id=' . $config['telegram_target_id'] . '&text=' . urlencode($txt)); + run_hook('send_telegram', [$txt, $chat_id = null]); #HOOK + if (!empty($config['telegram_bot'])) { + if (empty($chat_id)) { + $chat_id = $config['telegram_target_id']; + } + return Http::getData('https://api.telegram.org/bot' . $config['telegram_bot'] . '/sendMessage?chat_id=' . $chat_id . '&text=' . urlencode($txt)); } } @@ -73,7 +76,7 @@ class Message $iport = explode(":", $mikrotik['ip_address']); $client_m = new RouterOS\Client($iport[0], $mikrotik['username'], $mikrotik['password'], ($iport[1]) ? $iport[1] : null); } - if(empty($config['mikrotik_sms_command'])){ + if (empty($config['mikrotik_sms_command'])) { $config['mikrotik_sms_command'] = "/tool sms send"; } $smsRequest = new RouterOS\Request($config['mikrotik_sms_command']); @@ -185,7 +188,7 @@ class Message // Add bills to the note if there are any additional costs if ($add_cost != 0) { foreach ($bills as $k => $v) { - $note .= $k . " : " . Lang::moneyFormat($v) . "\n"; + $note .= $k . " : " . Lang::moneyFormat($v) . "\n"; } $total += $add_cost; } @@ -221,7 +224,7 @@ class Message if (strpos($msg, '[[payment_link]]') !== false) { // token only valid for 1 day, for security reason $token = User::generateToken($customer['id'], 1); - if(!empty($token['token'])){ + if (!empty($token['token'])) { $tur = ORM::for_table('tbl_user_recharges') ->where('customer_id', $customer['id']) ->where('namebp', $package) @@ -230,7 +233,7 @@ class Message $url = '?_route=home&recharge=' . $tur['id'] . '&uid=' . urlencode($token['token']); $msg = str_replace('[[payment_link]]', $url, $msg); } - }else{ + } else { $msg = str_replace('[[payment_link]]', '', $msg); } } diff --git a/system/controllers/coupons.php b/system/controllers/coupons.php new file mode 100644 index 00000000..b76b0f59 --- /dev/null +++ b/system/controllers/coupons.php @@ -0,0 +1,315 @@ +assign('_title', Lang::T('Coupons')); +$ui->assign('_system_menu', 'crm'); + +$action = $routes['1']; +$ui->assign('_admin', $admin); + +switch ($action) { + + case 'add': + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) { + echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]); + exit; + } + $ui->assign('_title', Lang::T('Add Coupon')); + $ui->assign('csrf_token', Csrf::generateAndStoreToken()); + $ui->display('coupons-add.tpl'); + break; + + case 'add-post': + + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) { + echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]); + exit; + } + $csrf_token = _post('csrf_token'); + if (!Csrf::check($csrf_token)) { + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T('Invalid or Expired CSRF Token') . "."); + } + $code = Text::alphanumeric(_post('code', '')); + $type = _post('type', ''); + $value = floatval(_post('value', '')); + $description = _post('description', ''); + $max_usage = _post('max_usage', ''); + $min_order_amount = _post('min_order_amount', ''); + $max_discount_amount = intval(_post('max_discount_amount', '')); + $status = _post('status', 'active'); + $start_date = strtotime(_post('start_date', '0000-00-00')); + $end_date = strtotime(_post('end_date', '0000-00-00')); + + $error = []; + if (empty($code)) { + $error[] = Lang::T('Coupon Code is required'); + } + if (empty($type)) { + $error[] = Lang::T('Coupon Type is required'); + } + if (empty($value)) { + $error[] = Lang::T('Coupon Value is required'); + } + if (empty($description)) { + $error[] = Lang::T('Coupon Description is required'); + } + if (empty($max_usage)) { + $error[] = Lang::T('Coupon Maximum Usage is required'); + } + if (empty($min_order_amount)) { + $error[] = Lang::T('Coupon Minimum Order Amount is required'); + } + if (empty($max_discount_amount)) { + $error[] = Lang::T('Coupon Maximum Discount Amount is required'); + } + if (empty($status)) { + $error[] = Lang::T('Coupon Status is required'); + } + if (empty($start_date)) { + $error[] = Lang::T('Coupon Start Date is required'); + } + if (empty($end_date)) { + $error[] = Lang::T('Coupon End Date is required'); + } + + if (!empty($error)) { + r2(U . 'coupons/add', 'e', implode('
', $error)); + exit; + } + + //check if coupon code already exists + $coupon = ORM::for_table('tbl_coupons')->where('code', $code)->find_one(); + if ($coupon) { + r2(U . 'coupons/add', 'e', Lang::T('Coupon Code already exists')); + exit; + } + + $coupon = ORM::for_table('tbl_coupons')->create(); + $coupon->code = $code; + $coupon->type = $type; + $coupon->value = $value; + $coupon->description = $description; + $coupon->max_usage = $max_usage; + $coupon->min_order_amount = $min_order_amount; + $coupon->max_discount_amount = $max_discount_amount; + $coupon->status = $status; + $coupon->start_date = date('Y-m-d', $start_date); + $coupon->end_date = date('Y-m-d', $end_date); + $coupon->created_at = date('Y-m-d H:i:s'); + try { + $coupon->save(); + r2(U . 'coupons', 's', Lang::T('Coupon has been added successfully')); + } catch (Exception $e) { + _log(Lang::T('Error adding coupon: ' . $e->getMessage())); + r2(U . 'coupons/add', 'e', Lang::T('Error adding coupon: ' . $e->getMessage())); + } + break; + + case 'edit': + + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) { + echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]); + exit; + } + + $coupon_id = intval($routes['2']); + if (empty($coupon_id)) { + r2(U . 'coupons', 'e', Lang::T('Invalid Coupon ID')); + exit; + } + $coupon = ORM::for_table('tbl_coupons')->find_one($coupon_id); + if (!$coupon) { + r2(U . 'coupons', 'e', Lang::T('Coupon Not Found')); + exit; + } + $ui->assign('coupon', $coupon); + $ui->assign('_title', Lang::T('Edit Coupon: ' . $coupon['code'])); + $ui->assign('csrf_token', Csrf::generateAndStoreToken()); + $ui->display('coupons-edit.tpl'); + break; + + case 'edit-post': + + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) { + echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]); + exit; + } + + $csrf_token = _post('csrf_token'); + if (!Csrf::check($csrf_token)) { + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T('Invalid or Expired CSRF Token') . "."); + } + + $code = Text::alphanumeric(_post('code', '')); + $type = _post('type', ''); + $value = floatval(_post('value', '')); + $description = _post('description', ''); + $max_usage = _post('max_usage', ''); + $min_order_amount = _post('min_order_amount', ''); + $max_discount_amount = intval(_post('max_discount_amount', '')); + $status = _post('status', 'active'); + $start_date = strtotime(_post('start_date', '0000-00-00')); + $end_date = strtotime(_post('end_date', '0000-00-00')); + + $error = []; + if (empty($code)) { + $error[] = Lang::T('Coupon code is required'); + } + if (empty($type)) { + $error[] = Lang::T('Coupon type is required'); + } + if (empty($value)) { + $error[] = Lang::T('Coupon value is required'); + } + if (empty($description)) { + $error[] = Lang::T('Coupon description is required'); + } + if (empty($max_usage)) { + $error[] = Lang::T('Coupon maximum usage is required'); + } + if (empty($min_order_amount)) { + $error[] = Lang::T('Coupon minimum order amount is required'); + } + if (empty($max_discount_amount)) { + $error[] = Lang::T('Coupon maximum discount amount is required'); + } + if (empty($status)) { + $error[] = Lang::T('Coupon status is required'); + } + if (empty($start_date)) { + $error[] = Lang::T('Coupon start date is required'); + } + if (empty($end_date)) { + $error[] = Lang::T('Coupon end date is required'); + } + if (!empty($error)) { + r2(U . 'coupons/edit/' . $coupon_id, 'e', implode('
', $error)); + exit; + } + $coupon = ORM::for_table('tbl_coupons')->find_one($coupon_id); + $coupon->code = $code; + $coupon->type = $type; + $coupon->value = $value; + $coupon->description = $description; + $coupon->max_usage = $max_usage; + $coupon->min_order_amount = $min_order_amount; + $coupon->max_discount_amount = $max_discount_amount; + $coupon->status = $status; + $coupon->start_date = date('Y-m-d', $start_date); + $coupon->end_date = date('Y-m-d', $end_date); + $coupon->updated_at = date('Y-m-d H:i:s'); + try { + $coupon->save(); + r2(U . 'coupons', 's', Lang::T('Coupon has been updated successfully')); + } catch (Exception $e) { + _log(Lang::T('Error updating coupon: ') . $e->getMessage()); + r2(U . 'coupons/edit/' . $coupon_id, 'e', Lang::T('Error updating coupon: ') . $e->getMessage()); + } + break; + + case 'delete': + + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) { + echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]); + exit; + } + + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $couponIds = json_decode($_POST['couponIds'], true); + + if (is_array($couponIds) && !empty($couponIds)) { + // Delete coupons from the database + ORM::for_table('tbl_coupons') + ->where_in('id', $couponIds) + ->delete_many(); + + // Return success response + echo json_encode(['status' => 'success', 'message' => Lang::T("Coupons Deleted Successfully.")]); + exit; + } else { + echo json_encode(['status' => 'error', 'message' => Lang::T("Invalid or missing coupon IDs.")]); + exit; + } + } else { + echo json_encode(['status' => 'error', 'message' => Lang::T("Invalid request method.")]); + } + break; + + case 'status': + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) { + _alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard"); + exit; + } + + if ($_SERVER['REQUEST_METHOD'] === 'GET') { + $couponId = $_GET['coupon_id'] ?? ''; + $csrf_token = $_GET['csrf_token'] ?? ''; + $status = $_GET['status'] ?? ''; + if (empty($couponId) || empty($csrf_token) || !hotspot_validateCsrfToken($csrf_token) || empty($status)) { + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Invalid request")); + exit; + } + $coupon = ORM::for_table('tbl_coupons')->where('id', $couponId)->find_one(); + if (!$coupon) { + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon not found.")); + exit; + } + $coupon->status = $status; + $coupon->save(); + r2($_SERVER['HTTP_REFERER'], 's', Lang::T("Coupon status updated successfully.")); + } else { + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Invalid request method")); + } + break; + + default: + if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Sales'])) { + echo json_encode(['status' => 'error', 'message' => Lang::T('You do not have permission to access this page')]); + exit; + } + $ui->assign('_title', Lang::T('Coupons')); + $ui->assign('_system_menu', 'crm'); + + $search = _post('search'); + $filter = _post('filter', 'none'); + + $couponsData = ORM::for_table('tbl_coupons') + ->table_alias('c') + ->select_many( + 'c.id', + 'c.code', + 'c.type', + 'c.value', + 'c.description', + 'c.max_usage', + 'c.usage_count', + 'c.status', + 'c.min_order_amount', + 'c.max_discount_amount', + 'c.start_date', + 'c.end_date', + 'c.created_at', + 'c.updated_at' + ); + + // Apply filters + if ($search != '') { + $searchLike = "%$search%"; + $couponsData->whereRaw( + "code LIKE ? OR type LIKE ? OR value LIKE ? OR max_usage LIKE ? OR usage_count LIKE ? OR status LIKE ? OR min_order_amount LIKE ? OR max_discount_amount LIKE ?", + [$searchLike, $searchLike, $searchLike, $searchLike, $searchLike, $searchLike, $searchLike, $searchLike] + ); + } + $couponsData->order_by_asc('c.id'); + $coupons = Paginator::findMany($couponsData, ['search' => $search], 5, ''); + $ui->assign('csrf_token', Csrf::generateAndStoreToken()); + $ui->assign('coupons', $coupons); + $ui->display('coupons.tpl'); + break; +} diff --git a/system/controllers/order.php b/system/controllers/order.php index ca2fd298..d1be3771 100644 --- a/system/controllers/order.php +++ b/system/controllers/order.php @@ -26,7 +26,7 @@ switch ($action) { $query = ORM::for_table('tbl_payment_gateway')->where('username', $user['username'])->order_by_desc('id'); $d = Paginator::findMany($query); } - + $ui->assign('d', $d); $ui->assign('_title', Lang::T('Order History')); run_hook('customer_view_order_history'); #HOOK @@ -410,6 +410,78 @@ switch ($action) { if ($router['name'] != 'balance') { list($bills, $add_cost) = User::getBills($id_customer); } + + if (!isset($_SESSION['coupon_attempts'])) { + $_SESSION['coupon_attempts'] = 0; + } + + if ($_SESSION['coupon_attempts'] >= 5) { + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Too many invalid attempts. Please try again later.")); + } + + if (_post('coupon')) { + + if ($plan['routers'] === 'balance') { + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon not available for Balance")); + } + + $coupon = ORM::for_table('tbl_coupons')->where('code', _post('coupon'))->find_one(); + + if (!$coupon) { + $_SESSION['coupon_attempts']++; + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon not found")); + } + + if ($coupon['status'] != 'active') { + $_SESSION['coupon_attempts']++; + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon is not active")); + } + + $today = date('Y-m-d'); + if ($today < $coupon['start_date'] || $today > $coupon['end_date']) { + $_SESSION['coupon_attempts']++; + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon is not valid for today")); + } + + if ($coupon['usage_count'] >= $coupon['max_usage']) { + $_SESSION['coupon_attempts']++; + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon usage limit reached")); + } + + if ($plan['price'] < $coupon['min_order_amount']) { + $_SESSION['coupon_attempts']++; + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("The order amount does not meet the minimum requirement for this coupon")); + } + + $_SESSION['coupon_attempts'] = 0; + + // Calculate discount value + $discount = 0; + switch ($coupon['type']) { + case 'percent': + $discount = ($coupon['value'] / 100) * $plan['price']; + if ($discount > $coupon['max_discount_amount']) { + $discount = $coupon['max_discount_amount']; + } + break; + case 'fixed': + $discount = $coupon['value']; + break; + } + + // Ensure discount does not exceed the plan price + if ($discount >= $plan['price']) { + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Discount value exceeds the plan price")); + } + + $plan['price'] -= $discount; + $coupon->usage_count = $coupon['usage_count'] + 1; + $coupon->save(); + $ui->assign('discount', $discount); + $ui->assign('notify', Lang::T("Coupon applied successfully. You saved " . Lang::moneyFormat($discount))); + $ui->assign('notify_t', 's'); + } + $tax = Package::tax($plan['price'] + $add_cost, $tax_rate); $pgs = array_values(explode(',', $config['payment_gateway'])); if (count($pgs) == 0) { @@ -446,6 +518,7 @@ switch ($action) { } case 'buy': $gateway = _post('gateway'); + $discount = _post('discount') ?: 0; if ($gateway == 'balance') { unset($_SESSION['gateway']); r2(U . 'order/pay/' . $routes[2] . '/' . $routes[3]); @@ -566,12 +639,12 @@ switch ($action) { // Postpaid price from field $add_inv = User::getAttribute("Invoice", $id_customer); if (empty($add_inv) or $add_inv == 0) { - $d->price = ($plan['price'] + $add_cost + $tax); + $d->price = $plan['price'] + $add_cost + $tax - $discount; } else { - $d->price = ($add_inv + $add_cost + $tax); + $d->price = $add_inv + $add_cost + $tax - $discount; } } else { - $d->price = ($plan['price'] + $add_cost + $tax); + $d->price = $plan['price'] + $add_cost + $tax - $discount; } $d->created_date = date('Y-m-d H:i:s'); $d->status = 1; @@ -589,12 +662,12 @@ switch ($action) { // Postpaid price from field $add_inv = User::getAttribute("Invoice", $id_customer); if (empty($add_inv) or $add_inv == 0) { - $d->price = ($plan['price'] + $add_cost + $tax); + $d->price = ($plan['price'] + $add_cost + $tax - $discount); } else { - $d->price = ($add_inv + $add_cost + $tax); + $d->price = ($add_inv + $add_cost + $tax - $discount); } } else { - $d->price = ($plan['price'] + $add_cost + $tax); + $d->price = ($plan['price'] + $add_cost + $tax - $discount); } //$d->price = ($plan['price'] + $add_cost); $d->created_date = date('Y-m-d H:i:s'); diff --git a/system/controllers/plan.php b/system/controllers/plan.php index c19e37e5..91a84631 100644 --- a/system/controllers/plan.php +++ b/system/controllers/plan.php @@ -412,7 +412,7 @@ switch ($action) { break; case 'voucher': - $ui->assign('_title', Lang::T('Vouchers')); + $ui->assign('_title', Lang::T('Voucher Cards')); $search = _req('search'); $router = _req('router'); $customer = _req('customer'); @@ -422,6 +422,7 @@ switch ($action) { $ui->assign('customer', $customer); $ui->assign('status', $status); $ui->assign('plan', $plan); + $ui->assign('_system_menu', 'cards'); $query = ORM::for_table('tbl_plans') ->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan')); diff --git a/system/updates.json b/system/updates.json index dd97c432..eb2526bb 100644 --- a/system/updates.json +++ b/system/updates.json @@ -59,108 +59,108 @@ "2024.2.19": [ "CREATE TABLE `tbl_customers_fields` (`id` INT PRIMARY KEY AUTO_INCREMENT, `customer_id` INT NOT NULL, `field_name` VARCHAR(255) NOT NULL, `field_value` VARCHAR(255) NOT NULL, FOREIGN KEY (customer_id) REFERENCES tbl_customers(id));" ], - "2024.2.20" : [ + "2024.2.20": [ "ALTER TABLE `tbl_plans` ADD `list_expired` VARCHAR(32) NOT NULL DEFAULT '' COMMENT 'address list' AFTER `pool_expired`;", "ALTER TABLE `tbl_bandwidth` ADD `burst` VARCHAR(128) NOT NULL DEFAULT '' AFTER `rate_up_unit`;" ], - "2024.2.20.1" : [ + "2024.2.20.1": [ "DROP TABLE IF EXISTS `tbl_customers_meta`;" ], - "2024.2.23" : [ + "2024.2.23": [ "ALTER TABLE `tbl_transactions` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;", "ALTER TABLE `tbl_user_recharges` ADD `admin_id` INT NOT NULL DEFAULT '1' AFTER `type`;" ], - "2024.3.3" : [ - "ALTER TABLE `tbl_plans` CHANGE `validity_unit` `validity_unit` ENUM('Mins','Hrs','Days','Months','Period') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;" + "2024.3.3": [ + "ALTER TABLE `tbl_plans` CHANGE `validity_unit` `validity_unit` ENUM('Mins','Hrs','Days','Months','Period') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;" ], - "2024.3.12" : [ + "2024.3.12": [ "ALTER TABLE `tbl_plans` CHANGE `allow_purchase` `prepaid` ENUM('yes','no') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'yes' COMMENT 'is prepaid';" ], - "2024.3.14" : [ + "2024.3.14": [ "ALTER TABLE `tbl_transactions` ADD `note` VARCHAR(256) NOT NULL DEFAULT '' COMMENT 'for note' AFTER `type`;" ], - "2024.3.19" : [ + "2024.3.19": [ "ALTER TABLE `tbl_customers` ADD `coordinates` VARCHAR(50) NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates' AFTER `email`;" ], - "2024.3.19.1" : [ + "2024.3.19.1": [ "ALTER TABLE `tbl_customers` ADD `account_type` ENUM('Business', 'Personal') DEFAULT 'Personal' COMMENT 'For selecting account type' AFTER `coordinates`;" ], - "2024.3.19.2" : [ + "2024.3.19.2": [ "ALTER TABLE `tbl_plans` ADD `plan_type` ENUM('Business', 'Personal') DEFAULT 'Personal' COMMENT 'For selecting account type' ;" ], "2023.3.20": [ "ALTER TABLE `tbl_customers` CHANGE `pppoe_password` `pppoe_password` VARCHAR(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login';" ], - "2024.4.5" : [ + "2024.4.5": [ "ALTER TABLE `tbl_payment_gateway` ADD `trx_invoice` VARCHAR(25) NOT NULL DEFAULT '' COMMENT 'from tbl_transactions' AFTER `paid_date`;" ], - "2024.5.17" : [ + "2024.5.17": [ "ALTER TABLE `tbl_customers` ADD `status` ENUM('Active','Banned','Disabled') NOT NULL DEFAULT 'Active' AFTER `auto_renewal`;" ], - "2024.5.18" : [ + "2024.5.18": [ "ALTER TABLE `tbl_customers` CHANGE `status` `status` ENUM('Active','Banned','Disabled','Inactive','Limited','Suspended') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'Active';" ], - "2024.5.20" : [ + "2024.5.20": [ "ALTER TABLE `tbl_customers` ADD `city` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `address`, ADD `district` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `city`, ADD `state` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `district`, ADD `zip` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci AFTER `state`;" ], - "2024.6.5" : [ + "2024.6.5": [ "ALTER TABLE `tbl_plans` ADD `price_old` VARCHAR(40) NOT NULL DEFAULT '' AFTER `price`;", "ALTER TABLE `tbl_plans` ADD `device` VARCHAR(32) NOT NULL DEFAULT '' AFTER `plan_type`;" ], - "2024.6.10" : [ + "2024.6.10": [ "ALTER TABLE `tbl_pool` ADD `local_ip` VARCHAR(40) NOT NULL DEFAULT '' AFTER `pool_name`;" ], - "2024.6.11" : [ + "2024.6.11": [ "ALTER TABLE `tbl_plans` ADD `plan_expired` INT NOT NULL DEFAULT '0' AFTER `pool`;", "ALTER TABLE `tbl_plans` DROP `pool_expired`, DROP `list_expired`;" ], - "2024.6.19" : [ + "2024.6.19": [ "ALTER TABLE `tbl_plans` ADD `expired_date` TINYINT(1) NOT NULL DEFAULT '20' AFTER `plan_expired`;" ], - "2024.6.21" : [ + "2024.6.21": [ "ALTER TABLE `tbl_plans` ADD `on_login` TEXT NULL DEFAULT NULL AFTER `device`;", "ALTER TABLE `tbl_plans` ADD `on_logout` TEXT NULL DEFAULT NULL AFTER `on_login`;" ], - "2024.7.6" : [ + "2024.7.6": [ "CREATE TABLE IF NOT EXISTS `rad_acct` ( `id` bigint NOT NULL, `acctsessionid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `realm` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasid` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasportid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `nasporttype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `framedipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',`acctstatustype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `macaddr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `dateAdded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;", "ALTER TABLE `rad_acct` ADD PRIMARY KEY (`id`), ADD KEY `username` (`username`), ADD KEY `framedipaddress` (`framedipaddress`), ADD KEY `acctsessionid` (`acctsessionid`), ADD KEY `nasipaddress` (`nasipaddress`);", "ALTER TABLE `rad_acct` MODIFY `id` bigint NOT NULL AUTO_INCREMENT;" ], - "2024.7.24" : [ + "2024.7.24": [ "ALTER TABLE `tbl_voucher` ADD `used_date` DATETIME NULL DEFAULT NULL AFTER `status`;", "UPDATE `tbl_voucher` SET `used_date`=now() WHERE `status`=1;" ], - "2024.8.1" : [ + "2024.8.1": [ "ALTER TABLE `tbl_payment_gateway` CHANGE `gateway_trx_id` `gateway_trx_id` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';", "ALTER TABLE `tbl_payment_gateway` CHANGE `pg_url_payment` `pg_url_payment` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '';" ], - "2024.8.2" : [ + "2024.8.2": [ "CREATE TABLE IF NOT EXISTS `tbl_customers_inbox` (`id` int UNSIGNED NOT NULL AUTO_INCREMENT, `customer_id` int NOT NULL, `date_created` datetime NOT NULL, `date_read` datetime DEFAULT NULL, `subject` varchar(64) COLLATE utf8mb4_general_ci NOT NULL, `body` TEXT NULL DEFAULT NULL, `from` varchar(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'System' COMMENT 'System or Admin or Else',`admin_id` int NOT NULL DEFAULT '0' COMMENT 'other than admin is 0', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;" ], - "2024.8.5" : [ + "2024.8.5": [ "ALTER TABLE `tbl_customers` ADD `pppoe_username` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `password`;", "ALTER TABLE `tbl_customers` ADD `pppoe_ip` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'For PPPOE Login' AFTER `pppoe_password`;" ], - "2024.8.5.1" : [ + "2024.8.5.1": [ "ALTER TABLE `tbl_routers` ADD `coordinates` VARCHAR(50) NOT NULL DEFAULT '' AFTER `description`;", "ALTER TABLE `tbl_routers` ADD `coverage` VARCHAR(8) NOT NULL DEFAULT '0' AFTER `coordinates`;" ], - "2024.8.6" : [ + "2024.8.6": [ "ALTER TABLE `rad_acct` ADD `acctinputoctets` BIGINT NOT NULL DEFAULT '0' AFTER `framedipaddress`;", "ALTER TABLE `rad_acct` ADD `acctoutputoctets` BIGINT NOT NULL DEFAULT '0' AFTER `acctinputoctets`;" ], - "2024.8.7" : [ + "2024.8.7": [ "ALTER TABLE `tbl_customers` CHANGE `coordinates` `coordinates` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'Latitude and Longitude coordinates';" ], - "2024.8.28" : [ + "2024.8.28": [ "ALTER TABLE `tbl_routers` ADD `status` ENUM('Online', 'Offline') DEFAULT 'Online' AFTER `coordinates`;", "ALTER TABLE `tbl_routers` ADD `last_seen` DATETIME AFTER `status`;" ], - "2024.9.13" : [ + "2024.9.13": [ "ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;", "ALTER TABLE `tbl_customers` CHANGE `service_type` `service_type` ENUM('Hotspot','PPPoE','VPN','Others') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'Others' COMMENT 'For selecting user type';", - "ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;", - "CREATE TABLE IF NOT EXISTS `tbl_port_pool` ( `id` int(10) NOT NULL AUTO_INCREMENT , `public_ip` varchar(40) NOT NULL, `port_name` varchar(40) NOT NULL, `range_port` varchar(40) NOT NULL, `routers` varchar(40) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;" + "ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','VPN','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;", + "CREATE TABLE IF NOT EXISTS `tbl_port_pool` ( `id` int(10) NOT NULL AUTO_INCREMENT , `public_ip` varchar(40) NOT NULL, `port_name` varchar(40) NOT NULL, `range_port` varchar(40) NOT NULL, `routers` varchar(40) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;" ], "2024.10.10": [ "ALTER TABLE `tbl_users` ADD `login_token` VARCHAR(40) AFTER `last_login`;" @@ -168,15 +168,18 @@ "2024.10.17": [ "CREATE TABLE IF NOT EXISTS `tbl_meta` ( `id` int UNSIGNED NOT NULL AUTO_INCREMENT, `tbl` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT 'Table name', `tbl_id` int NOT NULL COMMENT 'table value id', `name` varchar(32) COLLATE utf8mb4_general_ci NOT NULL, `value` mediumtext COLLATE utf8mb4_general_ci, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='This Table to add additional data for any table';" ], - "2024.10.30" : [ + "2024.10.30": [ "ALTER TABLE `tbl_users` ADD `photo` VARCHAR(128) NOT NULL DEFAULT '/admin.default.png' AFTER `root`;", "ALTER TABLE `tbl_users` ADD `data` TEXT NULL DEFAULT NULL COMMENT 'to put additional data' AFTER `status`;" ], - "2024.10.31" : [ + "2024.10.31": [ "ALTER TABLE `tbl_customers` ADD `photo` VARCHAR(128) NOT NULL DEFAULT '/user.default.jpg' AFTER `password`;" ], - "2024.12.5" : [ + "2024.12.5": [ "ALTER TABLE `tbl_transactions` ADD `user_id` INT(11) NULL AFTER `username`;", "ALTER TABLE `tbl_payment_gateway` ADD `user_id` INT(11) NULL AFTER `username`;" + ], + "2024.12.16": [ + "CREATE TABLE `tbl_coupons` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `code` VARCHAR(50) NOT NULL UNIQUE, `type` ENUM('fixed', 'percent') NOT NULL, `value` DECIMAL(10,2) NOT NULL, `description` TEXT NOT NULL, `max_usage` INT NOT NULL DEFAULT 1,`usage_count` INT NOT NULL DEFAULT 0,`status` ENUM('active', 'inactive') NOT NULL, `min_order_amount` DECIMAL(10,2) NOT NULL, `max_discount_amount` DECIMAL(10,2) NOT NULL, `start_date` DATE NOT NULL,`end_date` DATE NOT NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);" ] } \ No newline at end of file diff --git a/ui/ui/coupons-add.tpl b/ui/ui/coupons-add.tpl new file mode 100644 index 00000000..551a3f93 --- /dev/null +++ b/ui/ui/coupons-add.tpl @@ -0,0 +1,153 @@ +{include file="sections/header.tpl"} + + +
+
+
+
{Lang::T('Add Coupon')}
+
+
+ + + +
+ +
+
+ + + + +
+

{Lang::T('Unique code for the coupon')}

+
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +

{Lang::T('Value of the discount (amount or percentage)')}

+
+
+ + +
+ +
+ +

{Lang::T('Brief explanation of the coupon')}

+
+
+ + +
+ +
+ +

{Lang::T('Maximum number of times this coupon can be used')}

+
+
+ + +
+ +
+ +

{Lang::T('Minimum cart total required to use this coupon')}

+
+
+ + +
+ +
+ +

{Lang::T('Maximum discount amount applicable (for percent type)')}

+
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+
+ + Or {Lang::T('Cancel')} +
+
+
+ +
+
+
+
+ + + + + +{include file="sections/footer.tpl"} \ No newline at end of file diff --git a/ui/ui/coupons-edit.tpl b/ui/ui/coupons-edit.tpl new file mode 100644 index 00000000..f6396efb --- /dev/null +++ b/ui/ui/coupons-edit.tpl @@ -0,0 +1,150 @@ +{include file="sections/header.tpl"} + + + + +
+
+
+
{Lang::T('Edit Coupon')}
+
+
+ + + +
+ +
+ +

{Lang::T('Unique code for the coupon')}

+
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +

{Lang::T('Value of the discount (amount or + percentage)')}

+
+
+ + +
+ +
+ +

{Lang::T('Brief explanation of the coupon')}

+
+
+ + +
+ +
+ +

{Lang::T('Maximum number of times this coupon can be + used')}

+
+
+ + +
+ +
+ +

{Lang::T('Minimum cart total required to use this + coupon')}

+
+
+ + +
+ +
+ +

{Lang::T('Maximum discount amount applicable (for percent + type)')}

+
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+
+ + Or {Lang::T('Cancel')} +
+
+
+ +
+
+
+
+ + + + + +{include file="sections/footer.tpl"} \ No newline at end of file diff --git a/ui/ui/coupons.tpl b/ui/ui/coupons.tpl new file mode 100644 index 00000000..11d5d20f --- /dev/null +++ b/ui/ui/coupons.tpl @@ -0,0 +1,363 @@ +{include file="sections/header.tpl"} + + + +
+
+
+ +
+ +
+ +
+
+
+
+ +
+
+ +
+
+
+
+   +
+
+ + + + + + + + + + + + + + + + + + + + + + {if $coupons} + {foreach $coupons as $coupon} + + + + + + + + + + + + + + + + + + + {/foreach} + {else} + + + + {/if} + +
{Lang::T('Code')}{Lang::T('Type')}{Lang::T('Value')}{Lang::T('Description')}{Lang::T('Max Usage')}{Lang::T('Usage Count')}{Lang::T('Status')}{Lang::T('Min Order')}{Lang::T('Max Discount')}{Lang::T('Start Date')}{Lang::T('End Date')}{Lang::T('Created Date')}{Lang::T('Updated Date')}{Lang::T('Action')}
+ {$coupon['code']} + {$coupon['type']}{$coupon['value']}{$coupon['description']}{$coupon['max_usage']}{$coupon['usage_count']} + {if $coupon['status'] == 'inactive'} + {Lang::T('Inactive')} + {elseif $coupon['status'] == 'active'} + {Lang::T('Active')} + {else} + {Lang::T('Unknown')} + {/if} + {$coupon['min_order_amount']}{$coupon['max_discount_amount']}{$coupon['start_date']}{$coupon['end_date']}{$coupon['created_at']}{$coupon['updated_at']} +
+ {Lang::T('Edit')} + {if $coupon['status'] neq 'inactive'} + + {Lang::T('Block')} + + {else} + + {Lang::T('Unblock')} + + {/if} +
+
+ {Lang::T('No coupons found.')} +
+ {include file="pagination.tpl"} +
+
+
+
+ {if in_array($_admin['user_type'],['SuperAdmin','Admin'])} + + {/if} +
+
+
+
+
+   +
+ + + +{literal} + +{/literal} + +{include file="sections/footer.tpl"} \ No newline at end of file diff --git a/ui/ui/customer/selectGateway.tpl b/ui/ui/customer/selectGateway.tpl index 510bceae..c6ff6236 100644 --- a/ui/ui/customer/selectGateway.tpl +++ b/ui/ui/customer/selectGateway.tpl @@ -2,166 +2,178 @@
{if file_exists("$PAGES_PATH/Payment_Info.html")} -
-
-
{Lang::T('Payment Info')}
-
{include file="$PAGES_PATH/Payment_Info.html"}
-
+
+
+
{Lang::T('Payment Info')}
+
{include file="$PAGES_PATH/Payment_Info.html"}
+
{/if}
-
{Lang::T('Available Payment Gateway')}
- -
-
{Lang::T('Package Details')}
+
{Lang::T('Make Payment')}
-
    -
  • - {Lang::T('Plan Name')} {$plan['name_plan']} -
  • - {if $plan['is_radius'] or $plan['routers']} -
  • - {Lang::T('Location')} {if - $plan['is_radius']}Radius{else}{$plan['routers']} - {/if} -
  • - {/if} +
    +
    {Lang::T('Package Details')}
    + {if !$custom} +
    • - {Lang::T('Type')} {if $plan['prepaid'] eq - 'yes'}{Lang::T('Prepaid')}{else}{Lang::T('Postpaid')} - {/if} - {$plan['type']} + {Lang::T('Plan Name')} + {$plan['name_plan']}
    • + + {if $plan['is_radius'] or $plan['routers']}
    • - {Lang::T('Package Price')} + {Lang::T('Location')} + {if $plan['is_radius']}Radius{else}{$plan['routers']}{/if} +
    • + {/if} + +
    • + {Lang::T('Type')} + + {if $plan['prepaid'] eq 'yes'}{Lang::T('Prepaid')}{else}{Lang::T('Postpaid')}{/if} + {$plan['type']} + +
    • + +
    • + {Lang::T('Package Price')} + {if !empty($plan['price_old'])} - {Lang::moneyFormat($plan['price_old'])} + + {Lang::moneyFormat($plan['price_old'])} + {/if} {Lang::moneyFormat($plan['price'])}
    • + {if $plan['validity']} -
    • - {Lang::T('Validity Periode')} {$plan['validity']} - {$plan['validity_unit']} -
    • +
    • + {Lang::T('Validity Period')} + {$plan['validity']} {$plan['validity_unit']} +
    • {/if}
    + {else} +
      +
    • + {Lang::T('Plan Name')} + {Lang::T('Custom Balance')} +
    • + +
    • + {Lang::T('Amount')} + + {Lang::moneyFormat($amount)} + +
    • +
    + {/if} + {if $discount == '' && $plan['type'] neq 'Balance' && $custom == ''} + + +
    + +
    +
    + + + + +
    +
    +
    + + {/if} +
    {Lang::T('Summary')}
      - {if $tax} -
    • - {Lang::T('Tax')} {Lang::moneyFormat($tax)} -
    • - {if $add_cost!=0} - {foreach $bills as $k => $v} -
    • - {$k} {Lang::moneyFormat($v)} -
    • - {/foreach} -
    • - {Lang::T('Additional Cost')} {Lang::moneyFormat($add_cost)} -
    • -
    • - {Lang::T('Total')} ({Lang::T('Package Price')} - +{Lang::T('Additional Cost')}){Lang::moneyFormat($plan['price']+$add_cost+$tax)} -
    • - {else} -
    • - {Lang::T('Total')} ({Lang::T('Plan Price')} + {Lang::T('Tax')}){Lang::moneyFormat($plan['price']+$tax)} -
    • - {/if} + + {if $add_cost != 0} + {foreach $bills as $k => $v} +
    • + {$k} + {Lang::moneyFormat($v)} +
    • + {/foreach} +
    • + {Lang::T('Additional Cost')} + {Lang::moneyFormat($add_cost)} +
    • + {/if} + {if $discount} +
    • + {Lang::T('Discount Applied')} + {Lang::moneyFormat($discount)} +
    • + {/if} + + {if $amount neq '' && $custom == '1'} +
    • + {Lang::T('Total')} + + {Lang::moneyFormat($amount)} + +
    • + {elseif $plan['type'] eq 'Balance'} +
    • + {Lang::T('Total')} + + {Lang::moneyFormat($plan['price'] + $add_cost)} + +
    • {else} - {if $add_cost!=0} - {foreach $bills as $k => $v} -
    • - {$k} {Lang::moneyFormat($v)} -
    • - {/foreach} -
    • - {Lang::T('Additional Cost')} {Lang::moneyFormat($add_cost)} -
    • -
    • - {Lang::T('Total')} ({Lang::T('Package Price')} - +{Lang::T('Additional Cost')}){Lang::moneyFormat($plan['price']+$add_cost)} -
    • - {else} -
    • - {Lang::T('Total')} {Lang::moneyFormat($plan['price'])} -
    • - {/if} + {if $tax} +
    • + {Lang::T('Tax')} + {Lang::moneyFormat($tax)} +
    • + {/if} +
    • + {Lang::T('Total')} + + {Lang::moneyFormat($plan['price'] + $add_cost + $tax)} + +
    • {/if}
    -
    -
    - {Lang::T('Cancel')} -
    - - {else} -
    + +
    + + {if $custom == '1' && $amount neq ''} + + + {/if}
    -
      - - -
    • - {Lang::T('Custom Balance')} {Lang::moneyFormat($amount)} -
    • -


      -
    • - {Lang::T('Total')} {Lang::moneyFormat($amount)} -
    • -
    -
    - {Lang::T('Cancel')} + + {Lang::T('Cancel')}
    - {/if} -
+
-{include file="customer/footer.tpl"} +{include file="customer/footer.tpl"} \ No newline at end of file diff --git a/ui/ui/sections/header.tpl b/ui/ui/sections/header.tpl index 1d8ad302..f4993f15 100644 --- a/ui/ui/sections/header.tpl +++ b/ui/ui/sections/header.tpl @@ -1240,7 +1240,7 @@ {if !in_array($_admin['user_type'],['Report'])}
  • - {Lang::T('Customer')} + {Lang::T('Customer')} @@ -1265,8 +1265,6 @@
  • {Lang::T('Active Users')}
  • {if $_c['disable_voucher'] != 'yes'} -
  • {Lang::T('Vouchers')}
  • {Lang::T('Refill Customer')}
  • {/if} @@ -1307,6 +1305,38 @@ {/if} {$_MENU_AFTER_PLANS} +
  • + + {Lang::T('Cards')} + + + + + +
  • + {$_MENU_AFTER_CRM} +
  • + + {Lang::T('CRM')} + + + + + +
  • + {$_MENU_AFTER_CARDS}
  • {if in_array($_admin['user_type'],['SuperAdmin','Admin', 'Report'])} From 67a097fddfd21d56a3226faf4874b8a014556943 Mon Sep 17 00:00:00 2001 From: Focuslinkstech <45756999+Focuslinkstech@users.noreply.github.com> Date: Tue, 17 Dec 2024 19:59:04 +0100 Subject: [PATCH 02/21] Enhancement Add coupon unlimited usage 0 is unlimited Add more logic to coupon brute force attack --- system/controllers/order.php | 26 ++++++++++++++++++++------ ui/ui/coupons-add.tpl | 4 ++-- ui/ui/coupons-edit.tpl | 5 ++--- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/system/controllers/order.php b/system/controllers/order.php index d1be3771..2b5a83ba 100644 --- a/system/controllers/order.php +++ b/system/controllers/order.php @@ -413,14 +413,23 @@ switch ($action) { if (!isset($_SESSION['coupon_attempts'])) { $_SESSION['coupon_attempts'] = 0; + $_SESSION['last_attempt_time'] = time(); } if ($_SESSION['coupon_attempts'] >= 5) { - r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Too many invalid attempts. Please try again later.")); + $timeout = 10 * 60; // 10 minutes in seconds + $time_diff = time() - $_SESSION['last_attempt_time']; + + if ($time_diff >= $timeout) { + $_SESSION['coupon_attempts'] = 0; + $_SESSION['last_attempt_time'] = time(); + } else { + $remaining_time = ceil(($timeout - $time_diff) / 60); + r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Too many invalid attempts. Please try again after $remaining_time minutes.")); + } } if (_post('coupon')) { - if ($plan['routers'] === 'balance') { r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon not available for Balance")); } @@ -429,32 +438,36 @@ switch ($action) { if (!$coupon) { $_SESSION['coupon_attempts']++; + $_SESSION['last_attempt_time'] = time(); r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon not found")); } if ($coupon['status'] != 'active') { $_SESSION['coupon_attempts']++; + $_SESSION['last_attempt_time'] = time(); r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon is not active")); } + // Reset attempts after a successful coupon validation + $_SESSION['coupon_attempts'] = 0; + $_SESSION['last_attempt_time'] = time(); + $today = date('Y-m-d'); if ($today < $coupon['start_date'] || $today > $coupon['end_date']) { $_SESSION['coupon_attempts']++; r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon is not valid for today")); } - if ($coupon['usage_count'] >= $coupon['max_usage']) { + if ($coupon['max_usage'] > 0 && $coupon['usage_count'] >= $coupon['max_usage']) { $_SESSION['coupon_attempts']++; r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Coupon usage limit reached")); - } + } if ($plan['price'] < $coupon['min_order_amount']) { $_SESSION['coupon_attempts']++; r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("The order amount does not meet the minimum requirement for this coupon")); } - $_SESSION['coupon_attempts'] = 0; - // Calculate discount value $discount = 0; switch ($coupon['type']) { @@ -477,6 +490,7 @@ switch ($action) { $plan['price'] -= $discount; $coupon->usage_count = $coupon['usage_count'] + 1; $coupon->save(); + $ui->assign('discount', $discount); $ui->assign('notify', Lang::T("Coupon applied successfully. You saved " . Lang::moneyFormat($discount))); $ui->assign('notify_t', 's'); diff --git a/ui/ui/coupons-add.tpl b/ui/ui/coupons-add.tpl index 551a3f93..a838b648 100644 --- a/ui/ui/coupons-add.tpl +++ b/ui/ui/coupons-add.tpl @@ -56,8 +56,8 @@
    - -

    {Lang::T('Maximum number of times this coupon can be used')}

    + +

    {Lang::T('Maximum number of times this coupon can be used 0 is Unlimited')}

    diff --git a/ui/ui/coupons-edit.tpl b/ui/ui/coupons-edit.tpl index f6396efb..042bc323 100644 --- a/ui/ui/coupons-edit.tpl +++ b/ui/ui/coupons-edit.tpl @@ -56,9 +56,8 @@
    - -

    {Lang::T('Maximum number of times this coupon can be - used')}

    + +

    {Lang::T('Maximum number of times this coupon can be used 0 is Unlimited')}

    From 5d492def3cd717955b3ae40f9fddc5626c9f2255 Mon Sep 17 00:00:00 2001 From: Ahmad Husein <48185559+ahmadhusein17@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:08:29 +0700 Subject: [PATCH 03/21] Update selectGateway.tpl Update translation --- ui/ui/customer/selectGateway.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/ui/customer/selectGateway.tpl b/ui/ui/customer/selectGateway.tpl index c6ff6236..e98b3d66 100644 --- a/ui/ui/customer/selectGateway.tpl +++ b/ui/ui/customer/selectGateway.tpl @@ -18,7 +18,7 @@ {if !$custom}
    • - {Lang::T('Plan Name')} + {Lang::T('Package Name')} {$plan['name_plan']}
    • @@ -59,7 +59,7 @@ {else}
      • - {Lang::T('Plan Name')} + {Lang::T('Package Name')} {Lang::T('Custom Balance')}
      • @@ -176,4 +176,4 @@
  • -{include file="customer/footer.tpl"} \ No newline at end of file +{include file="customer/footer.tpl"} From b3fd1d5a3e2913a66e3f2593a7841cab57421335 Mon Sep 17 00:00:00 2001 From: gerandonk Date: Fri, 20 Dec 2024 16:48:06 +0700 Subject: [PATCH 04/21] add voucher date created and print by date add voucher date created and print by date --- install/phpnuxbill.sql | 1 + system/controllers/plan.php | 15 +++++++++++++++ system/updates.json | 3 +++ ui/ui/print-voucher.tpl | 8 ++++++++ ui/ui/voucher.tpl | 2 ++ 5 files changed, 29 insertions(+) diff --git a/install/phpnuxbill.sql b/install/phpnuxbill.sql index a76642a9..9b887850 100644 --- a/install/phpnuxbill.sql +++ b/install/phpnuxbill.sql @@ -213,6 +213,7 @@ CREATE TABLE `tbl_voucher` ( `code` varchar(55) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `user` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `status` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `used_date` DATETIME NULL DEFAULT NULL, `generated_by` int NOT NULL DEFAULT '0' COMMENT 'id admin' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/system/controllers/plan.php b/system/controllers/plan.php index 91a84631..ab738245 100644 --- a/system/controllers/plan.php +++ b/system/controllers/plan.php @@ -552,6 +552,7 @@ switch ($action) { $pagebreak = _post('pagebreak'); $limit = _post('limit'); $vpl = _post('vpl'); + $selected_datetime = _post('selected_datetime'); if (empty($vpl)) { $vpl = 3; } @@ -616,6 +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)) { + $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); @@ -628,6 +634,14 @@ switch ($action) { $ui->assign('plans', $plans); $ui->assign('limit', $limit); $ui->assign('planid', $planid); + + $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); $voucher = []; $n = 1; @@ -644,6 +658,7 @@ switch ($action) { $ui->assign('voucher', $voucher); $ui->assign('vc', $vc); + $ui->assign('selected_datetime', $selected_datetime); //for counting pagebreak $ui->assign('jml', 0); diff --git a/system/updates.json b/system/updates.json index eb2526bb..63a1c5a2 100644 --- a/system/updates.json +++ b/system/updates.json @@ -181,5 +181,8 @@ ], "2024.12.16": [ "CREATE TABLE `tbl_coupons` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `code` VARCHAR(50) NOT NULL UNIQUE, `type` ENUM('fixed', 'percent') NOT NULL, `value` DECIMAL(10,2) NOT NULL, `description` TEXT NOT NULL, `max_usage` INT NOT NULL DEFAULT 1,`usage_count` INT NOT NULL DEFAULT 0,`status` ENUM('active', 'inactive') NOT NULL, `min_order_amount` DECIMAL(10,2) NOT NULL, `max_discount_amount` DECIMAL(10,2) NOT NULL, `start_date` DATE NOT NULL,`end_date` DATE NOT NULL, `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);" + ], + "2024.12.20": [ + "ALTER TABLE `tbl_voucher` ADD `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`;" ] } \ No newline at end of file diff --git a/ui/ui/print-voucher.tpl b/ui/ui/print-voucher.tpl index 04d47443..fb8ea612 100644 --- a/ui/ui/print-voucher.tpl +++ b/ui/ui/print-voucher.tpl @@ -75,6 +75,14 @@ {/foreach} + + Date diff --git a/ui/ui/voucher.tpl b/ui/ui/voucher.tpl index f11c552e..65e32f0a 100644 --- a/ui/ui/voucher.tpl +++ b/ui/ui/voucher.tpl @@ -97,6 +97,7 @@ {Lang::T('Code Voucher')} {Lang::T('Status Voucher')} {Lang::T('Customer')} + {Lang::T('Create Date')} {Lang::T('Used Date')} {Lang::T('Generated By')} {Lang::T('Manage')} @@ -119,6 +120,7 @@ {if $ds['user'] eq '0'} - {else}{$ds['user']} {/if} + {if $ds['created_at']}{Lang::dateTimeFormat($ds['created_at'])}{/if} {if $ds['used_date']}{Lang::dateTimeFormat($ds['used_date'])}{/if} {if $ds['generated_by']} Date: Sat, 21 Dec 2024 11:16:29 +0100 Subject: [PATCH 05/21] Update autoload.php Add null condition too --- system/controllers/autoload.php | 34 +++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/system/controllers/autoload.php b/system/controllers/autoload.php index 26e5a781..c5ec2afd 100644 --- a/system/controllers/autoload.php +++ b/system/controllers/autoload.php @@ -34,9 +34,9 @@ switch ($action) { die(); case 'balance': $balance = ORM::for_table('tbl_customers')->select("balance")->find_one($routes['2'])['balance']; - if($routes['3']=='1'){ + if ($routes['3'] == '1') { echo Lang::moneyFormat($balance); - }else{ + } else { echo $balance; } die(); @@ -76,16 +76,26 @@ switch ($action) { $server = _post('server'); $jenis = _post('jenis'); if (in_array($admin['user_type'], array('SuperAdmin', 'Admin'))) { - if ($server == 'radius') { - $d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->find_many(); - } else { - $d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->find_many(); + switch ($server) { + case 'radius': + $d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->find_many(); + break; + case '': + break; + default: + $d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->find_many(); + break; } } else { - if ($server == 'radius') { - $d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->where('enabled', '1')->find_many(); - } else { - $d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->where('enabled', '1')->find_many(); + switch ($server) { + case 'radius': + $d = ORM::for_table('tbl_plans')->where('is_radius', 1)->where('type', $jenis)->find_many(); + break; + case '': + break; + default: + $d = ORM::for_table('tbl_plans')->where('routers', $server)->where('type', $jenis)->find_many(); + break; } } $ui->assign('d', $d); @@ -97,7 +107,7 @@ switch ($action) { $c = ORM::for_table('tbl_customers')->where('username', $routes['2'])->find_one(); $p = ORM::for_table('tbl_plans')->find_one($routes['3']); $dvc = Package::getDevice($p); - if ($_app_stage != 'demo') { + if ($_app_stage != 'Demo') { if (file_exists($dvc)) { require_once $dvc; try { @@ -124,7 +134,7 @@ switch ($action) { $p = ORM::for_table('tbl_plans')->find_one($d['plan_id']); $dvc = Package::getDevice($p); $status = ""; - if ($_app_stage != 'demo') { + if ($_app_stage != 'Demo') { if (file_exists($dvc)) { require_once $dvc; try { From 7c547c967e2b09ea4aa75c8ccbc0fe1ca701ecab Mon Sep 17 00:00:00 2001 From: iBNu Maksum Date: Sun, 22 Dec 2024 12:23:49 +0700 Subject: [PATCH 06/21] fix whereRaw --- radius.php | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/radius.php b/radius.php index 915a7bd0..e4c37913 100644 --- a/radius.php +++ b/radius.php @@ -42,7 +42,7 @@ try { $CHAPchallenge = _req('CHAPchallenge'); $isCHAP = false; if (!empty($CHAPassword)) { - $c = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY username = '$username'")->find_one(); + $c = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY username = '$username' AND status = 'Active'")->find_one(); if ($c) { if (Password::chap_verify($c['password'], $CHAPassword, $CHAPchallenge)) { $password = $c['password']; @@ -68,7 +68,7 @@ try { } } } else { - $c = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY `pppoe_username` = '$username'")->find_one(); + $c = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY pppoe_username = '$username' AND status = 'Active'")->find_one(); if ($c) { if (Password::chap_verify($c['password'], $CHAPassword, $CHAPchallenge)) { $password = $c['password']; @@ -111,7 +111,7 @@ try { $username = Text::alphanumeric($username, "-_.,"); $d = ORM::for_table('tbl_voucher')->whereRaw("BINARY code = '$username'")->find_one(); } else { - $d = ORM::for_table('tbl_customers')->whereRaw("BINARY username = '$username'")->find_one(); + $d = ORM::for_table('tbl_customers')->whereRaw("BINARY username = '$username' AND status = 'Active'")->find_one(); if ($d['password'] != $password) { if ($d['pppoe_password'] != $password) { unset($d); @@ -136,7 +136,7 @@ try { $CHAPchallenge = _req('CHAPchallenge'); $isCHAP = false; if (!empty($CHAPassword)) { - $c = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY username = '$username'")->find_one(); + $c = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY username = '$username' AND status = 'Active'")->find_one(); if ($c) { if (Password::chap_verify($c['password'], $CHAPassword, $CHAPchallenge)) { $password = $c['password']; @@ -162,7 +162,7 @@ try { } } } else { - $c = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY `pppoe_username` = '$username'")->find_one(); + $c = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY pppoe_username = '$username' AND status = 'Active'")->find_one(); if ($c) { if (Password::chap_verify($c['password'], $CHAPassword, $CHAPchallenge)) { $password = $c['password']; @@ -204,7 +204,7 @@ try { $tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username'")->find_one(); if ($tur) { if (!$isVoucher && !$isCHAP) { - $d = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY username = '$username'")->find_one(); + $d = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY username = '$username' AND status = 'Active'")->find_one(); if ($d) { if ($d['password'] != $password) { if ($d['pppoe_password'] != $password) { @@ -212,7 +212,7 @@ try { } } } else { - $d = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY `pppoe_username` = '$username'")->find_one(); + $d = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY pppoe_username = '$username' AND status = 'Active'")->find_one(); if ($d) { if ($d['password'] != $password) { if ($d['pppoe_password'] != $password) { @@ -226,7 +226,7 @@ try { } else { if ($isVoucher) { $username = Text::alphanumeric($username, "-_.,"); - $v = ORM::for_table('tbl_voucher')->whereRaw("BINARY code = '$username'")->where('routers', 'radius')->find_one(); + $v = ORM::for_table('tbl_voucher')->whereRaw("BINARY code = '$username' AND routers = 'radius'")->find_one(); if ($v) { if ($v['status'] == 0) { if (Package::rechargeUser(0, $v['routers'], $v['id_plan'], "Voucher", $username)) { @@ -264,8 +264,7 @@ try { } header("HTTP/1.1 200 ok"); $d = ORM::for_table('rad_acct') - ->whereRaw("BINARY username = '$username'") - ->where('acctsessionid', _post('acctsessionid')) + ->whereRaw("BINARY username = '$username' AND acctsessionid = '"._post('acctsessionid')."'") ->findOne(); if (!$d) { $d = ORM::for_table('rad_acct')->create(); @@ -292,7 +291,7 @@ try { $d->dateAdded = date('Y-m-d H:i:s'); $d->save(); if (_post('acctStatusType') == 'Start') { - $tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username'")->where('status', 'on')->where('routers', 'radius')->find_one(); + $tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username' AND `status` = 'on' AND `routers` = 'radius'")->find_one(); $plan = ORM::for_table('tbl_plans')->where('id', $tur['plan_id'])->find_one(); if ($plan['limit_type'] == "Data_Limit" || $plan['limit_type'] == "Both_Limit") { $totalUsage = $d['acctOutputOctets'] + $d['acctInputOctets']; @@ -334,7 +333,7 @@ function process_radiust_rest($tur, $code) $bw = ORM::for_table("tbl_bandwidth")->find_one($plan['id_bw']); // Count User Onlines $USRon = ORM::for_table('rad_acct') - ->where('username', $tur['username']) + ->whereRaw("BINARY username = '".$tur['username']."'") ->where("acctStatusType", 'Start') ->count(); if ($USRon >= $plan['shared_users'] && $plan['type'] == 'Hotspot') { @@ -434,6 +433,5 @@ function show_radius_result($array, $code = 200) header("HTTP/1.1 204 No Content"); die(); } - echo json_encode($array); - die(); + die(json_encode($array)); } From daf1bb7d67b2bf0576def35e6770becdaa5d8adb Mon Sep 17 00:00:00 2001 From: iBNu Maksum Date: Sun, 22 Dec 2024 15:10:01 +0700 Subject: [PATCH 07/21] fix radius rest pppoe_username --- radius.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/radius.php b/radius.php index e4c37913..ef857563 100644 --- a/radius.php +++ b/radius.php @@ -162,14 +162,16 @@ try { } } } else { - $c = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY pppoe_username = '$username' AND status = 'Active'")->find_one(); + $c = ORM::for_table('tbl_customers')->select('password')->select('username')->select('pppoe_password')->whereRaw("BINARY pppoe_username = '$username' AND status = 'Active'")->find_one(); if ($c) { if (Password::chap_verify($c['password'], $CHAPassword, $CHAPchallenge)) { $password = $c['password']; + $username = $c['username']; $isVoucher = false; $isCHAP = true; } else if (!empty($c['pppoe_password']) && Password::chap_verify($c['pppoe_password'], $CHAPassword, $CHAPchallenge)) { $password = $c['pppoe_password']; + $username = $c['username']; $isVoucher = false; $isCHAP = true; } else { @@ -202,6 +204,12 @@ try { } } $tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username'")->find_one(); + if (!$tur) { + // if check if pppoe_username + $c = ORM::for_table('tbl_customers')->select('username')->select('pppoe_password')->whereRaw("BINARY pppoe_username = '$username'")->find_one(); + $username = $c['username']; + $tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username'")->find_one(); + } if ($tur) { if (!$isVoucher && !$isCHAP) { $d = ORM::for_table('tbl_customers')->select('password')->select('pppoe_password')->whereRaw("BINARY username = '$username' AND status = 'Active'")->find_one(); From fce910455a8b2524de81d414a2a8d7e431ad109a Mon Sep 17 00:00:00 2001 From: iBNu Maksum Date: Sun, 22 Dec 2024 15:16:00 +0700 Subject: [PATCH 08/21] Lang --- system/lan/english.json | 6 +++++- system/lan/indonesia.json | 3 --- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/system/lan/english.json b/system/lan/english.json index d2ea4a1e..fbdf1f95 100644 --- a/system/lan/english.json +++ b/system/lan/english.json @@ -923,5 +923,9 @@ "Notify_Admin_upon_self_registration": "Notify Admin upon self registration", "Registration_Mandatory_Fields": "Registration Mandatory Fields", "Mikrotik_SMS_Command": "Mikrotik SMS Command", - "Mandatory_Fields": "Mandatory Fields" + "Mandatory_Fields": "Mandatory Fields", + "Usernames": "Usernames", + "Yours_Balance": "Yours Balance", + "Friend_Usernames": "Friend Usernames", + "Send_yours_balance___": "Send yours balance ? " } \ No newline at end of file diff --git a/system/lan/indonesia.json b/system/lan/indonesia.json index 22116ef4..15c64b72 100644 --- a/system/lan/indonesia.json +++ b/system/lan/indonesia.json @@ -848,9 +848,6 @@ "Forgot_Password": "Lupa Kata Sandi", "Validity_Periode": "Periode Validitas", "Transaction_History_List": "Daftar Riwayat Transaksi", - "Plan": "Paket", "plan": "paket", - "Plans" "Paket", - "Package": "Paket", "package": "paket" } From c7a09c60cb438fa53c92171a11b3d76a486f7175 Mon Sep 17 00:00:00 2001 From: iBNu Maksum Date: Sun, 22 Dec 2024 15:22:08 +0700 Subject: [PATCH 09/21] fix lang --- system/lan/english.json | 5 ++++- system/lan/indonesia.json | 47 ++++++++++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/system/lan/english.json b/system/lan/english.json index fbdf1f95..b8e4bba2 100644 --- a/system/lan/english.json +++ b/system/lan/english.json @@ -927,5 +927,8 @@ "Usernames": "Usernames", "Yours_Balance": "Yours Balance", "Friend_Usernames": "Friend Usernames", - "Send_yours_balance___": "Send yours balance ? " + "Send_yours_balance___": "Send yours balance ? ", + "Cards": "Cards", + "CRM": "CRM", + "Coupons": "Coupons" } \ No newline at end of file diff --git a/system/lan/indonesia.json b/system/lan/indonesia.json index 15c64b72..63cd00db 100644 --- a/system/lan/indonesia.json +++ b/system/lan/indonesia.json @@ -381,8 +381,8 @@ "SuperAdmin": "Super Admin", "Lists": "Daftar", "Vouchers": "Voucher", - "Refill_Customer": "Isi Ulang Pelanggan", - "Recharge_Customer": "Isi Ulang Pelanggan", + "Refill_Customer": "Isi Ulang Voucher", + "Recharge_Customer": "Isi Ulang Paket", "Plans": "Paket", "PPPOE": "PPPOE", "Bandwidth": "Bandwidth", @@ -822,7 +822,6 @@ "Methods": "Metode", "Hap_Lite": "Hap Lite", "balance": "saldo", - "Balance": "Saldo", "radius": "radius", "Max_30_days": "Maksimal 30 hari", "Information": "Informasi", @@ -849,5 +848,43 @@ "Validity_Periode": "Periode Validitas", "Transaction_History_List": "Daftar Riwayat Transaksi", "plan": "paket", - "package": "paket" -} + "package": "paket", + "Cards": "Kartu", + "CRM": "CRM", + "Coupons": "Kupon", + "Custom_Fields": "Bidang Kustom", + "Search_Coupons": "Cari Kupon", + "Add_Coupon": "Tambahkan Kupon", + "Value": "Nilai", + "Max_Usage": "Penggunaan Maksimal", + "Usage_Count": "Jumlah Pemakaian", + "Min_Order": "Pesanan Min", + "Max_Discount": "Diskon Maksimal", + "Updated_Date": "Tanggal Diperbarui", + "Action": "Tindakan", + "No_coupons_found_": "Tidak ada kupon yang ditemukan.", + "Delete_Selected": "Hapus yang Dipilih", + "Coupon_Code": "Kode Kupon", + "Random": "Acak", + "Unique_code_for_the_coupon": "Kode unik untuk kupon", + "Fixed_Discount": "Diskon Tetap", + "Percent_Discount": "Diskon Persen", + "Discount_Value": "Nilai Diskon", + "Value_of_the_discount__amount_or_percentage_": "Nilai diskon (jumlah atau persentase)", + "Brief_explanation_of_the_coupon": "Penjelasan singkat tentang kupon", + "Maximum_number_of_times_this_coupon_can_be_used_0_is_Unlimited": "Jumlah maksimum penggunaan kupon ini 0 adalah Tidak Terbatas", + "Minimum_Order_Amount": "Jumlah Pesanan Minimum", + "Minimum_cart_total_required_to_use_this_coupon": "Total keranjang minimum yang diperlukan untuk menggunakan kupon ini", + "Max_Discount_Amount": "Jumlah Diskon Maksimum", + "Maximum_discount_amount_applicable__for_percent_type_": "Jumlah diskon maksimum yang berlaku (untuk jenis persen)", + "Value_of_the_discount__percentage__max_100_": "Nilai diskon (persentase, maks 100)", + "Value_of_the_discount__amount_": "Nilai diskon (jumlah)", + "Voucher_Cards": "Kartu Voucher", + "Create_Date": "Tanggal Pembuatan", + "Postpaid_Recharge_for_the_first_time_use": "Isi Ulang Pascabayar untuk penggunaan pertama kali", + "Select_Balance_Package_or_Custom_Amount": "Pilih Paket Saldo atau Jumlah Kustom", + "Or_custom_balance_amount_below": "Atau jumlah saldo khusus di bawah ini", + "Balance_Amount": "Jumlah Saldo", + "Input_custom_balance__will_ignore_plan_above": "Masukkan saldo khusus, akan mengabaikan rencana di atas", + "Note": "Catatan" +} \ No newline at end of file From 2dc50c36371520330a4e69bd4477992ac58a60d8 Mon Sep 17 00:00:00 2001 From: Focuslinkstech <45756999+Focuslinkstech@users.noreply.github.com> Date: Wed, 25 Dec 2024 10:04:20 +0100 Subject: [PATCH 10/21] Update coupons.php fixed change status error --- system/controllers/coupons.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/controllers/coupons.php b/system/controllers/coupons.php index b76b0f59..a4ffac8d 100644 --- a/system/controllers/coupons.php +++ b/system/controllers/coupons.php @@ -251,7 +251,7 @@ switch ($action) { $couponId = $_GET['coupon_id'] ?? ''; $csrf_token = $_GET['csrf_token'] ?? ''; $status = $_GET['status'] ?? ''; - if (empty($couponId) || empty($csrf_token) || !hotspot_validateCsrfToken($csrf_token) || empty($status)) { + if (empty($couponId) || empty($csrf_token) || !Csrf::check($csrf_token) || empty($status)) { r2($_SERVER['HTTP_REFERER'], 'e', Lang::T("Invalid request")); exit; } From cf6f89b3f2bde6b9ae5c00c42fbecdf1226ffe97 Mon Sep 17 00:00:00 2001 From: Focuslinkstech <45756999+Focuslinkstech@users.noreply.github.com> Date: Wed, 25 Dec 2024 10:20:29 +0100 Subject: [PATCH 11/21] fixed coupon bugs fix add and edit bug --- system/controllers/coupons.php | 10 +++++----- ui/ui/coupons-add.tpl | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/system/controllers/coupons.php b/system/controllers/coupons.php index a4ffac8d..78550832 100644 --- a/system/controllers/coupons.php +++ b/system/controllers/coupons.php @@ -39,7 +39,7 @@ switch ($action) { $type = _post('type', ''); $value = floatval(_post('value', '')); $description = _post('description', ''); - $max_usage = _post('max_usage', ''); + $max_usage = _post('max_usage', '0'); $min_order_amount = _post('min_order_amount', ''); $max_discount_amount = intval(_post('max_discount_amount', '')); $status = _post('status', 'active'); @@ -59,8 +59,8 @@ switch ($action) { if (empty($description)) { $error[] = Lang::T('Coupon Description is required'); } - if (empty($max_usage)) { - $error[] = Lang::T('Coupon Maximum Usage is required'); + if ($max_usage < 0) { + $error[] = Lang::T('Coupon Maximum Usage must be greater than or equal to 0'); } if (empty($min_order_amount)) { $error[] = Lang::T('Coupon Minimum Order Amount is required'); @@ -170,8 +170,8 @@ switch ($action) { if (empty($description)) { $error[] = Lang::T('Coupon description is required'); } - if (empty($max_usage)) { - $error[] = Lang::T('Coupon maximum usage is required'); + if ($max_usage < 0) { + $error[] = Lang::T('Coupon Maximum Usage must be greater than or equal to 0'); } if (empty($min_order_amount)) { $error[] = Lang::T('Coupon minimum order amount is required'); diff --git a/ui/ui/coupons-add.tpl b/ui/ui/coupons-add.tpl index a838b648..137e2ccf 100644 --- a/ui/ui/coupons-add.tpl +++ b/ui/ui/coupons-add.tpl @@ -56,7 +56,7 @@
    - +

    {Lang::T('Maximum number of times this coupon can be used 0 is Unlimited')}

    From 1ea971d3f5b246757b5d73d80f98ec2b69d9c319 Mon Sep 17 00:00:00 2001 From: iBNu Maksum Date: Wed, 25 Dec 2024 20:49:47 +0700 Subject: [PATCH 12/21] fix acctSessionId --- radius.php | 4 ++-- system/lan/indonesia.json | 25 ++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/radius.php b/radius.php index ef857563..34abc380 100644 --- a/radius.php +++ b/radius.php @@ -272,7 +272,7 @@ try { } header("HTTP/1.1 200 ok"); $d = ORM::for_table('rad_acct') - ->whereRaw("BINARY username = '$username' AND acctsessionid = '"._post('acctsessionid')."'") + ->whereRaw("BINARY username = '$username' AND acctsessionid = '"._post('acctSessionId')."'") ->findOne(); if (!$d) { $d = ORM::for_table('rad_acct')->create(); @@ -286,7 +286,7 @@ try { $d->acctOutputOctets = 0; $d->acctInputOctets = 0; } - $d->acctSessionId = _post('acctSessionId'); + $d->acctsessionid = _post('acctSessionId'); $d->username = $username; $d->realm = _post('realm'); $d->nasipaddress = _post('nasip'); diff --git a/system/lan/indonesia.json b/system/lan/indonesia.json index 63cd00db..97829563 100644 --- a/system/lan/indonesia.json +++ b/system/lan/indonesia.json @@ -886,5 +886,28 @@ "Or_custom_balance_amount_below": "Atau jumlah saldo khusus di bawah ini", "Balance_Amount": "Jumlah Saldo", "Input_custom_balance__will_ignore_plan_above": "Masukkan saldo khusus, akan mengabaikan rencana di atas", - "Note": "Catatan" + "Note": "Catatan", + "Customer_Login_Page_Settings": "Pengaturan Halaman Login Pelanggan", + "Choose_Template": "Pilih Template", + "Select_your_login_template_type": "Pilih jenis template login Anda", + "Select_Login_Page": "Pilih Halaman Login", + "Select_your_preferred_login_template": "Pilih template login pilihan Anda", + "Page_Heading___Company_Name": "Judul Halaman \/ Nama Perusahaan", + "This_Name_will_be_shown_on_the_login_wallpaper": "Nama ini akan ditampilkan pada wallpaper login", + "Page_Description": "Deskripsi Halaman", + "This_will_also_display_on_wallpaper__You_can_use_html_tag": "Ini juga akan ditampilkan di wallpaper, Anda dapat menggunakan tag html", + "Favicon": "Ikon favicon", + "Best_size_30_x_30___uploaded_image_will_be_autosize": "Ukuran terbaik 30 x 30 | gambar yang diunggah akan berukuran otomatis", + "Login_Page_Logo": "Logo Halaman Login", + "Best_size_300_x_60___uploaded_image_will_be_autosize": "Ukuran terbaik 300 x 60 | gambar yang diunggah akan berukuran otomatis", + "Login_Page_Wallpaper": "Wallpaper Halaman Login", + "Best_size_1920_x_1080___uploaded_image_will_be_autosize": "Ukuran terbaik 1920 x 1080 | gambar yang diunggah akan berukuran otomatis", + "Photo_Required": "Foto Diperlukan", + "Customer_Registration_need_to_upload_their_photo": "Registrasi Pelanggan perlu mengunggah foto mereka", + "Notify_Admin": "Beritahu Admin", + "Notify_Admin_upon_self_registration": "Beritahu Admin saat registrasi mandiri", + "Mandatory_Fields": "Bidang yang wajib diisi", + "Single_Admin_Session": "Sesi Admin Tunggal", + "Mikrotik_SMS_Command": "Perintah SMS Mikrotik", + "Expired_Cronjob_Every_5_Minutes__Recommended_": "Cronjob Kedaluwarsa Setiap 5 Menit [Direkomendasikan]" } \ No newline at end of file From c5fd7c0249fbaa8e4f2ae388716d70603654d82a Mon Sep 17 00:00:00 2001 From: Focuslinkstech <45756999+Focuslinkstech@users.noreply.github.com> Date: Wed, 25 Dec 2024 14:52:10 +0100 Subject: [PATCH 13/21] 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 --- system/controllers/plan.php | 215 ++++++++++++++++++++++++------------ ui/ui/voucher-add.tpl | 8 ++ ui/ui/voucher.tpl | 114 ++++++++++++++++++- 3 files changed, 266 insertions(+), 71 deletions(-) diff --git a/system/controllers/plan.php b/system/controllers/plan.php index ab738245..d10bf624 100644 --- a/system/controllers/plan.php +++ b/system/controllers/plan.php @@ -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') . '
    '; + if (empty($type) || empty($plan) || empty($server) || empty($numbervoucher) || empty($lengthcode)) { + $msg .= Lang::T('All fields are required') . '
    '; } - if (Validator::UnsignedNumber($numbervoucher) == false) { + if (!Validator::UnsignedNumber($numbervoucher)) { $msg .= 'The Number of Vouchers must be a number' . '
    '; } - if (Validator::UnsignedNumber($lengthcode) == false) { + if (!Validator::UnsignedNumber($lengthcode)) { $msg .= 'The Length Code must be a number' . '
    '; } + 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' . '
    '; + if ($lengthcode < 6) { + $msg .= 'The Length Code must be more than 6 for numbers' . '
    '; } $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]]', '', $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']); diff --git a/ui/ui/voucher-add.tpl b/ui/ui/voucher-add.tpl index 232ebd6f..eb5c3301 100644 --- a/ui/ui/voucher-add.tpl +++ b/ui/ui/voucher-add.tpl @@ -71,6 +71,14 @@
    + +
    + + +
    + +
    +
    diff --git a/ui/ui/voucher.tpl b/ui/ui/voucher.tpl index 65e32f0a..7b08c40f 100644 --- a/ui/ui/voucher.tpl +++ b/ui/ui/voucher.tpl @@ -90,6 +90,7 @@ + @@ -97,7 +98,7 @@ - + @@ -106,6 +107,7 @@ {foreach $d as $ds} + @@ -120,7 +122,7 @@ - + - - + + - @@ -88,47 +84,29 @@
    ID {Lang::T('Type')} {Lang::T('Routers')}{Lang::T('Code Voucher')} {Lang::T('Status Voucher')} {Lang::T('Customer')}{Lang::T('Create Date')}{Lang::T('Create Date')} {Lang::T('Used Date')} {Lang::T('Generated By')} {Lang::T('Manage')}
    {$ds['id']} {$ds['type']} {$ds['routers']}{if $ds['user'] eq '0'} - {else}{$ds['user']} {/if}{if $ds['created_at']}{Lang::dateTimeFormat($ds['created_at'])}{/if}{if $ds['created_at']}{Lang::dateTimeFormat($ds['created_at'])}{/if} {if $ds['used_date']}{Lang::dateTimeFormat($ds['used_date'])}{/if} {if $ds['generated_by']} - {include file="pagination.tpl"} +
    +
    +
    +
    + {if in_array($_admin['user_type'],['SuperAdmin','Admin'])} + + {/if} +
    +
    +
    +
    +{include file="pagination.tpl"} + + {include file="sections/footer.tpl"} \ No newline at end of file From c527f1cc7c3a0742f746ee6d540a73ce5941a184 Mon Sep 17 00:00:00 2001 From: iBNu Maksum Date: Wed, 25 Dec 2024 21:12:58 +0700 Subject: [PATCH 14/21] fix radius rest pppoe --- radius.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radius.php b/radius.php index 34abc380..792ab2f1 100644 --- a/radius.php +++ b/radius.php @@ -207,8 +207,10 @@ try { if (!$tur) { // if check if pppoe_username $c = ORM::for_table('tbl_customers')->select('username')->select('pppoe_password')->whereRaw("BINARY pppoe_username = '$username'")->find_one(); - $username = $c['username']; - $tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username'")->find_one(); + if($c){ + $username = $c['username']; + $tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username'")->find_one(); + } } if ($tur) { if (!$isVoucher && !$isCHAP) { From 51b68b648c98365759f70a2445a781f238619acd Mon Sep 17 00:00:00 2001 From: AGSTR <144728914+agstrxyz@users.noreply.github.com> Date: Fri, 27 Dec 2024 17:13:53 +0700 Subject: [PATCH 15/21] Update App.php --- system/autoload/App.php | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/system/autoload/App.php b/system/autoload/App.php index e39b9611..c278dd9e 100644 --- a/system/autoload/App.php +++ b/system/autoload/App.php @@ -29,4 +29,23 @@ class App{ } } -} \ No newline at end of file + public static function getVoucher(){ + return md5(microtime()); + } + + public static function setVoucher($token, $value){ + $_SESSION[$token] = $value; + } + + public static function getVoucherValue($key){ + if(empty($key)){ + return ""; + } + if(isset($_SESSION[$key])){ + return $_SESSION[$key]; + }else{ + return ""; + } + } + +} From 1906b1aefd02aeb96d061d020c9651cd0a76a8fc Mon Sep 17 00:00:00 2001 From: AGSTR <144728914+agstrxyz@users.noreply.github.com> Date: Sun, 29 Dec 2024 19:15:19 +0700 Subject: [PATCH 16/21] Update cron.php radius rest interim update check --- system/cron.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/system/cron.php b/system/cron.php index b6b12800..16bef18a 100644 --- a/system/cron.php +++ b/system/cron.php @@ -103,6 +103,23 @@ foreach ($d as $ds) { } } + //Cek interim-update radiusrest + if ($config['frrest_interim_update'] != 0) { + + $r_a = ORM::for_table('rad_acct') + ->whereRaw("BINARY acctstatustype = 'Start' OR acctstatustype = 'Interim-Update'") + ->where_lte('dateAdded', date("Y-m-d H:i:s"))->find_many(); + + foreach ($r_a as $ra) { + $interval = $_c['frrest_interim_update']*60; + $timeUpdate = strtotime($ra['dateAdded'])+$interval; + $timeNow = strtotime(date("Y-m-d H:i:s")); + if ($timeNow >= $timeUpdate) { + $ra->acctstatustype = 'Stop'; + $ra->save(); + } + } +} if ($config['router_check']) { echo "Checking router status...\n"; @@ -208,4 +225,4 @@ $timestampFile = "$UPLOAD_PATH/cron_last_run.txt"; file_put_contents($timestampFile, time()); -run_hook('cronjob_end'); #HOOK \ No newline at end of file +run_hook('cronjob_end'); #HOOK From c5907c194bbfa4d6e26c147789255fe362a2a6f6 Mon Sep 17 00:00:00 2001 From: AGSTR <144728914+agstrxyz@users.noreply.github.com> Date: Sun, 29 Dec 2024 19:17:55 +0700 Subject: [PATCH 17/21] Update app-miscellaneous.tpl radius rest interim update --- ui/ui/app-miscellaneous.tpl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ui/ui/app-miscellaneous.tpl b/ui/ui/app-miscellaneous.tpl index af6aff1b..b2052e11 100644 --- a/ui/ui/app-miscellaneous.tpl +++ b/ui/ui/app-miscellaneous.tpl @@ -134,6 +134,16 @@ phpnuxbill-login-hotspot

    +
    + +
    + +
    +

    + {Lang::T('in minutes, leave 0 to disable this feature.')} +

    +
    @@ -197,4 +207,4 @@
    -{include file="sections/footer.tpl"} \ No newline at end of file +{include file="sections/footer.tpl"} From 1a931ef6179581cb3e0eb2484f81e670e3a759c3 Mon Sep 17 00:00:00 2001 From: AGSTR <144728914+agstrxyz@users.noreply.github.com> Date: Sun, 29 Dec 2024 21:03:46 +0700 Subject: [PATCH 18/21] Update radius.php Interim-Update freeradius rest --- radius.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/radius.php b/radius.php index 792ab2f1..e2b47695 100644 --- a/radius.php +++ b/radius.php @@ -296,7 +296,11 @@ try { $d->nasportid = _post('nasPortId'); $d->nasporttype = _post('nasPortType'); $d->framedipaddress = _post('framedIPAddress'); - $d->acctstatustype = _post('acctStatusType'); + if (_post('acctStatusType') == 'Start') { + $d->acctstatustype = 'Interim-Update'; + } else { + $d->acctstatustype = _post('acctStatusType'); + } $d->macaddr = _post('macAddr'); $d->dateAdded = date('Y-m-d H:i:s'); $d->save(); @@ -344,7 +348,7 @@ function process_radiust_rest($tur, $code) // Count User Onlines $USRon = ORM::for_table('rad_acct') ->whereRaw("BINARY username = '".$tur['username']."'") - ->where("acctStatusType", 'Start') + ->where("acctStatusType", 'Interim-Update') ->count(); if ($USRon >= $plan['shared_users'] && $plan['type'] == 'Hotspot') { show_radius_result(["control:Auth-Type" => "Accept", 'Reply-Message' => 'You are already logged in - access denied ('.$USRon.')'], 401); From b1e145f0dc7c3bed133da9a8bc03eddce625e678 Mon Sep 17 00:00:00 2001 From: iBNu Maksum Date: Mon, 30 Dec 2024 09:53:40 +0700 Subject: [PATCH 19/21] by mac and nasid --- radius.php | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/radius.php b/radius.php index e2b47695..7c9898f4 100644 --- a/radius.php +++ b/radius.php @@ -274,7 +274,7 @@ try { } header("HTTP/1.1 200 ok"); $d = ORM::for_table('rad_acct') - ->whereRaw("BINARY username = '$username' AND acctsessionid = '"._post('acctSessionId')."'") + ->whereRaw("BINARY username = '$username' AND macaddr = '"._post('macAddr')."' AND nasid = '"._post('nasid')."'") ->findOne(); if (!$d) { $d = ORM::for_table('rad_acct')->create(); @@ -296,23 +296,24 @@ try { $d->nasportid = _post('nasPortId'); $d->nasporttype = _post('nasPortType'); $d->framedipaddress = _post('framedIPAddress'); - if (_post('acctStatusType') == 'Start') { - $d->acctstatustype = 'Interim-Update'; - } else { - $d->acctstatustype = _post('acctStatusType'); - } + if(in_array(_post('acctStatusType'), ['Start', 'Stop'])){ + $d->acctstatustype = _post('acctStatusType'); + } $d->macaddr = _post('macAddr'); $d->dateAdded = date('Y-m-d H:i:s'); - $d->save(); - if (_post('acctStatusType') == 'Start') { - $tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username' AND `status` = 'on' AND `routers` = 'radius'")->find_one(); - $plan = ORM::for_table('tbl_plans')->where('id', $tur['plan_id'])->find_one(); - if ($plan['limit_type'] == "Data_Limit" || $plan['limit_type'] == "Both_Limit") { - $totalUsage = $d['acctOutputOctets'] + $d['acctInputOctets']; - $attrs['reply:Mikrotik-Total-Limit'] = Text::convertDataUnit($plan['data_limit'], $plan['data_unit']) - $totalUsage; - if ($attrs['reply:Mikrotik-Total-Limit'] < 0) { - $attrs['reply:Mikrotik-Total-Limit'] = 0; - show_radius_result(["control:Auth-Type" => "Accept", 'Reply-Message' => 'You have exceeded your data limit.'], 401); + // pastikan data akunting yang disimpan memang customer aktif phpnuxbill + $tur = ORM::for_table('tbl_user_recharges')->whereRaw("BINARY username = '$username' AND `status` = 'on' AND `routers` = 'radius'")->find_one(); + if($tur){ + $d->save(); + if (_post('acctStatusType') == 'Start') { + $plan = ORM::for_table('tbl_plans')->where('id', $tur['plan_id'])->find_one(); + if ($plan['limit_type'] == "Data_Limit" || $plan['limit_type'] == "Both_Limit") { + $totalUsage = $d['acctOutputOctets'] + $d['acctInputOctets']; + $attrs['reply:Mikrotik-Total-Limit'] = Text::convertDataUnit($plan['data_limit'], $plan['data_unit']) - $totalUsage; + if ($attrs['reply:Mikrotik-Total-Limit'] < 0) { + $attrs['reply:Mikrotik-Total-Limit'] = 0; + show_radius_result(["control:Auth-Type" => "Accept", 'Reply-Message' => 'You have exceeded your data limit.'], 401); + } } } } @@ -347,10 +348,12 @@ function process_radiust_rest($tur, $code) $bw = ORM::for_table("tbl_bandwidth")->find_one($plan['id_bw']); // Count User Onlines $USRon = ORM::for_table('rad_acct') - ->whereRaw("BINARY username = '".$tur['username']."'") - ->where("acctStatusType", 'Interim-Update') - ->count(); - if ($USRon >= $plan['shared_users'] && $plan['type'] == 'Hotspot') { + ->whereRaw("BINARY username = '".$tur['username']."' AND acctStatusType = 'Start'") + ->find_array(); + // get all the IP + $ips = array_column($USRon, 'framedipaddress'); + // check if user reach shared_users limit but IP is not in the list active + if (count($USRon) >= $plan['shared_users'] && $plan['type'] == 'Hotspot' && !in_array(_post('framedIPAddress'), $ips)) { show_radius_result(["control:Auth-Type" => "Accept", 'Reply-Message' => 'You are already logged in - access denied ('.$USRon.')'], 401); } if ($bw['rate_down_unit'] == 'Kbps') { @@ -448,4 +451,4 @@ function show_radius_result($array, $code = 200) die(); } die(json_encode($array)); -} +} \ No newline at end of file From cfe0a14f3177911dbd1a35de84ebafcab7a52bd0 Mon Sep 17 00:00:00 2001 From: Focuslinkstech <45756999+Focuslinkstech@users.noreply.github.com> Date: Mon, 30 Dec 2024 22:49:46 +0100 Subject: [PATCH 20/21] Enhancements and Fixes Added Vouchers Printing Per Page option, default is 36 for A4 papers. Fix voucher printing date showing time and seconds (its annoying), replaced with group by days created, and also added number of vouchers created on each days to make it more unique and classy --- system/controllers/plan.php | 29 +++++++++++---- system/lan/english.json | 12 +++++- ui/ui/print-voucher.tpl | 74 +++++++++++++------------------------ ui/ui/voucher-add.tpl | 44 +++++++++++++++++----- 4 files changed, 93 insertions(+), 66 deletions(-) diff --git a/system/controllers/plan.php b/system/controllers/plan.php index d10bf624..53c1a93d 100644 --- a/system/controllers/plan.php +++ b/system/controllers/plan.php @@ -595,6 +595,17 @@ switch ($action) { ->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan')) ->where('tbl_voucher.status', '0') ->where_gt('tbl_voucher.id', $from_id); + } else if ($from_id > 0 && $planid == 0 && $selected_datetime != '') { + $v = ORM::for_table('tbl_plans') + ->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan')) + ->where('tbl_voucher.status', '0') + ->where_raw("DATE(created_at) = ?", [$selected_datetime]) + ->where_gt('tbl_voucher.id', $from_id) + ->limit($limit); + $vc = ORM::for_table('tbl_plans') + ->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan')) + ->where('tbl_voucher.status', '0') + ->where_gt('tbl_voucher.id', $from_id); } else { $v = ORM::for_table('tbl_plans') ->left_outer_join('tbl_voucher', array('tbl_plans.id', '=', 'tbl_voucher.id_plan')) @@ -617,11 +628,7 @@ switch ($action) { $v = $v->where_in('generated_by', $sales)->find_many(); $vc = $vc->where_in('generated_by', $sales)->count(); } - 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); @@ -636,9 +643,14 @@ switch ($action) { $ui->assign('planid', $planid); $createdate = ORM::for_table('tbl_voucher') - ->select_expr('DISTINCT created_at', 'created_datetime') + ->select_expr( + "CASE WHEN DATE(created_at) = CURDATE() THEN 'Today' ELSE DATE(created_at) END", + 'created_datetime' + ) ->where_not_equal('created_at', '0') - ->order_by_desc('created_at') + ->select_expr('COUNT(*)', 'voucher_count') + ->group_by('created_datetime') + ->order_by_desc('created_datetime') ->find_array(); $ui->assign('createdate', $createdate); @@ -678,6 +690,7 @@ switch ($action) { $numbervoucher = _post('numbervoucher'); $lengthcode = _post('lengthcode'); $printNow = _post('print_now', 'no'); + $voucherPerPage = _post('voucher_per_page', '36'); $msg = ''; if (empty($type) || empty($plan) || empty($server) || empty($numbervoucher) || empty($lengthcode)) { @@ -768,7 +781,7 @@ switch ($action) { $ui->assign('jml', 0); $ui->assign('from_id', 0); $ui->assign('vpl', '3'); - $ui->assign('pagebreak', '12'); + $ui->assign('pagebreak', $voucherPerPage); $ui->display('print-voucher.tpl'); } diff --git a/system/lan/english.json b/system/lan/english.json index b8e4bba2..f5892a3d 100644 --- a/system/lan/english.json +++ b/system/lan/english.json @@ -930,5 +930,15 @@ "Send_yours_balance___": "Send yours balance ? ", "Cards": "Cards", "CRM": "CRM", - "Coupons": "Coupons" + "Coupons": "Coupons", + "Voucher_Cards": "Voucher Cards", + "Create_Date": "Create Date", + "Delete_Selected": "Delete Selected", + "Print_Now": "Print Now", + "Vouchers_Per_Page": "Vouchers Per Page", + "Save_as_template": "Save as template", + "Template_Name": "Template Name", + "Voucher_Code": "Voucher Code", + "Voucher_Package": "Voucher Package", + "Counter": "Counter" } \ No newline at end of file diff --git a/ui/ui/print-voucher.tpl b/ui/ui/print-voucher.tpl index fb8ea612..c090fa07 100644 --- a/ui/ui/print-voucher.tpl +++ b/ui/ui/print-voucher.tpl @@ -9,14 +9,14 @@ .ukuran { size: A4; } - + body, td, th { font-size: 12px; font-family: Segoe, "Segoe UI", "DejaVu Sans", "Trebuchet MS", Verdana, sans-serif; } - + page[size="A4"] { background: white; width: 21cm; @@ -24,32 +24,28 @@ display: block; margin: 0 auto; margin-bottom: 0.5cm; - html, body { width: 210mm; height: 297mm; } } - + @media print { body { size: auto; margin: 0; box-shadow: 0; } - page[size="A4"] { margin: 0; size: auto; box-shadow: 0; } - .page-break { display: block; page-break-before: always; } - .no-print, .no-print * { display: none !important; @@ -65,10 +61,10 @@
    From ID > limit Voucher PerLine - vouchersPageBreak after - vouchersVoucher PerLine vouchers + PageBreak after vouchers + Plans Date Date

    -

    - {Lang::T('Print side by side, it will easy to cut')}
    - show {$v|@count} vouchers from {$vc} vouchers
    - from ID {$v[0]['id']} limit {$limit} vouchers +

    {Lang::T('Print side by side, it will easy to cut')}
    show {$v|@count} vouchers from {$vc} vouchers
    from ID {$v[0]['id']} limit {$limit} vouchers
    -
    - {$n = 1} - {foreach $voucher as $vs} - {$jml = $jml + 1} - {if $n == 1} - - - {/if} - - {if $n == $vpl} -
    {$vs}
    - {$n = 1} - {else} - {$n = $n + 1} - {/if} - - - {if $jml == $pagebreak} - {$jml = 0} - -
    -
    -- pageBreak -- -
    -
    -
    - {/if} - {/foreach} +
    {$n = 1} {foreach $voucher as $vs} {$jml = $jml + 1} {if $n == 1} + + + {/if} + + {if $n == $vpl} +
    {$vs}
    + {$n = 1} {else} {$n = $n + 1} {/if} {if $jml == $pagebreak} {$jml = 0} + +
    +
    -- pageBreak -- +
    +
    +
    + {/if} {/foreach}
    - {if isset($xfooter)} - {$xfooter} - {/if} + {if isset($xfooter)} {$xfooter} {/if} + +{include file="sections/footer.tpl"} \ No newline at end of file From 0780438ca89c12a0f0cefd9b12b9b97f568da428 Mon Sep 17 00:00:00 2001 From: Focuslinkstech <45756999+Focuslinkstech@users.noreply.github.com> Date: Mon, 30 Dec 2024 22:57:37 +0100 Subject: [PATCH 21/21] Update radius.php Fix Package not showing on voucher when sorting using date created while printing Its should be Invalid Voucher... in radius-rest --- radius.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radius.php b/radius.php index 7c9898f4..ae9145b3 100644 --- a/radius.php +++ b/radius.php @@ -256,7 +256,7 @@ try { show_radius_result(['Reply-Message' => 'Voucher Expired...'], 401); } } else { - show_radius_result(['Reply-Message' => 'Voucher Expired..'], 401); + show_radius_result(['Reply-Message' => 'Invalid Voucher..'], 401); } } else { show_radius_result(['Reply-Message' => 'Internet Plan Expired..'], 401);