Merge branch 'master' of gitlab.com:CIEFWorldwideSdnBhd/exchange-2.0 into bulk-download-invoices

This commit is contained in:
edmondlang
2023-10-10 23:31:08 +08:00
19 changed files with 396 additions and 368 deletions
@@ -135,7 +135,7 @@ class ApproveDuplicateBankStatementDetailsStatusLogic extends AbstractController
public function logic(Request $request): JsonResponse
{
// Determine the approval status
$status = $this->getApprovalStatus($request);
$status = $this->getConstantStatus($request->route('status'));
// Find the statement transaction owner
$owner = $this->getOwner($request);
@@ -143,21 +143,33 @@ class ApproveDuplicateBankStatementDetailsStatusLogic extends AbstractController
// Update owner status
$this->updateOwnerStatus($owner, $status);
// If the status is 'approved', handle the approval process
if ($status === ApprovalStatus::APPROVED) {
$this->handleApprovedStatus($owner);
}
// handle the siblings process
$this->handleSiblingsStatus($owner, $this->getSiblingsStatus($status));
// Check and approve remaining matches if any
$this->checkAndApproveRemainingMatches($owner);
$this->checkAndApproveRemainingMatches($owner, $status);
// Return an empty response
return $this->response([]);
}
private function getApprovalStatus(Request $request): int
private function getConstantStatus(String $statusName=null): int
{
return $request->route('status') == 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED;
switch ($statusName) {
case 'approve':
return ApprovalStatus::APPROVED;
case 'pending_verification':
return ApprovalStatus::PENDING_VERIFICATION;
default:
return ApprovalStatus::REJECTED;
}
}
private function getSiblingsStatus(int $status): int
{
return $status == ApprovalStatus::APPROVED ? ApprovalStatus::REJECTED : ApprovalStatus::PENDING_VERIFICATION;
}
private function getOwner(Request $request): StatementTransactionOwner
@@ -170,23 +182,24 @@ class ApproveDuplicateBankStatementDetailsStatusLogic extends AbstractController
$this->updatesBankStatementTransactionOwnerStatus->execute($owner, $status);
}
private function handleApprovedStatus(StatementTransactionOwner $owner): void
private function handleSiblingsStatus(StatementTransactionOwner $owner, int $siblingStatus): void
{
// Reject all other owners with the same system, owner type, and owner ID
$this->rejectOtherOwners($owner);
// update all other owners with the same system, owner type, and owner ID
$this->updateOtherOwners($owner, $siblingStatus);
// Find all siblings and process them
$siblings = $this->getSiblings($owner);
$this->processSiblings($siblings);
foreach ($siblings as $sibling) {
$this->processSibling($sibling, $siblingStatus);
}
}
private function rejectOtherOwners(StatementTransactionOwner $owner): void
private function updateOtherOwners(StatementTransactionOwner $owner, int $status): void
{
StatementTransactionOwner::where('system', $owner->system)
->where('owner_type', $owner->owner_type)
->where('owner_id', $owner->owner_id)
StatementTransactionOwner::getSiblingsOwner()
->where('id', '!=', $owner->id)
->update(['status' => ApprovalStatus::REJECTED]);
->update(['status' => $status]);
}
private function getSiblings(StatementTransactionOwner $owner): Collection
@@ -196,86 +209,65 @@ class ApproveDuplicateBankStatementDetailsStatusLogic extends AbstractController
->get();
}
private function processSiblings(Collection $siblings): void
{
foreach ($siblings as $sibling) {
$this->processSibling($sibling);
}
}
private function processSibling(StatementTransactionOwner $sibling): void
private function processSibling(StatementTransactionOwner $sibling, int $siblingStatus): void
{
// Reject the sibling and save the changes
$sibling->status = ApprovalStatus::REJECTED;
$sibling->status = $siblingStatus;
$sibling->save();
// Find all twins and process them
$twins = $this->getTwins($sibling);
$this->processTwins($twins);
}
private function getTwins(StatementTransactionOwner $sibling): Collection
{
return StatementTransactionOwner::where('system', $sibling->system)
->where('owner_type', $sibling->owner_type)
->where('owner_id', $sibling->owner_id)
->where('id', '!=', $sibling->id)
->get();
}
private function processTwins(Collection $twins): void
{
foreach ($twins as $twin) {
$this->processTwin($twin);
}
}
private function getTwins(StatementTransactionOwner $sibling): Collection
{
return StatementTransactionOwner::getSiblingsOwner()
->where('id', '!=', $sibling->id)
->get();
}
private function processTwin(StatementTransactionOwner $twin): void
{
// Find all owners with the same statement transaction ID as the twin
$owners = $this->getOwners($twin);
$owners = $this->getSiblings($twin);
// If there is only one owner (the twin itself), approve it
if ($owners->count() === 1) {
$this->updateOwnerStatus($twin, ApprovalStatus::APPROVED);
$this->updateOwnerStatus($twin, $this->getConstantStatus(request()->route('status')));
}
}
private function getOwners(StatementTransactionOwner $transactionOwner): Collection
{
return StatementTransactionOwner::where('statement_transaction_id', $transactionOwner->statement_transaction_id)
->where('id', '!=', $transactionOwner->id)
->get();
}
private function checkAndApproveRemainingMatches(StatementTransactionOwner $owner): void
private function checkAndApproveRemainingMatches(StatementTransactionOwner $owner, int $status): void
{
// Find all remaining matching owners for the related transaction
$remainingMatches = $this->getRemainingMatches($owner);
$checkStatus = ($status == ApprovalStatus::APPROVED ? ApprovalStatus::PENDING_VERIFICATION : ApprovalStatus::APPROVED);
$remainingMatches = $this->getRemainingMatches($owner, $checkStatus);
// Process each remaining match
foreach ($remainingMatches as $match) {
$this->processRemainingMatch($match);
$this->processRemainingMatch($match, $status);
}
}
private function getRemainingMatches(StatementTransactionOwner $owner): Collection
private function getRemainingMatches(StatementTransactionOwner $owner, int $checkStatus): Collection
{
return StatementTransactionOwner::where('system', $owner->system)
->where('owner_type', $owner->owner_type)
->where('owner_id', $owner->owner_id)
->where('status', ApprovalStatus::PENDING_VERIFICATION)
return StatementTransactionOwner::getSiblingsOwner()
->where('status', $checkStatus)
->get();
}
private function processRemainingMatch(StatementTransactionOwner $match): void
private function processRemainingMatch(StatementTransactionOwner $match, int $status): void
{
// Find all owners with the same statement transaction ID as the match
$owners = $this->getOwners($match);
$owners = $this->getSiblings($match);
// If there is only one owner (the match itself), approve it
if ($owners->count() === 1) {
$this->updateOwnerStatus($match, ApprovalStatus::APPROVED);
$this->updateOwnerStatus($match, $status);
}
}
@@ -57,13 +57,8 @@ class GroupApproveStatementTransactionLogic extends AbstractControllerLogic
public function logic(Request $request): JsonResponse
{
$filters = [
"min_amount" => 0,
"is_mapped" => true,
"is_mapped_with_multiple" => false,
"statement_transaction_owner_type_in" => [1, 2],
"statement_transaction_owner_status_in" => [1]
];
$filters = $request->except(['per_page','order_by']);
$statementTransactions = $this->listsBankStatementTransactions->execute($filters);
foreach ($statementTransactions as $statementTransaction) {
@@ -0,0 +1,111 @@
<?php
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
use App\Classes\Modules\Transactions\Processors\CreatePurchaseOrderTransactionProcessor;
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Http\Resources\TransactionResource;
use App\Models\Booking;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;
class ImportPurchaseOrderTransactionLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Update Purchase Order',
'message' => 'You have successfully updated you booking\'s purchase order'
];
}
/** @var FetchesBooking */
private $fetchesBooking;
/** @var GeneratesTransactionBillNumber */
private $generatesTransactionBillNumber;
/** @var CreatePurchaseOrderTransactionProcessor */
private $createPurchaseOrderTransactionProcessor;
/**
* CreatePurchaseOrderTransactionLogic constructor.
* @param FetchesBooking $fetchesBooking
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
* @param CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor
*/
public function __construct(FetchesBooking $fetchesBooking, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor)
{
$this->fetchesBooking = $fetchesBooking;
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
$this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor;
}
/**
* @param Request $request
* @param string $id
* @return JsonResponse
* @throws \App\Classes\Exceptions\MalformedRequestException
*/
public function logic(Request $request, $id = '') : JsonResponse
{
$files = $request->file('files');
$object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports');
foreach ($object->getFiles() as $file){
$collection = Excel::toCollection(null, json_decode($file)->file_info->original->file, null, null, true);
$sheet = $collection->first()->skip(1);
$products = $sheet->map(function ($row) {
Log::info($row);
$stockCode = $row[0];
$description = $row[1];
$quantity = $row[2];
$unit_price = $row[3];
return [
'stockCode' => $stockCode,
'description' => $description,
'quantity' => $quantity,
'unit_price' => $unit_price
];
})->all();
}
// dd($products);
/** @var Booking $booking */
$booking = $this->fetchesBooking->execute(['id' => $request->route('id') ?? $id]);
$billNumber = $this->generatesTransactionBillNumber->execute('PO-');
$total = collect($products)->sum(function($product){
return $product['quantity'] * floatval(str_replace(',', '', $product['unit_price']));
});
$object = new TransactionObject($billNumber, TransactionType::PURCHASE_ORDER, $booking->company->id, 1,
1, PaymentMethodType::CASH,
$total, $total, $booking->fix_currency_id, $booking->fix_currency_id,
1, 0, 0, null, ApprovalStatus::PENDING_SUBMISSION, $products);
$transaction = $this->createPurchaseOrderTransactionProcessor->execute($booking, $object);
return $this->resourceResponse(new TransactionResource($transaction));
}
}
@@ -8,9 +8,21 @@ final class SystemType {
public const SHIPPING_PORTAL = 'SHIPPING_PORTAL';
public const CNTR = 'CNTR';
public const LITE = 'LITE';
public const PROBASHI = 'PROBASHI';
public const PETS = 'PETS';
public const SYSTEM_NAMES = [
'exchange' => self::EXCHANGE,
'shipping_portal' => self::SHIPPING_PORTAL,
'izyim' => self::SHIPPING_PORTAL,
'lite' => self::LITE,
'cntr' => self::CNTR,
'probashi' => self::PROBASHI,
'pets' => self::PETS
];
}
@@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Transactions;
use App\Classes\Modules\Transactions\ControllersLogic\ImportPurchaseOrderTransactionLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class ImportPurchaseOrderTransactionController
{
/**
* @param Request $request
* @param ImportPurchaseOrderTransactionLogic $logic
* @return JsonResponse
*/
public function import(Request $request, ImportPurchaseOrderTransactionLogic $logic) : JsonResponse {
return $logic->execute($request);
}
}
@@ -21,6 +21,10 @@ class BankStatementDetailResource extends JsonResource
'id' => $this->id,
'date' => Carbon::parse($transaction->posting_date)->format('Y-m-d'),
'transaction_description_1' => $transaction->transaction_description,
'transaction_description_2' => $transaction->transaction_description_2,
'transaction_description_3' => $transaction->transaction_description_3,
'transaction_description_4' => $transaction->transaction_description_4,
'transaction_description_5' => $transaction->transaction_description_5,
'pay_for' => $transaction->transaction_description_2,
'system_references' => $this->system,
'amount' => $transaction->amount,
@@ -16,6 +16,7 @@ class TransactionDetailResource extends JsonResource
{
return [
'id' => $this->id,
'stockCode' => $this->product_code,
'description' => $this->product_name,
'quantity' => $this->quantity,
+6
View File
@@ -26,4 +26,10 @@ class StatementTransactionOwner extends Model
{
return $this->belongsTo(StatementTransaction::class, 'statement_transaction_id', 'id');
}
public function scopeGetSiblingsOwner($query) {
$query->where('system', $this->system)
->where('owner_type', $this->owner_type)
->where('owner_id', $this->owner_id);
}
}
Binary file not shown.
@@ -11,6 +11,36 @@
<div class="col"><a :href="item.owners.pending_verification[0].reference_link" target="_blank">{{item.owners.pending_verification[0].reference}}</a></div>
</div>
</div>
<!-- Pending Export tab -->
<div class="col-5" v-if="item.owners.approved.length === 1">
<div class="row">
<div class="col">{{item.owners.approved[0].system}}</div>
<div class="col">{{ typeString(item.owners.approved[0].type) }}</div>
<div class="col">
<div class="col d-flex justify-content-between">
<a :href="item.owners.approved[0].reference_link" target="_blank">{{item.owners.approved[0].reference}}</a>
<div v-if="stage === 4">
<button class="btn btn-xs btn-outline-danger b-rad-none m-r-5 requestModal" data-type="approveCorrectMappingTransaction">
<i class="fa fa-times fa-fw"></i>
</button>
<modal-component class="animate__animated animate__fast animate__fadeIn" type="approveCorrectMappingTransaction">
<general-confirmation-form-component
:contentText="returnTextRevert(item.owners.approved[0].reference)"
modalType="confirm"
class="text-center"
:apiRoute="route('api.accounting.bankStatement.details.status.update', item.owners.approved[0].id, 'pending_verification')"
apiMethod="post"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
</div>
</div>
</div>
</div>
</div>
<div class="col-5" v-if="item.owners.pending_verification.length > 1">
<div class="row">
<div class="col">
@@ -52,7 +82,8 @@
</div>
</div>
</div>
<div class="col-5" v-if="!item.owners.pending_verification.length">
<div class="col-5" v-if="!item.owners.pending_verification.length && !item.owners.approved.length">
<div class="row">
<div class="col">
<button class="btn btn-xs btn-outline-success b-rad-none m-r-5 requestModal" data-type="updateOwner">
@@ -81,7 +112,7 @@
</div>
<div class="col-1">{{ item.amount }}</div>
<div class="col-1 text-success" v-if="item.owners.approved.length">{{ [6, 7, 8, 9, 10, 11, 12, 13, 14].include(item.owners.approved[0].type) ? 'Miscellaneous' : 'Approved' }}</div>
<div class="col-1 text-success" v-if="item.owners.approved.length">{{ [6, 7, 8, 9, 10, 11, 12, 13, 14].includes(item.owners.approved[0].type) ? 'Miscellaneous' : 'Approved' }}</div>
<div class="col-1 text-danger" v-if="!item.owners.approved.length">Pending...</div>
<div class="col-1" v-if="stage === 1">
<button class="btn btn-xs btn-outline-danger b-rad-none m-r-5 requestModal" data-type="deleteMappingTransaction">
@@ -154,6 +185,9 @@
},
returnTextWithVariable(variable) {
return "Are you sure you want to choose this mapping " + variable + "?";
},
returnTextRevert(variable) {
return "Are you sure you want to revert this mapping " + variable + "?";
}
},
mixins: [componentHandler]
@@ -55,7 +55,7 @@
<div class="col"></div>
<div class="col-auto pointer bold text-danger" @click="step=0">X</div>
</div>
<div class="row parentContainer" v-if="step > 0 && stage !== 4">
<div class="row parentContainer" v-if="step > 0">
<div class="col">
<div class="row">
<div class="col">
@@ -68,6 +68,7 @@
class="text-center"
:apiRoute="route('api.accounting.statement_transaction.owner.groupApprove')"
apiMethod="post"
:params="filter"
:section="section"
>
</general-confirmation-form-component>
@@ -19,7 +19,7 @@
</div>
<div class="row">
<div class="col">
<div class="row" v-for="(product, index) in products">
<div class="row" v-for="(product, index) in products" :key="product.id">
<div class="col p-b-10 p-t-10 " :class="[{'b-grey' : index !== Object.keys(products).length - 1}, {'b-b' : index !== Object.keys(products).length - 1}]">
<purchase-order-item-form-component :data="product" :index="index" :currency="data.fixed_currency.short_code" :editable="!submitted" :section="section" @change="updateProduct($event, index)" v-on:remove="removeProduct(index)"></purchase-order-item-form-component>
</div>
@@ -81,6 +81,25 @@
<button id="add-product" name="add-product" class="btn btn-sm btn-block btn-complete b-rad-none" @click="addProduct()">Add Product</button>
</div>
</div>
<div class="row" v-if="$store.getters.isAdmin">
<div class="col">
<div class="row">
<div class="col p-b-15 pb-md-0">
<file-input-component :validator="$v.files" v-model="files">
<template slot="label">
<div class="font-heading fs-11 text-primary all-caps">Import Product CSV</div>
<div class="font-heading fs-9 all-caps muted text-underline"><a href="/template/import-po-template.xlsx" class="muted" download>Click here to download the template</a></div>
</template>
</file-input-component>
</div>
</div>
<div class="row">
<div class="col">
<button type="button" class="btn btn-sm btn-block p-t-10 p-b-10 p-r-35 p-l-35 btn-success b-rad-none" @click="uploadProducts">Upload</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -175,6 +194,8 @@
</template>
<script>
import formHandler from '../../../general/mixins/formHandler';
import { required, requiredIf } from "vuelidate/lib/validators";
export default {
data(){
return {
@@ -187,6 +208,13 @@
unit_price: 0,
},
products: [],
files: [],
uploadFiles: false,
}
},
validations: {
files: {
required: requiredIf(function () { return this.uploadFiles })
}
},
created() {
@@ -203,6 +231,11 @@
}, 0);
}
},
watch: {
'data': function () {
this.products = this.data.purchase_order.details
}
},
methods: {
updateQuantity(type){
if(!this.interval){
@@ -225,13 +258,21 @@
},
submitForm(){
this.uploadFiles = false;
this.parameters = {
products: this.products
};
this.submit(route('api.transaction.po.create', this.data.id), 'post', this.section, true, true);
},
uploadProducts() {
this.uploadFiles = true
this.parameters = {
files: this.files
};
this.submit(route('api.transaction.po.import', this.data.id), 'post', this.section, true, true);
},
successHandler(){
if((Math.round((this.poTotal + Number.EPSILON) * 1000) / 1000).toFixed(3) === (Math.round((this.data.amount + Number.EPSILON) * 1000) / 1000).toFixed(3)){
this.submitted = true;
@@ -15,7 +15,7 @@
<div class="btn btn-sm btn-default btn-block bg-master-lighter" data-dismiss="modal">Cancel</div>
</div>
<div class="col p-l-5">
<div class="btn btn-sm btn-block b-rad-none" :class="[{'btn-danger': modalType !== 'confirm'}, {'btn-success': modalType === 'confirm'}]" @click="submit(apiRoute, apiMethod, section, true, true)">{{ buttonText }}</div>
<div class="btn btn-sm btn-block b-rad-none" :class="[{'btn-danger': modalType !== 'confirm'}, {'btn-success': modalType === 'confirm'}]" @click="submitForm()">{{ buttonText }}</div>
</div>
</div>
</div>
@@ -32,6 +32,10 @@
type: String,
required: true
},
params: {
type: Array,
required: false
},
modalType: {
type: String,
default: 'confirm'
@@ -50,6 +54,10 @@
},
},
methods: {
submitForm() {
if (this.params) this.parameters = this.params;
return this.submit(this.apiRoute, this.apiMethod, this.section, true, true);
}
},
mixins: [componentHandler, ModalFormHandler]
@@ -69,109 +69,8 @@
<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%">Stock Code</th>
<th class="description">Description</th>
<th width="10%">Quantity</th>
<th width="15%">Unit Price (RM)</th>
<th width="10%">Total Amount<br>(RM)</th>
</tr>
</thead>
<tbody>
@php
$subtotal = "0";
$voucherDiscount = $voucher_redemption ? bcmul((string)$voucher_redemption->value, "-1", 5) : "0";
$displayedSubtotal = "0";
$exactTotal = "0";
@endphp
@include('pages.pdfs.purchase_order_table')
@foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail)
@php
$exactUnitPrice = bcdiv($transaction_detail->price, $transaction->currency_rate, 5);
$itemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 5);
// Round half to even for displayed item total
$displayedItemTotal = round(bcmul($exactUnitPrice, $transaction_detail->quantity, 2), 2, PHP_ROUND_HALF_EVEN);
$displayedSubtotal = bcadd($displayedSubtotal, $displayedItemTotal, 2);
$subtotal = bcadd($subtotal, $itemTotal, 5);
$exactTotal = bcadd($exactTotal, $displayedItemTotal, 5);
@endphp
<tr>
<td width="5%" class="center top">{{ $key + 1 }}</td>
<td class="stock-code top" width="10%">{{ $transaction_detail->product_code }}</td>
<td class="description">{{ $transaction_detail->product_name }}</td>
<td width="10%" class="center top">{{ $transaction_detail->quantity }}</td>
<td width="15%" class="center top">
{{ number_format($exactUnitPrice, 2) }}
</td>
<td width="20%" class="right top">
{{ number_format($itemTotal, 2) }}
</td>
</tr>
@endforeach
</tbody>
<tfoot>
@php
$subtotalWithDiscount = bcsub($subtotal, $voucherDiscount, 5);
@endphp
<tr class="subtotal">
<td colspan="4"></td>
<td class="right middle">Subtotal</td>
<td class="right middle">{{ number_format($subtotal, 2) }}</td>
</tr>
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Service Charges</td>
<td class="right">{{ number_format($transaction->service_charge, 2) }}</td>
</tr>
@if($voucher_redemption)
<tr class="voucher">
<td colspan="4"></td>
<td class="right middle">Voucher ({{ $voucher_redemption->voucher->code }})</td>
<td class="right middle">-{{ number_format($voucherDiscount, 2) }}</td>
</tr>
@endif
@if($transaction->tax > 0)
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Tax</td>
<td class="right">{{ number_format($transaction->tax, 2) }}</td>
</tr>
@endif
@php
// Calculate the totals with 5 decimal places
$expectedTotal = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
// Calculate the displayed totals with 2 decimal places
$displayedTotal = bcadd(bcadd(bcadd($displayedSubtotal, $transaction->service_charge, 2), $transaction->tax, 2), $voucherDiscount, 2);
// Calculate the discrepancy
$discrepancy = bcsub($expectedTotal, $displayedTotal, 5);
// Calculate the final total
$total = bcadd($expectedTotal, $discrepancy, 5);
@endphp
<tr>
<td colspan="4"></td>
<td class="right middle">Adjustment</td>
<td class="right middle">{{number_format($discrepancy, 5)}}</td>
</tr>
<tr>
<td colspan="4"></td>
<td class="right middle">Total</td>
<td class="total right middle">
{{ number_format($total, 2) }}
</td>
</tr>
</tfoot>
</table>
<htmlpagefooter name="page-footer">
<table width="100%">
<tr>
+1 -90
View File
@@ -58,97 +58,8 @@
<br>
<!-- Invoice Table -->
<table class="line-table" style="overflow: wrap" autosize="1">
<!-- Table Header -->
<thead>
<tr>
<th width="5%">No</th>
<th class="stock-code" width="10%">Stock Code</th>
<th class="description">Description</th>
<th width="10%">Quantity</th>
<th width="15%">Unit Price (RM)</th>
<th width="10%">Total Amount<br>(RM)</th>
</tr>
</thead>
<tbody>
@php
$subtotal = "0";
$voucherDiscount = $voucher_redemption ? bcmul((string)$voucher_redemption->value, "-1", 2) : "0";
$displayedSubtotal = 0;
@endphp
@include('pages.pdfs.purchase_order_table')
@foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail)
@php
$exactUnitPrice = bcdiv($transaction_detail->price, $transaction->currency_rate, 7);
$itemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 5);
$displayedItemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 2);
$displayedSubtotal = bcadd($displayedSubtotal, $displayedItemTotal, 2);
$subtotal = bcadd($subtotal, $itemTotal, 5);
@endphp
<tr>
<td width="5%" class="center top">{{ $key + 1 }}</td>
<td class="stock-code top" width="10%">{{ $transaction_detail->product_code }}</td>
<td class="description">{{ $transaction_detail->product_name }}</td>
<td width="10%" class="center top">{{ $transaction_detail->quantity }}</td>
<td width="15%" class="center top">
{{ number_format($exactUnitPrice, 2) }}
</td>
<td width="20%" class="right top">
{{ number_format($itemTotal, 2) }}
</td>
</tr>
@endforeach
</tbody>
<tfoot>
@php
$subtotalWithDiscount = bcsub($subtotal, $voucherDiscount, 5);
@endphp
<tr class="subtotal">
<td colspan="4"></td>
<td class="right middle">Subtotal</td>
<td class="right middle">{{ number_format($subtotal, 2) }}</td>
</tr>
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Service Charges</td>
<td class="right">{{ number_format($transaction->service_charge, 2) }}</td>
</tr>
@if($voucher_redemption)
<tr class="voucher">
<td colspan="4"></td>
<td class="right middle">Voucher ({{ $voucher_redemption->voucher->code }})</td>
<td class="right middle">-{{ number_format($voucherDiscount, 2) }}</td>
</tr>
@endif
@if($transaction->tax > 0)
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Tax</td>
<td class="right">{{ number_format($transaction->tax, 2) }}</td>
</tr>
@endif
@php
$displayedTotal = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
$expectedTotal = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
$discrepancy = bcsub($displayedTotal, $expectedTotal, 5);
$total = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
@endphp
<tr>
<td colspan="4"></td>
<td class="right middle">Adjustment</td>
<td class="right middle">{{number_format($discrepancy, 5)}}</td>
</tr>
<tr>
<td colspan="4"></td>
<td class="right middle">Total</td>
<td class="total right middle">
{{ number_format($total, 2) }}
</td>
</tr>
</tfoot>
</table>
<br>
<div class="note">
<strong>Note:</strong> All items purchased are subject to our Terms & Conditions. Please refer to our official website for more information.
@@ -76,109 +76,8 @@
<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%">Stock Code</th>
<th class="description">Description</th>
<th width="10%">Quantity</th>
<th width="15%">Unit Price (RM)</th>
<th width="10%">Total Amount<br>(RM)</th>
</tr>
</thead>
<tbody>
@php
$subtotal = "0";
$voucherDiscount = $voucher_redemption ? bcmul((string)$voucher_redemption->value, "-1", 5) : "0";
$displayedSubtotal = "0";
$exactTotal = "0";
@endphp
@include('pages.pdfs.purchase_order_table')
@foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail)
@php
$exactUnitPrice = bcdiv($transaction_detail->price, $transaction->currency_rate, 5);
$itemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 5);
// Round half to even for displayed item total
$displayedItemTotal = round(bcmul($exactUnitPrice, $transaction_detail->quantity, 2), 2, PHP_ROUND_HALF_EVEN);
$displayedSubtotal = bcadd($displayedSubtotal, $displayedItemTotal, 2);
$subtotal = bcadd($subtotal, $itemTotal, 5);
$exactTotal = bcadd($exactTotal, $displayedItemTotal, 5);
@endphp
<tr>
<td width="5%" class="center top">{{ $key + 1 }}</td>
<td class="stock-code top" width="10%">{{ $transaction_detail->product_code }}</td>
<td class="description">{{ $transaction_detail->product_name }}</td>
<td width="10%" class="center top">{{ $transaction_detail->quantity }}</td>
<td width="15%" class="center top">
{{ number_format($exactUnitPrice, 2) }}
</td>
<td width="20%" class="right top">
{{ number_format($itemTotal, 2) }}
</td>
</tr>
@endforeach
</tbody>
<tfoot>
@php
$subtotalWithDiscount = bcsub($subtotal, $voucherDiscount, 5);
@endphp
<tr class="subtotal">
<td colspan="4"></td>
<td class="right middle">Subtotal</td>
<td class="right middle">{{ number_format($subtotal, 2) }}</td>
</tr>
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Service Charges</td>
<td class="right">{{ number_format($transaction->service_charge, 2) }}</td>
</tr>
@if($voucher_redemption)
<tr class="voucher">
<td colspan="4"></td>
<td class="right middle">Voucher ({{ $voucher_redemption->voucher->code }})</td>
<td class="right middle">-{{ number_format($voucherDiscount, 2) }}</td>
</tr>
@endif
@if($transaction->tax > 0)
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Tax</td>
<td class="right">{{ number_format($transaction->tax, 2) }}</td>
</tr>
@endif
@php
// Calculate the totals with 5 decimal places
$expectedTotal = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
// Calculate the displayed totals with 2 decimal places
$displayedTotal = bcadd(bcadd(bcadd($displayedSubtotal, $transaction->service_charge, 2), $transaction->tax, 2), $voucherDiscount, 2);
// Calculate the discrepancy
$discrepancy = bcsub($expectedTotal, $displayedTotal, 5);
// Calculate the final total
$total = bcadd($expectedTotal, $discrepancy, 5);
@endphp
<tr>
<td colspan="4"></td>
<td class="right middle">Adjustment</td>
<td class="right middle">{{number_format($discrepancy, 5)}}</td>
</tr>
<tr>
<td colspan="4"></td>
<td class="right middle">Total</td>
<td class="total right middle">
{{ number_format($total, 2) }}
</td>
</tr>
</tfoot>
</table>
<htmlpagefooter name="page-footer">
<table width="100%">
<tr>
@@ -0,0 +1,92 @@
<table class="line-table" style="overflow: wrap" autosize="1">
<!-- Table Header -->
<thead>
<tr>
<th width="5%">No</th>
<th class="stock-code" width="10%">Stock Code</th>
<th class="description">Description</th>
<th width="10%">Quantity</th>
<th width="15%">Unit Price (RM)</th>
<th width="10%">Total Amount<br>(RM)</th>
</tr>
</thead>
<tbody>
@php
$subtotal = "0";
$voucherDiscount = $voucher_redemption ? bcmul((string)$voucher_redemption->value, "-1", 2) : "0";
$displayedSubtotal = 0;
@endphp
@foreach ($po_order_transaction->transactionDetails as $key => $transaction_detail)
@php
$exactUnitPrice = bcdiv($transaction_detail->price, $transaction->currency_rate, 7);
$displayUnitPrice = round($exactUnitPrice, 2);
$itemTotal = bcmul($exactUnitPrice, $transaction_detail->quantity, 5);
$displayedItemTotal = round(bcmul($displayUnitPrice, $transaction_detail->quantity, 7), 2);
$displayedSubtotal = bcadd($displayedSubtotal, $displayedItemTotal, 2);
$subtotal = bcadd($subtotal, $itemTotal, 5);
@endphp
<tr>
<td width="5%" class="center top">{{ $key + 1 }}</td>
<td class="stock-code top" width="10%">{{ $transaction_detail->product_code }}</td>
<td class="description">{{ $transaction_detail->product_name }}</td>
<td width="10%" class="center top">{{ $transaction_detail->quantity }}</td>
<td width="15%" class="center top">
{{ number_format($displayUnitPrice, 2) }}
</td>
<td width="20%" class="right top">
{{ number_format($displayedItemTotal, 2) }}
</td>
</tr>
@endforeach
</tbody>
<tfoot>
@php
$subtotalWithDiscount = bcsub($subtotal, $voucherDiscount, 5);
@endphp
<tr class="subtotal">
<td colspan="4"></td>
<td class="right middle">Subtotal</td>
<td class="right middle">{{ number_format($displayedSubtotal, 2) }}</td>
</tr>
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Service Charges</td>
<td class="right">{{ number_format($transaction->service_charge, 2) }}</td>
</tr>
@if($voucher_redemption)
<tr class="voucher">
<td colspan="4"></td>
<td class="right middle">Voucher ({{ $voucher_redemption->voucher->code }})</td>
<td class="right middle">-{{ number_format($voucherDiscount, 2) }}</td>
</tr>
@endif
@if($transaction->tax > 0)
<tr class="billingcharges">
<td colspan="4"></td>
<td class="right">Tax</td>
<td class="right">{{ number_format($transaction->tax, 2) }}</td>
</tr>
@endif
@php
$displayedTotal = bcadd(bcadd(bcadd($displayedSubtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
$expectedTotal = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
$discrepancy = bcsub($expectedTotal, $displayedTotal, 5);
$total = bcadd(bcadd(bcadd($subtotal, $transaction->service_charge, 5), $transaction->tax, 5), $voucherDiscount, 5);
@endphp
<tr>
<td colspan="4"></td>
<td class="right middle">Adjustment</td>
<td class="right middle">{{number_format($discrepancy, 5)}}</td>
</tr>
<tr>
<td colspan="4"></td>
<td class="right middle">Total</td>
<td class="total right middle">
{{ number_format($total, 2) }}
</td>
</tr>
</tfoot>
</table>
+1 -1
View File
@@ -10,7 +10,7 @@ Route::group(['prefix' => 'accounting', 'as' => 'accounting.', 'namespace' => 'A
Route::put('/details/update', 'BankStatementController@update')->name('details.update');
});
Route::post('bankStatement/{id}/details/{status}', 'ApproveDuplicateBankStatementDetailsStatusController@update')->where('status', 'approve|reject')->name('bankStatement.details.status.update');
Route::post('bankStatement/{id}/details/{status}', 'ApproveDuplicateBankStatementDetailsStatusController@update')->where('status', 'approve|reject|pending_verification')->name('bankStatement.details.status.update');
Route::group(['prefix' => 'statement_transaction', 'as' => 'statement_transaction.'], function () {
Route::post('/owner/group-approve', 'GroupApproveStatementTransactionController@approve')->name('owner.groupApprove');
+1
View File
@@ -16,6 +16,7 @@ Route::group(['prefix' => 'transactions', 'namespace' => 'Transactions', 'as' =>
route::delete('{id}/bill/delete', 'DeletePaymentProofDocumentController@delete')->name('bill.delete');
Route::post('booking/{id}/details/update', 'CreatePurchaseOrderTransactionController@create')->name('po.create');
Route::post('booking/{id}/details/import', 'ImportPurchaseOrderTransactionController@import')->name('po.import');
Route::get('bulk/po/{issuer_id}/{start_date}/{end_date}', 'CreateBulkPurchaseOrderTransactionController@create')->name('po.bulk.create');