deposit sort

dev
Aji Kamaludin 1 year ago
parent d42065944b
commit 5cf19db71d
No known key found for this signature in database
GPG Key ID: 19058F67F0083AD3

@ -54,7 +54,7 @@ rsync -arP -e 'ssh -p 224' --exclude=node_modules --exclude=database/database.sq
### v2
```bash
rsync -arP -e 'ssh -p 225' --exclude=node_modules --exclude=database/database.sqlite --exclude=.git --exclude=.env --exclude=public/hot . arm@ajikamaludin.id:/home/arm/projects/www/voucher
rsync -arP -e 'ssh -p 225' --exclude=node_modules --exclude=public/uploads --exclude=database/database.sqlite --exclude=.git --exclude=.env --exclude=public/hot . arm@ajikamaludin.id:/home/arm/projects/www/voucher
ssh -p 225 arm@ajikamaludin.id -C docker exec php82 php /var/www/voucher/artisan migrate:refresh --seed
```

@ -27,7 +27,7 @@
- [x] tambah biaya admin di deposit manual transfer
- [x] info di ubah jadi html
- [ ] tambahan detail customer untuk detail mitra wbb
- [x] tambahan detail customer untuk detail mitra wbb
- [x] detail customer level untuk tampilan screen level customer di depan
- [x] rombak fitur affiliasi
- [x] tambah detail di user admin

@ -25,7 +25,7 @@ class Kernel extends ConsoleKernel
*/
protected function commands()
{
$this->load(__DIR__ . '/Commands');
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}

@ -50,7 +50,7 @@ class AccountController extends Controller
public function edit(Account $account)
{
return inertia('Account/Form', [
'account' => $account
'account' => $account,
]);
}
@ -64,7 +64,6 @@ class AccountController extends Controller
'logo' => 'nullable|image',
]);
if ($request->hasFile('logo')) {
$file = $request->file('logo');
$file->store('uploads', 'public');

@ -55,7 +55,7 @@ class CustomerController extends Controller
{
return inertia('Customer/Form', [
'levels' => CustomerLevel::all(),
'statuses' => Customer::STATUS
'statuses' => Customer::STATUS,
]);
}
@ -101,22 +101,22 @@ class CustomerController extends Controller
return inertia('Customer/Form', [
'customer' => $customer->load(['level', 'partner']),
'levels' => CustomerLevel::all(),
'statuses' => Customer::STATUS
'statuses' => Customer::STATUS,
]);
}
public function update(Request $request, Customer $customer)
{
$request->validate([
'email' => 'nullable|email|unique:customers,email,' . $customer->id,
'username' => 'required|string|min:5|alpha_dash|unique:customers,username,' . $customer->id,
'email' => 'nullable|email|unique:customers,email,'.$customer->id,
'username' => 'required|string|min:5|alpha_dash|unique:customers,username,'.$customer->id,
'password' => 'nullable|string|min:8',
'name' => 'required|string',
'fullname' => 'required|string',
'address' => 'required|string',
'phone' => 'required|string',
'image' => 'nullable|image',
'status' => 'required|numeric'
'status' => 'required|numeric',
]);
if ($request->password != '') {
@ -138,7 +138,7 @@ class CustomerController extends Controller
'address' => $request->address,
'phone' => $request->phone,
'image' => $customer->image,
'status' => $request->status
'status' => $request->status,
]);
return redirect()->route('customer.index')
@ -169,7 +169,7 @@ class CustomerController extends Controller
'customer_id' => $customer->id,
], [
'limit' => $request->paylater_limit,
'day_deadline' => $request->day_deadline
'day_deadline' => $request->day_deadline,
]);
return redirect()->route('customer.index')
@ -189,7 +189,7 @@ class CustomerController extends Controller
'items.*.type' => 'required|in:text,file',
'items.*.value' => 'nullable|string',
]);
//
//
$partner = CustomerAsDataPartner::updateOrCreate([
'customer_id' => $customer->id,

@ -0,0 +1,28 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
class CustomerHistoryController extends Controller
{
public function deposit()
{
}
public function sale()
{
}
public function paylater()
{
}
public function paylater_limit()
{
}
public function paylater_deadline()
{
}
}

@ -20,7 +20,7 @@ class CustomerLevelController extends Controller
public function edit(CustomerLevel $customerLevel)
{
return inertia('CustomerLevel/Form', [
'customer_level' => $customerLevel
'customer_level' => $customerLevel,
]);
}

@ -103,11 +103,11 @@ class CustomerMitraController extends Controller
'status' => 'required|numeric',
'image' => 'nullable|image',
'identity_image' => 'nullable|image',
//
//
'level' => 'required|exists:customer_levels,key',
'paylater_limit' => 'required|numeric',
'day_deadline' => 'required|numeric|max:365',
//
//
'id_number' => 'nullable|string',
'job' => 'nullable|string',
'image_selfie' => 'nullable|image',
@ -134,7 +134,7 @@ class CustomerMitraController extends Controller
'phone' => $request->phone,
'status' => $request->status,
'customer_level_id' => $level->id,
'identity_verified' => $request->hasFile('identity_image') ? Customer::VERIFIED : Customer::NOT_VERIFIED
'identity_verified' => $request->hasFile('identity_image') ? Customer::VERIFIED : Customer::NOT_VERIFIED,
]);
if ($request->hasFile('image')) {
@ -157,7 +157,7 @@ class CustomerMitraController extends Controller
$customer->paylater()->create([
'limit' => $request->paylater_limit,
'day_deadline' => $request->day_deadline
'day_deadline' => $request->day_deadline,
]);
$partner = $customer->partner()->create([
@ -213,8 +213,8 @@ class CustomerMitraController extends Controller
public function update(Request $request, Customer $customer)
{
$request->validate([
'email' => 'nullable|email|unique:customers,email,' . $customer->id,
'username' => 'required|string|min:5|alpha_dash|unique:customers,username,' . $customer->id,
'email' => 'nullable|email|unique:customers,email,'.$customer->id,
'username' => 'required|string|min:5|alpha_dash|unique:customers,username,'.$customer->id,
'password' => 'nullable|string|min:8',
'name' => 'required|string',
'fullname' => 'required|string',
@ -223,11 +223,11 @@ class CustomerMitraController extends Controller
'status' => 'required|numeric',
'image' => 'nullable|image',
'identity_image' => 'nullable|image',
//
//
'level' => 'required|exists:customer_levels,key',
'paylater_limit' => 'required|numeric',
'day_deadline' => 'required|numeric',
//
//
'id_number' => 'nullable|string',
'job' => 'nullable|string',
'image_selfie' => 'nullable|image',
@ -251,7 +251,7 @@ class CustomerMitraController extends Controller
'phone' => $request->phone,
'status' => $request->status,
'customer_level_id' => $level->id,
'identity_verified' => $request->hasFile('identity_image') ? Customer::VERIFIED : Customer::NOT_VERIFIED
'identity_verified' => $request->hasFile('identity_image') ? Customer::VERIFIED : Customer::NOT_VERIFIED,
]);
if ($request->password != '') {
@ -281,7 +281,7 @@ class CustomerMitraController extends Controller
'customer_id' => $customer->id,
], [
'limit' => $request->paylater_limit,
'day_deadline' => $request->day_deadline
'day_deadline' => $request->day_deadline,
]);
$partner = $customer->partner()->updateOrCreate([

@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use App\Models\DepositHistory;
use App\Models\PaylaterHistory;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
@ -13,13 +14,11 @@ class DepositController extends Controller
{
public function index(Request $request)
{
$query = DepositHistory::with(['customer', 'account', 'depositLocation', 'editor'])
->where('credit', 0)
->orderBy('is_valid', 'desc')
->orderBy('updated_at', 'desc');
$deposits = DepositHistory::with(['customer', 'account', 'depositLocation', 'editor'])
->where('credit', 0);
if ($request->q != '') {
$query->where(function ($query) use ($request) {
$deposits->where(function ($query) use ($request) {
$query->where('description', 'ilike', "%$request->q%")
->orWhereHas('customer', function ($query) use ($request) {
$query->where('fullname', 'ilike', "%$request->q%")
@ -29,31 +28,63 @@ class DepositController extends Controller
});
}
$sortBy = 'updated_at';
$sortRule = 'desc';
if ($request->sortBy != '' && $request->sortRule != '') {
$sortBy = $request->sortBy;
$sortRule = $request->sortRule;
}
$deposits->orderBy($sortBy, $sortRule);
if ($request->status != '') {
$query->where('is_valid', $request->status);
$deposits->where('is_valid', $request->status);
}
if ($request->customer_id != '') {
$query->where('is_valid', $request->customer_id);
$deposits->where('is_valid', $request->customer_id);
}
$customers = Customer::with(['paylater'])->orderBy('deposit_balance', 'desc');
$stats = [
'deposit_this_month' => DepositHistory::whereMonth('created_at', now()->month)
->whereYear('created_at', now()->year)
->sum('debit'),
'deposit_today' => DepositHistory::whereDate('created_at', now())
->sum('debit'),
'paylater_this_month' => PaylaterHistory::whereMonth('created_at', now()->month)
->whereYear('created_at', now()->year)
->sum('debit'),
'paylater_today' => PaylaterHistory::whereDate('created_at', now())
->sum('debit'),
];
return inertia('DepositHistory/Index', [
'query' => $query->paginate(),
'deposits' => $deposits->paginate(),
'_q' => $request->q,
'_sortBy' => $sortBy,
'_sortRule' => $sortRule,
'customers' => $customers->paginate(10, '*', 'customer_page'),
'stats' => $stats,
]);
}
// TODO: ubah deposit confirm menggunakan page form
public function edit(DepositHistory $deposit)
{
return inertia('DepositHistory/Form', [
'deposit' => $deposit->load(['customer', 'account', 'depositLocation', 'editor']),
]);
}
public function update(Request $request, DepositHistory $deposit)
{
$request->validate([
'status' => [
'is_valid' => [
'required',
Rule::in([DepositHistory::STATUS_VALID, DepositHistory::STATUS_REJECT]),
],
'debit' => 'required|numeric',
]);
if ($request->status == DepositHistory::STATUS_REJECT) {
@ -62,14 +93,13 @@ class DepositController extends Controller
DB::beginTransaction();
$deposit->update([
'is_valid' => $request->status,
'note' => $request->reject_reason
'debit' => $request->debit,
'is_valid' => $request->is_valid,
'note' => $request->reject_reason,
]);
if ($request->status == DepositHistory::STATUS_VALID) {
$deposit->update_customer_balance();
$customer = Customer::find($deposit->customer_id);
$customer->repayPaylater($deposit);
$deposit->create_notification_user();
}
DB::commit();

@ -14,7 +14,7 @@ class DepositLocationController extends Controller
$query = DepositLocation::orderBy('updated_at', 'desc');
return inertia('DepositLocation/Index', [
'query' => $query->paginate()
'query' => $query->paginate(),
]);
}
@ -59,7 +59,7 @@ class DepositLocationController extends Controller
public function edit(DepositLocation $location)
{
return inertia('DepositLocation/Form', [
'location' => $location
'location' => $location,
]);
}

@ -43,7 +43,7 @@ class InfoController extends Controller
public function edit(Info $info)
{
return inertia('Info/Form', [
'info' => $info
'info' => $info,
]);
}

@ -101,7 +101,7 @@ class SettingController extends Controller
return inertia('Setting/Affilate', [
'setting' => $setting,
'levels' => CustomerLevel::all()
'levels' => CustomerLevel::all(),
]);
}
@ -132,7 +132,7 @@ class SettingController extends Controller
->toArray();
Setting::where('key', 'AFFILATE_ALLOWED_LEVELS')->update([
'value' => json_encode($allowedLevel)
'value' => json_encode($allowedLevel),
]);
Cache::flush();

@ -62,7 +62,7 @@ class UserController extends Controller
public function edit(User $user)
{
return inertia('User/Form', [
'user' => $user->load(['role'])
'user' => $user->load(['role']),
]);
}
@ -70,9 +70,9 @@ class UserController extends Controller
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email,' . $user->id,
'email' => 'required|email|unique:users,email,'.$user->id,
'password' => 'nullable|string|max:255',
'username' => 'required|alpha_dash|unique:users,username,' . $user->id,
'username' => 'required|alpha_dash|unique:users,username,'.$user->id,
'phone_wa' => 'required|string',
'photo' => 'nullable|image',
]);

@ -11,7 +11,6 @@ use App\Services\GeneralService;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class VoucherController extends Controller
{
@ -101,7 +100,7 @@ class VoucherController extends Controller
return redirect()->route('voucher.index', [
'location' => $profile->location_id,
'profile' => $profile->id
'profile' => $profile->id,
])
->with('message', ['type' => 'success', 'message' => 'Item has beed saved']);
}
@ -131,7 +130,7 @@ class VoucherController extends Controller
return redirect()->route('voucher.index', [
'location' => $profile->location_id,
'profile' => $profile->id
'profile' => $profile->id,
])
->with('message', ['type' => 'success', 'message' => 'Item has beed updated']);
}
@ -169,7 +168,7 @@ class VoucherController extends Controller
if (count($vouchers) <= 0) {
return redirect()->route('voucher.index', [
'location' => $profile->location_id,
'profile' => $profile->id
'profile' => $profile->id,
])
->with('message', ['type' => 'error', 'message' => 'Nothing to import']);
}
@ -187,10 +186,9 @@ class VoucherController extends Controller
}
DB::commit();
return redirect()->route('voucher.index', [
'location' => $profile->location_id,
'profile' => $profile->id
'profile' => $profile->id,
])
->with('message', ['type' => 'success', 'message' => 'Items has beed saved']);
}
@ -226,8 +224,7 @@ class VoucherController extends Controller
->with('message', ['type' => 'success', 'message' => "$count Item has beed deleted"]);
}
return redirect()->route('voucher.location')
->with('message', ['type' => 'error', 'message' => "Items not found to delete"]);
->with('message', ['type' => 'error', 'message' => 'Items not found to delete']);
}
}

@ -54,7 +54,7 @@ class AuthController extends Controller
}
$password = Hash::check($request->password, $user->password);
if (!$password) {
if (! $password) {
return redirect()->route('customer.login')
->with('message', ['type' => 'error', 'message' => 'Invalid credentials']);
}
@ -72,7 +72,7 @@ class AuthController extends Controller
$isAuth = Auth::guard('customer')->login($user);
if ($isAuth) {
return redirect()->route('home.index');
return redirect()->route('home.index', ['direct' => 1]);
}
return redirect()->route('customer.login')
@ -94,6 +94,7 @@ class AuthController extends Controller
->user();
} catch (\Exception $e) {
info('auth google error', ['exception' => $e]);
return redirect()->route('customer.login')
->with('message', ['type' => 'error', 'message' => 'Google authentication fail, please try again']);
}
@ -108,7 +109,7 @@ class AuthController extends Controller
'fullname' => $user->name,
'name' => $user->nickname,
'email' => $user->email,
'username' => Str::slug($user->name . '_' . Str::random(5), '_'),
'username' => Str::slug($user->name.'_'.Str::random(5), '_'),
'google_id' => $user->id,
'google_oauth_response' => json_encode($user),
'status' => Customer::STATUS_ACTIVE,
@ -127,7 +128,7 @@ class AuthController extends Controller
Auth::guard('customer')->loginUsingId($customer->id);
return redirect()->route('home.index');
return redirect()->route('home.index', ['direct' => 1]);
}
public function register(Request $request)
@ -213,6 +214,7 @@ class AuthController extends Controller
$refferal = Customer::where('referral_code', $code)->first();
if ($refferal == null) {
session()->forget('referral_code');
return;
}
@ -228,7 +230,7 @@ class AuthController extends Controller
$poin = $refferal->poins()->create([
'debit' => $bonuspoin,
'description' => GeneralService::generateBonusPoinCode(),
'narration' => 'Bonus Poin Affilate (Register)'
'narration' => 'Bonus Poin Affilate (Register)',
]);
$poin->update_customer_balance();

@ -3,17 +3,12 @@
namespace App\Http\Controllers\Customer;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use App\Models\DepositHistory;
use App\Models\LocationProfile;
use App\Models\PoinReward;
use App\Models\Sale;
use App\Models\Setting;
use App\Models\Voucher;
use App\Services\GeneralService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
class CartController extends Controller
@ -33,7 +28,7 @@ class CartController extends Controller
$checkAllowProcess = [
$customer->deposit_balance >= $total,
$customer->paylater_remain >= $total
$customer->paylater_remain >= $total,
];
$allowProcess = in_array(true, $checkAllowProcess);
@ -54,10 +49,11 @@ class CartController extends Controller
$operator = $request->param ?? 'add'; //delete, sub, add
$customer = $request->user('customer');
if (!$customer->allow_transaction) {
if (! $customer->allow_transaction) {
$customer->carts()->delete();
return redirect()->back()
->with('message', ['type' => 'error', 'message' => 'akun anda dibekukan tidak dapat melakukan transaksi',]);
->with('message', ['type' => 'error', 'message' => 'akun anda dibekukan tidak dapat melakukan transaksi']);
}
$item = $customer->carts()->where(['entity_id' => $profile->id])->first();
@ -69,20 +65,20 @@ class CartController extends Controller
if ($operator == 'add') {
// bisa tambah filter stock vouchernya
$item->update([
'quantity' => $item->quantity + 1
'quantity' => $item->quantity + 1,
]);
}
if ($operator == 'sub') {
if ($item->quantity - 1 != 0) {
$item->update([
'quantity' => $item->quantity - 1
'quantity' => $item->quantity - 1,
]);
}
}
} else {
$customer->carts()->create([
'entity_id' => $profile->id,
'quantity' => 1
'quantity' => 1,
]);
session()->flash('message', ['type' => 'success', 'message' => 'voucher ditambahkan ke keranjang', 'cart' => 1]);
@ -151,7 +147,7 @@ class CartController extends Controller
'price' => $voucher->validate_price,
'quantity' => 1,
'additional_info_json' => json_encode([
'voucher' => $voucher->load(['locationProfile.location'])
'voucher' => $voucher->load(['locationProfile.location']),
]),
]);

@ -4,7 +4,6 @@ namespace App\Http\Controllers\Customer;
use App\Http\Controllers\Controller;
use App\Models\CustomerLevel;
use Illuminate\Http\Request;
class CustomerLevelController extends Controller
{

@ -4,7 +4,6 @@ namespace App\Http\Controllers\Customer;
use App\Http\Controllers\Controller;
use App\Models\Account;
use App\Models\Customer;
use App\Models\DepositHistory;
use App\Models\DepositLocation;
use App\Models\Setting;
@ -13,7 +12,6 @@ use App\Services\MidtransService;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
class DepositController extends Controller
@ -37,16 +35,16 @@ class DepositController extends Controller
return inertia('Deposit/Index', [
'histories' => $histories->paginate(20),
'_start_date' => $start_date->format('m/d/Y'),
'_end_date' => $end_date->format('m/d/Y')
'_end_date' => $end_date->format('m/d/Y'),
]);
}
public function create(Request $request)
{
$customer = $request->user('customer');
if (!$customer->allow_transaction) {
if (! $customer->allow_transaction) {
return redirect()->back()
->with('message', ['type' => 'error', 'message' => 'akun anda dibekukan tidak dapat melakukan transaksi',]);
->with('message', ['type' => 'error', 'message' => 'akun anda dibekukan tidak dapat melakukan transaksi']);
}
return inertia('Deposit/Topup', [
@ -103,7 +101,7 @@ class DepositController extends Controller
'is_production' => app()->isProduction(),
'direct' => $request->direct,
'bank_admin_fee' => Setting::getByKey('ADMINFEE_MANUAL_TRANSFER'),
'cash_admin_fee' => Setting::getByKey('ADMINFEE_CASH_DEPOSIT')
'cash_admin_fee' => Setting::getByKey('ADMINFEE_CASH_DEPOSIT'),
]);
}
@ -157,9 +155,6 @@ class DepositController extends Controller
if ($is_valid == DepositHistory::STATUS_VALID) {
$deposit->update_customer_balance();
$customer = Customer::find($deposit->customer_id);
$customer->repayPaylater($deposit);
}
DB::commit();
@ -183,8 +178,6 @@ class DepositController extends Controller
if ($request->transaction_status == 'settlement' || $request->transaction_status == 'capture') {
$deposit->fill(['payment_status' => DepositHistory::STATUS_VALID]);
$deposit->update_customer_balance();
$customer = Customer::find($deposit->customer_id);
$customer->repayPaylater($deposit);
$deposit->create_notification();
$deposit->create_notification_user();
} elseif ($request->transaction_status == 'pending') {

@ -4,7 +4,6 @@ namespace App\Http\Controllers\Customer;
use App\Http\Controllers\Controller;
use App\Models\DepositLocation;
use Illuminate\Http\Request;
class DepositLocationController extends Controller
{

@ -5,7 +5,6 @@ namespace App\Http\Controllers\Customer;
use App\Http\Controllers\Controller;
use App\Models\Banner;
use App\Models\Customer;
use App\Models\CustomerLocationFavorite;
use App\Models\Info;
use App\Models\Location;
use App\Models\LocationProfile;
@ -56,7 +55,7 @@ class HomeController extends Controller
'locations' => $locations,
'profiles' => $profiles,
'_slocations' => $slocations,
'_status' => 0
'_status' => 0,
]);
}
@ -81,7 +80,7 @@ class HomeController extends Controller
'locations' => $locations,
'profiles' => $profiles->paginate(self::LIMIT),
'_flocations' => $customer->locationFavorites,
'_status' => 1
'_status' => 1,
]);
}

@ -27,7 +27,7 @@ class PoinController extends Controller
return inertia('Poin/Index', [
'poins' => $poins->paginate(20),
'_start_date' => $start_date->format('m/d/Y'),
'_end_date' => $end_date->format('m/d/Y')
'_end_date' => $end_date->format('m/d/Y'),
]);
}

@ -58,16 +58,16 @@ class PoinExchangeController extends Controller
'profiles' => $profiles,
'_slocations' => $slocations,
'_flocations' => $flocations,
'_favorite' => $favorite
'_favorite' => $favorite,
]);
}
public function exchange(Request $request, LocationProfile $profile)
{
$customer = $request->user('customer');
if (!$customer->allow_transaction) {
if (! $customer->allow_transaction) {
return redirect()->back()
->with('message', ['type' => 'error', 'message' => 'akun anda dibekukan tidak dapat melakukan transaksi',]);
->with('message', ['type' => 'error', 'message' => 'akun anda dibekukan tidak dapat melakukan transaksi']);
}
$batchCount = $profile->count_unsold();
@ -99,7 +99,7 @@ class PoinExchangeController extends Controller
'price' => $voucher->validate_price_poin,
'quantity' => 1,
'additional_info_json' => json_encode([
'voucher' => $voucher->load(['locationProfile.location'])
'voucher' => $voucher->load(['locationProfile.location']),
]),
]);
@ -109,7 +109,7 @@ class PoinExchangeController extends Controller
$poin = $customer->poins()->create([
'credit' => $voucher->validate_price_poin,
'description' => $sale->code,
'narration' => 'Penukaran Voucher Poin'
'narration' => 'Penukaran Voucher Poin',
]);
$poin->update_customer_balance(true);

@ -12,8 +12,9 @@ class ProfileController extends Controller
public function index()
{
$shareText = Setting::getByKey('AFFILATE_SHARE_REFFERAL_CODE');
return inertia('Profile/Index', [
'share_text' => $shareText
'share_text' => $shareText,
]);
}
@ -31,7 +32,7 @@ class ProfileController extends Controller
'name' => 'string|required',
'address' => 'string|required',
'phone' => 'string|required|numeric',
'username' => 'string|required|min:5|alpha_dash|unique:customers,username,' . $customer->id,
'username' => 'string|required|min:5|alpha_dash|unique:customers,username,'.$customer->id,
'password' => 'nullable|string|min:8|confirmed',
'image' => 'nullable|image',
]);

@ -26,7 +26,7 @@ class TransactionController extends Controller
return inertia('Trx/Index', [
'query' => $query->paginate(20),
'_start_date' => $start_date->format('m/d/Y'),
'_end_date' => $end_date->format('m/d/Y')
'_end_date' => $end_date->format('m/d/Y'),
]);
}

@ -3,7 +3,6 @@
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;

@ -5,7 +5,6 @@ namespace App\Jobs;
use App\Models\DepositHistory;
use App\Models\Setting;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;

@ -4,7 +4,6 @@ namespace App\Mail;
use App\Models\Customer;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
@ -19,7 +18,7 @@ class CustomerVerification extends Mailable
*/
public function __construct(public Customer $customer)
{
//
//
}
/**

@ -6,7 +6,6 @@ use App\Models\Traits\UserTrackable;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Concerns\HasUlids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Carbon;
@ -31,7 +30,7 @@ class Customer extends Authenticatable
const STATUS = [
self::STATUS_INACTIVE => 'Belum Aktif',
self::STATUS_ACTIVE => 'Aktif',
self::STATUS_SUSPEND => 'Suspend/Block'
self::STATUS_SUSPEND => 'Suspend/Block',
];
protected $fillable = [
@ -68,6 +67,7 @@ class Customer extends Authenticatable
'display_phone',
'paylater_remain',
'paylater_limit',
'paylater_usage',
'is_allow_paylater',
'verification_status',
'status_text',
@ -145,7 +145,7 @@ class Customer extends Authenticatable
return ' - ';
}
return '+62' . $this->phone;
return '+62'.$this->phone;
});
}
@ -163,6 +163,17 @@ class Customer extends Authenticatable
});
}
public function paylaterUsage(): Attribute
{
return Attribute::make(get: function () {
if ($this->is_allow_paylater) {
return $this->paylater->usage;
}
return '';
});
}
public function paylaterRemain(): Attribute
{
return Attribute::make(get: function () {
@ -215,8 +226,10 @@ class Customer extends Authenticatable
return Attribute::make(get: function () {
if ($this->poin_expired_at != null) {
$date = Carbon::parse($this->poin_expired_at)->translatedFormat('d F Y');
return "poin kadaluarsa pada $date";
}
return 'informasi masa kadaluarsan poin';
});
}
@ -227,6 +240,7 @@ class Customer extends Authenticatable
if ($this->status == self::STATUS_SUSPEND) {
return false;
}
return true;
});
}
@ -280,23 +294,4 @@ class Customer extends Authenticatable
{
return $this->hasOne(CustomerAsDataPartner::class);
}
public function repayPaylater(DepositHistory $deposit): void
{
if ($this->paylater != null && $this->paylater->usage > 0) {
$cut = $deposit->debit > $this->paylater->usage ? $this->paylater->usage : $deposit->debit;
$paylater = $this->paylaterHistories()->create([
'credit' => $cut,
'description' => $deposit->description . ' (Pengembalian)',
]);
$paylater->update_customer_paylater();
$deposit = $this->deposites()->create([
'credit' => $cut,
'description' => 'Pembayaran Paylater',
]);
$deposit->update_customer_balance();
}
}
}

@ -28,25 +28,27 @@ class CustomerAsDataPartner extends Model
if ($this->image_selfie != null) {
return asset($this->image_selfie);
}
return null;
});
}
public function fileStatementUrl(): Attribute
{
return Attribute::make(get: function () {
if ($this->file_statement != null) {
return asset($this->file_statement);
}
return null;
});
}
public function fileAgreementUrl(): Attribute
{
return Attribute::make(get: function () {
if ($this->file_agreement != null) {
return asset($this->file_agreement);
}
return null;
});
}
}

@ -43,6 +43,7 @@ class CustomerLevel extends Model
return asset($this->logo);
});
}
public static function getByKey($key)
{
return CustomerLevel::where('key', $key)->first();

@ -98,10 +98,10 @@ class DepositHistory extends Model
{
return Attribute::make(get: function () {
if ($this->credit == 0) {
return 'Rp ' . number_format($this->debit, is_float($this->debit) ? 2 : 0, ',', '.');
return 'Rp '.number_format($this->debit, is_float($this->debit) ? 2 : 0, ',', '.');
}
return '-Rp ' . number_format($this->credit, is_float($this->credit) ? 2 : 0, ',', '.');
return '-Rp '.number_format($this->credit, is_float($this->credit) ? 2 : 0, ',', '.');
});
}
@ -142,7 +142,7 @@ class DepositHistory extends Model
}
Notification::create([
'entity_type' => User::class,
'description' => $this->customer->fullname . ' melakukan deposit transfer manual sebesar : ' . $this->amount . $status,
'description' => $this->customer->fullname.' melakukan deposit transfer manual sebesar : '.$this->amount.$status,
]);
}
@ -153,14 +153,14 @@ class DepositHistory extends Model
}
Notification::create([
'entity_type' => User::class,
'description' => $this->customer->fullname . ' melakukan deposit manual sebesar : ' . $this->amount . $status,
'description' => $this->customer->fullname.' melakukan deposit manual sebesar : '.$this->amount.$status,
]);
}
if ($this->payment_channel == Setting::PAYMENT_MIDTRANS) {
Notification::create([
'entity_type' => User::class,
'description' => $this->customer->fullname . ' melakukan deposit via midtrans sebesar : ' . $this->amount,
'description' => $this->customer->fullname.' melakukan deposit via midtrans sebesar : '.$this->amount,
]);
}
}
@ -169,7 +169,7 @@ class DepositHistory extends Model
{
Notification::create([
'entity_id' => $this->customer_id,
'description' => 'Deposit ' . $this->description . ' sebesar ' . $this->amount . ' sudah sukses diterima',
'description' => 'Deposit '.$this->description.' sebesar '.$this->amount.' sudah sukses diterima',
]);
}
}

@ -20,7 +20,7 @@ class DepositLocation extends Model
protected $appends = [
'image_url',
'operational_hour'
'operational_hour',
];
protected function imageUrl(): Attribute
@ -33,7 +33,7 @@ class DepositLocation extends Model
protected function operationalHour(): Attribute
{
return Attribute::make(get: function () {
return $this->open_hour . ' - ' . $this->close_hour;
return $this->open_hour.' - '.$this->close_hour;
});
}
}

@ -71,7 +71,7 @@ class LocationProfile extends Model
{
if (count(self::$instance) == 0) {
self::$instance = [
'customer' => Customer::find(auth()->guard('customer')->id())
'customer' => Customer::find(auth()->guard('customer')->id()),
];
}
@ -96,7 +96,7 @@ class LocationProfile extends Model
public function displayExpired(): Attribute
{
return Attribute::make(get: function () {
return $this->expired . ' ' . $this->expired_unit;
return $this->expired.' '.$this->expired_unit;
});
}
@ -119,11 +119,14 @@ class LocationProfile extends Model
$price = $this->prices;
if (auth()->guard('customer')->check()) {
$customer = self::getInstance()['customer'];
return $price->where('customer_level_id', $customer->customer_level_id)
->value('price');
}
return $price->max('price');
}
return $this->price;
});
}
@ -135,11 +138,14 @@ class LocationProfile extends Model
$price = $this->prices;
if (auth()->guard('customer')->check()) {
$customer = self::getInstance()['customer'];
return $price->where('customer_level_id', $customer->customer_level_id)
->value('display_price');
}
return $price->max('display_price');
}
return $this->display_price;
});
}
@ -151,11 +157,14 @@ class LocationProfile extends Model
$price = $this->prices;
if (auth()->guard('customer')->check()) {
$customer = self::getInstance()['customer'];
return $price->where('customer_level_id', $customer->customer_level_id)
->value('discount');
}
return $price->min('discount');
}
return $this->discount;
});
}
@ -167,11 +176,14 @@ class LocationProfile extends Model
$price = $this->prices;
if (auth()->guard('customer')->check()) {
$customer = self::getInstance()['customer'];
return $price->where('customer_level_id', $customer->customer_level_id)
->value('bonus_poin');
}
return $price->max('bonus_poin');
}
return $this->bonus_poin;
});
}
@ -183,11 +195,14 @@ class LocationProfile extends Model
$price = $this->prices;
if (auth()->guard('customer')->check()) {
$customer = self::getInstance()['customer'];
return $price->where('customer_level_id', $customer->customer_level_id)
->value('price_poin');
}
return $price->max('price_poin');
}
return $this->price_poin;
});
}

@ -10,7 +10,7 @@ class PaylaterCustomer extends Model
'description',
'customer_id',
'day_deadline',
'day_deadline_at'
'day_deadline_at',
];
public function customer()

@ -34,7 +34,7 @@ class PaylaterHistory extends Model
'customer_id',
'type',
'is_valid',
'image_prove'
'image_prove',
];
protected $appends = [
@ -71,10 +71,10 @@ class PaylaterHistory extends Model
{
return Attribute::make(get: function () {
if ($this->credit == 0) {
return 'Rp' . number_format($this->debit, is_float($this->debit) ? 2 : 0, ',', '.');
return 'Rp'.number_format($this->debit, is_float($this->debit) ? 2 : 0, ',', '.');
}
return '-Rp' . number_format($this->credit, is_float($this->credit) ? 2 : 0, ',', '.');
return '-Rp'.number_format($this->credit, is_float($this->credit) ? 2 : 0, ',', '.');
});
}
}

@ -64,7 +64,7 @@ class PoinHistory extends Model
$customer->update([
'poin_balance' => $customer->poin_balance + $this->debit - $this->credit,
'poin_expired_at' => $customer->poin_expired_at
'poin_expired_at' => $customer->poin_expired_at,
]);
}
}

@ -27,7 +27,7 @@ class Role extends Model
);
}
public function users()
public function users()
{
return $this->hasMany(User::class);
}

@ -73,7 +73,7 @@ class Sale extends Model
public function displayAmount(): Attribute
{
return Attribute::make(get: function () {
return 'Rp ' . number_format($this->amount, is_float($this->amount) ? 2 : 0, ',', '.');
return 'Rp '.number_format($this->amount, is_float($this->amount) ? 2 : 0, ',', '.');
});
}
@ -82,12 +82,12 @@ class Sale extends Model
if ($this->payed_with == self::PAYED_WITH_POIN) {
Notification::create([
'entity_type' => User::class,
'description' => $this->customer->fullname . ' melakukan penukaran ' . $this->items()->count() . ' voucher sebesar ' . $this->items->value('price') . ' poin',
'description' => $this->customer->fullname.' melakukan penukaran '.$this->items()->count().' voucher sebesar '.$this->items->value('price').' poin',
]);
Notification::create([
'entity_id' => auth()->id(),
'description' => 'Transaksi ' . $this->code . ' berhasil',
'description' => 'Transaksi '.$this->code.' berhasil',
]);
return;
@ -95,17 +95,17 @@ class Sale extends Model
Notification::create([
'entity_type' => User::class,
'description' => $this->customer->fullname . ' melakukan pembelian ' . $this->items()->count() . ' voucher sebesar ' . $this->display_amount,
'description' => $this->customer->fullname.' melakukan pembelian '.$this->items()->count().' voucher sebesar '.$this->display_amount,
]);
Notification::create([
'entity_id' => auth()->id(),
'description' => 'Transaksi pembelian anda ' . $this->code . ' sebesar ' . $this->display_amount . ' berhasil',
'description' => 'Transaksi pembelian anda '.$this->code.' sebesar '.$this->display_amount.' berhasil',
]);
}
public function create_payment()
{
{
// payed with deposit
if ($this->payed_with == Sale::PAYED_WITH_DEPOSIT) {
$deposit = $this->customer->deposites()->create([
@ -139,7 +139,7 @@ class Sale extends Model
$poin = $this->customer->poins()->create([
'debit' => $bonus->bonus_poin,
'description' => GeneralService::generateBonusPoinCode(),
'narration' => 'Bonus Poin Reward'
'narration' => 'Bonus Poin Reward',
]);
$poin->update_customer_balance();
@ -159,7 +159,7 @@ class Sale extends Model
$poin = $customer->poins()->create([
'debit' => $bonus,
'description' => GeneralService::generateBonusPoinCode(),
'narration' => 'Bonus Poin Affilate (Downline)'
'narration' => 'Bonus Poin Affilate (Downline)',
]);
$poin->update_customer_balance();

@ -51,7 +51,7 @@ class User extends Authenticatable
];
protected $appends = [
'photo_url'
'photo_url',
];
public function role()
@ -82,7 +82,7 @@ class User extends Authenticatable
public function photoUrl(): Attribute
{
return Attribute::make(get: function() {
return Attribute::make(get: function () {
return asset($this->photo);
});
}

@ -4,7 +4,6 @@ namespace App\Models;
use App\Services\GeneralService;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Facades\Auth;
class Voucher extends Model
{
@ -31,7 +30,7 @@ class Voucher extends Model
'validate_bonus_poin',
'discount',
'status',
'created_at_formated'
'created_at_formated',
];
private static $instance = [];
@ -40,7 +39,7 @@ class Voucher extends Model
{
if (count(self::$instance) == 0) {
self::$instance = [
'customer' => Customer::find(auth()->guard('customer')->id())
'customer' => Customer::find(auth()->guard('customer')->id()),
];
}
@ -59,11 +58,14 @@ class Voucher extends Model
$price = $this->locationProfile->prices;
if (auth()->guard('customer')->check()) {
$customer = self::getInstance()['customer'];
return $price->where('customer_level_id', $customer->customer_level_id)
->value('price');
}
return $price->max('price');
}
return $this->locationProfile->price;
});
}
@ -75,11 +77,14 @@ class Voucher extends Model
$price = $this->locationProfile->prices;
if (auth()->guard('customer')->check()) {
$customer = self::getInstance()['customer'];
return $price->where('customer_level_id', $customer->customer_level_id)
->value('display_price');
}
return $price->max('display_price');
}
return $this->locationProfile->display_price;
});
}
@ -91,11 +96,14 @@ class Voucher extends Model
$price = $this->locationProfile->prices;
if (auth()->guard('customer')->check()) {
$customer = self::getInstance()['customer'];
return $price->where('customer_level_id', $customer->customer_level_id)
->value('bonus_poin');
}
return $price->max('bonus_poin');
}
return $this->locationProfile->bonus_poin;
});
}
@ -107,11 +115,14 @@ class Voucher extends Model
$price = $this->locationProfile->prices;
if (auth()->guard('customer')->check()) {
$customer = self::getInstance()['customer'];
return $price->where('customer_level_id', $customer->customer_level_id)
->value('price_poin');
}
return $price->max('price_poin');
}
return $this->locationProfile->price_poin;
});
}
@ -123,11 +134,14 @@ class Voucher extends Model
$price = $this->locationProfile->prices;
if (auth()->guard('customer')->check()) {
$customer = self::getInstance()['customer'];
return $price->where('customer_level_id', $customer->customer_level_id)
->value('discount');
}
return $price->min('discount');
}
return $this->locationProfile->discount;
});
}
@ -137,7 +151,7 @@ class Voucher extends Model
return Attribute::make(get: function () {
return [
'color' => $this->sold == self::SOLD ? 'bg-green-200 border-green-600' : 'bg-yellow-100 border-yellow-300',
'text' => $this->sold == self::SOLD ? 'Ya' : 'Tidak'
'text' => $this->sold == self::SOLD ? 'Ya' : 'Tidak',
];
});
}
@ -161,7 +175,7 @@ class Voucher extends Model
if ($count <= $treshold) {
Notification::create([
'entity_type' => User::class,
'description' => 'stok voucher ' . $this->locationProfile->name . 'tersisa : ' . $count,
'description' => 'stok voucher '.$this->locationProfile->name.'tersisa : '.$count,
]);
}
}
@ -169,18 +183,18 @@ class Voucher extends Model
public static function stats(Location $location)
{
$locationCallback = fn ($q) => $q->where('location_id', $location->id);
$count_voucher_total = Voucher::whereHas('locationProfile', $locationCallback)->count();
$count_voucher_total = Voucher::whereHas('locationProfile', $locationCallback)->count();
$sum_voucher_total = Voucher::whereHas('locationProfile', $locationCallback)
$sum_voucher_total = Voucher::whereHas('locationProfile', $locationCallback)
->join('location_profiles', 'location_profiles.id', '=', 'vouchers.location_profile_id')
->selectRaw('(sum(location_profiles.price)) as total')
->value('total');
$count_voucher_sold = Voucher::whereHas('locationProfile', $locationCallback)
$count_voucher_sold = Voucher::whereHas('locationProfile', $locationCallback)
->where('is_sold', Voucher::SOLD)->count();
$count_voucher_unsold = Voucher::whereHas('locationProfile', $locationCallback)
$count_voucher_unsold = Voucher::whereHas('locationProfile', $locationCallback)
->where('is_sold', Voucher::UNSOLD)->count();
$sum_voucher_unsold = Voucher::whereHas('locationProfile', $locationCallback)
$sum_voucher_unsold = Voucher::whereHas('locationProfile', $locationCallback)
->where('is_sold', Voucher::UNSOLD)
->join('location_profiles', 'location_profiles.id', '=', 'vouchers.location_profile_id')
->selectRaw('(sum(location_profiles.price)) as total')
@ -215,7 +229,7 @@ class Voucher extends Model
$poin = $customer->poins()->create([
'debit' => $bonus,
'description' => GeneralService::generateBonusPoinCode(),
'narration' => 'Bonus Poin Pembelian Voucher'
'narration' => 'Bonus Poin Pembelian Voucher',
]);
$poin->update_customer_balance();

@ -5,7 +5,6 @@ namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
class EventServiceProvider extends ServiceProvider
{

@ -10,10 +10,17 @@ class AsyncService
{
public static function async(Closure $closure, $isAsync = true)
{
info('async service', [$closure]);
info(self::class, [$closure]);
if ($isAsync) {
Loop::addTimer(0.1, async(function () use ($closure) {
$closure();
try {
$closure();
} catch (\Exception $e) {
info(self::class, [
'message' => $e->getMessage(),
'error' => $e,
]);
}
}));
} else {
$closure();

@ -9,7 +9,6 @@ use App\Models\PoinHistory;
use App\Models\Sale;
use App\Models\Setting;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
class GeneralService
{
@ -83,7 +82,7 @@ class GeneralService
'name' => Setting::PAYMENT_MANUAL,
'logo' => null,
'display_name' => 'Transfer Manual',
'admin_fee' => Setting::getByKey('ADMINFEE_MANUAL_TRANSFER')
'admin_fee' => Setting::getByKey('ADMINFEE_MANUAL_TRANSFER'),
];
}
}
@ -104,7 +103,7 @@ class GeneralService
'name' => Setting::PAYMENT_CASH_DEPOSIT,
'logo' => null,
'display_name' => Setting::getByKey('TEXT_CASH_DEPOSIT'),
'admin_fee' => Setting::getByKey('ADMINFEE_CASH_DEPOSIT')
'admin_fee' => Setting::getByKey('ADMINFEE_CASH_DEPOSIT'),
];
}
}
@ -116,18 +115,17 @@ class GeneralService
{
$payments = [];
$payments[] = [
'name' => Sale::PAYED_WITH_DEPOSIT,
'display_name' => 'Bayar dengan saldo Deposit',
'is_enable' => $customer->deposit_balance >= $total
'is_enable' => $customer->deposit_balance >= $total,
];
if ($customer->is_allow_paylater) {
$payments[] = [
'name' => Sale::PAYED_WITH_PAYLATER,
'display_name' => 'Bayar dengan saldo Hutang',
'is_enable' => $customer->paylater_remain >= $total
'is_enable' => $customer->paylater_remain >= $total,
];
}
@ -138,11 +136,11 @@ class GeneralService
{
$r = '';
$time = explode(':', $time);
foreach ($time as $t) { //00 : 00
foreach ($time as $t) { //00 : 00
if ($t < 10) {
$r .= '0' . (int) $t . ':';
$r .= '0'.(int) $t.':';
} else {
$r .= $t . ':';
$r .= $t.':';
}
}
@ -155,7 +153,7 @@ class GeneralService
->whereDate('created_at', now())
->count() + 1;
return 'Invoice #DSR' . now()->format('dmy') . GeneralService::formatNumberCode($code);
return 'Invoice #DSR'.now()->format('dmy').GeneralService::formatNumberCode($code);
}
public static function generateSaleVoucherCode()
@ -164,14 +162,14 @@ class GeneralService
->where('payed_with', '!=', Sale::PAYED_WITH_POIN)
->count() + 1;
return 'Invoice #VCR' . now()->format('dmy') . GeneralService::formatNumberCode($code);
return 'Invoice #VCR'.now()->format('dmy').GeneralService::formatNumberCode($code);
}
public static function generateBonusPoinCode()
{
$code = PoinHistory::whereDate('created_at', now())->count() + 1;
return 'Invoice #BPN' . now()->format('dmy') . GeneralService::formatNumberCode($code);
return 'Invoice #BPN'.now()->format('dmy').GeneralService::formatNumberCode($code);
}
public static function generateExchangePoinCode()
@ -180,20 +178,21 @@ class GeneralService
->where('payed_with', '=', Sale::PAYED_WITH_POIN)
->count() + 1;
return 'Invoice #PVC' . now()->format('dmy') . GeneralService::formatNumberCode($code);
return 'Invoice #PVC'.now()->format('dmy').GeneralService::formatNumberCode($code);
}
public static function formatNumberCode($number)
{
if ($number < 10) {
return '000' . $number;
return '000'.$number;
}
if ($number < 100) {
return '00' . $number;
return '00'.$number;
}
if ($number < 1000) {
return '0' . $number;
return '0'.$number;
}
return $number;
}
@ -206,6 +205,7 @@ class GeneralService
$isAllow = true;
}
}
return $isAllow;
}
}

@ -29,7 +29,7 @@ class MidtransService
'price' => $this->deposit->debit,
'quantity' => 1,
'name' => $this->deposit->description,
]
],
];
$adminFee = Setting::getByKey('MIDTRANS_ADMIN_FEE');

@ -20,7 +20,7 @@ return new class extends Migration
$table->ulid('customer_id')->nullable();
$table->smallInteger('type')->default(0);
$table->smallInteger('is_valid')->default(0);
$table->string('image_prove');
$table->string('image_prove')->nullable();
$table->timestamps();
$table->softDeletes();

@ -5,7 +5,6 @@ namespace Database\Seeders;
use App\Models\Account;
use App\Models\Banner;
use App\Models\CustomerLevel;
use App\Models\DepositHistory;
use App\Models\DepositLocation;
use App\Models\Info;
use App\Models\Location;
@ -50,8 +49,8 @@ class DummySeeder extends Seeder
$images = ['1.webp', '2.webp', '3.webp'];
foreach ($images as $index => $image) {
Banner::create([
'title' => 'Banner ' . $index,
'image' => 'sample/' . $image,
'title' => 'Banner '.$index,
'image' => 'sample/'.$image,
'description' => '<h1>Banner </h1>',
]);
}
@ -110,7 +109,7 @@ class DummySeeder extends Seeder
$lp = LocationProfile::create([
'location_id' => $location->id,
'name' => 'Profile ' . $quota,
'name' => 'Profile '.$quota,
'quota' => $quota,
'display_note' => rand(0, 1) == 1 ? 'bisa semua' : null,
'expired' => rand(1, 3),

@ -70,9 +70,8 @@ class InstallationSeed extends Seeder
<pre>&nbsp; 1. contoh 1</pre>
<pre>&nbsp; 2. contoh 2</pre>
<p>&nbsp;</p>',
'min_amount' =>
'100000',
'max_amount' => '500000'
'min_amount' => '100000',
'max_amount' => '500000',
],
[
'name' => 'Silver',
@ -86,7 +85,7 @@ class InstallationSeed extends Seeder
<pre>&nbsp; 2. contoh 2</pre>
<p>&nbsp;</p>',
'min_amount' => '100000',
'max_amount' => '1000000'
'max_amount' => '1000000',
],
[
'name' => 'Gold',
@ -100,7 +99,7 @@ class InstallationSeed extends Seeder
<pre>&nbsp; 2. contoh 2</pre>
<p>&nbsp;</p>',
'min_amount' => '100000',
'max_amount' => '2000000'
'max_amount' => '2000000',
],
[
'name' => 'Platinum',
@ -114,7 +113,7 @@ class InstallationSeed extends Seeder
<pre>&nbsp; 2. contoh 2</pre>
<p>&nbsp;</p>',
'min_amount' => '100000',
'max_amount' => '3000000'
'max_amount' => '3000000',
],
];

@ -65,7 +65,7 @@ services:
cpus: 1
networks:
voucher:
ipv4_address: 10.25.10.99
ipv4_address: 10.25.99.99
postgresql:
image: postgres:14-alpine3.17
container_name: voucher-postgres
@ -83,7 +83,7 @@ services:
cpus: 0.5
networks:
voucher:
ipv4_address: 10.25.10.199
ipv4_address: 10.25.99.199
portainer:
image: portainer/portainer-ce:latest
ports:
@ -122,5 +122,5 @@ networks:
ipam:
driver: default
config:
- subnet: 10.25.10.0/24
gateway: 10.25.10.1
- subnet: 10.25.99.0/24
gateway: 10.25.99.1

@ -23,7 +23,7 @@ const FormLocation = forwardRef(
<input
id={name}
type="text"
className="bg-gray-50 border text-gray-900 text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:placeholder-gray-400 dark:text-white active:ring-blue-500 focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-500 dark:focus:border-blue-500"
className="bg-gray-50 border text-gray-900 text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:placeholder-gray-400 dark:text-white active:ring-blue-500 focus:ring-blue-500 focus:border-blue-500 dark:focus:ring-blue-500 dark:focus:border-blue-500"
onChange={onChange}
name={name}
value={value}

@ -0,0 +1,176 @@
import React, { useEffect, useState } from 'react'
import { Link, router } from '@inertiajs/react'
import { usePrevious } from 'react-use'
import { Head } from '@inertiajs/react'
import { HiEye } from 'react-icons/hi2'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import Pagination from '@/Components/Pagination'
import FormModal from './FormModal'
import SearchInput from '@/Components/SearchInput'
import { hasPermission } from '@/utils'
import { useModalState } from '@/hooks'
export default function Index(props) {
const {
query: { links, data },
auth,
} = props
const [search, setSearch] = useState('')
const preValue = usePrevious(search)
const formModal = useModalState()
const toggleFormModal = (deposit = null) => {
formModal.setData(deposit)
formModal.toggle()
}
const params = { q: search }
useEffect(() => {
if (preValue) {
router.get(
route(route().current()),
{ q: search },
{
replace: true,
preserveState: true,
}
)
}
}, [search])
const canUpdate = hasPermission(auth, 'update-deposit')
return (
<AuthenticatedLayout page={'Deposit'} action={''}>
<Head title="Deposit" />
<div>
<div className="mx-auto sm:px-6 lg:px-8 ">
<div className="p-6 overflow-hidden shadow-sm sm:rounded-lg bg-gray-200 dark:bg-gray-800 space-y-4">
<div className="flex justify-between">
<div className="flex items-center">
<SearchInput
onChange={(e) => setSearch(e.target.value)}
value={search}
/>
</div>
</div>
<div className="overflow-auto">
<div>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 mb-4">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th
scope="col"
className="py-3 px-6"
>
#
</th>
<th
scope="col"
className="py-3 px-6"
>
Customer
</th>
<th
scope="col"
className="py-3 px-6"
>
Deposit
</th>
<th
scope="col"
className="py-3 px-6"
>
Tanggal
</th>
<th
scope="col"
className="py-3 px-6"
>
Status
</th>
<th
scope="col"
className="py-3 px-6"
>
Approver
</th>
<th
scope="col"
className="py-3 px-6"
/>
</tr>
</thead>
<tbody>
{data.map((deposit) => (
<tr
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
key={deposit.id}
>
<td
scope="row"
className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
{deposit.description}
</td>
<td className="py-4 px-6">
<Link
className="hover:underline"
href={route(
'customer.edit',
deposit.customer.id
)}
>
{deposit.customer.name}
</Link>
</td>
<td className="py-4 px-6">
{deposit.amount}
</td>
<td className="py-4 px-6">
{deposit.format_created_at}
</td>
<td
className={`py-4 px-6 ${deposit.status.text_color}`}
>
{deposit.status.text}
</td>
<td className="py-4 px-6">
{deposit.editor?.name}
</td>
<td className="py-4 px-6 flex justify-center">
{canUpdate && (
<div
className="flex space-x-1 items-center hover:underline"
onClick={() =>
toggleFormModal(
deposit
)
}
>
<HiEye />
<div>Lihat</div>
</div>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="w-full flex items-center justify-center">
<Pagination links={links} params={params} />
</div>
</div>
</div>
</div>
</div>
<FormModal modalState={formModal} />
</AuthenticatedLayout>
)
}

@ -1,9 +1,11 @@
import React, { useEffect, useState, useRef } from 'react'
import { Head, useForm } from '@inertiajs/react'
import { Head, Link, useForm } from '@inertiajs/react'
import { isEmpty } from 'lodash'
import { HiXCircle } from 'react-icons/hi2'
import { Spinner } from 'flowbite-react'
import { Button as FButton } from 'flowbite-react'
import { useModalState } from '@/hooks'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import FormInput from '@/Components/FormInput'
import FormInputWith from '@/Components/FormInputWith'
@ -11,7 +13,6 @@ import TextArea from '@/Components/TextArea'
import FormFile from '@/Components/FormFile'
import Button from '@/Components/Button'
import FormInputNumeric from '@/Components/FormInputNumeric'
import { useModalState } from '@/hooks'
import LocationModal from './LocationModal'
export default function Form(props) {
@ -19,6 +20,8 @@ export default function Form(props) {
const locationModal = useModalState()
const [isDisable, setDisable] = useState(false)
const [uploadIndex, setUploadIndex] = useState(null)
const [loading, setLoading] = useState(false)
const generalUploadRef = useRef()
@ -178,6 +181,7 @@ export default function Form(props) {
items: items,
locations: customer.location_favorites,
})
setDisable(true)
}
}, [customer])
@ -191,7 +195,72 @@ export default function Form(props) {
<div>
<div className="mx-auto sm:px-6 lg:px-8">
<div className="overflow-hidden p-4 shadow-sm sm:rounded-lg bg-white dark:bg-gray-800 flex flex-col ">
<div className="text-xl font-bold mb-4">Mitra WBB</div>
<div className="flex flex-col lg:flex-row justify-between">
<div className="text-xl font-bold mb-4">
Mitra WBB
</div>
{isEmpty(customer) === false && (
<div className="flex flex-col lg:flex-row gap-2 justify-end">
<Link href="#">
<FButton
size="xs"
color="primary"
outline
>
Riwayat Deposit
</FButton>
</Link>
<Link href="#">
<FButton
size="xs"
color="primary"
outline
>
Riwayat Pembelian
</FButton>
</Link>
<Link href="#">
<FButton
size="xs"
color="primary"
outline
>
Riwayat Pembayaran Hutang
</FButton>
</Link>
<Link href="#">
<FButton
size="xs"
color="primary"
outline
>
Riwayat Topup Limit
</FButton>
</Link>
<Link href="#">
<FButton
size="xs"
color="primary"
outline
>
Riwayat Penambahan Tenor
</FButton>
</Link>
</div>
)}
</div>
{isEmpty(customer) === false && (
<div className="flex flex-row justify-end">
<FButton
size="xs"
color="primary"
onClick={() => setDisable(!isDisable)}
>
{isDisable ? 'Edit' : 'Batal'}
</FButton>
</div>
)}
<FormInput
name="fullname"
value={data.fullname}
@ -512,6 +581,9 @@ export default function Form(props) {
e
)
}
readOnly={
isDisable
}
/>
) : (
<div className="w-full flex flex-row gap-1 items-center">
@ -562,9 +634,11 @@ export default function Form(props) {
</tbody>
</table>
<div className="w-full flex flex-row justify-end">
<Button onClick={() => addItem()}>
Tambah
</Button>
{!isDisable && (
<Button onClick={() => addItem()}>
Tambah
</Button>
)}
</div>
</>
)}
@ -575,11 +649,15 @@ export default function Form(props) {
Lokasi Favorit
</div>
<div>
<Button
onClick={() => locationModal.toggle()}
>
Tambah
</Button>
{!isDisable && (
<Button
onClick={() =>
locationModal.toggle()
}
>
Tambah
</Button>
)}
</div>
</div>
<div className="w-full px-2 mb-2">
@ -622,12 +700,14 @@ export default function Form(props) {
</div>
</div>
<div className="mt-8">
<Button
onClick={handleSubmit}
processing={processing}
>
Simpan
</Button>
{!isDisable && (
<Button
onClick={handleSubmit}
processing={processing}
>
Simpan
</Button>
)}
</div>
</div>
</div>

@ -362,83 +362,6 @@ export default function Customer(props) {
dismissOnClick={true}
size={'sm'}
>
<Dropdown.Item>
<Link
href={route(
'mitra.edit',
customer
)}
className="flex space-x-1 items-center"
>
<HiEye />
<div>
Transaksi
Pembelian
</div>
</Link>
</Dropdown.Item>
<Dropdown.Item>
<Link
href={route(
'mitra.edit',
customer
)}
className="flex space-x-1 items-center"
>
<HiEye />
<div>
Riwayat
Hutang
</div>
</Link>
</Dropdown.Item>
<Dropdown.Item>
<Link
href={route(
'mitra.edit',
customer
)}
className="flex space-x-1 items-center"
>
<HiEye />
<div>
Riwayat
Topup limit
</div>
</Link>
</Dropdown.Item>
<Dropdown.Item>
<Link
href={route(
'mitra.edit',
customer
)}
className="flex space-x-1 items-center"
>
<HiEye />
<div>
Riwayat
penambahan
tenor
</div>
</Link>
</Dropdown.Item>
<Dropdown.Item>
<Link
href={route(
'mitra.edit',
customer
)}
className="flex space-x-1 items-center"
>
<HiEye />
<div>
Riwayat
Topup
Deposit
</div>
</Link>
</Dropdown.Item>
{canUpdate && (
<Dropdown.Item>
<Link
@ -448,9 +371,9 @@ export default function Customer(props) {
)}
className="flex space-x-1 items-center"
>
<HiPencil />
<HiEye />
<div>
Ubah
Lihat
</div>
</Link>
</Dropdown.Item>

@ -0,0 +1,212 @@
import React, { useEffect } from 'react'
import { Head, Link, useForm } from '@inertiajs/react'
import { isEmpty } from 'lodash'
import { STATUS_APPROVE, STATUS_REJECT } from '@/constant'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import FormInput from '@/Components/FormInput'
import Button from '@/Components/Button'
import FormInputNumeric from '@/Components/FormInputNumeric'
export default function Form(props) {
const { deposit } = props
const { data, setData, post, processing, errors } = useForm({
debit: 0,
is_valid: 0,
reject_reason: '',
})
const handleOnChange = (event) => {
setData(
event.target.name,
event.target.type === 'checkbox'
? event.target.checked
? 1
: 0
: event.target.value
)
}
const showForm =
+deposit.is_valid !== STATUS_APPROVE &&
+deposit.is_valid !== STATUS_REJECT
const handleSubmit = () => {
post(route('deposit.update', deposit))
}
useEffect(() => {
if (isEmpty(deposit) === false) {
setData({
debit: deposit.debit,
is_valid: deposit.is_valid,
reject_reason: deposit.reject_reason,
})
return
}
}, [deposit])
return (
<AuthenticatedLayout
page={'Deposit'}
action={'Form'}
parent={route('deposit.index')}
>
<Head title="Deposit" />
<div>
<div className="mx-auto sm:px-6 lg:px-8">
<div className="overflow-hidden p-4 shadow-sm sm:rounded-lg bg-white dark:bg-gray-800 flex flex-col ">
<div className="text-xl font-bold mb-4">Deposit</div>
<table className="relative w-full overflow-x-auto p-2 rounded">
<tbody>
<tr>
<td className="font-bold">Customer</td>
<td>:</td>
<td>
<Link
href={route('customer.edit', {
customer: deposit.customer,
})}
className="hover:underline"
>
{deposit.customer.name}
</Link>
</td>
</tr>
<tr>
<td className="font-bold">Deskripsi</td>
<td>:</td>
<td>{deposit.description}</td>
</tr>
<tr>
<td className="font-bold">
Metode Pembayaran
</td>
<td>:</td>
<td>{deposit.payment_channel}</td>
</tr>
{deposit.account !== null && (
<tr>
<td className="font-bold">Bank Akun</td>
<td>:</td>
<td>
{deposit.account.name} (
{deposit.account.bank_name})
</td>
</tr>
)}
{deposit.deposit_location !== null && (
<tr>
<td className="font-bold">
Lokasi Cash / Setor Tunai
</td>
<td>:</td>
<td>{deposit.deposit_location.name}</td>
</tr>
)}
<tr>
<td className="font-bold">Jumlah</td>
<td>:</td>
<td>{deposit.amount}</td>
</tr>
<tr>
<td className="font-bold">Status</td>
<td>:</td>
<td className={deposit.status.text_color}>
{deposit.status.text}
</td>
</tr>
<tr>
<td className="font-bold">
Alasan Penolakan
</td>
<td>:</td>
<td>{deposit.reject_reason}</td>
</tr>
{isEmpty(deposit.editor) === false && (
<tr>
<td className="font-bold">Approver</td>
<td>:</td>
<td>{deposit.editor.name}</td>
</tr>
)}
</tbody>
</table>
{isEmpty(deposit.image_prove_url) === false && (
<div>
<a
href={deposit.image_prove_url}
target="_blank"
>
<img
src={deposit.image_prove_url}
className="w-full object-fill h-96"
loading="lazy"
/>
</a>
</div>
)}
{showForm && (
<>
<div className="my-4">
<FormInputNumeric
type="number"
label="Jumlah Deposit"
name="debit"
onChange={handleOnChange}
value={data.debit}
error={errors.debit}
/>
<div className="mb-1 text-sm">Status</div>
<select
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
onChange={handleOnChange}
value={+data.is_valid}
name="is_valid"
>
<option value="">
{' '}
-- pilih status --{' '}
</option>
<option value={STATUS_APPROVE}>
Approve
</option>
<option value={STATUS_REJECT}>
Reject
</option>
</select>
{errors.status && (
<div className="text-sm text-red-500">
{errors.status}
</div>
)}
</div>
{+data.is_valid === STATUS_REJECT && (
<FormInput
label="Alasan penolakan"
name="reject_reason"
value={data.reject_reason}
onChange={handleOnChange}
error={errors.reject_reason}
/>
)}
<div className="mt-8">
<Button
onClick={handleSubmit}
processing={processing}
>
Simpan
</Button>
</div>
</>
)}
</div>
</div>
</div>
</AuthenticatedLayout>
)
}

@ -1,208 +0,0 @@
import React, { useEffect } from 'react'
import { Link, useForm } from '@inertiajs/react'
import { isEmpty } from 'lodash'
import { STATUS_APPROVE, STATUS_REJECT } from '@/constant'
import Modal from '@/Components/Modal'
import Button from '@/Components/Button'
import FormInput from '@/Components/FormInput'
export default function FormModal(props) {
const { modalState } = props
const { data, setData, post, processing, errors, reset, clearErrors } =
useForm({
amount: '',
image_prove_url: '',
status: '',
account: {
name: '',
bank_name: '',
holder_name: '',
account_number: '',
},
deposit_location: null,
payment_channel: '',
is_valid: 0,
status_text: '',
text_color: '',
customer: '',
description: '',
reject_reason: '',
})
const handleOnChange = (event) => {
setData(
event.target.name,
event.target.type === 'checkbox'
? event.target.checked
? 1
: 0
: event.target.value
)
}
const handleReset = () => {
modalState.setData(null)
reset()
clearErrors()
}
const handleClose = () => {
handleReset()
modalState.toggle()
}
const handleSubmit = () => {
const deposit = modalState.data
post(route('deposit.update', deposit), {
onSuccess: () => handleClose(),
})
}
useEffect(() => {
const deposit = modalState.data
if (isEmpty(deposit) === false) {
setData({
amount: deposit.amount,
account: deposit.account,
image_prove_url: deposit.image_prove_url,
payment_channel: deposit.payment_channel,
is_valid: deposit.is_valid,
status_text: deposit.status.text,
text_color: deposit.status.text_color,
customer: deposit.customer,
description: deposit.description,
reject_reason: deposit.note,
deposit_location: deposit.deposit_location,
})
return
}
}, [modalState])
return (
<Modal
isOpen={modalState.isOpen}
toggle={handleClose}
title={'Deposit'}
>
<table className="relative w-full overflow-x-auto p-2 rounded">
<tbody>
<tr>
<td className="font-bold">Customer</td>
<td>:</td>
<td>
<Link
href={route('customer.edit', {
customer: data.customer,
})}
className="hover:underline"
>
{data.customer.name}
</Link>
</td>
</tr>
<tr>
<td className="font-bold">Deskripsi</td>
<td>:</td>
<td>{data.description}</td>
</tr>
<tr>
<td className="font-bold">Metode Pembayaran</td>
<td>:</td>
<td>{data.payment_channel}</td>
</tr>
{data.account !== null && (
<tr>
<td className="font-bold">Bank Akun</td>
<td>:</td>
<td>
{data.account.name} ({data.account.bank_name})
</td>
</tr>
)}
{data.deposit_location !== null && (
<tr>
<td className="font-bold">
Lokasi Cash / Setor Tunai
</td>
<td>:</td>
<td>{data.deposit_location.name}</td>
</tr>
)}
<tr>
<td className="font-bold">Jumlah</td>
<td>:</td>
<td>{data.amount}</td>
</tr>
<tr>
<td className="font-bold">Status</td>
<td>:</td>
<td className={data.text_color}>{data.status_text}</td>
</tr>
{+data.is_valid === STATUS_REJECT && (
<tr>
<td className="font-bold">Alasan Penolakan</td>
<td>:</td>
<td>{data.reject_reason}</td>
</tr>
)}
</tbody>
</table>
{isEmpty(data.image_prove_url) === false && (
<div>
<a href={data.image_prove_url} target="_blank">
<img
src={data.image_prove_url}
className="w-full object-fill h-96"
loading="lazy"
/>
</a>
</div>
)}
{+data.is_valid !== STATUS_APPROVE &&
+data.is_valid !== STATUS_REJECT && (
<>
<div className="my-4">
<div className="mb-1 text-sm">Status </div>
<select
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
onChange={handleOnChange}
value={+data.status}
name="status"
>
<option value=""> -- pilih status -- </option>
<option value={STATUS_APPROVE}>Approve</option>
<option value={STATUS_REJECT}>Reject</option>
</select>
{errors.status && (
<div className="text-sm text-red-500">
{errors.status}
</div>
)}
</div>
{+data.status === STATUS_REJECT && (
<FormInput
label="Alasan penolakan"
name="reject_reason"
value={data.reject_reason}
onChange={handleOnChange}
error={errors.reject_reason}
/>
)}
<div className="flex items-center">
<Button
onClick={handleSubmit}
processing={processing}
>
Simpan
</Button>
<Button onClick={handleClose} type="secondary">
Batal
</Button>
</div>
</>
)}
</Modal>
)
}

@ -1,45 +1,70 @@
import React, { useEffect, useState } from 'react'
import { Link, router } from '@inertiajs/react'
import { Head, Link, router } from '@inertiajs/react'
import { usePrevious } from 'react-use'
import { Head } from '@inertiajs/react'
import { HiEye } from 'react-icons/hi2'
import { formatIDR, hasPermission } from '@/utils'
import { DEPOSIT_STATUSES } from '@/constant'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import Pagination from '@/Components/Pagination'
import FormModal from './FormModal'
import SearchInput from '@/Components/SearchInput'
import { hasPermission } from '@/utils'
import { useModalState } from '@/hooks'
import ThSort from '@/Components/ThSortComponent'
export default function Index(props) {
const {
query: { links, data },
deposits: { links, data },
customers: { links: customer_links, data: customer_data },
auth,
stats,
_search,
_sortBy,
_sortRule,
} = props
const [search, setSearch] = useState('')
const preValue = usePrevious(search)
const [search, setSearch] = useState({
q: _search,
sortBy: _sortBy,
sortRule: _sortRule,
})
const [status, setStatus] = useState('')
const preValue = usePrevious(`${search}${status}`)
const formModal = useModalState()
const handleChangeSearch = (e) => {
setSearch({
...search,
q: e.target.value,
})
}
const toggleFormModal = (deposit = null) => {
formModal.setData(deposit)
formModal.toggle()
const sort = (key, sort = null) => {
if (sort !== null) {
setSearch({
...search,
sortBy: key,
sortRule: sort,
})
return
}
setSearch({
...search,
sortBy: key,
sortRule: search.sortRule == 'asc' ? 'desc' : 'asc',
})
}
const params = { q: search }
const params = { ...search, status: status }
useEffect(() => {
if (preValue) {
router.get(
route(route().current()),
{ q: search },
{ ...search, status: status },
{
replace: true,
preserveState: true,
}
)
}
}, [search])
}, [search, status])
const canUpdate = hasPermission(auth, 'update-deposit')
@ -49,16 +74,42 @@ export default function Index(props) {
<div>
<div className="mx-auto sm:px-6 lg:px-8 ">
<div className="p-6 overflow-hidden shadow-sm sm:rounded-lg bg-gray-200 dark:bg-gray-800 space-y-4">
<div className="flex justify-between">
<div className="flex items-center">
<SearchInput
onChange={(e) => setSearch(e.target.value)}
value={search}
/>
<div className="w-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2 mb-2">
<div className="border rounded-md shadow bg-white px-4 py-2 flex flex-col">
<div className="text-gray-600 text-lg">
Todal Deposit Bulan Ini{' '}
</div>
<div className="font-bold text-xl pt-2">
Rp. {formatIDR(stats.deposit_this_month)}
</div>
</div>
<div className="overflow-auto">
<div className="border rounded-md shadow bg-white px-4 py-2 flex flex-col">
<div className="text-gray-600 text-lg">
Total Deposit Hari ini{' '}
</div>
<div className="font-bold text-xl pt-2">
Rp. {formatIDR(stats.deposit_today)}
</div>
</div>
<div className="border rounded-md shadow bg-white px-4 py-2 flex flex-col">
<div className="text-gray-600 text-lg">
Todal Hutang Bulan Ini{' '}
</div>
<div className="font-bold text-xl pt-2">
Rp. {formatIDR(stats.paylater_this_month)}
</div>
</div>
<div className="border rounded-md shadow bg-white px-4 py-2 flex flex-col">
<div className="text-gray-600 text-lg">
Total Hutang Hari ini{' '}
</div>
<div className="font-bold text-xl pt-2">
Rp. {formatIDR(stats.paylater_today)}
</div>
</div>
</div>
<div className="p-6 overflow-hidden shadow-sm sm:rounded-lg bg-gray-200 dark:bg-gray-800 space-y-4 mb-2">
<div className="w-full overflow-auto">
<div>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 mb-4">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
@ -67,39 +118,137 @@ export default function Index(props) {
scope="col"
className="py-3 px-6"
>
#
Nama
</th>
<th
scope="col"
className="py-3 px-6"
>
Customer
Deposit
</th>
<th
scope="col"
className="py-3 px-6"
>
Deposit
Saldo Hutang
</th>
<th
scope="col"
className="py-3 px-6"
</tr>
</thead>
<tbody>
{customer_data.map((customer) => (
<tr
className="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
key={customer.id}
>
<td
scope="row"
className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white hover:underline"
>
<Link
href={route(
'customer.edit',
customer
)}
>
{customer.name}
</Link>
</td>
<td
scope="row"
className="py-4 px-6"
>
{customer.display_deposit}
</td>
<td
scope="row"
className="py-4 px-6"
>
{formatIDR(
customer.paylater_usage
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="w-full flex items-center justify-center">
<Pagination links={customer_links} />
</div>
</div>
</div>
<div className="p-6 overflow-hidden shadow-sm sm:rounded-lg bg-gray-200 dark:bg-gray-800 space-y-4">
<div className="flex flex-col lg:flex-row gap-1 justify-between">
<div className="flex flex-row space-x-2">
<SearchInput
onChange={handleChangeSearch}
value={search.q}
/>
</div>
<div className="flex flex-row space-x-2">
<select
className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
onChange={(e) => setStatus(e.target.value)}
>
<option value="">Semua Status</option>
{DEPOSIT_STATUSES.map((status) => (
<option
value={status.value}
key={status.value}
>
{status.key}
</option>
))}
</select>
</div>
</div>
<div className="overflow-auto">
<div>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 mb-4">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<ThSort
sort={sort}
label={'description'}
search={search}
>
#
</ThSort>
<ThSort
sort={sort}
label={'customer_id'}
search={search}
>
Customer
</ThSort>
<ThSort
sort={sort}
label={'debit'}
search={search}
>
Deposit
</ThSort>
<ThSort
sort={sort}
label={'updated_at'}
search={search}
>
Tanggal
</th>
<th
scope="col"
className="py-3 px-6"
</ThSort>
<ThSort
sort={sort}
label={'is_valid'}
search={search}
>
Status
</th>
<th
scope="col"
className="py-3 px-6"
</ThSort>
<ThSort
sort={sort}
label={'updated_by'}
search={search}
>
Approver
</th>
</ThSort>
<th
scope="col"
className="py-3 px-6"
@ -145,17 +294,16 @@ export default function Index(props) {
</td>
<td className="py-4 px-6 flex justify-center">
{canUpdate && (
<div
<Link
href={route(
'deposit.edit',
deposit
)}
className="flex space-x-1 items-center hover:underline"
onClick={() =>
toggleFormModal(
deposit
)
}
>
<HiEye />
<div>Lihat</div>
</div>
</Link>
)}
</td>
</tr>
@ -170,7 +318,6 @@ export default function Index(props) {
</div>
</div>
</div>
<FormModal modalState={formModal} />
</AuthenticatedLayout>
)
}

@ -2,6 +2,14 @@ export const DEFAULT_EXPIRED_UNIT = 'Hari'
export const STATUS_APPROVE = 0
export const STATUS_WAIT_UPLOAD = 1
export const STATUS_WAIT_APPROVE = 2
export const STATUS_WAIT_PAYMENT = 3
export const STATUS_INVALID = 4
export const STATUS_REJECT = 5
export const STATUS_EXPIRED = 6
@ -23,3 +31,13 @@ export const PAYED_WITH_PAYLATER = 'paylater'
export const PAYED_WITH_POIN = 'poin'
export const STATUS_SUSPEND = 2
export const DEPOSIT_STATUSES = [
{ key: 'Success', value: STATUS_APPROVE },
{ key: 'Upload bukti bayar', value: STATUS_WAIT_UPLOAD },
{ key: 'Menunggu Approve', value: STATUS_WAIT_APPROVE },
{ key: 'Menunggu Pembayaran', value: STATUS_WAIT_PAYMENT },
{ key: 'Error', value: STATUS_INVALID },
{ key: 'Reject', value: STATUS_REJECT },
{ key: 'Expired', value: STATUS_EXPIRED },
]

@ -1,9 +1,9 @@
<?php
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Admin\AccountController;
use App\Http\Controllers\Admin\BannerController;
use App\Http\Controllers\Admin\CustomerController;
use App\Http\Controllers\Admin\CustomerHistoryController;
use App\Http\Controllers\Admin\CustomerLevelController;
use App\Http\Controllers\Admin\CustomerMitraController;
use App\Http\Controllers\Admin\DepositController;
@ -21,6 +21,7 @@ use App\Http\Controllers\Admin\SettingController;
use App\Http\Controllers\Admin\UserController;
use App\Http\Controllers\Admin\VerificationController;
use App\Http\Controllers\Admin\VoucherController;
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use Illuminate\Support\Facades\Route;
Route::middleware(['http_secure_aware', 'inertia.admin'])
@ -128,6 +129,9 @@ Route::middleware(['http_secure_aware', 'inertia.admin'])
Route::post('/mitra/{customer}', [CustomerMitraController::class, 'update'])->name('mitra.update');
Route::delete('/mitra/{customer}', [CustomerMitraController::class, 'destroy'])->name('mitra.destroy');
// mitra history
Route::get('/mitra/{customer}/deposit', [CustomerHistoryController::class, 'deposit'])->name('mitra.history.deposit');
// voucher
Route::get('/vouchers/import', [VoucherController::class, 'form_import'])->name('voucher.form_import');
Route::post('/vouchers/import', [VoucherController::class, 'import'])->name('voucher.import');
@ -151,6 +155,7 @@ Route::middleware(['http_secure_aware', 'inertia.admin'])
// deposit
Route::get('/deposites', [DepositController::class, 'index'])->name('deposit.index');
Route::get('/deposites/{deposit}', [DepositController::class, 'edit'])->name('deposit.edit');
Route::post('/deposites/{deposit}', [DepositController::class, 'update'])->name('deposit.update');
// poin rewared
@ -166,7 +171,7 @@ Route::middleware(['http_secure_aware', 'inertia.admin'])
// notification
Route::get('/notifications', [NotificationController::class, 'index'])->name('notifications.index');
// deposit location
// deposit location
Route::get('/deposit-location', [DepositLocationController::class, 'index'])->name('deposit-location.index');
Route::get('/deposit-location/create', [DepositLocationController::class, 'create'])->name('deposit-location.create');
Route::post('/deposit-location', [DepositLocationController::class, 'store'])->name('deposit-location.store');

@ -1,8 +1,8 @@
<?php
use App\Http\Controllers\Customer\CustomerLevelController;
use App\Http\Controllers\Customer\AuthController;
use App\Http\Controllers\Customer\CartController;
use App\Http\Controllers\Customer\CustomerLevelController;
use App\Http\Controllers\Customer\DepositController;
use App\Http\Controllers\Customer\DepositLocationController;
use App\Http\Controllers\Customer\HomeController;
@ -96,7 +96,7 @@ Route::middleware(['http_secure_aware', 'guard_should_customer', 'inertia.custom
Route::get('/register', [AuthController::class, 'register'])->name('customer.register');
Route::post('/register', [AuthController::class, 'store']);
//
//
Route::get('/customer/{customer:id}/active', [AuthController::class, 'active'])->name('customer.active');
});
});

@ -1,6 +1,6 @@
#!/bin/bash
npm run build
rsync -arP -e 'ssh -p 225' --exclude=node_modules --exclude=database/database.sqlite --exclude=.git --exclude=.env --exclude=public/hot . arm@ajikamaludin.id:/home/arm/projects/www/voucher
rsync -arP -e 'ssh -p 225' --exclude=node_modules --exclude=public/uploads --exclude=database/database.sqlite --exclude=.git --exclude=.env --exclude=public/hot . arm@ajikamaludin.id:/home/arm/projects/www/voucher
ssh -p 225 arm@ajikamaludin.id -C docker exec php82 php /var/www/voucher/artisan migrate:refresh --seed

@ -2,11 +2,11 @@ import defaultTheme from 'tailwindcss/defaultTheme'
import forms from '@tailwindcss/forms'
/** @type {import('tailwindcss').Config} */
export default {
module.exports = {
darkMode: 'class',
content: [
'./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
'./resources/views/**/*.blade.php',
'vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
'/resources/views/**/*.blade.php',
'./app/Models/*.php',
'./resources/js/**/*.jsx',
'node_modules/flowbite-react/**/*.{js,jsx,ts,tsx}',
@ -45,6 +45,6 @@ export default {
},
},
},
plugins: [forms, require('flowbite/plugin')],
},
plugins: [forms, require('flowbite/plugin')],
}

Loading…
Cancel
Save