From 56bba0eaaf211033963585be94e52fb3c32ca260 Mon Sep 17 00:00:00 2001 From: JiaSheng Date: Sun, 22 Oct 2023 23:46:09 +0800 Subject: [PATCH] wallet pagination, export --- .../Eloquent/Filters/CreatedAfterOrEqual.php | 23 ++++ .../Eloquent/Filters/CreatedBeforeOrEqual.php | 22 ++++ .../General/Eloquent/Filters/OwnerId.php | 3 +- .../General/Eloquent/Filters/OwnerType.php | 21 ++++ .../General/Eloquent/Filters/StatusIn.php | 3 +- .../Filters/WithOrderReferenceLike.php | 27 +++++ ...portsCustomersWalletTransactionHistory.php | 108 ++++++++++++++++++ .../ListWalletTransactionsLogic.php | 79 +++++++++++++ ...mersWalletTransactionToExcelController.php | 36 ++++++ .../ListWalletTransactionsController.php | 21 ++++ .../Resources/WalletTransactionResource.php | 7 ++ .../CustomerTransactionSectionComponent.vue | 99 +++++++++++++--- .../CustomerWalletTransactionComponent.vue | 35 ++++++ routes/transaction.php | 2 + routes/web.php | 2 + 15 files changed, 468 insertions(+), 20 deletions(-) create mode 100644 app/Classes/General/Eloquent/Filters/CreatedAfterOrEqual.php create mode 100644 app/Classes/General/Eloquent/Filters/CreatedBeforeOrEqual.php create mode 100644 app/Classes/General/Eloquent/Filters/OwnerType.php create mode 100644 app/Classes/General/Eloquent/Filters/WithOrderReferenceLike.php create mode 100644 app/Classes/Modules/Exports/Services/ExportsCustomersWalletTransactionHistory.php create mode 100644 app/Classes/Modules/Transactions/ControllersLogic/ListWalletTransactionsLogic.php create mode 100644 app/Http/Controllers/Exports/ExportCustomersWalletTransactionToExcelController.php create mode 100644 app/Http/Controllers/Transactions/ListWalletTransactionsController.php create mode 100644 resources/assets/vue/components/wallets/elements/CustomerWalletTransactionComponent.vue diff --git a/app/Classes/General/Eloquent/Filters/CreatedAfterOrEqual.php b/app/Classes/General/Eloquent/Filters/CreatedAfterOrEqual.php new file mode 100644 index 00000000..9df98f40 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/CreatedAfterOrEqual.php @@ -0,0 +1,23 @@ +getModel()->getTable(); + $startDate = Carbon::createFromFormat('d-m-Y', $value)->startOfDay(); + return $builder->where("{$table}.created_at", '>=', $startDate); + } +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/CreatedBeforeOrEqual.php b/app/Classes/General/Eloquent/Filters/CreatedBeforeOrEqual.php new file mode 100644 index 00000000..5b349540 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/CreatedBeforeOrEqual.php @@ -0,0 +1,22 @@ +getModel()->getTable(); + $endDate = Carbon::createFromFormat('d-m-Y', $value)->endOfDay(); + return $builder->where("{$table}.created_at", '<=', $endDate); + } +} diff --git a/app/Classes/General/Eloquent/Filters/OwnerId.php b/app/Classes/General/Eloquent/Filters/OwnerId.php index eac7e32d..c2dfa685 100644 --- a/app/Classes/General/Eloquent/Filters/OwnerId.php +++ b/app/Classes/General/Eloquent/Filters/OwnerId.php @@ -14,7 +14,8 @@ class OwnerId implements Filter */ public static function apply(Builder $builder, $value) { - return $builder->where('owner_id', $value); + $table = $builder->getModel()->getTable(); + return $builder->where("{$table}.owner_id", $value); } } \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/OwnerType.php b/app/Classes/General/Eloquent/Filters/OwnerType.php new file mode 100644 index 00000000..3feedccd --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/OwnerType.php @@ -0,0 +1,21 @@ +getModel()->getTable(); + return $builder->where("{$table}.owner_type", $value); + } + +} \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/StatusIn.php b/app/Classes/General/Eloquent/Filters/StatusIn.php index cbfdfbd0..85425802 100644 --- a/app/Classes/General/Eloquent/Filters/StatusIn.php +++ b/app/Classes/General/Eloquent/Filters/StatusIn.php @@ -14,7 +14,8 @@ class StatusIn implements Filter */ public static function apply(Builder $builder, $value) { - return $builder->whereIn('status', $value); + $table = $builder->getModel()->getTable(); + return $builder->whereIn("{$table}.status", $value); } } \ No newline at end of file diff --git a/app/Classes/General/Eloquent/Filters/WithOrderReferenceLike.php b/app/Classes/General/Eloquent/Filters/WithOrderReferenceLike.php new file mode 100644 index 00000000..acc9f161 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/WithOrderReferenceLike.php @@ -0,0 +1,27 @@ +join('transactions as t2', 't2.payment_reference', '=', 'transactions.bill_no') + ->join('transactions as t3', 't3.id', '=', 't2.owner_id') + ->join('packing_lists', 'packing_lists.id', '=', 't3.owner_id') + ->join('orders', function ($join) use ($value) { + $join->on('orders.id', '=', 'packing_lists.owner_id') + ->where('orders.reference', 'LIKE', '%'.$value.'%'); + }) + ->addSelect(['transactions.*', 't2.id as paymentTransactionId', 't3.id as invoiceTransactionId', 'packing_lists.id as packingListId', 'orders.reference as orderReference']); + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Exports/Services/ExportsCustomersWalletTransactionHistory.php b/app/Classes/Modules/Exports/Services/ExportsCustomersWalletTransactionHistory.php new file mode 100644 index 00000000..69571b1f --- /dev/null +++ b/app/Classes/Modules/Exports/Services/ExportsCustomersWalletTransactionHistory.php @@ -0,0 +1,108 @@ +request = $request; + } + + public function headings(): array + { + return [ + 'Date', + 'Description', + 'Incoming', + 'Outgoing', + 'Balance', + ]; + } + + /** + * @return \Illuminate\Support\Collection|mixed + */ + public function query() + { + $wallet = Wallet::find($this->request->route('wallet_id')); + $transactions = $wallet->transactions()->whereIn('transactions.status', [2, 3])->orderBy('id'); + + return $transactions; + } + + /** + * @param Transaction $transaction + * + * @return array + */ + public function map($transaction): array + { + $decimals = $this->request->route('is_precise') == 'true' ? 5 : 2; + + $description = ''; + switch ((int) $transaction->type) { + case TransactionType::TOP_UP: + $description = (float) $transaction->amount . ' Credit Top up'; + break; + case TransactionType::GROUP_PAYMENT: + $description = (float) $transaction->amount . ' Credit Top up'; + break; + case TransactionType::CREDIT_NOTE: + $description = 'Credit Voucher for ' . $transaction->payment_reference; + break; + case TransactionType::PAYMENT: + $booking = Transaction::where('payment_reference', $transaction->bill_no)->first()->owner; + + if (!$booking) { + $description = 'Payment for unknown booking, please contact tech support.'; + break; + } + + $marking = $booking->marking; + $description = 'Payment For booking refs' . $marking; + break; + case TransactionType::DEBIT_NOTE: + $description = 'Debit Voucher for ' . $transaction->payment_reference; + break; + } + + $incoming = $outgoing = ''; + + if (in_array($transaction->type, [TransactionType::TOP_UP, TransactionType::CREDIT_NOTE, TransactionType::GROUP_PAYMENT])) { + $incoming = number_format($transaction->amount, $decimals, '.', ','); + $this->runningBalance += $transaction->amount; + } + + if (in_array($transaction->type, [TransactionType::PAYMENT, TransactionType::DEBIT_NOTE])) { + $outgoing = number_format($transaction->amount, $decimals, '.', ','); + $this->runningBalance -= $transaction->amount; + } + + return [ + Carbon::parse($transaction->created_at)->format('d-m-Y h:i:s A'), + $description, + $incoming, + $outgoing, + number_format($this->runningBalance, $decimals, '.', ',') + ]; + } +} diff --git a/app/Classes/Modules/Transactions/ControllersLogic/ListWalletTransactionsLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/ListWalletTransactionsLogic.php new file mode 100644 index 00000000..af51b2e9 --- /dev/null +++ b/app/Classes/Modules/Transactions/ControllersLogic/ListWalletTransactionsLogic.php @@ -0,0 +1,79 @@ +listsTransactions = $listsTransactions; + } + + /** + * @return array + */ + protected function notification():array { + return [ + 'title' => 'Retrieved Wallet Transactions', + 'message' => 'You have successfully retrieved a list of transactions' + ]; + } + + /** @var ListsTransactions */ + private $listsTransactions; + + public function logic(Request $request) : JsonResponse + { + $query = $this->listsTransactions->execute($this->listsTransactions->deserializeFilters($request->input('filters'))); + + if (str_contains($request->input('filters'), "owner_id") && $query->count() > 0) { + $wallet_total_incoming = Transaction::where('owner_type', $query->first()->owner_type) + ->where('owner_id', $query->first()->owner_id) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->whereIn('type', [TransactionType::TOP_UP, TransactionType::CREDIT_NOTE, TransactionType::GROUP_PAYMENT]) + ->sum('amount'); + + $wallet_total_outgoing = Transaction::where('owner_type', $query->first()->owner_type) + ->where('owner_id', $query->first()->owner_id) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->whereIn('type', [TransactionType::PAYMENT, TransactionType::DEBIT_NOTE]) + ->sum('amount'); + + $currentWalletBalance = $wallet_total_incoming - $wallet_total_outgoing; + $incoming = Transaction::where('owner_type', $query->first()->owner_type) + ->where('owner_id', $query->first()->owner_id) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->whereIn('type', [TransactionType::TOP_UP, TransactionType::CREDIT_NOTE, TransactionType::GROUP_PAYMENT]) + ->where('id', '>', $query->first()->id) + ->sum('amount'); + $outgoing = Transaction::where('owner_type', $query->first()->owner_type) + ->where('owner_id', $query->first()->owner_id) + ->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]) + ->whereIn('type', [TransactionType::PAYMENT, TransactionType::DEBIT_NOTE]) + ->where('id', '>', $query->first()->id) + ->sum('amount'); + $runningBalanceInReverse = $currentWalletBalance - $incoming + $outgoing; + $request['running_balance'] = $runningBalanceInReverse; + } + + return $this->collectionResponse(WalletTransactionResource::collection($query)); + + } + + + +} diff --git a/app/Http/Controllers/Exports/ExportCustomersWalletTransactionToExcelController.php b/app/Http/Controllers/Exports/ExportCustomersWalletTransactionToExcelController.php new file mode 100644 index 00000000..742c4be1 --- /dev/null +++ b/app/Http/Controllers/Exports/ExportCustomersWalletTransactionToExcelController.php @@ -0,0 +1,36 @@ +headers->set('Authorization', 'Bearer ' . $token); + } + + public function export(Request $request) + { + $exportsTransactions = new ExportsCustomersWalletTransactionHistory($request); + $wallet = Wallet::find($request->route('wallet_id')); + $company_marking = $wallet->owner->connections->first()->invitee_reference; + + $filename = $company_marking . '-wallet-' . ($request->route('is_precise') == 'true' ? 'precise-' : '') . 'transaction-history.xls'; + $response = $exportsTransactions->download($filename, Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']); + ob_end_clean(); + return $response; + } +} diff --git a/app/Http/Controllers/Transactions/ListWalletTransactionsController.php b/app/Http/Controllers/Transactions/ListWalletTransactionsController.php new file mode 100644 index 00000000..e83d7b58 --- /dev/null +++ b/app/Http/Controllers/Transactions/ListWalletTransactionsController.php @@ -0,0 +1,21 @@ +execute($request); + } +} diff --git a/app/Http/Resources/WalletTransactionResource.php b/app/Http/Resources/WalletTransactionResource.php index 0d643d1d..1293c8aa 100644 --- a/app/Http/Resources/WalletTransactionResource.php +++ b/app/Http/Resources/WalletTransactionResource.php @@ -20,12 +20,15 @@ class WalletTransactionResource extends JsonResource public function toArray($request) { $description = ''; + $current_running_balance = $request['running_balance']; switch((int) $this->type){ case TransactionType::TOP_UP: $description = (double) $this->amount.' Credit Top up'; + $request['running_balance'] = bcsub($request['running_balance'], $this->amount, 5); break; case TransactionType::CREDIT_NOTE: $description = 'Credit Voucher for '.$this->payment_reference; + $request['running_balance'] = bcsub($request['running_balance'], $this->amount, 5); break; case TransactionType::PAYMENT: $order = Transaction::where('payment_reference', $this->bill_no)->first()->owner->owner->owner; @@ -35,13 +38,16 @@ class WalletTransactionResource extends JsonResource break; } + $request['running_balance'] = bcadd($request['running_balance'], $this->amount, 5); $marking = $order->reference; $description = 'Payment For order refs.'.''.$marking.''; break; case 11: + $request['running_balance'] = bcadd($request['running_balance'], $this->amount, 5); $description = 'Debit Voucher for '.$this->payment_reference; break; case 15: + $request['running_balance'] = bcsub($request['running_balance'], $this->amount, 5); $description = (double) $this->amount.' Credit Top up'; break; @@ -56,6 +62,7 @@ class WalletTransactionResource extends JsonResource 'payment_method' => (float) $this->payment_method, // 'issuer_name' => $this->issuerCompany->name, 'amount' => (double) $this->amount, + 'running_balance' => (double) $current_running_balance, 'service_charge' => (double) $this->service_charge, 'tax' => (double) $this->tax, 'status' => (int) $this->status, diff --git a/resources/assets/vue/components/wallets/elements/CustomerTransactionSectionComponent.vue b/resources/assets/vue/components/wallets/elements/CustomerTransactionSectionComponent.vue index d60032e0..7ad12009 100644 --- a/resources/assets/vue/components/wallets/elements/CustomerTransactionSectionComponent.vue +++ b/resources/assets/vue/components/wallets/elements/CustomerTransactionSectionComponent.vue @@ -9,6 +9,43 @@
Transaction History
+
+
+
+ {{ showingPreciseAmount ? 'Showing Precise Wallet Transaction' : 'Show Precise Wallet Transaction'}} + {{ showingPreciseAmount ? 'Download Precise Transaction' : 'Download Transaction'}} +
+
+
+ + + + +
+
+
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ +
+
@@ -19,13 +56,11 @@
Balance
-
-
{{item.created_at}}
-
-
{{[5, 9, 15].includes(parseFloat(item.type)) ? (Math.round((parseFloat(item.amount) + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : ''}}
-
{{[2, 11].includes(parseFloat(item.type)) ? '- ' + (Math.round((parseFloat(item.amount) + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : ''}}
-
{{remainingBalance(index)}}
-
+ + +
@@ -103,10 +138,22 @@ export default { }, data(){ return { + key: 1, section: 'customerTransactionSection', isLoading: true, wallet: null, - attention: false + showingPreciseAmount: false, + showingTransactionCount: 10, + attention: false, + reference_no: null, + startDate: null, + endDate: null, + options: { + status_in: [2, 3], + owner_type: 'App\\Models\\Wallet', + owner_id: 0, + per_page: this.showingTransactionCount + } } }, computed: { @@ -119,8 +166,17 @@ export default { if(inComplete){ this.fetchWallet(); } + }, + showingTransactionCount() { + this.key ++; } }, + validations: { + showingTransactionCount: { }, + reference_no: { }, + startDate: { }, + endDate: { }, + }, created(){ this.$store.dispatch('updateListQueue', {'name': this.section}); }, @@ -130,22 +186,29 @@ export default { var filters = {with_transactions: true}; this.submit(route('api.wallet.company_module.show', this.id) + '?filters=' + JSON.stringify(filters), 'get', this.section, false, false); }, - remainingBalance(index) { - let tempBalance = 0; + submitSearch() { + console.log("searcvhing"); + delete this.options.with_order_reference_like; + delete this.options.created_after_or_equal; + delete this.options.created_before_or_equal; - if(this.wallet){ - let transactions = this.wallet.transactions.slice().reverse(); - transactions.slice(0, transactions.length - index).map(function(transaction) { - [2, 11].includes(transaction.type) ? tempBalance -= (transaction.amount) : tempBalance += (transaction.amount); - return tempBalance - }, 0); + if (this.reference_no) { + this.options.with_order_reference_like = this.reference_no } - - return (Math.round((tempBalance + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); + if (this.startDate) { + this.options.created_after_or_equal = this.startDate + } + if (this.endDate) { + this.options.created_before_or_equal = this.endDate + } + + this.key ++; }, successHandler(response){ this.isLoading = false; this.wallet = response.payload.data; + this.options.owner_id = this.wallet.id + this.key ++; } } } diff --git a/resources/assets/vue/components/wallets/elements/CustomerWalletTransactionComponent.vue b/resources/assets/vue/components/wallets/elements/CustomerWalletTransactionComponent.vue new file mode 100644 index 00000000..cb0bb88f --- /dev/null +++ b/resources/assets/vue/components/wallets/elements/CustomerWalletTransactionComponent.vue @@ -0,0 +1,35 @@ + + \ No newline at end of file diff --git a/routes/transaction.php b/routes/transaction.php index 9f770be8..07a1cf1c 100644 --- a/routes/transaction.php +++ b/routes/transaction.php @@ -10,6 +10,8 @@ Route::group(['prefix' => 'transactions', 'namespace' => 'Transactions', 'as' => Route::delete('/delete-payment/{id}', 'DeletePaymentTransactionController@delete')->name('payment.delete'); Route::put('{id}/status/update/{status}', 'UpdateTransactionStatusController@update')->where('status', 'approve|expire|reject')->name('update'); + Route::get('wallet/list', 'ListWalletTransactionsController@list')->name('wallet.list'); + Route::group(['prefix' => 'payment', 'as' => 'payment.'], function () { Route::post('/create', 'CreatePaymentTransactionController@create')->name('create'); Route::post('/upload-verification-document/{transaction_id}', 'UploadPaymentVerificationDocumentController@upload')->name('verification.create'); diff --git a/routes/web.php b/routes/web.php index 88bab558..849d6e7f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1068,6 +1068,8 @@ Route::get('/wallet/{marking}/details', function ($marking) { return view('pages.wallet.index', ['id' => $id, 'marking' => $marking]); })->name('wallet.details'); +Route::get('/wallet/{wallet_id}/{is_precise}/export', 'Exports\ExportCustomersWalletTransactionToExcelController@export')->name('wallet.details-export'); + Route::get('/wallet/audit', function (Request $request) { $wallets = \App\Models\Wallet::all();