account-statement-pagination

This commit is contained in:
JiaSheng
2023-11-04 00:00:58 +08:00
parent 4bf32ac285
commit d6754014aa
7 changed files with 436 additions and 10 deletions
@@ -13,6 +13,7 @@ use App\Models\Booking;
use App\Models\Company;
use App\Models\Transaction;
use App\Models\Wallet;
use Illuminate\Support\Facades\DB;
class FetchCompanyTransactionStatementLogic extends AbstractControllerLogic
{
@@ -37,14 +38,15 @@ class FetchCompanyTransactionStatementLogic extends AbstractControllerLogic
public function logic(Request $request): JsonResponse
{
$companyId = $request->route('id');
$filterArray = json_decode($request->input('filters'));
$transactions = Transaction::where(function ($query) use ($companyId) {
$query
->where('type', TransactionType::PAYMENT)
->where('owner_type', Booking::class)
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->whereHas('booking', function ($query) use ($companyId) {
$query->where('company_id', $companyId);
->whereHas('booking', function ($q) use ($companyId) {
$q->where('company_id', $companyId);
});
})
->orWhere(function ($query) use ($companyId) {
@@ -66,7 +68,83 @@ class FetchCompanyTransactionStatementLogic extends AbstractControllerLogic
})
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->orderBy('created_at', 'desc')
->get();
->paginate($filterArray->per_page);
$total_incoming = Transaction::where(function ($query) use ($companyId, $transactions) {
$query->whereHas('owner', function ($query) use ($companyId) {
$query->where('owner_id', $companyId);
$query->where('owner_type', Company::class);
})
->where('owner_type', Wallet::class)
->where('type', '!=', TransactionType::PAYMENT);
})
->orWhere(function ($query) use ($companyId, $transactions) {
$query
->where('type', TransactionType::INVOICE)
->where('owner_type', Booking::class)
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->whereHas('booking', function ($query) use ($companyId) {
$query->where('company_id', $companyId);
});
})
->sum(
DB::raw(
'if (transactions.type = 2,(transactions.amount / transactions.currency_rate + transactions.service_charge),transactions.amount)'
)
);
$total_outgoing = Transaction::where(function ($query) use ($companyId, $transactions) {
$query
->where('type', TransactionType::PAYMENT)
->where('owner_type', Booking::class)
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->whereHas('booking', function ($query) use ($companyId) {
$query->where('company_id', $companyId);
});
})
->sum('transactions.amount');
$currentBalance = $total_incoming - $total_outgoing;
$incoming = Transaction::where(function ($query) use ($companyId, $transactions) {
$query->whereHas('owner', function ($query) use ($companyId) {
$query->where('owner_id', $companyId);
$query->where('owner_type', Company::class);
})
->where('owner_type', Wallet::class)
->where('type', '!=', TransactionType::PAYMENT)
->where('id', '>', $transactions->first()->id);
})
->orWhere(function ($query) use ($companyId, $transactions) {
$query
->where('type', TransactionType::INVOICE)
->where('owner_type', Booking::class)
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->whereHas('booking', function ($query) use ($companyId) {
$query->where('company_id', $companyId);
})
->where('id', '>', $transactions->first()->id);
})
->sum(
DB::raw(
'if (transactions.type = 2,(transactions.amount / transactions.currency_rate + transactions.service_charge),transactions.amount)'
)
);
$outgoing = Transaction::where(function ($query) use ($companyId, $transactions) {
$query
->where('type', TransactionType::PAYMENT)
->where('owner_type', Booking::class)
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->whereHas('booking', function ($query) use ($companyId) {
$query->where('company_id', $companyId);
})
->where('id', '>', $transactions->first()->id);
})
->sum('transactions.amount');
$runningBalanceInReverse = $currentBalance - $incoming + $outgoing;
$request['running_balance'] = $runningBalanceInReverse;
return $this->collectionResponse(PaymentTransactionResource::collection($transactions));
}
@@ -0,0 +1,110 @@
<?php
namespace App\Http\Controllers\Exports;
use App\Classes\Modules\Exports\Services\ExportsCustomersWalletTransactionHistory;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Models\Booking;
use App\Models\Company;
use App\Models\Transaction;
use App\Models\User;
use App\Models\Wallet;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Excel;
use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf;
class ExportCustomersAccountStatementTransactionToPDFController
{
/**
* ExportCustomersWalletTransactionToExcelController constructor.
* @param Request $request
*/
public function __construct(Request $request)
{
$token = Auth::fromUser(User::find(1));
$request->headers->set('Authorization', 'Bearer ' . $token);
}
public function export(Request $request)
{
ini_set('max_execution_time', '1000000');
ini_set("pcre.backtrack_limit", "5000000");
$companyId = $request->route('id');
$company = Company::find($companyId);
$transactions = Transaction::where(function ($query) use ($companyId) {
$query
->where('type', TransactionType::PAYMENT)
->where('owner_type', Booking::class)
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->whereHas('booking', function ($q) use ($companyId) {
$q->where('company_id', $companyId);
});
})
->orWhere(function ($query) use ($companyId) {
$query->whereHas('owner', function ($query) use ($companyId) {
$query->where('owner_id', $companyId);
$query->where('owner_type', Company::class);
})
->where('owner_type', Wallet::class)
->where('type', '!=', TransactionType::PAYMENT);
})
->orWhere(function ($query) use ($companyId) {
$query
->where('type', TransactionType::INVOICE)
->where('owner_type', Booking::class)
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->whereHas('booking', function ($query) use ($companyId) {
$query->where('company_id', $companyId);
});
})
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->orderBy('created_at', 'desc')
->get();
$total_incoming = Transaction::where(function ($query) use ($companyId, $transactions) {
$query->whereHas('owner', function ($query) use ($companyId) {
$query->where('owner_id', $companyId);
$query->where('owner_type', Company::class);
})
->where('owner_type', Wallet::class)
->where('type', '!=', TransactionType::PAYMENT);
})
->orWhere(function ($query) use ($companyId, $transactions) {
$query
->where('type', TransactionType::INVOICE)
->where('owner_type', Booking::class)
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->whereHas('booking', function ($query) use ($companyId) {
$query->where('company_id', $companyId);
});
})
->sum(
DB::raw(
'if (transactions.type = 2,(transactions.amount / transactions.currency_rate + transactions.service_charge),transactions.amount)'
)
);
$total_outgoing = Transaction::where(function ($query) use ($companyId) {
$query
->where('type', TransactionType::PAYMENT)
->where('owner_type', Booking::class)
->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])
->whereHas('booking', function ($query) use ($companyId) {
$query->where('company_id', $companyId);
});
})
->sum('transactions.amount');
$currentBalance = $total_incoming - $total_outgoing;
$pdf = LaravelMpdf::loadView('pages.pdfs.company_account_statement_transaction', ['transactions' => $transactions, 'runningBalance' => $currentBalance, 'company' => $company, 'isPrecise' => $request->route('is_precise') == 'true']);
return $pdf->download("test.pdf");
}
}
@@ -20,6 +20,22 @@ class PaymentTransactionResource extends JsonResource
{
$booking = in_array((int)$this->type, [TransactionType::BILL, TransactionType::REFUND])? $this->owner->owner : $this->owner;
$current_running_balance = $request['running_balance'];
switch((int) $this->type){
case 1:
$request['running_balance'] = bcadd($request['running_balance'], $this->amount, 7);
break;
case 2:
$request['running_balance'] = bcsub($request['running_balance'], $this->amount / $this->currency_rate + $this->service_charge, 7);
break;
case 5:
$request['running_balance'] = bcsub($request['running_balance'], $this->amount, 7);
break;
case 9:
$request['running_balance'] = bcsub($request['running_balance'], $this->amount, 7);
break;
}
$booking_marking = '';
switch ($this->owner_type) {
@@ -43,6 +59,7 @@ class PaymentTransactionResource extends JsonResource
'issuer_id' => $this->issuerCompany->id,
'amount' => (double) $this->amount,
'original_amount' => (double) $this->original_amount,
'running_balance' => (double) $current_running_balance,
'currency' => new CurrencyResource($this->currency),
'original_currency' => new CurrencyResource($this->original_currency),
'service_charge' => (double) $this->service_charge,
@@ -0,0 +1,64 @@
<template>
<div class="row bg-white padding-10 m-b-10 rounded align-datas-center">
<div class="col-2 fs-12">{{data.created_at}}</div>
<div class="col-3 fs-12">{{ convertTransactionType(data.type) }} {{ data.payment_reference ? ' - ' + data.payment_reference : '' }} <a target=_blank v-if="[9,11].includes(data.type) " :href="route('transaction.credit_note.download', data.id)"><i class="fa fa-download fs-11 m-l-5 text-secondary hover-primary"></i></a></div>
<div class="col fs-12"><a :href="route('booking.details', data.booking_marking)">{{data.booking_marking}}</a></div>
<div class="col-2 text-success text-center" v-if="parseFloat(data.type) !== 2">{{[5, 9].includes(parseFloat(data.type)) ? formatValue(data.amount) : ''}}</div>
<div class="col-2 text-success text-center" v-if="parseFloat(data.type) === 2">{{formatValue((parseFloat(data.amount) / parseFloat(data.currency_rate) + parseFloat(data.service_charge)))}}</div>
<div class="col-2 text-danger text-center">{{[1, 11].includes(parseFloat(data.type)) ? '- ' + (formatValue(data.amount)) : ''}}</div>
<div class="col-2 text-right">{{formatValue(data.running_balance)}}</div>
</div>
</template>
<script>
import componentHandler from '../../../general/mixins/componentHandler';
export default {
props: {
data: {
required: true,
type: Object
},
showingPreciseAmount: {
type: Boolean,
required: true
},
},
methods: {
convertTransactionType(type){
var transactionTypeArray = [];
transactionTypeArray[0] = 'Payment Attempt';
transactionTypeArray[1] = 'Payment';
transactionTypeArray[2] = 'Invoice';
transactionTypeArray[3] = 'Bill';
transactionTypeArray[4] = 'Proforma';
transactionTypeArray[5] = 'Top Up';
transactionTypeArray[6] = 'Refund';
transactionTypeArray[7] = 'Purchase Order';
transactionTypeArray[8] = 'Supplier Deliver';
transactionTypeArray[9] = 'Credit Note';
transactionTypeArray[11] = 'Debit Note';
transactionTypeArray[10] = 'Withdraw';
transactionTypeArray[12] = 'Transfer Fee';
transactionTypeArray[13] = 'Cash Back';
return transactionTypeArray[type];
},
convertPaymentMethod(paymentMethod){
var paymentMethodArray = [];
paymentMethodArray[1] = 'Cash';
paymentMethodArray[2] = 'Cheque';
paymentMethodArray[3] = 'ba';
paymentMethodArray[4] = 'Wallet';
paymentMethodArray[5] = 'Payment Gateway';
return paymentMethodArray[paymentMethod];
},
formatValue(value) {
if (this.showingPreciseAmount) {
return (Math.round((parseFloat(value) + Number.EPSILON) * 100000) / 100000).toLocaleString('en-US', { minimumFractionDigits: 5, maximumFractionDigits: 5 });
}
return (Math.round((parseFloat(value) + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
},
mixins: [componentHandler],
}
</script>
@@ -2,14 +2,28 @@
<div class="row">
<div class="col">
<loading-component style="height: 200px; top: 0;" key="1" color="success" v-show="isLoading"></loading-component>
<div class="row" v-if="transaction">
<div class="row">
<div class="col">
<div class="row p-b-5 b-b b-grey m-b-10 m-l-0 m-r-0">
<div class="col no-padding">
<h6>Account Statement - Transaction History</h6>
</div>
</div>
<div class="row" v-if="transaction">
<div class="row m-t-10 m-b-10">
<div class="col">
<div class="d-flex align-items-center h-100">
<span class="btn btn-md fs-11 bg-primary text-white fs-12 m-r-5" :class="[{'bg-primary-darker': showingPreciseAmount}]" @click="showingPreciseAmount=!showingPreciseAmount">{{ showingPreciseAmount ? 'Showing Precise Wallet Transaction' : 'Show Precise Wallet Transaction'}}</span>
<a v-if="this.id" :href="route('company.transaction.export', this.id, showingPreciseAmount)" target="_blank" class="btn btn-md btn-primary fs-11"><i class="fa fa-download m-r-5"></i>{{ showingPreciseAmount ? 'Download Precise Transaction' : 'Download Transaction'}}</a>
</div>
</div>
<div class="col-2">
<validation-wrapper-component selectable :validator="$v.showingTransactionCount">
<label>Showing Rows</label>
<select-component :options="[5, 10, 20, 30, 50]" v-model="showingTransactionCount"></select-component>
</validation-wrapper-component>
</div>
</div>
<div class="row">
<div class="col">
<div class="row padding-10">
<div class="col-2 fs-10">Date</div>
@@ -20,7 +34,13 @@
<div class="col-2 fs-10 text-right">Balance</div>
</div>
<div class="row bg-white padding-10 m-b-10 rounded" v-for="(item, index) in transaction" v-bind:key="item.id" :data="item">
<list-component :key="key" section="accountStatementTransactionSection" :endpoint="route('api.transaction.company.transaction.fetch', this.id)" :options="options">
<template slot="list" slot-scope="{data}">
<customer-account-statement-transaction-section-component :data="data" :showingPreciseAmount="showingPreciseAmount" ></customer-account-statement-transaction-section-component>
</template>
</list-component>
<!-- <div class="row bg-white padding-10 m-b-10 rounded" v-for="(item, index) in transaction" v-bind:key="item.id" :data="item">
<div class="col-2 fs-12">{{item.created_at}}</div>
<div class="col-3 fs-12">{{ convertTransactionType(item.type) }} {{ item.payment_reference ? ' - ' + item.payment_reference : '' }} <a target=_blank v-if="[9,11].includes(item.type) " :href="route('transaction.credit_note.download', item.id)"><i class="fa fa-download fs-11 m-l-5 text-secondary hover-primary"></i></a></div>
<div class="col fs-12"><a :href="route('booking.details', item.booking_marking)">{{item.booking_marking}}</a></div>
@@ -28,7 +48,7 @@
<div class="col-2 text-success text-center" v-if="parseFloat(item.type) === 2">{{(Math.round(((parseFloat(item.amount) / parseFloat(item.currency_rate) + parseFloat(item.service_charge)) + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
<div class="col-2 text-danger text-center">{{[1, 11].includes(parseFloat(item.type)) ? '- ' + (Math.round((parseFloat(item.amount) + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : ''}}</div>
<div class="col-2 text-right">{{remainingBalance(index)}}</div>
</div>
</div> -->
</div>
</div>
</div>
@@ -46,15 +66,21 @@ export default {
},
data(){
return {
key: 1,
section: 'customerTransactionSection',
isLoading: true,
isLoading: false,
transaction: null,
attention: false
attention: false,
showingPreciseAmount: false,
showingTransactionCount: 10,
options: {
per_page: this.showingTransactionCount
}
}
},
computed: {
pendingQueue () {
return this.$store.getters.isInCompleteQueue(this.section);
// return this.$store.getters.isInCompleteQueue(this.section);
}
},
watch: {
@@ -62,8 +88,15 @@ export default {
if(inComplete){
this.fetchTransaction();
}
},
showingTransactionCount() {
this.options.per_page = this.showingTransactionCount
this.key ++;
}
},
validations: {
showingTransactionCount: { },
},
created(){
this.$store.dispatch('updateListQueue', {'name': this.section});
},
@@ -0,0 +1,123 @@
@extends('layouts.base_pdf')
@section('inner_content')
<br>
<htmlpageheader name="page-header">
<br><br>
<div class="separator"><strong><i>{{ $company->reference }}</i></strong></div>
</htmlpageheader>
<table>
<!-- Header Section -->
<tr>
<td class="header-logo">
<img src="{{ asset('images/ri_1.png') }}" alt="logo" id="logo" class="logo">
</td>
<td class="header-cief-address">
<span class="company-name"><strong>CIEF WORLDWIDE SDN BHD</strong></span>
<span class="company-reg">(1134596-M)</span><br>
No. 72-3, Jalan Jalil 1,<br>
The Earth Bukit Jalil,<br>
57000 Kuala Lumpur<br>
Tel: 03-8082 1252
</td>
<td class="header-details">
<div class="title right"><strong>Account Statement Transactions</strong></div>
<div>&nbsp;</div>
</td>
</tr>
<tr>
<td colspan="3" class="address">
<div class="label">{{ $company->name }}</div>
</td>
</tr>
</table>
<br>
<br>
<table class="line-table" style="overflow: wrap" autosize="1">
<!-- Table Header -->
<thead>
<tr>
<th width="5%">No</th>
<th class="stock-code" width="10%">Date</th>
<th class="description">Description</th>
<th width="10%">Marking</th>
<th width="15%">Incoming</th>
<th width="15%">Outgoing</th>
<th width="10%">Balance<br>(RM)</th>
</tr>
</thead>
<tbody>
@php
$roundOff = 2;
if ($isPrecise) {
$roundOff = 5;
}
@endphp
@foreach ($transactions as $key => $transaction)
@php
$description = "";
$incoming = "";
$outgoing = "";
$current_running_balance = $runningBalance;
$marking = "";
switch ($transaction->type) {
case 1:
$description = "Payment - " . $transaction->payment_reference;
$outgoing = number_format($transaction->amount, $roundOff);
$runningBalance = bcadd($runningBalance, $transaction->amount, 7);
$marking = $transaction->owner->marking;
break;
case 2:
$description = "Invoice";
$incoming = number_format(($transaction->amount / $transaction->currency_rate + $transaction->service_charge), $roundOff);
$runningBalance = bcsub($runningBalance, $transaction->amount, 7);
$marking = $transaction->owner->marking;
break;
case 5:
$description = "Top Up - " . $transaction->payment_reference;
$incoming = number_format($transaction->amount, $roundOff);
$runningBalance = bcsub($runningBalance, $transaction->amount, 7);
break;
case 9:
$description = "Credit Note - " . $transaction->payment_reference;
$incoming = number_format($transaction->amount, $roundOff);
$runningBalance = bcsub($runningBalance, $transaction->amount, 7);
break;
}
@endphp
<tr>
<td width="5%" class="center top">{{ $key + 1 }}</td>
<td class="stock-code top" width="10%" style="text-align: center;">{{ $transaction->created_at }}</td>
<td class="description">{{ $description }}</td>
<td width="10%" class="center top" style="text-align: center;">{{ $marking }}</td>
<td width="15%" class="center top" style="text-align: center;">
{{ $incoming }}
</td>
<td width="20%" class="center top" style="text-align: center;">
{{ $outgoing }}
</td>
<td width="20%" class="center top" style="text-align: center;">
{{ number_format($current_running_balance, $roundOff) }}
</td>
</tr>
@endforeach
</tbody>
</table>
<br>
<htmlpagefooter name="page-footer">
<table width="100%">
<tr>
<td style="text-align: right; ">This is generated by computer. No signature required.</td>
<td style="text-align: right; ">Page {PAGENO} of {nbpg}</td>
</tr>
</table>
</htmlpagefooter>
@endsection
+1
View File
@@ -238,6 +238,7 @@ Route::get('/wallet/{marking}/details', function ($marking) {
})->name('wallet.details');
Route::get('/wallet/{marking}/{is_precise}/export', 'Exports\ExportCustomersWalletTransactionToExcelController@export')->name('wallet.details-export');
Route::get('/company/{id}/{is_precise}/transaction/export', 'Exports\ExportCustomersAccountStatementTransactionToPDFController@export')->name('company.transaction.export');
Route::get('/wallets', function () {
return view('pages.wallet.wallets');