diff --git a/.gitignore b/.gitignore
index 5a2ce6e7..85d079af 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,4 +19,8 @@ system/plugin/ui/*
!system/plugin/ui/index.html
ui/ui_custom/**
!ui/ui_custom/index.html
-!ui/ui_custom/README.md
\ No newline at end of file
+!ui/ui_custom/README.md
+system/uploads/admin.png
+system/uploads/logo.png
+system/uploads/user.jpg
+system/uploads/notifications.json
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e65825bc..56210401 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,12 +2,28 @@
# CHANGELOG
+## 2023.8.15
+
+- Fix PPPOE Delete Customer
+- Fix Header Admin and Customer
+- Fix PDF Export by Period
+- Add pppoe_password for Customer, this pppoe_password only admin can change
+- Country Code Number Settings
+- Customer Meta Table for Custom Fields
+- Fix Add and Edit Customer Form for admin
+- add Notification Message Editor
+- cron reminder
+- Balance System, Customer can deposit money
+- Auto renewal when package expired using Customer Balance
+
+
## 2023.8.1
- Add Update file script, one click updating PHPNuxBill
- Add Custom UI folder, to custome your own template
- Delete debug text
- Fix Vendor JS
+
## 2023.7.28
- Fix link buy Voucher
@@ -22,13 +38,13 @@
Because the first time phpmixbill created, plan validity only for days and Months, many request ask for minutes and hours, i change it, but not the database.
## 2023.6.15
-- User can connect to internet from User Dashboard
+- Customer can connect to internet from Customer Dashboard
- Fix Confirm when delete
- Change Logo PHPNuxBill
- Using Composer
-- Fix Search User
-- Fix user check, if not found will logout
-- User password show but hidden
+- Fix Search Customer
+- Fix Customer check, if not found will logout
+- Customer password show but hidden
- Voucher code hidden
## 2023.6.8
diff --git a/README.md b/README.md
index d19af204..128d6cb4 100644
--- a/README.md
+++ b/README.md
@@ -33,8 +33,10 @@ Minimum Requirements
- Linux or Windows OS
- PHP Version 7.4
- Both PDO & MySQLi Support
-- GD2 Image Library
-- CURL support
+- PHP-GD2 Image Library
+- PHP-CURL
+- PHP-ZIP
+- PHP-Mbstring
- MySQL Version 4.1.x and above
can be Installed in Raspberry Pi Device.
@@ -48,6 +50,10 @@ The problem with windows is hard to set cronjob, better Linux
[Installation instructions](https://github.com/hotspotbilling/phpnuxbill/wiki)
+## Docker Version
+
+[Docker Repository](https://github.com/animegasan/phpnuxbill)
+
## RADIUS system
Still on development
diff --git a/install/phpnuxbill.sql b/install/phpnuxbill.sql
index 38169450..602f8364 100644
--- a/install/phpnuxbill.sql
+++ b/install/phpnuxbill.sql
@@ -50,10 +50,13 @@ CREATE TABLE
`id` int(10) NOT NULL,
`username` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`password` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+ `pppoe_password` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1',
`fullname` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`address` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
`phonenumber` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '0',
`email` varchar(128) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '1',
+ `balance` decimal(15,2) NOT NULL COMMENT 'For Money Deposit',
+ `auto_renewal` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Auto renewal from balance',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_login` datetime DEFAULT NULL
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci;
@@ -295,6 +298,19 @@ CREATE TABLE
`status` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci;
+
+--
+-- Struktur dari tabel `tbl_customers_meta`
+--
+
+CREATE TABLE `tbl_customers_meta` (
+ `id` int(11) NOT NULL,
+ `customer_id` int(11) NOT NULL,
+ `meta_key` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
+ `meta_value` longtext COLLATE utf8mb4_general_ci
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+
--
-- Indexes for dumped tables
@@ -543,6 +559,17 @@ ALTER TABLE
ALTER TABLE
`tbl_voucher` MODIFY `id` int(10) NOT NULL AUTO_INCREMENT;
+--
+-- Indeks untuk tabel `tbl_customers_meta`
+--
+ALTER TABLE `tbl_customers_meta`
+ ADD PRIMARY KEY (`id`);
+
+--
+-- AUTO_INCREMENT untuk tabel `tbl_customers_meta`
+--
+ALTER TABLE `tbl_customers_meta`
+ MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
--
diff --git a/install/step2.php b/install/step2.php
index f65f6de4..df1989e3 100644
--- a/install/step2.php
+++ b/install/step2.php
@@ -57,7 +57,7 @@
Continue Update System ");
} else {
echo (" $ltext Sorry. The requirements of PHPNuxBill is not available on your server.
- Please contact with us- iesien22@yahoo.com with this code- $passed Or contact with your server administrator
+ Contact with us at Telegram @phpnuxbill with this code- $passed Or contact with your server administrator
Correct The Problem To Continue ");
}
diff --git a/system/autoload/Balance.php b/system/autoload/Balance.php
new file mode 100644
index 00000000..2b3713d2
--- /dev/null
+++ b/system/autoload/Balance.php
@@ -0,0 +1,71 @@
+where('id', $id_customer)->find_one();
+ $c->balance = $amount + $c['balance'];
+ $c->save();
+ }
+
+ public static function transfer($id_customer, $phoneTarget, $amount)
+ {
+ global $config;
+ if ($config['allow_balance_transfer'] == 'yes') {
+ if(Balance::min($id_customer, $amount)){
+ if(Balance::plusByPhone($phoneTarget, $amount)){
+ return true;
+ }else{
+ Balance::plus($id_customer, $amount);
+ return false;
+ }
+ }else{
+ return false;
+ }
+ }else{
+ return false;
+ }
+ }
+
+ public static function min($id_customer, $amount)
+ {
+ $c = ORM::for_table('tbl_customers')->where('id', $id_customer)->find_one();
+ if ($c && $c['balance'] >= $amount) {
+ $c->balance = $amount - $c['balance'];
+ $c->save();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public static function plusByPhone($phone_customer, $amount)
+ {
+ $c = ORM::for_table('tbl_customers')->where('username', $phone_customer)->find_one();
+ if($c){
+ $c->balance = $amount + $c['balance'];
+ $c->save();
+ return true;
+ }
+ return false;
+ }
+
+ public static function minByPhone($phone_customer, $amount)
+ {
+ $c = ORM::for_table('tbl_customers')->where('username', $phone_customer)->find_one();
+ if ($c && $c['balance'] >= $amount) {
+ $c->balance = $amount - $c['balance'];
+ $c->save();
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/system/autoload/Lang.php b/system/autoload/Lang.php
index dc9292f6..6c790bd4 100644
--- a/system/autoload/Lang.php
+++ b/system/autoload/Lang.php
@@ -4,12 +4,31 @@
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
**/
-class Lang {
- public static function T($var) {
+class Lang
+{
+ public static function T($var)
+ {
return Lang($var);
}
- public static function htmlspecialchars($var) {
+ public static function htmlspecialchars($var)
+ {
return htmlspecialchars($var);
}
+
+ public static function moneyFormat($var)
+ {
+ global $config;
+ return $config['currency_code'] . ' ' .number_format($var, 0, $config['dec_point'], $config['thousands_sep']);
+ }
+
+ public static function phoneFormat($phone)
+ {
+ global $config;
+ if(Validator::UnsignedNumber($phone) && !empty($config['country_code_phone'])){
+ return preg_replace('/^0/', $config['country_code_phone'], $phone);
+ }else{
+ return $phone;
+ }
+ }
}
diff --git a/system/autoload/Message.php b/system/autoload/Message.php
index 0606f42c..0373d682 100644
--- a/system/autoload/Message.php
+++ b/system/autoload/Message.php
@@ -39,20 +39,21 @@ class Message
}
}
- public static function sendExpiredNotification($phone, $name, $package, $textExpired, $via)
+ public static function sendPackageNotification($phone, $name, $package, $message, $via)
{
+ $msg = str_replace('[[name]]', "*$name*", $message);
+ $msg = str_replace('[[package]]', "*$package*", $msg);
if (
!empty($phone) && strlen($phone) > 5
- && !empty($textExpired) && in_array($via, ['sms', 'wa'])
+ && !empty($message) && in_array($via, ['sms', 'wa'])
) {
- $msg = str_replace('[[name]]', "*$name*", $textExpired);
- $msg = str_replace('[[package]]', "*$package*", $msg);
if ($via == 'sms') {
Message::sendSMS($phone, $msg);
} else if ($via == 'wa') {
Message::sendWhatsapp($phone, $msg);
}
}
+ return "$via: $msg";
}
}
diff --git a/system/autoload/Mikrotik.php b/system/autoload/Mikrotik.php
index 0b98ecd4..17b344e0 100644
--- a/system/autoload/Mikrotik.php
+++ b/system/autoload/Mikrotik.php
@@ -204,24 +204,29 @@ class Mikrotik
'/ppp secret print .proplist=name',
RouterOS\Query::where('name', $username)
);
- $userName = $client->sendSync($printRequest)->getProperty('name');
+ $id = $client->sendSync($printRequest)->getProperty('.id');
$removeRequest = new RouterOS\Request('/ppp/secret/remove');
$client(
$removeRequest
- ->setArgument('numbers', $userName)
+ ->setArgument('numbers', $id)
);
}
public static function addPpoeUser($client, $plan, $customer)
{
$addRequest = new RouterOS\Request('/ppp/secret/add');
+ if(!empty($customer['pppoe_password'])){
+ $pass = $customer['pppoe_password'];
+ }else{
+ $pass = $customer['password'];
+ }
$client->sendSync(
$addRequest
->setArgument('name', $customer['username'])
->setArgument('service', 'pppoe')
->setArgument('profile', $plan['name_plan'])
- ->setArgument('password', $customer['password'])
+ ->setArgument('password', $pass)
);
}
diff --git a/system/autoload/Package.php b/system/autoload/Package.php
index 384140dd..26e1a5cc 100644
--- a/system/autoload/Package.php
+++ b/system/autoload/Package.php
@@ -19,7 +19,7 @@ class Package
*/
public static function rechargeUser($id_customer, $router_name, $plan_id, $gateway, $channel)
{
- global $_c, $_L;
+ global $_c, $_L, $_notifmsg;
$date_now = date("Y-m-d H:i:s");
$date_only = date("Y-m-d");
$time = date("H:i:s");
@@ -30,6 +30,49 @@ class Package
$c = ORM::for_table('tbl_customers')->where('id', $id_customer)->find_one();
$p = ORM::for_table('tbl_plans')->where('id', $plan_id)->where('enabled', '1')->find_one();
+
+ if ($router_name == 'balance') {
+ // insert table transactions
+ $inv = "INV-" . _raid(5);
+ $t = ORM::for_table('tbl_transactions')->create();
+ $t->invoice = $inv;
+ $t->username = $c['username'];
+ $t->plan_name = $p['name_plan'];
+ $t->price = $p['price'];
+ $t->recharged_on = $date_only;
+ $t->expiration = $date_only;
+ $t->time = $time;
+ $t->method = "$gateway - $channel";
+ $t->routers = $router_name;
+ $t->type = "Balance";
+ $t->save();
+
+ Balance::plus($id_customer, $p['price']);
+
+ $textInvoice = $_notifmsg['invoice_balance'];
+ $textInvoice = str_replace('[[company_name]]', $_c['CompanyName'], $textInvoice);
+ $textInvoice = str_replace('[[address]]', $_c['address'], $textInvoice);
+ $textInvoice = str_replace('[[phone]]', $_c['phone'], $textInvoice);
+ $textInvoice = str_replace('[[invoice]]', $inv, $textInvoice);
+ $textInvoice = str_replace('[[date]]', date($_c['date_format'], strtotime($date_only)) . " " . $time, $textInvoice);
+ $textInvoice = str_replace('[[payment_gateway]]', $_c['gateway'], $textInvoice);
+ $textInvoice = str_replace('[[payment_channel]]', $_c['channel'], $textInvoice);
+ $textInvoice = str_replace('[[type]]', 'Balance', $textInvoice);
+ $textInvoice = str_replace('[[plan_name]]', $p['name_plan'], $textInvoice);
+ $textInvoice = str_replace('[[plan_price]]', $_c['currency_code'] . " " . number_format($p['price'], 2, $_c['dec_point'], $_c['thousands_sep']), $textInvoice);
+ $textInvoice = str_replace('[[user_name]]', $c['username'], $textInvoice);
+ $textInvoice = str_replace('[[user_password]]', $c['password'], $textInvoice);
+
+ if ($_c['user_notification_payment'] == 'sms') {
+ Message::sendSMS($c['phonenumber'], $textInvoice);
+ } else if ($_c['user_notification_payment'] == 'wa') {
+ Message::sendWhatsapp($c['phonenumber'], $textInvoice);
+ }
+
+ return true;
+ }
+
+
$b = ORM::for_table('tbl_user_recharges')->where('customer_id', $id_customer)->find_one();
$mikrotik = Mikrotik::info($router_name);
@@ -198,28 +241,25 @@ class Package
$in = ORM::for_table('tbl_transactions')->where('username', $c['username'])->order_by_desc('id')->find_one();
- $msg = "*$_c[CompanyName]*\n" .
- "$_c[address]\n" .
- "$_c[phone]\n" .
- "\n\n" .
- "INVOICE: *$in[invoice]*\n" .
- "$_L[Date] : $date_now\n" .
- "$gateway $channel\n" .
- "\n\n" .
- "$_L[Type] : *$in[type]*\n" .
- "$_L[Plan_Name] : *$in[plan_name]*\n" .
- "$_L[Plan_Price] : *$_c[currency_code] " . number_format($in['price'], 2, $_c['dec_point'], $_c['thousands_sep']) . "*\n\n" .
- "$_L[Username] : *$in[username]*\n" .
- "$_L[Password] : **********\n\n" .
- "$_L[Created_On] :\n*" . date($_c['date_format'], strtotime($in['recharged_on'])) . "*\n" .
- "$_L[Expires_On] :\n*" . date($_c['date_format'], strtotime($in['expiration'])) . " $in[time]*\n" .
- "\n\n" .
- "$_c[note]";
+ $textInvoice = $_notifmsg['invoice_paid'];
+ $textInvoice = str_replace('[[company_name]]', $_c['CompanyName'], $textInvoice);
+ $textInvoice = str_replace('[[address]]', $_c['address'], $textInvoice);
+ $textInvoice = str_replace('[[phone]]', $_c['phone'], $textInvoice);
+ $textInvoice = str_replace('[[invoice]]', $in['invoice'], $textInvoice);
+ $textInvoice = str_replace('[[date]]', date($_c['date_format'], strtotime($date_now)) . " " . $time, $textInvoice);
+ $textInvoice = str_replace('[[payment_gateway]]', $_c['gateway'], $textInvoice);
+ $textInvoice = str_replace('[[payment_channel]]', $_c['channel'], $textInvoice);
+ $textInvoice = str_replace('[[type]]', $in['type'], $textInvoice);
+ $textInvoice = str_replace('[[plan_name]]', $in['plan_name'], $textInvoice);
+ $textInvoice = str_replace('[[plan_price]]', $_c['currency_code'] . " " . number_format($in['price'], 2, $_c['dec_point'], $_c['thousands_sep']), $textInvoice);
+ $textInvoice = str_replace('[[user_name]]', $in['username'], $textInvoice);
+ $textInvoice = str_replace('[[user_password]]', $c['password'], $textInvoice);
+ $textInvoice = str_replace('[[expired_date]]', date($_c['date_format'], strtotime($in['expiration'])) . " " . $in['time'], $textInvoice);
if ($_c['user_notification_payment'] == 'sms') {
- Message::sendSMS($c['phonenumber'], $msg);
+ Message::sendSMS($c['phonenumber'], $textInvoice);
} else if ($_c['user_notification_payment'] == 'wa') {
- Message::sendWhatsapp($c['phonenumber'], $msg);
+ Message::sendWhatsapp($c['phonenumber'], $textInvoice);
}
return true;
}
diff --git a/system/boot.php b/system/boot.php
index c2e0c637..9e458289 100644
--- a/system/boot.php
+++ b/system/boot.php
@@ -128,6 +128,12 @@ if (isset($_SESSION['notify'])) {
include "autoload/Hookers.php";
+// notification message
+if(file_exists("system/uploads/notifications.json")){
+ $_notifmsg =json_decode(file_get_contents('system/uploads/notifications.json'), true);
+}else{
+ $_notifmsg = json_decode(file_get_contents('system/uploads/notifications.default.json'), true);
+}
//register all plugin
foreach (glob("system/plugin/*.php") as $filename) {
diff --git a/system/controllers/accounts.php b/system/controllers/accounts.php
index b586fdb2..559d7bd2 100644
--- a/system/controllers/accounts.php
+++ b/system/controllers/accounts.php
@@ -59,7 +59,11 @@ switch ($action) {
} else {
if (!$config['radius_mode']) {
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
- Mikrotik::setPpoeUser($client, $c['username'], $npass);
+ if(!empty($d['pppoe_password'])){
+ Mikrotik::setPpoeUser($client, $c['username'], $d['pppoe_password']);
+ }else{
+ Mikrotik::setPpoeUser($client, $c['username'], $npass);
+ }
Mikrotik::removePpoeActive($client, $user['username']);
}
$d->password = $npass;
diff --git a/system/controllers/customers.php b/system/controllers/customers.php
index 3aabc72b..cd12ed48 100644
--- a/system/controllers/customers.php
+++ b/system/controllers/customers.php
@@ -116,7 +116,8 @@ switch ($action) {
$username = _post('username');
$fullname = _post('fullname');
$password = _post('password');
- $cpassword = _post('cpassword');
+ $pppoe_password = _post('pppoe_password');
+ $email = _post('email');
$address = _post('address');
$phonenumber = _post('phonenumber');
run_hook('add_customer'); #HOOK
@@ -130,9 +131,6 @@ switch ($action) {
if (!Validator::Length($password, 35, 2)) {
$msg .= 'Password should be between 3 to 35 characters' . ' ';
}
- if ($password != $cpassword) {
- $msg .= 'Passwords does not match' . ' ';
- }
$d = ORM::for_table('tbl_customers')->where('username', $username)->find_one();
if ($d) {
@@ -141,11 +139,13 @@ switch ($action) {
if ($msg == '') {
$d = ORM::for_table('tbl_customers')->create();
- $d->username = $username;
+ $d->username = Lang::phoneFormat($username);
$d->password = $password;
+ $d->pppoe_password = $pppoe_password;
+ $d->email = $email;
$d->fullname = $fullname;
$d->address = $address;
- $d->phonenumber = $username;
+ $d->phonenumber = Lang::phoneFormat($phonenumber);
$d->save();
r2(U . 'customers/list', 's', $_L['account_created_successfully']);
} else {
@@ -154,27 +154,25 @@ switch ($action) {
break;
case 'edit-post':
- $username = _post('username');
+ $username = Lang::phoneFormat(_post('username'));
$fullname = _post('fullname');
$password = _post('password');
- $cpassword = _post('cpassword');
+ $pppoe_password = _post('pppoe_password');
+ $email = _post('email');
$address = _post('address');
- $phonenumber = _post('phonenumber');
+ $phonenumber = Lang::phoneFormat(_post('phonenumber'));
run_hook('edit_customer'); #HOOK
$msg = '';
if (Validator::Length($username, 16, 2) == false) {
$msg .= 'Username should be between 3 to 15 characters' . ' ';
}
- if (Validator::Length($fullname, 26, 2) == false) {
- $msg .= 'Full Name should be between 3 to 25 characters' . ' ';
+ if (Validator::Length($fullname, 26, 1) == false) {
+ $msg .= 'Full Name should be between 2 to 25 characters' . ' ';
}
if ($password != '') {
if (!Validator::Length($password, 15, 2)) {
$msg .= 'Password should be between 3 to 15 characters' . ' ';
}
- if ($password != $cpassword) {
- $msg .= 'Passwords does not match' . ' ';
- }
}
$id = _post('id');
@@ -206,7 +204,11 @@ switch ($action) {
} else {
if(!$config['radius_mode']){
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
- Mikrotik::setPpoeUser($client,$c['username'],$password);
+ if(!empty($d['pppoe_password'])){
+ Mikrotik::setPpoeUser($client, $c['username'], $d['pppoe_password']);
+ }else{
+ Mikrotik::setPpoeUser($client, $c['username'], $password);
+ }
Mikrotik::removePpoeActive($client,$user['username']);
}
@@ -217,7 +219,9 @@ switch ($action) {
if ($password != '') {
$d->password = $password;
}
+ $d->pppoe_password = $pppoe_password;
$d->fullname = $fullname;
+ $d->email = $email;
$d->address = $address;
$d->phonenumber = $phonenumber;
$d->save();
@@ -227,6 +231,8 @@ switch ($action) {
$d->password = $password;
}
$d->fullname = $fullname;
+ $d->pppoe_password = $pppoe_password;
+ $d->email = $email;
$d->address = $address;
$d->phonenumber = $phonenumber;
$d->save();
diff --git a/system/controllers/export.php b/system/controllers/export.php
index 0cd60ac7..0d2ba727 100644
--- a/system/controllers/export.php
+++ b/system/controllers/export.php
@@ -61,6 +61,12 @@ switch ($action) {
$title = ' Reports [' . $mdate . ']';
$title = str_replace('-', ' ', $title);
+ if(file_exists('system/uploads/logo.png')){
+ $logo = 'system/uploads/logo.png';
+ }else{
+ $logo = 'system/uploads/logo.default.png';
+ }
+
if ($x) {
$html = '
@@ -69,7 +75,7 @@ switch ($action) {
' . $config['address'] . '
' . $_L['Phone_Number'] . ': ' . $config['phone'] . '
-
+
@@ -178,7 +184,6 @@ EOF;
if ($stype != '') {
$d->where('type', $stype);
}
-
$d->where_gte('recharged_on', $fdate);
$d->where_lte('recharged_on', $tdate);
$d->order_by_desc('id');
@@ -207,7 +212,6 @@ EOF;
$fdate = _post('fdate');
$tdate = _post('tdate');
$stype = _post('stype');
-
$d = ORM::for_table('tbl_transactions');
if ($stype != '') {
$d->where('type', $stype);
@@ -229,6 +233,11 @@ EOF;
$title = ' Reports [' . $mdate . ']';
$title = str_replace('-', ' ', $title);
+ if(file_exists('system/uploads/logo.png')){
+ $logo = 'system/uploads/logo.png';
+ }else{
+ $logo = 'system/uploads/logo.default.png';
+ }
if ($x) {
$html = '
@@ -238,7 +247,7 @@ EOF;
' . $config['address'] . '
' . $_L['Phone_Number'] . ': ' . $config['phone'] . '
-
+
@@ -281,11 +290,7 @@ EOF;
' . $config['currency_code'] . ' ' . number_format($xy, 2, $config['dec_point'], $config['thousands_sep']) . ' ';
run_hook('pdf_by_period'); #HOOK
- define('_MPDF_PATH', 'system/vendors/mpdf/');
-
- require('system/vendors/mpdf/mpdf.php');
-
- $mpdf = new mPDF('c', 'A4', '', '', 20, 15, 25, 25, 10, 10);
+ $mpdf = new \Mpdf\Mpdf();
$mpdf->SetProtection(array('print'));
$mpdf->SetTitle($config['CompanyName'] . ' Reports');
$mpdf->SetAuthor($config['CompanyName']);
diff --git a/system/controllers/home.php b/system/controllers/home.php
index a4372225..6108d816 100644
--- a/system/controllers/home.php
+++ b/system/controllers/home.php
@@ -9,6 +9,11 @@ $ui->assign('_title', $_L['Dashboard']);
$user = User::_info();
$ui->assign('_user', $user);
+if(isset($_GET['renewal'])){
+ $user->auto_renewal = $_GET['renewal'];
+ $user->save();
+}
+
//Client Page
$bill = User::_billing();
diff --git a/system/controllers/order.php b/system/controllers/order.php
index 016c2c4c..d1098e27 100644
--- a/system/controllers/order.php
+++ b/system/controllers/order.php
@@ -35,9 +35,13 @@ switch ($action) {
$ui->assign('_title', 'Order Plan');
$ui->assign('_system_menu', 'package');
$routers = ORM::for_table('tbl_routers')->find_many();
- $plans = ORM::for_table('tbl_plans')->where('enabled', '1')->find_many();
+ $plans_pppoe = ORM::for_table('tbl_plans')->where('enabled', '1')->where('type', 'PPPOE')->find_many();
+ $plans_hotspot = ORM::for_table('tbl_plans')->where('enabled', '1')->where('type', 'Hotspot')->find_many();
+ $plans_balance = ORM::for_table('tbl_plans')->where('enabled', '1')->where('type', 'Balance')->find_many();
$ui->assign('routers', $routers);
- $ui->assign('plans', $plans);
+ $ui->assign('plans_pppoe', $plans_pppoe);
+ $ui->assign('plans_hotspot', $plans_hotspot);
+ $ui->assign('plans_balance', $plans_balance);
run_hook('customer_view_order_plan'); #HOOK
$ui->display('user-orderPlan.tpl');
break;
@@ -118,12 +122,16 @@ switch ($action) {
run_hook('customer_buy_plan'); #HOOK
include 'system/paymentgateway/' . $config['payment_gateway'] . '.php';
call_user_func($config['payment_gateway'] . '_validate_config');
-
- $router = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routes['2'] * 1);
- $plan = ORM::for_table('tbl_plans')->where('enabled', '1')->find_one($routes['3'] * 1);
- if (empty($router) || empty($plan)) {
- r2(U . $back, 'e', Lang::T("Plan Not found"));
+ if ($routes['2'] != '0') {
+ $router = ORM::for_table('tbl_routers')->where('enabled', '1')->find_one($routes['2']);
+ if (empty($router) || empty($plan)) {
+ r2(U . $back, 'e', Lang::T("Plan Not found"));
+ }
+ }else{
+ $router['id'] = 0;
+ $router['name'] = 'balance';
}
+ $plan = ORM::for_table('tbl_plans')->where('enabled', '1')->find_one($routes['3']);
$d = ORM::for_table('tbl_payment_gateway')
->where('username', $user['username'])
->where('status', 1)
@@ -172,5 +180,5 @@ switch ($action) {
}
break;
default:
- $ui->display('404.tpl');
+ r2(U . "order/package/", 's','');
}
diff --git a/system/controllers/register.php b/system/controllers/register.php
index 5f41f97f..8ebd8226 100644
--- a/system/controllers/register.php
+++ b/system/controllers/register.php
@@ -26,7 +26,8 @@ switch ($do) {
$cpassword = _post('cpassword');
$address = _post('address');
if(!empty($config['sms_url'])){
- $phonenumber = $username;
+ $phonenumber = Lang::phoneFormat($username);
+ $username = $phonenumber;
}else if(strlen($username)<21){
$phonenumber = $username;
}
diff --git a/system/controllers/services.php b/system/controllers/services.php
index 7621a535..d36277d7 100644
--- a/system/controllers/services.php
+++ b/system/controllers/services.php
@@ -70,10 +70,10 @@ switch ($action) {
$d = ORM::for_table('tbl_plans')->find_one($id);
if ($d) {
run_hook('delete_plan'); #HOOK
- if(!$config['radius_mode']){
+ if (!$config['radius_mode']) {
$mikrotik = Mikrotik::info($d['routers']);
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
- Mikrotik::removeHotspotPlan($client,$d['name_plan']);
+ Mikrotik::removeHotspotPlan($client, $d['name_plan']);
}
$d->delete();
@@ -96,7 +96,7 @@ switch ($action) {
$validity = _post('validity');
$validity_unit = _post('validity_unit');
$routers = _post('routers');
- $enabled = _post('enabled')*1;
+ $enabled = _post('enabled');
$msg = '';
if (Validator::UnsignedNumber($validity) == false) {
@@ -130,7 +130,7 @@ switch ($action) {
}
$rate = $b['rate_up'] . $unitup . "/" . $b['rate_down'] . $unitdown;
- if(!$config['radius_mode']){
+ if (!$config['radius_mode']) {
$mikrotik = Mikrotik::info($routers);
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
Mikrotik::addHotspotPlan($client, $name, $sharedusers, $rate);
@@ -176,7 +176,7 @@ switch ($action) {
$validity = _post('validity');
$validity_unit = _post('validity_unit');
$routers = _post('routers');
- $enabled = _post('enabled')*1;
+ $enabled = _post('enabled');
$msg = '';
if (Validator::UnsignedNumber($validity) == false) {
@@ -209,7 +209,7 @@ switch ($action) {
}
$rate = $b['rate_up'] . $unitup . "/" . $b['rate_down'] . $unitdown;
- if(!$config['radius_mode']){
+ if (!$config['radius_mode']) {
$mikrotik = Mikrotik::info($routers);
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
Mikrotik::setHotspotPlan($client, $name, $sharedusers, $rate);
@@ -293,7 +293,7 @@ switch ($action) {
$d = ORM::for_table('tbl_plans')->find_one($id);
if ($d) {
run_hook('delete_ppoe'); #HOOK
- if(!$config['radius_mode']){
+ if (!$config['radius_mode']) {
$mikrotik = Mikrotik::info($d['routers']);
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
Mikrotik::removePpoePlan($client, $d['name_plan']);
@@ -312,7 +312,7 @@ switch ($action) {
$validity_unit = _post('validity_unit');
$routers = _post('routers');
$pool = _post('pool_name');
- $enabled = _post('enabled')*1;
+ $enabled = _post('enabled');
$msg = '';
if (Validator::UnsignedNumber($validity) == false) {
@@ -344,7 +344,7 @@ switch ($action) {
}
$rate = $b['rate_up'] . $unitup . "/" . $b['rate_down'] . $unitdown;
- if(!$config['radius_mode']){
+ if (!$config['radius_mode']) {
$mikrotik = Mikrotik::info($routers);
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
Mikrotik::addPpoePlan($client, $name, $pool, $rate);
@@ -377,7 +377,7 @@ switch ($action) {
$validity_unit = _post('validity_unit');
$routers = _post('routers');
$pool = _post('pool_name');
- $enabled = _post('enabled')*1;
+ $enabled = _post('enabled');
$msg = '';
if (Validator::UnsignedNumber($validity) == false) {
@@ -410,7 +410,7 @@ switch ($action) {
}
$rate = $b['rate_up'] . $unitup . "/" . $b['rate_down'] . $unitdown;
- if(!$config['radius_mode']){
+ if (!$config['radius_mode']) {
$mikrotik = Mikrotik::info($routers);
$client = Mikrotik::getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']);
Mikrotik::setPpoePlan($client, $name, $pool, $rate);
@@ -431,7 +431,112 @@ switch ($action) {
r2(U . 'services/pppoe-edit/' . $id, 'e', $msg);
}
break;
+ case 'balance':
+ $ui->assign('_title', Lang::T('Balance Plans'));
+ $name = _post('name');
+ if ($name != '') {
+ $paginator = Paginator::bootstrap('tbl_plans', 'name_plan', '%' . $name . '%', 'type', 'Balance');
+ $d = ORM::for_table('tbl_plans')->where('tbl_plans.type', 'Balance')->where_like('tbl_plans.name_plan', '%' . $name . '%')->offset($paginator['startpoint'])->limit($paginator['limit'])->find_many();
+ } else {
+ $paginator = Paginator::bootstrap('tbl_plans', 'type', 'Hotspot');
+ $d = ORM::for_table('tbl_plans')->where('tbl_plans.type', 'Balance')->offset($paginator['startpoint'])->limit($paginator['limit'])->find_many();
+ }
+ $ui->assign('d', $d);
+ $ui->assign('paginator', $paginator);
+ run_hook('view_list_balance'); #HOOK
+ $ui->display('balance.tpl');
+ break;
+ case 'balance-add':
+ $ui->assign('_title', Lang::T('Balance Plans'));
+ run_hook('view_add_balance'); #HOOK
+ $ui->display('balance-add.tpl');
+ break;
+ case 'balance-edit':
+ $ui->assign('_title', Lang::T('Balance Plans'));
+ $id = $routes['2'];
+ $d = ORM::for_table('tbl_plans')->find_one($id);
+ $ui->assign('d', $d);
+ run_hook('view_edit_balance'); #HOOK
+ $ui->display('balance-edit.tpl');
+ break;
+ case 'balance-delete':
+ $id = $routes['2'];
+
+ $d = ORM::for_table('tbl_plans')->find_one($id);
+ if ($d) {
+ run_hook('delete_balance'); #HOOK
+ $d->delete();
+ r2(U . 'services/balance', 's', $_L['Delete_Successfully']);
+ }
+ break;
+ case 'balance-edit-post':
+ $id = _post('id');
+ $name = _post('name');
+ $price = _post('price');
+ $enabled = _post('enabled');
+
+ $msg = '';
+ if (Validator::UnsignedNumber($price) == false) {
+ $msg .= 'The price must be a number' . ' ';
+ }
+ if ($name == '') {
+ $msg .= $_L['All_field_is_required'] . ' ';
+ }
+
+ $d = ORM::for_table('tbl_plans')->where('id', $id)->find_one();
+ if ($d) {
+ } else {
+ $msg .= $_L['Data_Not_Found'] . ' ';
+ }
+ run_hook('edit_ppoe'); #HOOK
+ if ($msg == '') {
+ $d->name_plan = $name;
+ $d->price = $price;
+ $d->enabled = $enabled;
+ $d->save();
+
+ r2(U . 'services/balance', 's', $_L['Updated_Successfully']);
+ } else {
+ r2(U . 'services/balance-edit/' . $id, 'e', $msg);
+ }
+ break;
+ case 'balance-add-post':
+ $name = _post('name');
+ $price = _post('price');
+ $enabled = _post('enabled');
+
+ $msg = '';
+ if (Validator::UnsignedNumber($price) == false) {
+ $msg .= 'The price must be a number' . ' ';
+ }
+ if ($name == '') {
+ $msg .= $_L['All_field_is_required'] . ' ';
+ }
+
+ $d = ORM::for_table('tbl_plans')->where('name_plan', $name)->find_one();
+ if ($d) {
+ $msg .= $_L['Plan_already_exist'] . ' ';
+ }
+ run_hook('add_ppoe'); #HOOK
+ if ($msg == '') {
+ $d = ORM::for_table('tbl_plans')->create();
+ $d->type = 'Balance';
+ $d->name_plan = $name;
+ $d->id_bw = 0;
+ $d->price = $price;
+ $d->validity = 0;
+ $d->validity_unit = 'Months';
+ $d->routers = '';
+ $d->pool = '';
+ $d->enabled = $enabled;
+ $d->save();
+
+ r2(U . 'services/balance', 's', $_L['Created_Successfully']);
+ } else {
+ r2(U . 'services/balance-add', 'e', $msg);
+ }
+ break;
default:
echo 'action not defined';
}
diff --git a/system/controllers/settings.php b/system/controllers/settings.php
index 6a5625c6..0de22c16 100644
--- a/system/controllers/settings.php
+++ b/system/controllers/settings.php
@@ -206,13 +206,15 @@ switch ($action) {
case 'app-post':
$company = _post('company');
$footer = _post('footer');
+ $enable_balance = _post('enable_balance');
+ $allow_balance_transfer = _post('allow_balance_transfer');
$disable_voucher = _post('disable_voucher');
$telegram_bot = _post('telegram_bot');
$telegram_target_id = _post('telegram_target_id');
$sms_url = _post('sms_url');
$wa_url = _post('wa_url');
$user_notification_expired = _post('user_notification_expired');
- $user_notification_expired_text = _post('user_notification_expired_text');
+ $user_notification_reminder = _post('user_notification_reminder');
$user_notification_payment = _post('user_notification_payment');
$address = _post('address');
$tawkto = _post('tawkto');
@@ -257,6 +259,28 @@ switch ($action) {
$d->save();
}
+ $d = ORM::for_table('tbl_appconfig')->where('setting', 'enable_balance')->find_one();
+ if($d){
+ $d->value = $enable_balance;
+ $d->save();
+ }else{
+ $d = ORM::for_table('tbl_appconfig')->create();
+ $d->setting = 'enable_balance';
+ $d->value = $enable_balance;
+ $d->save();
+ }
+
+ $d = ORM::for_table('tbl_appconfig')->where('setting', 'allow_balance_transfer')->find_one();
+ if($d){
+ $d->value = $allow_balance_transfer;
+ $d->save();
+ }else{
+ $d = ORM::for_table('tbl_appconfig')->create();
+ $d->setting = 'allow_balance_transfer';
+ $d->value = $allow_balance_transfer;
+ $d->save();
+ }
+
$d = ORM::for_table('tbl_appconfig')->where('setting', 'telegram_bot')->find_one();
if($d){
$d->value = $telegram_bot;
@@ -312,14 +336,14 @@ switch ($action) {
$d->save();
}
- $d = ORM::for_table('tbl_appconfig')->where('setting', 'user_notification_expired_text')->find_one();
+ $d = ORM::for_table('tbl_appconfig')->where('setting', 'user_notification_reminder')->find_one();
if($d){
- $d->value = $user_notification_expired_text;
+ $d->value = $user_notification_reminder;
$d->save();
}else{
$d = ORM::for_table('tbl_appconfig')->create();
- $d->setting = 'user_notification_expired_text';
- $d->value = $user_notification_expired_text;
+ $d->setting = 'user_notification_reminder';
+ $d->value = $user_notification_reminder;
$d->save();
}
@@ -370,6 +394,7 @@ switch ($action) {
case 'localisation-post':
$tzone = _post('tzone');
$date_format = _post('date_format');
+ $country_code_phone = _post('country_code_phone');
$lan = _post('lan');
run_hook('save_localisation'); #HOOK
if ($tzone == '' or $date_format == '' or $lan == '') {
@@ -397,6 +422,18 @@ switch ($action) {
$d->save();
}
+
+ $d = ORM::for_table('tbl_appconfig')->where('setting', 'country_code_phone')->find_one();
+ if($d){
+ $d->value = $country_code_phone;
+ $d->save();
+ }else{
+ $d = ORM::for_table('tbl_appconfig')->create();
+ $d->setting = 'country_code_phone';
+ $d->value = $country_code_phone;
+ $d->save();
+ }
+
$currency_code = $_POST['currency_code'];
$d = ORM::for_table('tbl_appconfig')->where('setting', 'currency_code')->find_one();
$d->value = $currency_code;
@@ -455,7 +492,22 @@ switch ($action) {
}
break;
-
+ case 'notifications':
+ if ($admin['user_type'] != 'Admin' and $admin['user_type'] != 'Sales') {
+ r2(U . "dashboard", 'e', $_L['Do_Not_Access']);
+ }
+ run_hook('view_notifications'); #HOOK
+ if(file_exists("system/uploads/notifications.json")){
+ $ui->assign('_json', json_decode(file_get_contents('system/uploads/notifications.json'), true));
+ }else{
+ $ui->assign('_json', json_decode(file_get_contents('system/uploads/notifications.default.json'), true));
+ }
+ $ui->display('app-notifications.tpl');
+ break;
+ case 'notifications-post':
+ file_put_contents("system/uploads/notifications.json", json_encode($_POST));
+ r2(U . 'settings/notifications', 's', $_L['Settings_Saved_Successfully']);
+ break;
case 'dbstatus':
if ($admin['user_type'] != 'Admin') {
r2(U . "dashboard", 'e', $_L['Do_Not_Access']);
diff --git a/system/cron.php b/system/cron.php
index 13a4837f..e7774867 100644
--- a/system/cron.php
+++ b/system/cron.php
@@ -18,9 +18,15 @@ ORM::configure('logging', true);
include "autoload/Hookers.php";
+// notification message
+if (file_exists("uploads/notifications.json")) {
+ $_notifmsg = json_decode(file_get_contents('uploads/notifications.json'), true);
+} else {
+ $_notifmsg = json_decode(file_get_contents('uploads/notifications.default.json'), true);
+}
//register all plugin
-foreach (glob("system/plugin/*.php") as $filename) {
+foreach (glob("plugin/*.php") as $filename) {
include $filename;
}
@@ -55,7 +61,7 @@ foreach ($result as $value) {
}
date_default_timezone_set($config['timezone']);
-$textExpired = $config['user_notification_expired_text'];
+$textExpired = $_notifmsg['expired'];
$d = ORM::for_table('tbl_user_recharges')->where('status', 'on')->find_many();
@@ -76,11 +82,26 @@ foreach ($d as $ds) {
$client = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
Mikrotik::setHotspotLimitUptime($client, $c['username']);
Mikrotik::removeHotspotActiveUser($client, $c['username']);
- Message::sendExpiredNotification($c['phonenumber'], $c['fullname'], $u['namebp'], $textExpired, $config['user_notification_expired']);
+ Message::sendPackageNotification($c['phonenumber'], $c['fullname'], $u['namebp'], $textExpired, $config['user_notification_expired']);
}
//update database user dengan status off
$u->status = 'off';
$u->save();
+
+ // autorenewal from deposit
+ if ($config['enable_balance'] == 'yes' && $c['auto_renewal']) {
+ $p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
+ if ($p && $p['enabled'] && $c['balance'] >= $p['price']) {
+ if (Package::rechargeUser($ds['customer_id'], $p['routers'], $p['id'], 'Customer', 'Balance')) {
+ // if success, then get the balance
+ Balance::min($ds['customer_id'], $p['price']);
+ } else {
+ Message::sendTelegram("FAILED RENEWAL #cron\n\n#u$c[username] #buy #Hotspot \n" . $p['name_plan'] .
+ "\nRouter: " . $router_name .
+ "\nPrice: " . $p['price']);
+ }
+ }
+ }
} else echo " : ACTIVE \r\n";
} else {
$date_now = strtotime(date("Y-m-d H:i:s"));
@@ -96,11 +117,26 @@ foreach ($d as $ds) {
$client = Mikrotik::getClient($m['ip_address'], $m['username'], $m['password']);
Mikrotik::disablePpoeUser($client, $c['username']);
Mikrotik::removePpoeActive($client, $c['username']);
- Message::sendExpiredNotification($c['phonenumber'], $c['fullname'], $u['namebp'], $textExpired, $config['user_notification_expired']);
+ Message::sendPackageNotification($c['phonenumber'], $c['fullname'], $u['namebp'], $textExpired, $config['user_notification_expired']);
}
$u->status = 'off';
$u->save();
+
+ // autorenewal from deposit
+ if ($config['enable_balance'] == 'yes' && $c['auto_renewal']) {
+ $p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
+ if ($p && $p['enabled'] && $c['balance'] >= $p['price']) {
+ if (Package::rechargeUser($ds['customer_id'], $p['routers'], $p['id'], 'Customer', 'Balance')) {
+ // if success, then get the balance
+ Balance::min($ds['customer_id'], $p['price']);
+ } else {
+ Message::sendTelegram("FAILED RENEWAL #cron\n\n#u$c[username] #buy #Hotspot \n" . $p['name_plan'] .
+ "\nRouter: " . $router_name .
+ "\nPrice: " . $p['price']);
+ }
+ }
+ }
} else echo " : ACTIVE \r\n";
}
}
diff --git a/system/cron_reminder.php b/system/cron_reminder.php
new file mode 100644
index 00000000..8fd04c12
--- /dev/null
+++ b/system/cron_reminder.php
@@ -0,0 +1,90 @@
+find_many();
+foreach ($result as $value) {
+ $config[$value['setting']] = $value['value'];
+}
+date_default_timezone_set($config['timezone']);
+
+
+$d = ORM::for_table('tbl_user_recharges')->where('status', 'on')->find_many();
+
+run_hook('cronjob_reminder'); #HOOK
+
+
+
+$day7 = date('Y-m-d', strtotime("+7 day"));
+$day3 = date('Y-m-d', strtotime("+3 day"));
+$day1 = date('Y-m-d', strtotime("+1 day"));
+print_r([$day1,$day3,$day7]);
+foreach ($d as $ds) {
+ if(in_array($ds['expiration'],[$day1,$day3,$day7])){
+ $u = ORM::for_table('tbl_user_recharges')->where('id', $ds['id'])->find_one();
+ $c = ORM::for_table('tbl_customers')->where('id', $ds['customer_id'])->find_one();
+ if($ds['expiration']==$day7){
+ echo Message::sendPackageNotification($c['phonenumber'], $c['fullname'], $u['namebp'], $_notifmsg['reminder_7_day'], $config['user_notification_reminder'])."\n";
+ }else if($ds['expiration']==$day3){
+ echo Message::sendPackageNotification($c['phonenumber'], $c['fullname'], $u['namebp'], $_notifmsg['reminder_3_day'], $config['user_notification_reminder'])."\n";
+ }else if($ds['expiration']==$day1){
+ echo Message::sendPackageNotification($c['phonenumber'], $c['fullname'], $u['namebp'], $_notifmsg['reminder_1_day'], $config['user_notification_reminder'])."\n";
+ }
+ }
+}
diff --git a/system/lan/english/common.lan.php b/system/lan/english/common.lan.php
index a72756a0..0516eea8 100644
--- a/system/lan/english/common.lan.php
+++ b/system/lan/english/common.lan.php
@@ -332,3 +332,34 @@ $_L['Your_account_not_connected_to_internet'] = 'Your account not connected to i
$_L['Failed_to_create_transaction_'] = 'Failed to create transaction. ';
$_L['Failed_to_check_status_transaction_'] = 'Failed to check status transaction. ';
$_L['Disable_Voucher'] = 'Disable Voucher';
+$_L['Balance'] = 'Balance';
+$_L['Balance_System'] = 'Balance System';
+$_L['Enable_System'] = 'Enable System';
+$_L['Allow_Transfer'] = 'Allow Transfer';
+$_L['Telegram_Notification'] = 'Telegram Notification';
+$_L['SMS_OTP_Registration'] = 'SMS OTP Registration';
+$_L['Whatsapp_Notification'] = 'Whatsapp Notification';
+$_L['Tawkto_Chat_Widget'] = 'Tawk.to Chat Widget';
+$_L['Invoice'] = 'Invoice';
+$_L['Country_Code_Phone'] = 'Country Code Phone';
+$_L['Voucher_activation_menu_will_be_hidden'] = 'Voucher activation menu will be hidden';
+$_L['Customer_can_deposit_money_to_buy_voucher'] = 'Customer can deposit money to buy voucher';
+$_L['Allow_balance_transfer_between_customers'] = 'Allow balance transfer between customers';
+$_L['Reminder_Notification'] = 'Reminder Notification';
+$_L['Reminder_Notification_Message'] = 'Reminder Notification Message';
+$_L['Reminder_7_days'] = 'Reminder 7 days';
+$_L['Reminder_3_days'] = 'Reminder 3 days';
+$_L['Reminder_1_day'] = 'Reminder 1 day';
+$_L['PPPOE_Password'] = 'PPPOE Password';
+$_L['User_Cannot_change_this_only_admin_if_it_Empty_it_will_use_user_password'] = 'User Cannot change this, only admin. if it Empty it will use user password';
+$_L['Invoice_Balance_Message'] = 'Invoice Balance Message';
+$_L['Invoice_Notification_Payment'] = 'Invoice Notification Payment';
+$_L['Balance_Notification_Payment'] = 'Balance Notification Payment';
+$_L['Balance_Plans'] = 'Balance Plans';
+$_L['Buy_Balance'] = 'Buy Balance?';
+$_L['Price'] = 'Price';
+$_L['Validity'] = 'Validity';
+$_L['Disable_auto_renewal'] = 'Disable auto renewal?';
+$_L['Auto_Renewal_On'] = 'Auto Renewal On';
+$_L['Enable_auto_renewal'] = 'Enable auto renewal?';
+$_L['Auto_Renewal_Off'] = 'Auto Renewal Off';
diff --git a/system/lan/indonesia/common.lan.php b/system/lan/indonesia/common.lan.php
index aeaafba2..8a197d45 100644
--- a/system/lan/indonesia/common.lan.php
+++ b/system/lan/indonesia/common.lan.php
@@ -325,3 +325,16 @@ $_L['Not_Online_Login_now'] = 'Internet mati, Nyalakan?';
$_L['You_are_Online_Logout'] = 'Internet hidup, Putuskan?';
$_L['Connect_to_Internet'] = 'Koneksikan internet?';
$_L['Your_account_not_connected_to_internet'] = 'Akun tidak terkoneksi dengan internet';
+$_L['Balance'] = 'Saldo';
+$_L['Balance_System'] = 'Sistem Saldo';
+$_L['Enable_System'] = 'Aktifkan Saldo';
+$_L['Allow_Transfer'] = 'Bolehkan Transfer';
+$_L['Telegram_Notification'] = 'Telegram Notification';
+$_L['SMS_OTP_Registration'] = 'SMS OTP Registration';
+$_L['Whatsapp_Notification'] = 'Whatsapp Notification';
+$_L['Tawkto_Chat_Widget'] = 'Tawk.to Chat Widget';
+$_L['Invoice'] = 'Invoice';
+$_L['Country_Code_Phone'] = 'Kode Negara Telepon';
+$_L['Voucher_activation_menu_will_be_hidden'] = 'Info Pembelian Voucher dan Redeem akan disembunyikan';
+$_L['Customer_can_deposit_money_to_buy_voucher'] = 'Pelanggan dapat topup saldo untuk langganan Internet';
+$_L['Allow_balance_transfer_between_customers'] = 'Bolehkan transfer saldo antar pelanggan';
diff --git a/system/lan/spanish/common.lan.php b/system/lan/spanish/common.lan.php
index 661fb5dc..ec13dfd0 100644
--- a/system/lan/spanish/common.lan.php
+++ b/system/lan/spanish/common.lan.php
@@ -328,6 +328,16 @@ $_L['Not_Online_Login_now'] = 'Not Online, Login now?';
$_L['You_are_Online_Logout'] = 'You are Online, Logout?';
$_L['Connect_to_Internet'] = 'Connect to Internet?';
$_L['Your_account_not_connected_to_internet'] = 'Your account not connected to internet';
-
-
-
+$_L['Balance'] = 'Balance';
+$_L['Balance_System'] = 'Balance System';
+$_L['Enable_System'] = 'Enable System';
+$_L['Allow_Transfer'] = 'Allow Transfer';
+$_L['Telegram_Notification'] = 'Telegram Notification';
+$_L['SMS_OTP_Registration'] = 'SMS OTP Registration';
+$_L['Whatsapp_Notification'] = 'Whatsapp Notification';
+$_L['Tawkto_Chat_Widget'] = 'Tawk.to Chat Widget';
+$_L['Invoice'] = 'Invoice';
+$_L['Country_Code_Phone'] = 'Country Code Phone';
+$_L['Voucher_activation_menu_will_be_hidden'] = 'Voucher activation menu will be hidden';
+$_L['Customer_can_deposit_money_to_buy_voucher'] = 'Customer can deposit money to buy voucher';
+$_L['Allow_balance_transfer_between_customers'] = 'Allow balance transfer between customers';
\ No newline at end of file
diff --git a/system/lan/turkish/common.lan.php b/system/lan/turkish/common.lan.php
index ef852b7c..c21d9b8b 100755
--- a/system/lan/turkish/common.lan.php
+++ b/system/lan/turkish/common.lan.php
@@ -304,3 +304,16 @@ $_L['Not_Online_Login_now'] = 'Not Online, Login now?';
$_L['You_are_Online_Logout'] = 'You are Online, Logout?';
$_L['Connect_to_Internet'] = 'Connect to Internet?';
$_L['Your_account_not_connected_to_internet'] = 'Your account not connected to internet';
+$_L['Balance'] = 'Balance';
+$_L['Balance_System'] = 'Balance System';
+$_L['Enable_System'] = 'Enable System';
+$_L['Allow_Transfer'] = 'Allow Transfer';
+$_L['Telegram_Notification'] = 'Telegram Notification';
+$_L['SMS_OTP_Registration'] = 'SMS OTP Registration';
+$_L['Whatsapp_Notification'] = 'Whatsapp Notification';
+$_L['Tawkto_Chat_Widget'] = 'Tawk.to Chat Widget';
+$_L['Invoice'] = 'Invoice';
+$_L['Country_Code_Phone'] = 'Country Code Phone';
+$_L['Voucher_activation_menu_will_be_hidden'] = 'Voucher activation menu will be hidden';
+$_L['Customer_can_deposit_money_to_buy_voucher'] = 'Customer can deposit money to buy voucher';
+$_L['Allow_balance_transfer_between_customers'] = 'Allow balance transfer between customers';
\ No newline at end of file
diff --git a/system/updates.json b/system/updates.json
new file mode 100644
index 00000000..7dce27f7
--- /dev/null
+++ b/system/updates.json
@@ -0,0 +1,14 @@
+{
+ "2023.8.9": [
+ "ALTER TABLE `tbl_customers` ADD `balance` decimal(15,2) NOT NULL DEFAULT 0.00 COMMENT 'For Money Deposit' AFTER `email`;",
+ "CREATE TABLE `tbl_customers_meta` (`id` int(11) NOT NULL, `customer_id` int(11) NOT NULL,`meta_key` varchar(64) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `meta_value` longtext COLLATE utf8mb4_general_ci) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;",
+ "ALTER TABLE `tbl_customers_meta` ADD PRIMARY KEY (`id`);",
+ "ALTER TABLE `tbl_customers_meta` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;"
+ ],
+ "2023.8.14": [
+ "ALTER TABLE `tbl_customers` ADD `pppoe_password` varchar(45) NOT NULL DEFAULT '1' COMMENT 'For PPPOE Login' AFTER `password`;",
+ "ALTER TABLE `tbl_plans` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
+ "ALTER TABLE `tbl_transactions` CHANGE `type` `type` ENUM('Hotspot','PPPOE','Balance') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL;",
+ "ALTER TABLE `tbl_customers` ADD `auto_renewal` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Auto renewall using balance' AFTER `balance`;"
+ ]
+}
\ No newline at end of file
diff --git a/system/uploads/admin.png b/system/uploads/admin.default.png
similarity index 100%
rename from system/uploads/admin.png
rename to system/uploads/admin.default.png
diff --git a/system/uploads/logo.png b/system/uploads/logo.default.png
similarity index 100%
rename from system/uploads/logo.png
rename to system/uploads/logo.default.png
diff --git a/system/uploads/notifications.default.json b/system/uploads/notifications.default.json
new file mode 100644
index 00000000..aff56897
--- /dev/null
+++ b/system/uploads/notifications.default.json
@@ -0,0 +1,8 @@
+{
+ "expired": "Hello [[name]], your internet package [[package]] has been expired.",
+ "reminder_7_day": "Hello *[[name]]*, \r\nyour internet package *[[package]]* will be expired in 7 days.",
+ "reminder_3_day": "Hello *[[name]]*, \r\nyour internet package *[[package]]* will be expired in 3 days.",
+ "reminder_1_day": "Hello *[[name]]*,\r\n your internet package *[[package]]* will be expired tomorrow.",
+ "invoice_paid": "*[[company_name]]*\r\n[[address]]\r\n[[phone]]\r\n\r\n\r\nINVOICE: *[[invoice]]*\r\nDate : [[date]]\r\n[[payment_gateway]] [[payment_channel]]\r\n\r\n\r\nType : *[[type]]*\r\nPackage : *[[plan_name]]*\r\nPrice : *[[plan_price]]*\r\n\r\nUsername : *[[user_name]]*\r\nPassword : ***********\r\n\r\nExpired : *[[expired_date]]*\r\n\r\n\r\nThank you...",
+ "invoice_balance": "*[[company_name]]*\r\n[[address]]\r\n[[phone]]\r\n\r\n\r\nINVOICE: *[[invoice]]*\r\nDate : [[date]]\r\n[[payment_gateway]] [[payment_channel]]\r\n\r\n\r\nType : *[[type]]*\r\nPackage : *[[plan_name]]*\r\nPrice : *[[plan_price]]*\r\n\r\nThank you..."
+}
diff --git a/system/uploads/user.jpg b/system/uploads/user.default.jpg
similarity index 100%
rename from system/uploads/user.jpg
rename to system/uploads/user.default.jpg
diff --git a/ui/ui/app-localisation.tpl b/ui/ui/app-localisation.tpl
index ebbc960a..a5ca2ba5 100644
--- a/ui/ui/app-localisation.tpl
+++ b/ui/ui/app-localisation.tpl
@@ -1,80 +1,107 @@
{include file="sections/header.tpl"}
-
-
-
-
{$_L['Localisation']}
-
+
+
+
+
{$_L['Localisation']}
+
-
-
-
-{include file="sections/footer.tpl"}
+
+
+
+
+
+{include file="sections/footer.tpl"}
\ No newline at end of file
diff --git a/ui/ui/app-notifications.tpl b/ui/ui/app-notifications.tpl
new file mode 100644
index 00000000..ab5a40e1
--- /dev/null
+++ b/ui/ui/app-notifications.tpl
@@ -0,0 +1,125 @@
+{include file="sections/header.tpl"}
+
+
+{include file="sections/footer.tpl"}
\ No newline at end of file
diff --git a/ui/ui/app-settings.tpl b/ui/ui/app-settings.tpl
index f8ffb419..83ee92ae 100644
--- a/ui/ui/app-settings.tpl
+++ b/ui/ui/app-settings.tpl
@@ -4,31 +4,38 @@
-
{$_L['General_Settings']}
+
+
+
+
+ {$_L['General_Settings']}
+
+
Still on Testing.
+ Changing from Radius will not add existing user to Mikrotik Hotspot.
+ With Radius user can use Hotspot or PPOE.
-
Telegram Notification
+
+
+
+
+ {Lang::T('Balance System')}
+
+
+
+
+
+
+ {Lang::T('Telegram Notification')}
+
Telegram Bot Token
@@ -83,88 +130,122 @@
value="{$_c['telegram_target_id']}" placeholder="12345678">
-
You will get Payment and Error notification
+
You will get Payment and Error
+ notification
+
+
+
+
+
+ {Lang::T('SMS OTP Registration')}
-
SMS OTP Registration
-
You can use WhatsApp in here too. Free Server
+
You can use WhatsApp in here too. Free Server
+
+
+
+
+
+ {Lang::T('Whatsapp Notification')}
-
Whatsapp Notification
+
You can use WhatsApp in here too. Free Server
+
+
+
+
+
+ {Lang::T('User Notification')}
-
{Lang::T('User Notification')}
-
Tawk.to Chat Widget
+
+
+
+
+ {Lang::T('Tawk.to Chat Widget')}
+
-
Invoice
-
-
+
+
/ip hotspot walled-garden
+ add dst-host=tawk.to
+ add dst-host=*.tawk.to
@@ -173,11 +254,11 @@ add dst-host=*.tawk.to
{$_L['Save']}
-
+
/ip hotspot walled-garden
- add dst-host={$_domain}
- add dst-host=*.{$_domain}
+add dst-host={$_domain}
+add dst-host=*.{$_domain}
diff --git a/ui/ui/balance-add.tpl b/ui/ui/balance-add.tpl
new file mode 100644
index 00000000..8fa3c734
--- /dev/null
+++ b/ui/ui/balance-add.tpl
@@ -0,0 +1,48 @@
+{include file="sections/header.tpl"}
+
+
+
+{include file="sections/footer.tpl"}
diff --git a/ui/ui/balance-edit.tpl b/ui/ui/balance-edit.tpl
new file mode 100644
index 00000000..16922bfb
--- /dev/null
+++ b/ui/ui/balance-edit.tpl
@@ -0,0 +1,49 @@
+{include file="sections/header.tpl"}
+
+
+
+{include file="sections/footer.tpl"}
diff --git a/ui/ui/balance.tpl b/ui/ui/balance.tpl
new file mode 100644
index 00000000..17ace703
--- /dev/null
+++ b/ui/ui/balance.tpl
@@ -0,0 +1,56 @@
+{include file="sections/header.tpl"}
+
+
+
+
+
{Lang::T('Balance Plans')}
+
+
+
+
+
+
+ {$_L['Plan_Name']}
+ {$_L['Plan_Price']}
+ {$_L['Manage']}
+
+
+
+ {foreach $d as $ds}
+
+ {$ds['name_plan']}
+ {Lang::moneyFormat($ds['price'])}
+
+ {$_L['Edit']}
+ {$_L['Delete']}
+
+
+ {/foreach}
+
+
+
+ {$paginator['contents']}
+
+
+
+
+
+
+{include file="sections/footer.tpl"}
diff --git a/ui/ui/customers-add.tpl b/ui/ui/customers-add.tpl
index 0d4ecb9d..c367095e 100644
--- a/ui/ui/customers-add.tpl
+++ b/ui/ui/customers-add.tpl
@@ -9,9 +9,13 @@
{$_L['Full_Name']}
@@ -20,17 +24,34 @@
+
+
-