bitfix cart payment

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

@ -15,6 +15,7 @@ class CustomerHistoryController extends Controller
{ {
$query = DepositHistory::with(['editor']) $query = DepositHistory::with(['editor'])
->where('customer_id', $customer->id) ->where('customer_id', $customer->id)
->where('type', DepositHistory::TYPE_DEPOSIT)
->orderBy('created_at', 'desc'); ->orderBy('created_at', 'desc');
return inertia('CustomerHistory/DepositHistory', [ return inertia('CustomerHistory/DepositHistory', [

@ -15,7 +15,8 @@ class DepositController extends Controller
public function index(Request $request) public function index(Request $request)
{ {
$deposits = DepositHistory::with(['customer', 'account', 'depositLocation', 'editor']) $deposits = DepositHistory::with(['customer', 'account', 'depositLocation', 'editor'])
->where('credit', 0); ->where('credit', 0)
->where('type', DepositHistory::TYPE_DEPOSIT);
if ($request->q != '') { if ($request->q != '') {
$deposits->where(function ($query) use ($request) { $deposits->where(function ($query) use ($request) {
@ -51,16 +52,20 @@ class DepositController extends Controller
'deposit_this_month' => DepositHistory::whereMonth('created_at', now()->month) 'deposit_this_month' => DepositHistory::whereMonth('created_at', now()->month)
->whereYear('created_at', now()->year) ->whereYear('created_at', now()->year)
->where('is_valid', DepositHistory::STATUS_VALID) ->where('is_valid', DepositHistory::STATUS_VALID)
->where('type', DepositHistory::TYPE_DEPOSIT)
->sum('debit'), ->sum('debit'),
'deposit_today' => DepositHistory::whereDate('created_at', now()) 'deposit_today' => DepositHistory::whereDate('created_at', now())
->where('is_valid', DepositHistory::STATUS_VALID) ->where('is_valid', DepositHistory::STATUS_VALID)
->where('type', DepositHistory::TYPE_DEPOSIT)
->sum('debit'), ->sum('debit'),
'paylater_this_month' => PaylaterHistory::whereMonth('created_at', now()->month) 'paylater_this_month' => PaylaterHistory::whereMonth('created_at', now()->month)
->where('is_valid', PaylaterHistory::STATUS_VALID) ->where('is_valid', PaylaterHistory::STATUS_VALID)
->where('type', DepositHistory::TYPE_DEPOSIT)
->whereYear('created_at', now()->year) ->whereYear('created_at', now()->year)
->sum('debit'), ->sum('debit'),
'paylater_today' => PaylaterHistory::whereDate('created_at', now()) 'paylater_today' => PaylaterHistory::whereDate('created_at', now())
->where('is_valid', PaylaterHistory::STATUS_VALID) ->where('is_valid', PaylaterHistory::STATUS_VALID)
->where('type', DepositHistory::TYPE_DEPOSIT)
->sum('debit'), ->sum('debit'),
]; ];

@ -42,7 +42,7 @@ class DepositController extends Controller
public function create(Request $request) public function create(Request $request)
{ {
$customer = $request->user('customer'); $customer = $request->user('customer');
if (! $customer->allow_transaction) { if (!$customer->allow_transaction) {
return redirect()->back() 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']);
} }

@ -3,8 +3,15 @@
namespace App\Http\Controllers\Customer; namespace App\Http\Controllers\Customer;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Customer;
use App\Models\DepositHistory;
use App\Models\PaylaterHistory; use App\Models\PaylaterHistory;
use App\Models\Setting;
use App\Services\GeneralService;
use App\Services\MidtransService;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
class PaylaterController extends Controller class PaylaterController extends Controller
{ {
@ -25,11 +32,67 @@ class PaylaterController extends Controller
]); ]);
} }
public function create() public function create(Request $request)
{ {
return inertia('Paylater/Repay', [
'amount' => $request->user('customer')->paylater->usage,
'payments' => GeneralService::getEnablePayment(),
]);
} }
public function store() public function store(Request $request)
{ {
$request->validate([
'amount' => 'required|numeric|min:1000',
'payment' => [
'required',
Rule::in([Setting::PAYMENT_MANUAL, Setting::PAYMENT_MIDTRANS, Setting::PAYMENT_CASH_DEPOSIT]),
],
]);
$customer = $request->user('customer');
DB::beginTransaction();
$code = GeneralService::generateDepositRepayCode();
$paylater = $customer->paylaterHistories()->create([
'credit' => $request->amount,
'description' => $code,
'type' => PaylaterHistory::TYPE_REPAYMENT,
]);
$deposit = DepositHistory::make([
'description' => $code,
'related_type' => PaylaterHistory::class,
'related_id' => $paylater->id,
'customer_id' => $customer->id,
'debit' => $request->amount,
'payment_channel' => $request->payment,
'type' => DepositHistory::TYPE_REPAYMENT,
]);
if (in_array($request->payment, [Setting::PAYMENT_MANUAL, Setting::PAYMENT_CASH_DEPOSIT])) {
$deposit->is_valid = DepositHistory::STATUS_WAIT_UPLOAD;
$paylater->is_valid = PaylaterHistory::STATUS_WAIT_UPLOAD;
$deposit->save();
}
if ($request->payment == Setting::PAYMENT_MIDTRANS) {
$deposit->is_valid = DepositHistory::STATUS_WAIT_PAYMENT;
$paylater->is_valid = PaylaterHistory::STATUS_WAIT_PAYMENT;
$deposit->save();
$token = (new MidtransService($deposit, Setting::getByKey('MIDTRANS_SERVER_KEY')))->getSnapToken();
$deposit->update(['payment_token' => $token]);
}
$paylater->save();
DB::commit();
return redirect()->route('transactions.deposit.show', ['deposit' => $deposit->id, 'direct' => 'true']);
} }
} }

@ -22,8 +22,6 @@ class DepositHistory extends Model
const STATUS_EXPIRED = 6; const STATUS_EXPIRED = 6;
const STATUS_HIDDEN = 7;
const TYPE_DEPOSIT = 0; const TYPE_DEPOSIT = 0;
const TYPE_REPAYMENT = 1; const TYPE_REPAYMENT = 1;

@ -79,10 +79,10 @@ class PaylaterHistory extends Model
{ {
return Attribute::make(get: function () { return Attribute::make(get: function () {
if ($this->credit == 0) { 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, ',', '.');
}); });
} }
} }

@ -139,9 +139,9 @@ class GeneralService
$time = explode(':', $time); $time = explode(':', $time);
foreach ($time as $t) { //00 : 00 foreach ($time as $t) { //00 : 00
if ($t < 10) { if ($t < 10) {
$r .= '0'.(int) $t.':'; $r .= '0' . (int) $t . ':';
} else { } else {
$r .= $t.':'; $r .= $t . ':';
} }
} }
@ -154,7 +154,7 @@ class GeneralService
->whereDate('created_at', now()) ->whereDate('created_at', now())
->count() + 1; ->count() + 1;
return 'Invoice #TLH'.now()->format('dmy').GeneralService::formatNumberCode($code); return 'Invoice #TLH' . now()->format('dmy') . GeneralService::formatNumberCode($code);
} }
public static function generateDepositCode() public static function generateDepositCode()
@ -163,7 +163,16 @@ class GeneralService
->whereDate('created_at', now()) ->whereDate('created_at', now())
->count() + 1; ->count() + 1;
return 'Invoice #DSR'.now()->format('dmy').GeneralService::formatNumberCode($code); return 'Invoice #DSR' . now()->format('dmy') . GeneralService::formatNumberCode($code);
}
public static function generateDepositRepayCode()
{
$code = DepositHistory::where('type', DepositHistory::TYPE_REPAYMENT)
->whereDate('created_at', now())
->count() + 1;
return 'Invoice #PLH' . now()->format('dmy') . GeneralService::formatNumberCode($code);
} }
public static function generateSaleVoucherCode() public static function generateSaleVoucherCode()
@ -172,14 +181,14 @@ class GeneralService
->where('payed_with', '!=', Sale::PAYED_WITH_POIN) ->where('payed_with', '!=', Sale::PAYED_WITH_POIN)
->count() + 1; ->count() + 1;
return 'Invoice #VCR'.now()->format('dmy').GeneralService::formatNumberCode($code); return 'Invoice #VCR' . now()->format('dmy') . GeneralService::formatNumberCode($code);
} }
public static function generateBonusPoinCode() public static function generateBonusPoinCode()
{ {
$code = PoinHistory::whereDate('created_at', now())->count() + 1; $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() public static function generateExchangePoinCode()
@ -188,19 +197,19 @@ class GeneralService
->where('payed_with', '=', Sale::PAYED_WITH_POIN) ->where('payed_with', '=', Sale::PAYED_WITH_POIN)
->count() + 1; ->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) public static function formatNumberCode($number)
{ {
if ($number < 10) { if ($number < 10) {
return '000'.$number; return '000' . $number;
} }
if ($number < 100) { if ($number < 100) {
return '00'.$number; return '00' . $number;
} }
if ($number < 1000) { if ($number < 1000) {
return '0'.$number; return '0' . $number;
} }
return $number; return $number;

@ -2,6 +2,7 @@ import React, { useState } from 'react'
import { usePage, router } from '@inertiajs/react' import { usePage, router } from '@inertiajs/react'
import BottomSheet from '@/Customer/Components/BottomSheet' import BottomSheet from '@/Customer/Components/BottomSheet'
import { toastError } from '@/Customer/utils'
const Payment = ({ state }) => { const Payment = ({ state }) => {
const { const {
@ -18,13 +19,16 @@ const Payment = ({ state }) => {
} }
const pay = (payment) => { const pay = (payment) => {
if (!payment.is_enable) {
return
}
if (processing) { if (processing) {
return return
} }
router.post( router.post(
route('cart.purchase'), route('cart.purchase'),
{ {
payed_with: payment, payed_with: payment.name,
}, },
{ {
onBefore: () => setProcessing(true), onBefore: () => setProcessing(true),
@ -41,7 +45,7 @@ const Payment = ({ state }) => {
<div <div
key={payment.name} key={payment.name}
className={isEnable(payment.is_enable)} className={isEnable(payment.is_enable)}
onClick={() => pay(payment.name)} onClick={() => pay(payment)}
> >
{payment.display_name} {payment.display_name}
</div> </div>

@ -76,16 +76,13 @@ export default function Index(props) {
<Head title="Top Up" /> <Head title="Top Up" />
<div className="flex flex-col w-full min-h-[calc(90dvh)]"> <div className="flex flex-col w-full min-h-[calc(90dvh)]">
<HeaderTrx dates={dates} setDates={setDates} /> <HeaderTrx dates={dates} setDates={setDates} />
{deposites.length <= 0 && <EmptyHere />}
<div className="w-full"> <div className="w-full">
<div className="flex flex-col space-y-5 px-5"> <div className="flex flex-col space-y-5 px-5">
{deposites.length > 0 && ( <div className="text-sm text-gray-400">
<div className="text-sm text-gray-400"> {formatIDDate(dates.startDate)} s/d{' '}
{formatIDDate(dates.startDate)} s/d{' '} {formatIDDate(dates.endDate)}
{formatIDDate(dates.endDate)} </div>
</div>
)}
{deposites.map((history) => ( {deposites.map((history) => (
<div <div
key={history.id} key={history.id}
@ -131,6 +128,8 @@ export default function Index(props) {
)} )}
</div> </div>
</div> </div>
{deposites.length <= 0 && <EmptyHere />}
</div> </div>
</CustomerLayout> </CustomerLayout>
) )

@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import { Head, router } from '@inertiajs/react' import { Head, router } from '@inertiajs/react'
import { HiChevronLeft } from 'react-icons/hi2' import { HiChevronLeft } from 'react-icons/hi2'
import { isEmpty } from 'lodash'
import CustomerLayout from '@/Layouts/CustomerLayout' import CustomerLayout from '@/Layouts/CustomerLayout'
@ -34,9 +35,11 @@ export default function Detail({ paylater }) {
</div> </div>
<div className="w-full px-5"> <div className="w-full px-5">
<div className="my-5"> <div className="my-5">
<div className="bg-blue-50 text-blue-700 p-3 border rounded-md"> {isEmpty(paylater.note) === false && (
{paylater.note} <div className="bg-blue-50 text-blue-700 p-3 border rounded-md">
</div> {paylater.note}
</div>
)}
</div> </div>
</div> </div>
</div> </div>

@ -50,9 +50,7 @@ export default function Index({
<div <div
className="px-3 py-2 border rounded-full bg-blue-700 text-white hover:bg-transparent hover:text-black" className="px-3 py-2 border rounded-full bg-blue-700 text-white hover:bg-transparent hover:text-black"
onClick={() => onClick={() =>
router.get( router.get(route('customer.paylater.repay'))
route('transactions.deposit.topup')
)
} }
> >
Bayar Tagihan Bayar Tagihan

@ -0,0 +1,174 @@
import React from 'react'
import { Head, Link, router, useForm } from '@inertiajs/react'
import { HiCheck, HiChevronLeft, HiQuestionMarkCircle } from 'react-icons/hi2'
import { formatIDR } from '@/utils'
import { CASH_DEPOSIT } from '@/Customer/utils'
import CustomerLayout from '@/Layouts/CustomerLayout'
import Alert from '@/Components/Alert'
import FormInputNumeric from '@/Components/FormInputNumeric'
export default function Repay({ payments, amount }) {
const { data, setData, post, processing, errors } = useForm({
amount: amount,
payment: '',
})
const amounts = [amount]
const isActiveAmount = (amount) => {
return `px-3 py-3 border shadow-sm rounded ${
+amount === +data.amount ? 'bg-blue-700 text-white' : ''
}`
}
const setAmount = (amount) => {
setData('amount', amount)
}
const isActivePayment = (payment) => {
return `p-2 border shadow rounded flex flex-row items-center space-x-5 h-14 ${
payment === data.payment ? 'bg-blue-600 text-white' : ''
}`
}
const isActivePaymentAdminFee = (payment) => {
return `text-xs ${
payment === data.payment ? 'text-white' : 'text-gray-400'
}`
}
const handleSetPayment = (payment) => {
setData('payment', payment.name)
}
const handleSubmit = () => {
if (processing) {
return
}
post(route('customer.paylater.repay'))
}
return (
<CustomerLayout>
<Head title="Top Up" />
<div className="flex flex-col min-h-[calc(95dvh)]">
<div
className="w-full px-5 py-5"
onClick={() => {
router.get(route('customer.paylater.index'))
}}
>
<HiChevronLeft className="font-bold h-5 w-5" />
</div>
<div className="w-full px-5">
<div className="mb-2 font-bold">Nominal Tagihan</div>
<div className="w-full grid grid-cols-1 gap-2 text-center">
{amounts.map((amount) => (
<div
key={amount}
className={isActiveAmount(amount)}
onClick={() => setAmount(amount)}
>
{formatIDR(amount)}
</div>
))}
</div>
</div>
<div className="flex flex-row items-center space-x-2 justify-between w-full px-5 py-5">
<div className="border-b flex-1"></div>
<div>ATAU</div>
<div className="border-b flex-1"></div>
</div>
<div className="w-full px-5">
<FormInputNumeric
placeholder="masukan nominal, minimal 10.000"
value={data.amount}
onChange={(e) => setData('amount', e.target.value)}
error={errors.amount}
/>
</div>
<div className="w-full px-5 mt-10 flex flex-col">
<div className="font-bold mb-2">Metode Pembayaran</div>
{errors.payment && (
<Alert type="error">Pilih metode pembayaran</Alert>
)}
<div className="mb-2" />
<div className="w-full flex flex-col space-y-2">
{payments.length <= 0 && (
<Alert type="error">
Sistem pembayaran non-aktif{' '}
</Alert>
)}
{payments.map((payment) => (
<div
className="flex flex-col w-full"
key={payment.name}
>
<div
className={isActivePayment(payment.name)}
onClick={() => handleSetPayment(payment)}
>
{payment.name === data.payment ? (
<div className="w-5 h-5 rounded-md border">
<HiCheck />
</div>
) : (
<div className="w-5 h-5 rounded-md border"></div>
)}
<div className="flex flex-col">
{payment.logo === null ? (
<p>{payment.display_name}</p>
) : (
<img
src={payment.logo}
className="h-7 pt-1 object-cover"
loading="lazy"
/>
)}
{+payment.admin_fee !== 0 && (
<p
className={isActivePaymentAdminFee(
payment.name
)}
>
biaya admin:{' '}
{formatIDR(payment.admin_fee)}
</p>
)}
</div>
</div>
{payment.name === CASH_DEPOSIT && (
<Link
href={route(
'customer.deposit-location.index'
)}
className="flex flex-row items-center w-full text-sm text-gray-400 py-2 gap-1"
>
<div>Daftar lokasi pembayaran</div>
<div className="text-blue-400">
ada disini
</div>
<div>
<HiQuestionMarkCircle />
</div>
</Link>
)}
</div>
))}
</div>
</div>
</div>
<div className="fixed bottom-20 right-0 w-full px-2">
{payments.length > 0 && (
<div
onClick={handleSubmit}
className="bg-blue-700 text-white px-5 py-2 mx-auto rounded-full hover:text-black hover:bg-white max-w-sm"
>
Bayar
</div>
)}
</div>
</CustomerLayout>
)
}

@ -76,15 +76,12 @@ export default function Index(props) {
<Head title="Poin" /> <Head title="Poin" />
<div className="flex flex-col w-full min-h-[calc(90dvh)]"> <div className="flex flex-col w-full min-h-[calc(90dvh)]">
<HeaderTrx enable="poin" dates={dates} setDates={setDates} /> <HeaderTrx enable="poin" dates={dates} setDates={setDates} />
{_poins.length <= 0 && <EmptyHere />}
<div className="w-full"> <div className="w-full">
<div className="flex flex-col py-1 space-y-5 px-5"> <div className="flex flex-col py-1 space-y-5 px-5">
{_poins.length > 0 && ( <div className="text-sm text-gray-400">
<div className="text-sm text-gray-400"> {formatIDDate(dates.startDate)} s/d{' '}
{formatIDDate(dates.startDate)} s/d{' '} {formatIDDate(dates.endDate)}
{formatIDDate(dates.endDate)} </div>
</div>
)}
{_poins.map((poin) => ( {_poins.map((poin) => (
<div <div
key={poin.id} key={poin.id}
@ -120,6 +117,7 @@ export default function Index(props) {
)} )}
</div> </div>
</div> </div>
{_poins.length <= 0 && <EmptyHere />}
</div> </div>
</CustomerLayout> </CustomerLayout>
) )

@ -79,15 +79,13 @@ export default function Index(props) {
<Head title="Transaksi" /> <Head title="Transaksi" />
<div className="flex flex-col min-h-[calc(90dvh)]"> <div className="flex flex-col min-h-[calc(90dvh)]">
<HeaderTrx enable="trx" dates={dates} setDates={setDates} /> <HeaderTrx enable="trx" dates={dates} setDates={setDates} />
{sales.length <= 0 && <EmptyHere />}
<div className="w-full"> <div className="w-full">
<div className="flex flex-col space-y-5 px-5"> <div className="flex flex-col space-y-5 px-5">
{sales.length > 0 && ( <div className="text-sm text-gray-400">
<div className="text-sm text-gray-400"> {formatIDDate(dates.startDate)} s/d{' '}
{formatIDDate(dates.startDate)} s/d{' '} {formatIDDate(dates.endDate)}
{formatIDDate(dates.endDate)} </div>
</div>
)}
{sales.map((sale) => ( {sales.map((sale) => (
<div <div
key={sale.id} key={sale.id}
@ -121,6 +119,8 @@ export default function Index(props) {
)} )}
</div> </div>
</div> </div>
{sales.length <= 0 && <EmptyHere />}
</div> </div>
</CustomerLayout> </CustomerLayout>
) )

@ -50,6 +50,8 @@ Route::middleware(['http_secure_aware', 'guard_should_customer', 'inertia.custom
// paylater // paylater
Route::get('paylater', [PaylaterController::class, 'index'])->name('customer.paylater.index'); Route::get('paylater', [PaylaterController::class, 'index'])->name('customer.paylater.index');
Route::get('paylater/trx/{paylater}', [PaylaterController::class, 'show'])->name('customer.paylater.show'); Route::get('paylater/trx/{paylater}', [PaylaterController::class, 'show'])->name('customer.paylater.show');
Route::get('paylater/repay', [PaylaterController::class, 'create'])->name('customer.paylater.repay');
Route::post('paylater/repay', [PaylaterController::class, 'store']);
// deposite // deposite
Route::get('trx/deposit', [DepositController::class, 'index'])->name('transactions.deposit.index'); Route::get('trx/deposit', [DepositController::class, 'index'])->name('transactions.deposit.index');

Loading…
Cancel
Save