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.')} +

+
@@ -197,4 +207,4 @@
-{include file="sections/footer.tpl"} \ No newline at end of file +{include file="sections/footer.tpl"} diff --git a/ui/ui/coupons-add.tpl b/ui/ui/coupons-add.tpl new file mode 100644 index 00000000..137e2ccf --- /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 0 is Unlimited')}

+
+
+ + +
+ +
+ +

{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..042bc323 --- /dev/null +++ b/ui/ui/coupons-edit.tpl @@ -0,0 +1,149 @@ +{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 0 is Unlimited')}

+
+
+ + +
+ +
+ +

{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..e98b3d66 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('Package 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('Package 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"} diff --git a/ui/ui/print-voucher.tpl b/ui/ui/print-voucher.tpl index 04d47443..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 - vouchers - PageBreak after - vouchers + Voucher PerLine vouchers + + PageBreak after vouchers + Plans + 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 diff --git a/ui/ui/voucher.tpl b/ui/ui/voucher.tpl index f11c552e..7b08c40f 100644 --- a/ui/ui/voucher.tpl +++ b/ui/ui/voucher.tpl @@ -90,6 +90,7 @@ + @@ -97,6 +98,7 @@ + @@ -105,6 +107,7 @@ {foreach $d as $ds} + @@ -119,6 +122,7 @@ +
ID {Lang::T('Type')} {Lang::T('Routers')}{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')}
{$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['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