diff --git a/install/phpnuxbill.sql b/install/phpnuxbill.sql
index 2b8cf70b..75b27359 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),
+ `user_id` int(11) INT NOT NULL DEFAULT 0,
`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),
+ `user_id` int(11) INT NOT NULL DEFAULT 0,
`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,
@@ -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;
@@ -258,6 +259,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/radius.php b/radius.php
index 915a7bd0..ae9145b3 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,14 +162,16 @@ 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('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,9 +204,17 @@ 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();
+ if($c){
+ $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'")->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 +222,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 +236,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)) {
@@ -246,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);
@@ -264,8 +274,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 macaddr = '"._post('macAddr')."' AND nasid = '"._post('nasid')."'")
->findOne();
if (!$d) {
$d = ORM::for_table('rad_acct')->create();
@@ -279,7 +288,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');
@@ -287,19 +296,24 @@ try {
$d->nasportid = _post('nasPortId');
$d->nasporttype = _post('nasPortType');
$d->framedipaddress = _post('framedIPAddress');
- $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'")->where('status', 'on')->where('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);
+ }
}
}
}
@@ -334,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')
- ->where('username', $tur['username'])
- ->where("acctStatusType", 'Start')
- ->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') {
@@ -434,6 +450,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));
+}
\ No newline at end of file
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 "";
+ }
+ }
+
+}
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/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 {
diff --git a/system/controllers/coupons.php b/system/controllers/coupons.php
new file mode 100644
index 00000000..78550832
--- /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', '0');
+ $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 ($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');
+ }
+ 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 ($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');
+ }
+ 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) || !Csrf::check($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..2b5a83ba 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,92 @@ switch ($action) {
if ($router['name'] != 'balance') {
list($bills, $add_cost) = User::getBills($id_customer);
}
+
+ if (!isset($_SESSION['coupon_attempts'])) {
+ $_SESSION['coupon_attempts'] = 0;
+ $_SESSION['last_attempt_time'] = time();
+ }
+
+ if ($_SESSION['coupon_attempts'] >= 5) {
+ $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"));
+ }
+
+ $coupon = ORM::for_table('tbl_coupons')->where('code', _post('coupon'))->find_one();
+
+ 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['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"));
+ }
+
+ // 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 +532,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 +653,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 +676,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..53c1a93d 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();
@@ -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,9 +422,10 @@ 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'));
+ ->inner_join('tbl_voucher', ['tbl_plans.id', '=', 'tbl_voucher.id_plan']);
if (!empty($router)) {
$query->where('tbl_voucher.routers', $router);
@@ -551,6 +552,7 @@ switch ($action) {
$pagebreak = _post('pagebreak');
$limit = _post('limit');
$vpl = _post('vpl');
+ $selected_datetime = _post('selected_datetime');
if (empty($vpl)) {
$vpl = 3;
}
@@ -593,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'))
@@ -615,6 +628,7 @@ switch ($action) {
$v = $v->where_in('generated_by', $sales)->find_many();
$vc = $vc->where_in('generated_by', $sales)->count();
}
+
$template = file_get_contents("pages/Voucher.html");
$template = str_replace('[[company_name]]', $config['CompanyName'], $template);
@@ -628,6 +642,19 @@ switch ($action) {
$ui->assign('limit', $limit);
$ui->assign('planid', $planid);
+ $createdate = ORM::for_table('tbl_voucher')
+ ->select_expr(
+ "CASE WHEN DATE(created_at) = CURDATE() THEN 'Today' ELSE DATE(created_at) END",
+ 'created_datetime'
+ )
+ ->where_not_equal('created_at', '0')
+ ->select_expr('COUNT(*)', 'voucher_count')
+ ->group_by('created_datetime')
+ ->order_by_desc('created_datetime')
+ ->find_array();
+
+ $ui->assign('createdate', $createdate);
+
$voucher = [];
$n = 1;
foreach ($v as $vs) {
@@ -643,6 +670,7 @@ switch ($action) {
$ui->assign('voucher', $voucher);
$ui->assign('vc', $vc);
+ $ui->assign('selected_datetime', $selected_datetime);
//for counting pagebreak
$ui->assign('jml', 0);
@@ -653,6 +681,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');
@@ -660,18 +689,22 @@ switch ($action) {
$server = _post('server');
$numbervoucher = _post('numbervoucher');
$lengthcode = _post('lengthcode');
+ $printNow = _post('print_now', 'no');
+ $voucherPerPage = _post('voucher_per_page', '36');
$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) {
@@ -684,11 +717,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 {
@@ -708,12 +744,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', $voucherPerPage);
+ $ui->display('print-voucher.tpl');
+ }
+
if ($numbervoucher == 1) {
r2(U . 'plan/voucher-view/' . $d->id(), 's', Lang::T('Create Vouchers Successfully'));
}
@@ -724,6 +795,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'])) {
@@ -849,17 +957,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;
@@ -867,21 +975,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 {
@@ -894,8 +1002,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);
@@ -908,7 +1016,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/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
diff --git a/system/lan/english.json b/system/lan/english.json
index d2ea4a1e..f5892a3d 100644
--- a/system/lan/english.json
+++ b/system/lan/english.json
@@ -923,5 +923,22 @@
"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 ? ",
+ "Cards": "Cards",
+ "CRM": "CRM",
+ "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/system/lan/indonesia.json b/system/lan/indonesia.json
index 22116ef4..97829563 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",
@@ -848,9 +847,67 @@
"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"
-}
+ "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",
+ "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
diff --git a/system/updates.json b/system/updates.json
index dd97c432..6d9fa7a8 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,21 @@
"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" : [
- "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.5": [
+ "ALTER TABLE `tbl_transactions` ADD `user_id` INT(11) INT NOT NULL DEFAULT 0, AFTER `username`;",
+ "ALTER TABLE `tbl_payment_gateway` ADD `user_id` INT(11) INT NOT NULL DEFAULT 0, 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);"
+ ],
+ "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/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.')} +
++ | {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.')} + | +
{$vs} | - {if $n == $vpl} -
{$vs} | + {if $n == $vpl} +
ID | {Lang::T('Type')} | {Lang::T('Routers')} | @@ -97,6 +98,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')} | @@ -105,6 +107,7 @@|
---|---|---|---|---|---|---|---|---|---|---|
{$ds['id']} | {$ds['type']} | {$ds['routers']} | @@ -119,6 +122,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']}
- {include file="pagination.tpl"}
+
+
+{include file="pagination.tpl"}
+
+
{include file="sections/footer.tpl"}
\ No newline at end of file
+
+
+
+
+ {if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
+
+ {/if}
+
+ |