mirror of
https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal.git
synced 2026-08-19 04:24:12 +00:00
20231109 Meeting Feedback 1 Fixes for warehouse storage charges
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\General\Eloquent\Filters;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class OrderByIdDesc implements Filter
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Builder $builder
|
||||
* @param $value
|
||||
* @return Builder|mixed
|
||||
*/
|
||||
public static function apply(Builder $builder, $value)
|
||||
{
|
||||
return $builder->orderBy('id', 'desc');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\General\Eloquent\Filters;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class OrderByUpdatedAtDesc implements Filter
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Builder $builder
|
||||
* @param $value
|
||||
* @return Builder|mixed
|
||||
*/
|
||||
public static function apply(Builder $builder, $value)
|
||||
{
|
||||
return $builder->orderBy('updated_at', 'desc');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ use App\Classes\Modules\Billplzs\Processors\CallbackBillplzProcessor;
|
||||
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
|
||||
use App\Classes\Modules\Transactions\Processors\CreatePaymentTransactionProcessor;
|
||||
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
|
||||
use App\Classes\Modules\Transactions\Processors\CheckAndCreateStorageInvoiceTransactionProcessor;
|
||||
use App\Classes\ValueObjects\Constants\PaymentMethodType;
|
||||
use App\Classes\ValueObjects\Constants\TransactionType;
|
||||
use App\Models\Group;
|
||||
@@ -51,6 +52,9 @@ class CallbackBillplzLogic
|
||||
/** @var CallbackBillplzProcessor */
|
||||
private $callbackBillplzProcessor;
|
||||
|
||||
/** @var CheckAndCreateStorageInvoiceTransactionProcessor */
|
||||
private $storageInvoiceTransactionProcessor;
|
||||
|
||||
/**
|
||||
* CallbackBillplzLogic constructor.
|
||||
* @param GetBillplzBill $getBillplzBill
|
||||
@@ -60,8 +64,9 @@ class CallbackBillplzLogic
|
||||
* @param UpdateDoFromYDPortalProcessor $updateDoFromYDPortalProcessor
|
||||
* @param CreatePaymentTransactionProcessor $createPaymentTransactionProcessor
|
||||
* @param CallbackBillplzProcessor $callbackBillplzProcessor
|
||||
* @param CheckAndCreateStorageInvoiceTransactionProcessor $storageInvoiceTransactionProcessor
|
||||
*/
|
||||
public function __construct(GetBillplzBill $getBillplzBill, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, UpdateDoFromVTPortalProcessor $updateDoFromVTPortalProcessor, UpdateDoFromYDPortalProcessor $updateDoFromYDPortalProcessor, UpdatesWalletBalance $updatesWalletBalance, CreatePaymentTransactionProcessor $createPaymentTransactionProcessor, CallbackBillplzProcessor $callbackBillplzProcessor)
|
||||
public function __construct(GetBillplzBill $getBillplzBill, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, UpdateDoFromVTPortalProcessor $updateDoFromVTPortalProcessor, UpdateDoFromYDPortalProcessor $updateDoFromYDPortalProcessor, UpdatesWalletBalance $updatesWalletBalance, CreatePaymentTransactionProcessor $createPaymentTransactionProcessor, CallbackBillplzProcessor $callbackBillplzProcessor, CheckAndCreateStorageInvoiceTransactionProcessor $storageInvoiceTransactionProcessor)
|
||||
{
|
||||
$this->getBillplzBill = $getBillplzBill;
|
||||
$this->fetchesTransaction = $fetchesTransaction;
|
||||
@@ -71,6 +76,7 @@ class CallbackBillplzLogic
|
||||
$this->updatesWalletBalance = $updatesWalletBalance;
|
||||
$this->createPaymentTransactionProcessor = $createPaymentTransactionProcessor;
|
||||
$this->callbackBillplzProcessor = $callbackBillplzProcessor;
|
||||
$this->storageInvoiceTransactionProcessor = $storageInvoiceTransactionProcessor;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +121,8 @@ class CallbackBillplzLogic
|
||||
$token = Auth::fromUser(User::find(1));
|
||||
$request->headers->set('Authorization', 'Bearer '.$token);
|
||||
|
||||
$this->callbackBillplzProcessor->execute($transaction, $status);
|
||||
$this->storageInvoiceBackDoorPreventionCheck($transaction, $status);
|
||||
$result = $this->callbackBillplzProcessor->execute($transaction, $status);
|
||||
|
||||
$company_module_marking = $transaction->owner->owner->connections? $transaction->owner->owner->connections->first()->invitee_reference: null;
|
||||
|
||||
@@ -126,6 +133,22 @@ class CallbackBillplzLogic
|
||||
$company_module_marking = $order->companyModule->connections? $order->companyModule->connections->first()->invitee_reference: null;
|
||||
}
|
||||
|
||||
return $request->method() === 'POST' ? true : view('pages.payments_redirect', ['marking' => $order->reference ?? null, 'company_module_marking' => $company_module_marking ?? null, 'transaction' => $transaction, 'status' => $status]);
|
||||
return $request->method() === 'POST' ? true : view('pages.payments_redirect', ['marking' => $order->reference ?? null, 'company_module_marking' => $company_module_marking ?? null, 'transaction' => $transaction, 'status' => $status, 'result' => $result]);
|
||||
}
|
||||
|
||||
private function storageInvoiceBackDoorPreventionCheck($transaction, $status){
|
||||
if ($transaction->owner instanceof Wallet && $status === ApprovalStatus::APPROVED) {
|
||||
$group = Group::where('reference', $transaction->payment_reference)->first();
|
||||
if ($group) {
|
||||
foreach ($group->groupTransactions as $groupTransaction) {
|
||||
$invoice = $groupTransaction->transaction;
|
||||
$pL = $invoice->owner;
|
||||
$order = $pL->owner;
|
||||
if($order){
|
||||
$this->storageInvoiceTransactionProcessor->executeOrder($order, false); //original was set true here so that no group is soft deleted or billplz bill got deleted
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,9 @@ class CallbackBillplzProcessor
|
||||
$invoice = $transaction->owner;
|
||||
$packingList = $invoice->owner;
|
||||
|
||||
$proceed = $this->checkAmountPaidVSRequired($transaction);
|
||||
if(!$proceed) return false;
|
||||
|
||||
$this->updatesTransactionStatus->execute($transaction, $status);
|
||||
|
||||
// check if is wallet top up
|
||||
@@ -95,13 +98,39 @@ class CallbackBillplzProcessor
|
||||
if (!$transaction->owner instanceof Wallet) {
|
||||
$this->processPackingListAndInvoice($packingList, $invoice);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkAmountPaidVSRequired($transaction){
|
||||
//command:check-storage-invoices must already run for this part of the code to work properly
|
||||
$group = Group::withTrashed()->where('reference', $transaction->payment_reference)->first();
|
||||
if ($group) {
|
||||
$totalAmountToBePaid = 0;
|
||||
$actualAmountPaid = $transaction->amount;
|
||||
|
||||
foreach ($group->groupTransactions as $groupTransaction) {
|
||||
$invoice = $groupTransaction->transaction;
|
||||
$totalAmountToBePaid += $invoice->amount;
|
||||
}
|
||||
|
||||
if(($totalAmountToBePaid - $actualAmountPaid) < 0.01){
|
||||
|
||||
}
|
||||
else{
|
||||
Log::channel('storage_invoices')->info('Total amount from current transaction: '.$totalAmountToBePaid); //cief todo: to be removed
|
||||
Log::channel('storage_invoices')->info('Total amount from paid transaction: '.$transaction->amount); //cief todo: to be removed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function processPackingListAndInvoice($packingList, $invoice = null){
|
||||
$result = false;
|
||||
$totalInvoicesAmountPaid = 0.00;
|
||||
$totalInvoicesAmount = 0.00;
|
||||
$invoiceTransactions = $packingList->transactions()->whereIn('type', [TransactionType::SHIPPING_INVOICE, TransactionType::STORAGE_INVOICE])->get();
|
||||
$invoiceTransactions = $packingList->transactions()->where('status', [ApprovalStatus::APPROVED])->whereIn('type', [TransactionType::SHIPPING_INVOICE, TransactionType::STORAGE_INVOICE])->get();
|
||||
|
||||
//Part 1: Process each single invoice type and get the total of all invoices
|
||||
foreach($invoiceTransactions as $invoiceTransaction){
|
||||
@@ -121,12 +150,12 @@ class CallbackBillplzProcessor
|
||||
$totalInvoicesAmountPaid = $totalInvoicesAmountPaid + $totalInvoiceAmountPaid;
|
||||
}
|
||||
|
||||
Log::info('Total invoice amount paid 1: '.$totalInvoicesAmount); //cief todo: to be removed
|
||||
Log::info('Total invoice amount paid 2: '.$totalInvoicesAmountPaid); //cief todo: to be removed
|
||||
Log::channel('storage_invoices')->info('Total invoice amount paid 1: '.$totalInvoicesAmount); //cief todo: to be removed
|
||||
Log::channel('storage_invoices')->info('Total invoice amount paid 2: '.$totalInvoicesAmountPaid); //cief todo: to be removed
|
||||
|
||||
//Part 2: Based on the collected info for all the total of all invoices
|
||||
if (($totalInvoicesAmount - $totalInvoicesAmountPaid) < 0.01) {
|
||||
Log::info('Total invoice amount paid 3: '.$totalInvoicesAmountPaid); //cief todo: to be removed
|
||||
Log::channel('storage_invoices')->info('Total invoice amount paid 3: '.$totalInvoicesAmountPaid); //cief todo: to be removed
|
||||
|
||||
$packingList->status = ApprovalStatus::APPROVED;
|
||||
$packingList->save();
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Billplzs\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use App\Classes\Exceptions\MalformedRequestException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class DeletesBillplzBill
|
||||
{
|
||||
|
||||
/**
|
||||
* @param string $billID
|
||||
* @throws MalformedRequestException
|
||||
*/
|
||||
public function execute(string $billID) {
|
||||
try{
|
||||
$response = Http::withBasicAuth(config('billplz.api_key').':', '')->delete(config('billplz.base_url').'/api/v3/bills/'.$billID);
|
||||
Log::channel('storage_invoices')->info('DeletesBillplzBill response: '.json_encode($response));
|
||||
|
||||
if($response->successful()){
|
||||
$data = $response->json();
|
||||
|
||||
// $data['url'] = $data['url'].'?auto_submit=true';
|
||||
|
||||
return (object) $data;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}catch(\Exception $exception){
|
||||
throw new MalformedRequestException('Unable to get correct response from billplz server: ' . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,8 +52,10 @@ class FetchOrderV2Logic extends AbstractControllerLogic
|
||||
$this->canFetchOrder->passes();
|
||||
|
||||
$query = $this->fetchesOrder->execute(['reference' => $request->route('id'), 'with_packing_lists' => true]);
|
||||
$query->storages = $request->input('storages'); //from middleware
|
||||
|
||||
if($request->input('storages')){
|
||||
$query->storages = $request->input('storages'); //from middleware
|
||||
}
|
||||
|
||||
return $this->resourceResponse(new OrderV2Resource($query));
|
||||
|
||||
|
||||
+1
-1
@@ -121,7 +121,7 @@ class FetchContainersFromYdPortalProcessor
|
||||
$containerReference = explode('预计到港时间', $tracking[1])[0];
|
||||
$loadingDate = Carbon::parse($trackingRow->trackingtime);
|
||||
$etd = Carbon::parse($tracking[2])->subDays(5);
|
||||
$eta = Carbon::parse($tracking[2])->addDays(2);
|
||||
// $eta = Carbon::parse($tracking[2])->addDays(2); //omair to review
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ class FetchContainersUpdatesFromYdPortalProcessor
|
||||
|
||||
if($delayDate){
|
||||
|
||||
$delayDate = $delayDate->addDays(2);
|
||||
// $delayDate = $delayDate->addDays(2); //omair to review
|
||||
$transport = $container->transports()->first();
|
||||
|
||||
if(!$transport->schedules()->whereDate('eta', '>=', $delayDate)->first()) {
|
||||
|
||||
@@ -5,10 +5,11 @@ namespace App\Classes\Modules\Transactions\ControllersLogic;
|
||||
|
||||
use App\Classes\General\Abstracts\AbstractControllerLogic;
|
||||
use App\Classes\Modules\Transactions\Services\ListsTransactions;
|
||||
use App\Http\Resources\BookingResource;
|
||||
use App\Http\Resources\TransactionResource;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
|
||||
class ListTransactionsLogic extends AbstractControllerLogic
|
||||
{
|
||||
@@ -38,13 +39,18 @@ class ListTransactionsLogic extends AbstractControllerLogic
|
||||
|
||||
public function logic(Request $request) : JsonResponse
|
||||
{
|
||||
|
||||
$query = $this->listsTransactions->execute($this->listsTransactions->deserializeFilters($request->input('filters')));
|
||||
|
||||
if($request->input('storages')){
|
||||
foreach ($query->items() as &$item) {
|
||||
$transactionId = $item['id'];
|
||||
$filteredStorages = array_filter($request->input('storages'), function ($storage) use ($transactionId) {
|
||||
return isset($storage['parentInvoiceId']) && $storage['parentInvoiceId'] == $transactionId;
|
||||
});
|
||||
$item['storages'] = $filteredStorages;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->collectionResponse(TransactionResource::collection($query));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+114
-59
@@ -13,6 +13,7 @@ use App\Classes\Modules\Transactions\Services\FetchesTransaction;
|
||||
use App\Classes\Modules\Transactions\Services\UpdatesTransactionDetail;
|
||||
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
|
||||
use App\Classes\Modules\Transactions\Services\DeletesGroup;
|
||||
use App\Classes\Modules\Billplzs\Services\DeletesBillplzBill;
|
||||
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
|
||||
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionDetailObject;
|
||||
|
||||
@@ -22,7 +23,8 @@ use App\Classes\ValueObjects\Constants\PaymentMethodType;
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Classes\ValueObjects\Constants\PackageType;
|
||||
use App\Classes\ValueObjects\Constants\TransactionDetailType;
|
||||
|
||||
use App\Http\Resources\TransactionResource;
|
||||
use App\Models\Order;
|
||||
use App\Models\PackingList;
|
||||
use App\Models\Transaction;
|
||||
use Carbon\Carbon;
|
||||
@@ -58,6 +60,9 @@ class CheckAndCreateStorageInvoiceTransactionProcessor
|
||||
/** @var DeletesGroup */
|
||||
private $deletesGroup;
|
||||
|
||||
/** @var DeletesBillplzBill */
|
||||
private $deletesBillplzBill;
|
||||
|
||||
/**
|
||||
* @param FetchesOrder $fetchesOrder
|
||||
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
|
||||
@@ -68,8 +73,9 @@ class CheckAndCreateStorageInvoiceTransactionProcessor
|
||||
* @param UpdatesTransactionDetail $updatesTransactionDetail
|
||||
* @param UpdatesTransactionStatus $updatesTransactionStatus
|
||||
* @param DeletesGroup $deletesGroup
|
||||
* @param DeletesBillplzBill $deletesBillplzBill
|
||||
*/
|
||||
public function __construct(FetchesOrder $fetchesOrder, GeneratesTransactionBillNumber $generatesTransactionBillNumber, FetchesTransaction $fetchesTransaction, CreatesTransaction $createsTransaction, CreatesTransactionDetail $createsTransactionDetail, UpdatesTransaction $updatesTransaction, UpdatesTransactionDetail $updatesTransactionDetail, UpdatesTransactionStatus $updatesTransactionStatus, DeletesGroup $deletesGroup)
|
||||
public function __construct(FetchesOrder $fetchesOrder, GeneratesTransactionBillNumber $generatesTransactionBillNumber, FetchesTransaction $fetchesTransaction, CreatesTransaction $createsTransaction, CreatesTransactionDetail $createsTransactionDetail, UpdatesTransaction $updatesTransaction, UpdatesTransactionDetail $updatesTransactionDetail, UpdatesTransactionStatus $updatesTransactionStatus, DeletesGroup $deletesGroup, DeletesBillplzBill $deletesBillplzBill)
|
||||
{
|
||||
$this->fetchesOrder = $fetchesOrder;
|
||||
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
|
||||
@@ -80,50 +86,72 @@ class CheckAndCreateStorageInvoiceTransactionProcessor
|
||||
$this->updatesTransactionDetail = $updatesTransactionDetail;
|
||||
$this->updatesTransactionStatus = $updatesTransactionStatus;
|
||||
$this->deletesGroup = $deletesGroup;
|
||||
$this->deletesBillplzBill = $deletesBillplzBill;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws MalformedRequestException
|
||||
*/
|
||||
public function execute(int $orderId)
|
||||
public function execute(int $orderReference)
|
||||
{
|
||||
$multipleResults = array();
|
||||
$order = $this->fetchesOrder->execute(['reference' => $orderId, 'with_packing_lists' => true]);
|
||||
$order = $this->fetchesOrder->execute(['reference' => $orderReference, 'with_packing_lists' => true]);
|
||||
return $this->executeOrder($order);
|
||||
}
|
||||
|
||||
$eta = "";
|
||||
$destinationWarehousePackages = $order->destinationWarehousePackages;
|
||||
foreach ($destinationWarehousePackages as $destinationWarehousePackage){
|
||||
if ($destinationWarehousePackage) {
|
||||
$package = $destinationWarehousePackage->packages->first();
|
||||
public function executeOrder(Order $order, bool $isBackDoorCheck = false){
|
||||
$results = [];
|
||||
$marking = $order->companyModule->inviters()->withPivot('invitee_reference')->first()->pivot->invitee_reference;
|
||||
$is_credit_term = $order->companyModule->inviters()->withPivot('is_credit_term')->first()->pivot->is_credit_term;
|
||||
if(!$is_credit_term){
|
||||
Log::channel('storage_invoices')->info('orderId: '.$order->id.', orderReference: '.$order->reference.', marking: '.$marking.', is_credit_term: '.$is_credit_term);
|
||||
$packingLists = $order->destinationWarehousePackages;
|
||||
foreach ($packingLists as $packingList){
|
||||
Log::channel('storage_invoices')->info('destinationWarehousePackage: '.json_encode($packingList));
|
||||
$eta = $this->getEtaFromPackingList($packingList);
|
||||
if($eta){
|
||||
$transactions = $packingList->transactions()->where('transactions.type', TransactionType::SHIPPING_INVOICE)->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->get();
|
||||
// $transactions = $destinationWarehousePackage->transactions()->where('transactions.type', TransactionType::SHIPPING_INVOICE)->where('transactions.status', ApprovalStatus::APPROVED)->get();
|
||||
|
||||
/** @var Transaction $invoice_transaction */
|
||||
foreach ($transactions as $invoice_transaction){
|
||||
$result = $this->processSingleTransactionOfTypeShippingInvoice($invoice_transaction, $packingList, $order->company_module_id, $eta, $isBackDoorCheck);
|
||||
if($result){
|
||||
$results[] = $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function getEtaFromPackingList($packingList){
|
||||
if ($packingList) {
|
||||
$package = $packingList->packages->first();
|
||||
if ($package) {
|
||||
$container = $package->container()->first();
|
||||
if ($container) {
|
||||
$transport = $container->transports->first();
|
||||
if ($transport) {
|
||||
// $container->transports()->first()->update(['drop_date' => $unstuffingDate, 'status' => ApprovalStatus::COMPLETED]);
|
||||
// $arrivalDateAtChinaWarehouse = Carbon::parse($transport->drop_date); //cief todo: to uncomment this
|
||||
$arrivalDateAtChinaWarehouse = Carbon::parse('2021-11-13 00:00:00');
|
||||
$dateToCompare = Carbon::parse('2023-11-13 00:00:00');
|
||||
if ($dateToCompare->isAfter($arrivalDateAtChinaWarehouse)) {
|
||||
Log::channel('storage_invoices')->info('dateToCompare: '.$dateToCompare.', arrivalDateAtChinaWarehouse: '.$arrivalDateAtChinaWarehouse);
|
||||
$schedule = $transport->schedules->last();
|
||||
if ($schedule) {
|
||||
$eta = $schedule->eta;
|
||||
|
||||
if($eta){
|
||||
$transactions = $destinationWarehousePackage->transactions()->where('transactions.type', TransactionType::SHIPPING_INVOICE)->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->get();
|
||||
// $transactions = $destinationWarehousePackage->transactions()->where('transactions.type', TransactionType::SHIPPING_INVOICE)->where('transactions.status', ApprovalStatus::APPROVED)->get();
|
||||
|
||||
/** @var Transaction $invoice_transaction */
|
||||
foreach ($transactions as $invoice_transaction){
|
||||
$multipleResults[] = $this->processSingleTransactionOfTypeShippingInvoice($invoice_transaction, $destinationWarehousePackage, $order, $eta);
|
||||
}
|
||||
return $schedule->eta;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return $multipleResults;
|
||||
}
|
||||
|
||||
function processSingleTransactionOfTypeShippingInvoice($transaction, $destinationWarehousePackage, $order, $eta){
|
||||
private function processSingleTransactionOfTypeShippingInvoice($transaction, $destinationWarehousePackage, $company_module_id, $eta, $isBackDoorCheck){
|
||||
$pricePerCBM = 3;
|
||||
$resultNumberOfDaysFree = 10;
|
||||
$dt1 = $eta->copy()->addDay()->startOfDay();
|
||||
@@ -131,7 +159,16 @@ class CheckAndCreateStorageInvoiceTransactionProcessor
|
||||
$currentDatetime = Carbon::now();
|
||||
$dt2 = $currentDatetime->copy()->addDay()->startOfDay();
|
||||
$resultCurrentDate = $dt2->format('Y-m-d H:i:s');
|
||||
$test = Carbon::parse($dt2);
|
||||
$interval = Carbon::parse($dt2)->diff($dt1);
|
||||
$interval2 = $dt2->diff($dt1);
|
||||
|
||||
//cief todo: to be deleted
|
||||
// Log::channel('storage_invoices')->info('currentDatetime: '.$currentDatetime);
|
||||
// Log::channel('storage_invoices')->info('dt1: '.$dt1.', resultStartDate: '.$resultStartDate);
|
||||
// Log::channel('storage_invoices')->info('dt2: '.$dt2.', resultCurrentDate: '.$resultCurrentDate);
|
||||
// Log::channel('storage_invoices')->info('interval: '.$interval->days.', test: '.$test.', interval2: '.$interval2->days);
|
||||
|
||||
|
||||
$resultNumberOfDaysExceeded = $interval->days - $resultNumberOfDaysFree;
|
||||
$storageInvoice = $destinationWarehousePackage->transactions()->where('transactions.type', TransactionType::STORAGE_INVOICE)->first();
|
||||
@@ -150,52 +187,38 @@ class CheckAndCreateStorageInvoiceTransactionProcessor
|
||||
|
||||
if(!$storageInvoice && $resultNumberOfDaysExceeded > 0 && $transaction->status != ApprovalStatus::COMPLETED){
|
||||
$billNumber = $this->generatesTransactionBillNumber->execute('STOR-');
|
||||
$invoiceTransaction = $this->createTransaction($destinationWarehousePackage, $billNumber, $order->company_module_id, $price_cbm);
|
||||
$storageInvoiceId = $invoiceTransaction->id;
|
||||
$this->createTransactionDetails($invoiceTransaction, $destinationWarehousePackage, $cbm, 3 * $resultNumberOfDaysExceeded, $resultNumberOfDaysExceeded);
|
||||
$storageInvoice = $this->createStorageInvoiceTransaction($destinationWarehousePackage, $billNumber, $company_module_id, $price_cbm);
|
||||
$storageInvoiceId = $storageInvoice->id;
|
||||
$this->createStorageInvoiceTransactionDetails($storageInvoice, $destinationWarehousePackage, $cbm, 3 * $resultNumberOfDaysExceeded, $resultNumberOfDaysExceeded);
|
||||
}
|
||||
else if($storageInvoice){
|
||||
$amount = $storageInvoice->amount;
|
||||
$epsilon = 0.0001; // Tolerance for the comparison
|
||||
|
||||
//cief todo: to be removed - starts
|
||||
Log::info('price_cbm: '.$price_cbm."-".gettype($price_cbm));
|
||||
Log::info('amount: '.$amount."-".gettype($amount));
|
||||
if (abs($price_cbm - $amount) > $epsilon) {
|
||||
Log::info("The values are not equal.");
|
||||
} else {
|
||||
Log::info("The values are approximately equal.");
|
||||
}
|
||||
//cief todo: to be removed - ends
|
||||
Log::channel('storage_invoices')->info('$transaction->id,: '.$transaction->id);
|
||||
Log::channel('storage_invoices')->info('$storageInvoice->status,: '.$storageInvoice->status);
|
||||
Log::channel('storage_invoices')->info('price_cbm: '.$price_cbm."-".gettype($price_cbm));
|
||||
|
||||
if(abs($price_cbm - $amount) > $epsilon && $storageInvoice->status !== ApprovalStatus::COMPLETED){
|
||||
$paymentTransactions = $storageInvoice->transactions()->where('transactions.type', TransactionType::PAYMENT)->where('transactions.status', ApprovalStatus::PENDING_SUBMISSION)->get();
|
||||
if(count($paymentTransactions) > 0){ //PAYMENT type
|
||||
foreach ($paymentTransactions as $paymentTransaction){
|
||||
$this->updatesTransactionStatus->execute($paymentTransaction, ApprovalStatus::EXPIRED);
|
||||
}
|
||||
}
|
||||
else{ //TOP UP type
|
||||
$groups = $storageInvoice->groups()->get();
|
||||
foreach ($groups as $grp){
|
||||
$groupReference = $grp->reference;
|
||||
$walletTransaction = $this->fetchesTransaction->execute(['payment_reference' => $groupReference]);
|
||||
if($walletTransaction->status === ApprovalStatus::PENDING_SUBMISSION || $walletTransaction->status === ApprovalStatus::PENDING_VERIFICATION){
|
||||
$grp->status = ApprovalStatus::EXPIRED;
|
||||
$grp->save();
|
||||
$this->deletesGroup->execute($grp);
|
||||
$this->updatesTransactionStatus->execute($walletTransaction, ApprovalStatus::EXPIRED);
|
||||
|
||||
if(!$isBackDoorCheck){
|
||||
if(count($paymentTransactions) > 0){
|
||||
$this->updatePaymentTransactionViaNonGroupPayment($paymentTransactions);
|
||||
}
|
||||
else{
|
||||
$this->updatePaymentTransactionViaGroupPayment($storageInvoice);
|
||||
}
|
||||
}
|
||||
|
||||
$invoiceTransaction = $this->updateTransaction($storageInvoice, $price_cbm);
|
||||
$storageInvoice = $this->updateStorageInvoiceTransaction($storageInvoice, $price_cbm);
|
||||
$invoiceTransactionDetails = $storageInvoice->transactionDetails()->first();
|
||||
$this->updateTransactionDetails($invoiceTransactionDetails, $cbm, 3 * $resultNumberOfDaysExceeded);
|
||||
$this->updateStorageInvoiceTransactionDetails($invoiceTransactionDetails, $cbm, 3 * $resultNumberOfDaysExceeded);
|
||||
}
|
||||
|
||||
$storageInvoiceId = $storageInvoice->id;
|
||||
}
|
||||
|
||||
if($storageInvoiceId !== 0){
|
||||
$result = [
|
||||
'parentInvoiceId' => $transaction->id,
|
||||
'storageInvoiceId' => $storageInvoiceId,
|
||||
@@ -205,12 +228,44 @@ class CheckAndCreateStorageInvoiceTransactionProcessor
|
||||
'currentDate' => $resultCurrentDate,
|
||||
'cbm' => $cbm,
|
||||
'pricePerCBM' => $pricePerCBM,
|
||||
'storageInvoice' => new TransactionResource($storageInvoice)
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function updateTransaction(Transaction $transaction, float $totalAmount){
|
||||
}
|
||||
|
||||
private function updatePaymentTransactionViaGroupPayment($storageInvoice){
|
||||
Log::channel('storage_invoices')->info('updatePaymentTransactionViaGroupPayment');
|
||||
$groups = $storageInvoice->groups()->get();
|
||||
foreach ($groups as $grp){
|
||||
$groupReference = $grp->reference;
|
||||
$walletTransaction = $this->fetchesTransaction->execute(['payment_reference' => $groupReference]);
|
||||
if($walletTransaction->status === ApprovalStatus::PENDING_SUBMISSION || $walletTransaction->status === ApprovalStatus::PENDING_VERIFICATION){
|
||||
$grp->status = ApprovalStatus::EXPIRED;
|
||||
$grp->save();
|
||||
$this->deletesGroup->execute($grp);
|
||||
$this->updatesTransactionStatus->execute($walletTransaction, ApprovalStatus::EXPIRED);
|
||||
if($walletTransaction->payment_reference){
|
||||
$deletedBillplzBill = $this->deletesBillplzBill->execute($walletTransaction->payment_reference);
|
||||
Log::channel('storage_invoices')->info('deletedBillplzBill TransactionType::WALLET: '.json_encode($deletedBillplzBill));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function updatePaymentTransactionViaNonGroupPayment($paymentTransactions){
|
||||
Log::channel('storage_invoices')->info('updatePaymentTransactionViaNonGroupPayment');
|
||||
foreach ($paymentTransactions as $paymentTransaction){
|
||||
$this->updatesTransactionStatus->execute($paymentTransaction, ApprovalStatus::EXPIRED);
|
||||
if($paymentTransaction->payment_reference){
|
||||
$deletedBillplzBill = $this->deletesBillplzBill->execute($paymentTransaction->payment_reference);
|
||||
Log::channel('storage_invoices')->info('deletedBillplzBill TransactionType::WALLET: '.json_encode($deletedBillplzBill));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function updateStorageInvoiceTransaction(Transaction $transaction, float $totalAmount){
|
||||
|
||||
$object = new TransactionObject(
|
||||
$transaction->bill_no,
|
||||
@@ -236,7 +291,7 @@ class CheckAndCreateStorageInvoiceTransactionProcessor
|
||||
return $invoice_transaction;
|
||||
}
|
||||
|
||||
function updateTransactionDetails($invoice_transaction_details, $cbm, $price_cbm){
|
||||
private function updateStorageInvoiceTransactionDetails($invoice_transaction_details, $cbm, $price_cbm){
|
||||
$object_detail = new TransactionDetailObject(
|
||||
'STORAGE_FEE',
|
||||
$invoice_transaction_details->name,
|
||||
@@ -247,7 +302,7 @@ class CheckAndCreateStorageInvoiceTransactionProcessor
|
||||
$this->updatesTransactionDetail->execute($invoice_transaction_details, $object_detail);
|
||||
}
|
||||
|
||||
function createTransaction(PackingList $packing_list, string $billNumber, int $companyModuleId, float $totalAmount){
|
||||
private function createStorageInvoiceTransaction(PackingList $packing_list, string $billNumber, int $companyModuleId, float $totalAmount){
|
||||
|
||||
$object = new TransactionObject(
|
||||
$billNumber,
|
||||
@@ -273,7 +328,7 @@ class CheckAndCreateStorageInvoiceTransactionProcessor
|
||||
return $invoice_transaction;
|
||||
}
|
||||
|
||||
function createTransactionDetails($invoice_transaction, PackingList $packing_list, $cbm, $price_cbm, $numberOfDays){
|
||||
private function createStorageInvoiceTransactionDetails($invoice_transaction, PackingList $packing_list, $cbm, $price_cbm, $numberOfDays){
|
||||
$object_detail = new TransactionDetailObject(
|
||||
'SHIPPING_FEE',
|
||||
TransactionDetailType::STORAGE_FEE.' for '.$numberOfDays. ' days<br>'.round($packing_list->packages->where('type', '!=', PackageType::OVER_WEIGHT)->sum('quantity'), 3).' CTNS - '.round($cbm, 3).' CBM',
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
|
||||
use App\Classes\Modules\Transactions\Services\ListsGroups;
|
||||
use App\Classes\Modules\Transactions\Processors\CheckAndCreateStorageInvoiceTransactionProcessor;
|
||||
use App\Models\Order;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class CheckStorageInvoices extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'command:check-storage-invoices';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Check all storage invoices due to make sure that they are up to date daily, prevent \'back door\' cases';
|
||||
|
||||
/** @var ListsGroups */
|
||||
private $listsGroups;
|
||||
|
||||
/** @var CheckAndCreateStorageInvoiceTransactionProcessor */
|
||||
private $storageInvoiceTransactionProcessor;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(ListsGroups $listsGroups, CheckAndCreateStorageInvoiceTransactionProcessor $storageInvoiceTransactionProcessor)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->listsGroups = $listsGroups;
|
||||
$this->storageInvoiceTransactionProcessor = $storageInvoiceTransactionProcessor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
ini_set('memory_limit', '-1');
|
||||
|
||||
$this->info(Carbon::now() . ': Start Check all pending group payment with storage invoice is valid.');
|
||||
$start = new Carbon();
|
||||
|
||||
|
||||
$newfilters['order_by_updated_at_desc'] = true;
|
||||
$newfilters['status_in'] = [0, 1];
|
||||
$groups = $this->listsGroups->execute($newfilters);
|
||||
|
||||
foreach ($groups as $group){
|
||||
$this->info('CheckForStorageInvoiceByTransactions group: '.json_encode($group));
|
||||
foreach ($group->groupTransactions as $groupTransaction) {
|
||||
$invoice = $groupTransaction->transaction;
|
||||
$packingList = $invoice->owner()->first();
|
||||
if($packingList){
|
||||
$order = $packingList->owner()->first();
|
||||
if($order instanceof Order){
|
||||
$storages = $this->storageInvoiceTransactionProcessor->executeOrder($order);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$end = new Carbon();
|
||||
$elapsedTime = $start->diff($end)->format('%H:%I:%S');
|
||||
|
||||
$this->info(Carbon::now() . ': Done Check all pending group payment with storage invoice is valid. ElapsedTime: ' . $elapsedTime . '.');
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,11 @@ class Kernel extends ConsoleKernel
|
||||
->hourly()
|
||||
->withoutOverlapping()
|
||||
->appendOutputTo (storage_path().'/logs/fix_failed_callback_from_billplz.log');
|
||||
|
||||
$schedule->command('command:check-storage-invoices')
|
||||
->dailyAt('0:01')
|
||||
->withoutOverlapping()
|
||||
->appendOutputTo(storage_path().'/logs/check_storage_invoices.log');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,5 +72,6 @@ class Kernel extends HttpKernel
|
||||
'token.check' => \App\Http\Middleware\TokenCheckerMiddleware::class,
|
||||
'storage.invoice.check.byorder' => \App\Http\Middleware\CheckForStorageInvoiceByOrderId::class,
|
||||
'storage.invoice.check.bytransactions' => \App\Http\Middleware\CheckForStorageInvoiceByTransactions::class,
|
||||
'storage.invoice.check.bygroup' => \App\Http\Middleware\CheckForStorageInvoiceByGroup::class,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use App\Classes\Modules\Transactions\Processors\CheckAndCreateStorageInvoiceTransactionProcessor;
|
||||
use App\Classes\Modules\Transactions\Services\ListsGroups;
|
||||
use App\Models\Order;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class CheckForStorageInvoiceByGroup
|
||||
{
|
||||
|
||||
/** @var CheckAndCreateStorageInvoiceTransactionProcessor */
|
||||
private $storageInvoiceTransactionProcessor;
|
||||
|
||||
/** @var ListsGroups */
|
||||
private $listsGroups;
|
||||
|
||||
|
||||
public function __construct(CheckAndCreateStorageInvoiceTransactionProcessor $storageInvoiceTransactionProcessor, ListsGroups $listsGroups)
|
||||
{
|
||||
$this->storageInvoiceTransactionProcessor = $storageInvoiceTransactionProcessor;
|
||||
$this->listsGroups = $listsGroups;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
||||
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
// :options="{'per_page': 10, order_by: {column: 'id', DESC: true}, 'status_in': [0, 1], 'receiver': company_module_id, order_by: {column: 'updated_at', DESC: true}}"
|
||||
$filters = json_decode($request->input('filters'), true);
|
||||
if(json_encode($filters['order_by']) == '{"column":"updated_at","DESC":true}'){
|
||||
$filters['order_by_updated_at_desc'] = true;
|
||||
}
|
||||
unset($filters['order_by']);
|
||||
$groups = $this->listsGroups->execute($filters);
|
||||
|
||||
|
||||
if(isset($filters['check_for_storage_invoice'])){
|
||||
foreach ($groups as $group){
|
||||
foreach ($group->groupTransactions as $groupTransaction) {
|
||||
$invoice = $groupTransaction->transaction;
|
||||
$packingList = $invoice->owner()->first();
|
||||
if($packingList){
|
||||
$order = $packingList->owner()->first();
|
||||
if($order instanceof Order){
|
||||
$storages = $this->storageInvoiceTransactionProcessor->executeOrder($order);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,8 @@ class CheckForStorageInvoiceByOrderId
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$orderId = $request->route('id');
|
||||
$result = $this->storageInvoiceTransactionProcessor->execute($orderId);
|
||||
$request->merge(['storages' => $result]);
|
||||
|
||||
$storages = $this->storageInvoiceTransactionProcessor->execute($orderId);
|
||||
$request->merge(['storages' => $storages]);
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ namespace App\Http\Middleware;
|
||||
use Closure;
|
||||
use App\Classes\Modules\Transactions\Processors\CheckAndCreateStorageInvoiceTransactionProcessor;
|
||||
use App\Classes\Modules\Transactions\Services\ListsTransactions;
|
||||
use App\Classes\Modules\Transactions\Services\ListsGroups;
|
||||
use App\Classes\ValueObjects\Constants\TransactionType;
|
||||
use App\Models\CompanyConnection;
|
||||
use App\Models\Order;
|
||||
use App\Models\PackingList;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class CheckForStorageInvoiceByTransactions
|
||||
{
|
||||
@@ -19,11 +21,15 @@ class CheckForStorageInvoiceByTransactions
|
||||
/** @var ListsTransactions */
|
||||
private $listsTransactions;
|
||||
|
||||
/** @var ListsGroups */
|
||||
private $listsGroups;
|
||||
|
||||
public function __construct(CheckAndCreateStorageInvoiceTransactionProcessor $storageInvoiceTransactionProcessor, ListsTransactions $listsTransactions)
|
||||
|
||||
public function __construct(CheckAndCreateStorageInvoiceTransactionProcessor $storageInvoiceTransactionProcessor, ListsTransactions $listsTransactions, ListsGroups $listsGroups)
|
||||
{
|
||||
$this->storageInvoiceTransactionProcessor = $storageInvoiceTransactionProcessor;
|
||||
$this->listsTransactions = $listsTransactions;
|
||||
$this->listsGroups = $listsGroups;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,36 +42,55 @@ class CheckForStorageInvoiceByTransactions
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
//{"per_page":999,"order_by":{"column":"id","DESC":true},"status_in":[2],"receiver":298,"type_in":[1,16],"does_not_have_payment_status_in":[0,1],"does_not_have_groups":1}
|
||||
$results = [];
|
||||
$transactions = null;
|
||||
$marking = $request->route('marking');
|
||||
if($marking){
|
||||
if($marking){ //for web route /customer/{marking}/payment-and-billing
|
||||
$connection = CompanyConnection::where('invitee_reference', $marking)->first();
|
||||
$company_module_id = $connection->invitee->id;
|
||||
$filters = [
|
||||
'per_page' => 999,
|
||||
'status_in' => [2],
|
||||
'receiver' => $company_module_id,
|
||||
'type_in' => [1, 16]
|
||||
'type_in' => [TransactionType::SHIPPING_INVOICE]
|
||||
];
|
||||
$transactions = $this->listsTransactions->execute($filters);
|
||||
}
|
||||
else{
|
||||
else{ //for api route /transactions/list
|
||||
$filters = json_decode($request->input('filters'), true);
|
||||
$filters['per_page'] = 999;
|
||||
unset($filters['does_not_have_payment_status_in']);
|
||||
unset($filters['does_not_have_groups']);
|
||||
$filters['type_in'] = [TransactionType::SHIPPING_INVOICE];
|
||||
if(json_encode($filters['order_by']) == '{"column":"id","DESC":true}'){
|
||||
Log::channel('storage_invoices')->info('CheckForStorageInvoiceByTransactions 3 Match');
|
||||
$filters['order_by_id_desc'] = true;
|
||||
}
|
||||
unset($filters['order_by']);
|
||||
$transactions = $this->listsTransactions->execute($filters);
|
||||
}
|
||||
|
||||
|
||||
if(isset($filters['check_for_storage_invoice'])){
|
||||
foreach($transactions as $transaction){
|
||||
$packingList = $transaction->owner()->first();
|
||||
if($packingList){
|
||||
$order = $packingList->owner()->first();
|
||||
if($order instanceof Order){
|
||||
$this->storageInvoiceTransactionProcessor->execute($order->reference);
|
||||
$storages = $this->storageInvoiceTransactionProcessor->executeOrder($order);
|
||||
if($storages){
|
||||
$results = array_merge($results, $storages);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$filteredResults = array_values(array_filter($results, function($item, $key) {
|
||||
static $seen = array();
|
||||
$hash = md5($item['parentInvoiceId'] . $item['storageInvoiceId']);
|
||||
return !isset($seen[$hash]) && ($seen[$hash] = true);
|
||||
}, ARRAY_FILTER_USE_BOTH));
|
||||
|
||||
|
||||
$request->merge(['storages' => $filteredResults]);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Classes\ValueObjects\Constants\PaymentMethodType;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class GroupForOrderV2Resource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'original_amount' => (float) $this->original_amount,
|
||||
'original_currency' => new CurrencyResource($this->original_currency),
|
||||
'issuer_name' => $this->issuerCompany->name,
|
||||
'issuer_id' => $this->issuerCompany->id,
|
||||
'amount' => (float) $this->amount,
|
||||
'service_charge' => (float) $this->amount,
|
||||
'currency' => new CurrencyResource($this->currency),
|
||||
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y h:i:s A'),
|
||||
'currency_rate' => (float) $this->currency_rate,
|
||||
'status' => $this->status,
|
||||
'status_name' => ApprovalStatus::APPROVAL_STATUS_ID[$this->status],
|
||||
'payment_method' => (int)$this->payment_method,
|
||||
'payment_method_name' => ucwords(PaymentMethodType::PAYMENT_METHODS_ID[$this->payment_method]),
|
||||
'payment_reference' => $this->reference,
|
||||
'transactions_ids' => GroupTransactionsForOrderV2Resource::collection($this->groupTransactions)
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class GroupTransactionsForOrderV2Resource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
// 'id' => $this->id,
|
||||
'group_id' => $this->group_id,
|
||||
'transaction_id' => $this->transaction_id,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,14 @@
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Classes\ValueObjects\Constants\DocumentType;
|
||||
use App\Classes\ValueObjects\Constants\TransactionType;
|
||||
use App\Models\Group;
|
||||
use App\Models\Order;
|
||||
use App\Models\Transaction;
|
||||
use App\Models\Wallet;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
|
||||
class TransactionResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
@@ -24,6 +23,8 @@ class TransactionResource extends JsonResource
|
||||
{
|
||||
$order = null;
|
||||
$groupTransactions = null;
|
||||
$group_payment_attempts = null;
|
||||
$group_payment_expired = null;
|
||||
|
||||
if ($this->owner instanceof Transaction) {
|
||||
if ($this->owner) {
|
||||
@@ -35,6 +36,12 @@ class TransactionResource extends JsonResource
|
||||
if ($this->owner) {
|
||||
$order = new OrderResource($this->owner->owner);
|
||||
}
|
||||
|
||||
if($this->groups){
|
||||
$group_payment_attempts = GroupForOrderV2Resource::collection($this->groups->whereNotIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]));
|
||||
$group_payment_expired = GroupForOrderV2Resource::collection($this->groupsWithTrashed->whereIn('status', [ApprovalStatus::EXPIRED]));
|
||||
}
|
||||
|
||||
} else {
|
||||
$group = Group::where('reference', $this->payment_reference)->first();
|
||||
if ($group) {
|
||||
@@ -48,7 +55,9 @@ class TransactionResource extends JsonResource
|
||||
'owner_type' => $this->owner_type,
|
||||
'order' => $order,
|
||||
'group_transactions' => $groupTransactions,
|
||||
'group_reference' => $groupTransactions ? $group->reference : null,
|
||||
'group_reference' => $groupTransactions ? ($group ? $group->reference : null ) : null,
|
||||
'groups_payment_attempts' => $group_payment_attempts,
|
||||
'groups_payment_expired' => $group_payment_expired,
|
||||
'documents' => $groupTransactions ? DocumentResource::collection($this->documents->where('status', ApprovalStatus::PENDING_VERIFICATION)) : DocumentResource::collection($this->documents),
|
||||
'type' => (int) $this->type,
|
||||
'bill_no' => $this->bill_no,
|
||||
@@ -83,6 +92,7 @@ class TransactionResource extends JsonResource
|
||||
->get()
|
||||
),
|
||||
'remarks' => RemarkResource::collection($this->remarks),
|
||||
'storages' => $this->storages ? $this->storages : null, //from middleware
|
||||
'expires_on' => Carbon::parse($this->expires_on)->format('d-m-Y h:s:i'),
|
||||
'updated_at' => Carbon::parse($this->updated_at)->format('d-m-Y'),
|
||||
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y')
|
||||
|
||||
@@ -89,6 +89,14 @@ class Transaction extends AbstractModel implements Documentable, Transactionable
|
||||
return $this->BelongsToMany(Group::class, GroupTransaction::class, 'transaction_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function groupsWithTrashed(): BelongsToMany
|
||||
{
|
||||
return $this->BelongsToMany(Group::class, GroupTransaction::class, 'transaction_id')->withTrashed();;
|
||||
}
|
||||
|
||||
public function convert_original_amount()
|
||||
{
|
||||
if($this->booking()->first()->fix_currency_id !== 1) {
|
||||
|
||||
@@ -54,6 +54,12 @@ return [
|
||||
'days' => 14,
|
||||
],
|
||||
|
||||
'storage_invoices' => [
|
||||
'driver' => 'single',
|
||||
'path' => storage_path('logs/laravel_storage_invoices.log'),
|
||||
'level' => 'info',
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'driver' => 'slack',
|
||||
'url' => env('LOG_SLACK_WEBHOOK_URL'),
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddIsCreditTermToCompanyConnections extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('company_connections', function (Blueprint $table) {
|
||||
$table->boolean('is_credit_term')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('company_connections', function (Blueprint $table) {
|
||||
$table->dropColumn('is_credit_term');
|
||||
});
|
||||
}
|
||||
}
|
||||
+40
-4
@@ -6,7 +6,8 @@
|
||||
<div v-show="!$store.getters.isLoading(section)">
|
||||
<list-component :section="section" :endpoint="endpoint" :options="options">
|
||||
<template slot="list" slot-scope="{data}">
|
||||
<payments-billing-components v-if="endpoint == route('api.transaction.list')" :section="section" :data="data" :selectedInvoice="selectedInvoice" v-on:input="updateList($event)"></payments-billing-components>
|
||||
<!-- <payments-billing-components v-if="endpoint == route('api.transaction.list') && !data.storages" :section="section" :data="data" :selectedInvoice="selectedInvoice" v-on:input="updateList($event)"></payments-billing-components> -->
|
||||
<payments-billing-variant-2-components v-if="endpoint == route('api.transaction.list')" :section="section" :invoices="getMergedInvoices(data, data.storages)" :selectedInvoice="selectedInvoice" :isPaidInvoices="isPaidInvoices" v-on:input="updateList($event)"></payments-billing-variant-2-components>
|
||||
<group-payments-billing-components v-else :section="section" :data="data" :selectedInvoice="selectedInvoice" v-on:input="updateList($event)"></group-payments-billing-components>
|
||||
</template>
|
||||
</list-component>
|
||||
@@ -94,6 +95,10 @@
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
isPaidInvoices :{
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
@@ -117,7 +122,7 @@
|
||||
},
|
||||
selectedIds () {
|
||||
return this.selectedInvoice.map(s=>s.id);
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generateSummaryInvoice(){
|
||||
@@ -126,9 +131,40 @@
|
||||
makePayment(){
|
||||
this.submit(this.route('api.transaction.group.create', JSON.stringify(this.selectedId)), 'post', this.section, true, false);
|
||||
},
|
||||
updateList(packageList){
|
||||
this.selectedInvoice.includes(packageList) ? this.selectedInvoice.splice(this.selectedInvoice.indexOf(packageList), 1) : this.selectedInvoice.push(packageList);
|
||||
updateList(invoices){
|
||||
invoices.forEach(invoice => {
|
||||
if (this.selectedInvoice.includes(invoice)) {
|
||||
this.selectedInvoice.splice(this.selectedInvoice.indexOf(invoice), 1);
|
||||
} else {
|
||||
this.selectedInvoice.push(invoice);
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
getMergedInvoices(shippingInvoice, storages){
|
||||
if(storages){
|
||||
const storageMap = {};
|
||||
for (const storage of storages) {
|
||||
storageMap[storage.parentInvoiceId] = storage.storageInvoiceId;
|
||||
}
|
||||
|
||||
const storageInvoiceId = storageMap[shippingInvoice.id];
|
||||
const storage = storages.find((i) => {
|
||||
return i.storageInvoiceId === storageInvoiceId;
|
||||
});
|
||||
|
||||
const mergedArray = [
|
||||
...(shippingInvoice ? [shippingInvoice] : []),
|
||||
...(storage && storage.storageInvoice.status !== 3 ? [storage.storageInvoice] : [])
|
||||
];
|
||||
return mergedArray;
|
||||
}
|
||||
|
||||
const mergedArray = [
|
||||
...(shippingInvoice ? [shippingInvoice] : [])
|
||||
];
|
||||
return mergedArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
+5
-5
@@ -100,27 +100,27 @@
|
||||
<div class="col bg-master-lightest p-1 p-sm-4">
|
||||
<div class="row tabsContainer tabContent" tab-name="pendingPayment">
|
||||
<div class="col">
|
||||
<customer-payment-billing-inner-component :endpoint="route('api.transaction.list')" section="customerPendingPaymentInvoiceComponent" :company_module_id="company_module_id" :options="{'per_page': 10, order_by: {column: 'id', DESC: true}, 'status_in': [2], 'receiver': company_module_id, 'type_in': [1,16], 'does_not_have_payment_status_in': [0,1] ,'does_not_have_groups': 1}"></customer-payment-billing-inner-component>
|
||||
<customer-payment-billing-inner-component :endpoint="route('api.transaction.list')" section="customerPendingPaymentInvoiceComponent" :company_module_id="company_module_id" :options="{'per_page': 10, order_by: {column: 'id', DESC: true}, 'status_in': [2], 'receiver': company_module_id, 'type_in': [1], 'does_not_have_payment_status_in': [0,1] ,'does_not_have_groups': 1, 'check_for_storage_invoice': 1}"></customer-payment-billing-inner-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row tabsContainer tabContent hide" tab-name="groupPaymentInProgress">
|
||||
<div class="col">
|
||||
<customer-payment-billing-inner-component :endpoint="route('api.transaction.group.list')" section="customerGroupPaymentInProgressInvoiceComponent" :company_module_id="company_module_id" :options="{'per_page': 10, order_by: {column: 'id', DESC: true}, 'status_in': [0, 1], 'receiver': company_module_id, order_by: {column: 'updated_at', DESC: true}}"></customer-payment-billing-inner-component>
|
||||
<customer-payment-billing-inner-component :endpoint="route('api.transaction.group.list')" section="customerGroupPaymentInProgressInvoiceComponent" :company_module_id="company_module_id" :options="{'per_page': 10, 'status_in': [0, 1], 'receiver': company_module_id, order_by: {column: 'updated_at', DESC: true}}"></customer-payment-billing-inner-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row tabsContainer tabContent hide" tab-name="groupPaymentExpired">
|
||||
<div class="col">
|
||||
<customer-payment-billing-inner-component :endpoint="route('api.transaction.group.list')" section="customerGroupPaymentExpiredInvoiceComponent" :company_module_id="company_module_id" :options="{'per_page': 10, order_by: {column: 'id', DESC: true}, 'status_in': [6], 'receiver': company_module_id, 'with_trashed': true, order_by: {column: 'updated_at', DESC: true}}"></customer-payment-billing-inner-component>
|
||||
<customer-payment-billing-inner-component :endpoint="route('api.transaction.group.list')" section="customerGroupPaymentExpiredInvoiceComponent" :company_module_id="company_module_id" :options="{'per_page': 10, 'status_in': [6], 'receiver': company_module_id, 'with_trashed': true, order_by: {column: 'updated_at', DESC: true}}"></customer-payment-billing-inner-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row tabsContainer tabContent hide" tab-name="paidGroupInvoice">
|
||||
<div class="col">
|
||||
<customer-payment-billing-inner-component :endpoint="route('api.transaction.group.list')" section="customerPaidGroupInvoiceComponent" :company_module_id="company_module_id" :options="{'per_page': 10, order_by: {column: 'id', DESC: true}, 'status_in': [2], 'receiver': company_module_id, order_by: {column: 'updated_at', DESC: true}}"></customer-payment-billing-inner-component>
|
||||
<customer-payment-billing-inner-component :endpoint="route('api.transaction.group.list')" section="customerPaidGroupInvoiceComponent" :company_module_id="company_module_id" :options="{'per_page': 10, 'status_in': [2], 'receiver': company_module_id, order_by: {column: 'updated_at', DESC: true}}"></customer-payment-billing-inner-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row tabsContainer tabContent hide" tab-name="paidInvoice">
|
||||
<div class="col">
|
||||
<customer-payment-billing-inner-component :endpoint="route('api.transaction.list')" section="customerPaidInvoiceComponent" :company_module_id="company_module_id" :options="{'per_page': 10, order_by: {column: 'id', DESC: true}, 'status_in': [3], 'receiver': company_module_id, 'type_in': [1,16], order_by: {column: 'updated_at', DESC: true}}"></customer-payment-billing-inner-component>
|
||||
<customer-payment-billing-inner-component :endpoint="route('api.transaction.list')" section="customerPaidInvoiceComponent" :company_module_id="company_module_id" :options="{'per_page': 10, 'status_in': [3], 'receiver': company_module_id, 'type_in': [1,16], order_by: {column: 'updated_at', DESC: true}}" :isPaidInvoices="true"></customer-payment-billing-inner-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -147,9 +147,12 @@
|
||||
<p>You can view your invoices here and make payment.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row bg-master-lightest p-t-15" v-for="invoice in order.invoices">
|
||||
<div class="col">
|
||||
<customer-payments-billing-component :data="invoice" :storage="getStorage(invoice)" invoice_status="Pending Payment" :section="section"></customer-payments-billing-component>
|
||||
<div class="row bg-master-lightest p-t-15" v-for="invoice in order.invoices" v-if="invoice.type === 1">
|
||||
<div class="col" v-if="getMergedInvoices(invoice).length > 1">
|
||||
<customer-payments-billing-variant-2-component :data="getMergedInvoices(invoice)" :storage="getStorageInfo(invoice)" invoice_status="Pending Payment" :section="section"></customer-payments-billing-variant-2-component>
|
||||
</div>
|
||||
<div class="col" v-else>
|
||||
<customer-payments-billing-component :data="invoice" invoice_status="Pending Payment" :section="section"></customer-payments-billing-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -180,6 +183,16 @@
|
||||
computed: {
|
||||
pendingQueue () {
|
||||
return this.$store.getters.isInCompleteQueue(this.section);
|
||||
},
|
||||
orderStorageMap() {
|
||||
// Create a map to link parent invoice IDs to storageInvoiceIds
|
||||
const storageMap = {};
|
||||
if(this.order.storages){
|
||||
for (const storage of this.order.storages) {
|
||||
storageMap[storage.parentInvoiceId] = storage.storageInvoiceId;
|
||||
}
|
||||
}
|
||||
return storageMap;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -202,16 +215,35 @@
|
||||
this.isLoading = false;
|
||||
this.order = response.payload.data;
|
||||
},
|
||||
getStorage(invoice) {
|
||||
const storageObject = this.findStorageObject(invoice.id);
|
||||
getStorageInfo(invoice) {
|
||||
const storageObject = this.findStorageInfoObject(invoice.id);
|
||||
return storageObject;
|
||||
},
|
||||
findStorageObject(storageInvoiceId) {
|
||||
findStorageInfoObject(shippingInvoiceId) {
|
||||
if(this.order.storages){
|
||||
return this.order.storages.find(storage => storage.storageInvoiceId === storageInvoiceId);
|
||||
return this.order.storages.find(storage => storage.parentInvoiceId === shippingInvoiceId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
getMergedInvoices(invoice){
|
||||
const storageInvoice = this.getStorageInvoice(invoice);
|
||||
const mergedArray = [
|
||||
...(invoice ? [invoice] : []),
|
||||
...(storageInvoice ? [storageInvoice] : []),
|
||||
];
|
||||
return mergedArray;
|
||||
},
|
||||
getStorageInvoice(invoice) {
|
||||
// Retrieve the storageInvoiceId for the current shipping invoice from storage info (order.storages)
|
||||
const storageInvoiceId = this.orderStorageMap[invoice.id];
|
||||
|
||||
// Find and return the storage invoice (type 16) from order.invoices
|
||||
const storageInvoice = this.order.invoices.find((invoice) => {
|
||||
return invoice.type === 16 && invoice.id === storageInvoiceId;
|
||||
});
|
||||
|
||||
return storageInvoice;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
-14
@@ -1,17 +1,6 @@
|
||||
<template>
|
||||
<div class="row m-b-15 m-l-5 m-r-10 parentContainer">
|
||||
<div class="col bg-white rounded">
|
||||
<div class="row">
|
||||
<div class="col padding-20" v-if="storage" v-show="item.type === 16">
|
||||
<div v-if="storage.numberOfDaysExceeded > 0">
|
||||
If your items have been here for over 10 days after goods arrival in Malaysia, a storage fee of RM3 per cubic meter per day applies. Storage fees are recalculated daily, please make immediate payment before it expires at the end of the day.
|
||||
</div>
|
||||
<p >Warehouse Storage Fee (Days): {{ storage.numberOfDaysExceeded }} x 3 x {{ storage.cbm }}CBM</p>
|
||||
</div>
|
||||
<div class="col padding-20" v-show="item.type === 1 && item.status !== 3">
|
||||
<p> Warehouse Storage Fee applies ten days after goods arrival in Malaysia.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" :class="[{'b-danger': item.status == 5 ||item.status == 6, 'b-a': item.status == 5||item.status == 6}]">
|
||||
<div class="col padding-20">
|
||||
<div class="row align-items-center">
|
||||
@@ -213,9 +202,6 @@
|
||||
section:{
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
storage:{
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
data(){
|
||||
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
<template>
|
||||
<div class="row m-b-15 m-l-5 m-r-10 parentContainer">
|
||||
<div class="col bg-white rounded">
|
||||
<div v-for="item in items" class="row" :class="[{'b-danger': item.status == 5 ||item.status == 6, 'b-a': item.status == 5||item.status == 6}]">
|
||||
<div class="col padding-20">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-2">
|
||||
<p class="no-margin fs-10 all-caps">Invoice Date</p>
|
||||
<div> {{ item.updated_at }}</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="no-margin fs-10 all-caps">Status</p>
|
||||
<div class="all-caps" v-if="item.status == 3">Payment Completed</div>
|
||||
<div class="all-caps text-danger" v-else-if="item.status == 5">Dispute in progress</div>
|
||||
<div class="all-caps" v-else-if="item.status == 6">Cancelled Invoice</div>
|
||||
<div class="all-caps" v-else>Pending Payment</div>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<p class="no-margin fs-10 all-caps">Amount</p>
|
||||
<div>MYR {{ item.amount.toFixed(2) }}</div>
|
||||
</div>
|
||||
<div class="col-3" v-if="item.remarks.length">
|
||||
<p class="no-margin fs-10 all-caps">Billing Question</p>
|
||||
<div>
|
||||
{{ getLatestComment(item).content }}
|
||||
<span class="btn requestModal no-border" v-if="item.remarks.length" size="large" data-type="chatmodal">
|
||||
<i class="fa fa-comment-o"></i>
|
||||
</span>
|
||||
</div>
|
||||
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="chatmodal">
|
||||
<remark-component :section="section" :data="item" module_type="Transaction"></remark-component>
|
||||
</modal-component>
|
||||
</div>
|
||||
<div class="col-3" v-else>
|
||||
<p class="no-margin fs-10 all-caps invisible">Billing Question</p>
|
||||
<div>
|
||||
<span class="btn requestModal no-border invisible">
|
||||
<i class="fa fa-edit"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto p-l-0 p-r-0 d-flex justify-content-center align-items-center">
|
||||
<div :class="[{'invisible': [5, 6, 3].includes(item.status)}]">
|
||||
<span class="d-inline-block m-r-15 text-primary bold text-underline pointer requestModal" data-type="billingRemark">Billing Question?</span>
|
||||
</div>
|
||||
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="billingRemark">
|
||||
<customer-invoice-remark-form-component module_type="Transaction" :data="item" :section="section"></customer-invoice-remark-form-component>
|
||||
</modal-component>
|
||||
<div v-if="item.documents.length">
|
||||
<div v-for="file in item.documents[0].files" v-bind:key="file.id" class="col-auto no-padding">
|
||||
<document-file-viewer-component :file="file">
|
||||
<template slot="button">
|
||||
<div class="btn bg-grey no-border muted">
|
||||
<i class="fa fa-file-pdf-o"></i>
|
||||
</div>
|
||||
</template>
|
||||
</document-file-viewer-component>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="btn bg-grey no-border muted invisible">
|
||||
<i class="fa fa-file-pdf-o"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto p-l-0 p-r-0 d-flex justify-content-center align-items-center" v-if="$store.getters.isSuperAdmin">
|
||||
<span class="d-inline-block m-r-15 text-primary bold text-underline pointer requestModal" data-type="deleteInvoice">
|
||||
<i class="fa fa-close"></i>
|
||||
</span>
|
||||
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="deleteInvoice">
|
||||
<delete-invoice-form-component :data="item" :section="section"></delete-invoice-form-component>
|
||||
</modal-component>
|
||||
</div>
|
||||
<div class="col-auto hide">
|
||||
<div class="btn btn-sm all-caps b-rad-none btn-block" :class="{'btn-success': !expanded, 'btn-default': expanded}" @click="expanded = !expanded">
|
||||
{{ expanded ? 'Cancel' : 'Make Payment' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col" v-if="storage" v-show="item.type === 16">
|
||||
<p >Warehouse Storage Fee: {{ storage.numberOfDaysExceeded }} Days x RM {{ storage.pricePerCBM}} x {{ storage.cbm }} cbm</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row b-t b-grey p-t-10 m-l-5 m-r-5" v-show="expanded" v-if="[5, 6].includes(item.status)">
|
||||
<div class="col padding-20">
|
||||
<div class="row bg-master-lightest h-100 padding-20">
|
||||
<div class="col">
|
||||
<h6 class="all-caps m-b-5 no-margin text-underline bold">Billing question</h6>
|
||||
<remark-component :section="section" :data="item" module_type="Transaction"></remark-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row b-t b-grey p-t-10 m-l-5 m-r-5" >
|
||||
<div class="col-12 col-md-7 padding-20">
|
||||
<div class="row bg-master-lightest">
|
||||
<div class="col">
|
||||
<div v-for="(groupPaymentAttemptItem, index) in items" class="row bg-master-lightest" v-if="groupPaymentAttemptItem.groups_payment_attempts.length && groupPaymentAttemptItem.type == 1 && groupTransactionsExist(items, groupPaymentAttemptItem.groups_payment_attempts)">
|
||||
<div class="col">
|
||||
<div class="row m-t-10 m-b-10">
|
||||
<div class="col">
|
||||
<div v-if="index === 0" class="font-head fs-10 all-caps">Payment Attempt</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<shipping-transaction-component v-for="group in groupPaymentAttemptItem.groups_payment_attempts" v-bind:key="group.id" :data="group" :section="section"></shipping-transaction-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(groupPaymentAttemptItem, index) in items" class="row bg-master-lightest" v-if="groupPaymentAttemptItem.groups_payment_expired.length && groupPaymentAttemptItem.type == 1 && groupTransactionsExist(items, groupPaymentAttemptItem.groups_payment_expired)">
|
||||
<div class="col">
|
||||
<div class="row m-t-10 m-b-10">
|
||||
<div class="col">
|
||||
<div v-if="index === 0" class="font-head fs-10 all-caps">Payment Expired</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<payment-expired-component v-for="group in groupPaymentAttemptItem.groups_payment_expired" v-bind:key="group.id" :data="group" :section="section"></payment-expired-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(paymentAttemptItem, index) in items" class="row bg-master-lightest" v-if="items.some(x => x.payment_attempts && x.payment_attempts.length > 0)">
|
||||
<div class="col">
|
||||
<div class="row m-t-10 m-b-10">
|
||||
<div class="col">
|
||||
<div v-if="index === 0" class="font-head fs-10 all-caps">Payment Attempt</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<shipping-transaction-component v-for="i in paymentAttemptItem.payment_attempts" v-bind:key="i.id" :data="i" :section="section"></shipping-transaction-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(paymentExpiredItem, index) in items" class="row bg-master-lightest" v-if="items.some(x => x.payments_expired && x.payments_expired.length > 0)">
|
||||
<div class="col">
|
||||
<div class="row m-t-10 m-b-10">
|
||||
<div class="col">
|
||||
<div v-if="index === 0" class="font-head fs-10 all-caps">Payment Expired</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<payment-expired-component v-for="i in paymentExpiredItem.payments_expired" v-bind:key="i.id" :data="i" ></payment-expired-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(paymentHistoryItem, index) in items" class="row bg-master-lightest" v-if="items.some(x => x.payment_history && x.payment_history.length > 0)">
|
||||
<div class="col">
|
||||
<div class="row m-t-10 m-b-10">
|
||||
<div class="col">
|
||||
<div v-if="index === 0" class="font-head fs-10 all-caps">Payment History</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<payment-history-component v-for="i in paymentHistoryItem.payment_history" v-bind:key="i.id" :data="i" ></payment-history-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-5 padding-20 parentContainer">
|
||||
<div class="row bg-master-lightest h-100">
|
||||
<div class="col">
|
||||
<div class="row padding-10">
|
||||
<div class="col">
|
||||
<div class="row align-items-end m-b-10 text-complete">
|
||||
<div class="col">
|
||||
<div class="font-heading all-caps fs-12">Total Amount:</div>
|
||||
</div>
|
||||
<div class="col-auto text-right">
|
||||
<div class="font-heading fs-12">MYR {{(Math.round((totalAmount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-end m-b-10 text-success">
|
||||
<div class="col">
|
||||
<div class="font-heading all-caps fs-12">Paid Total:</div>
|
||||
</div>
|
||||
<div class="col-auto text-right">
|
||||
<div class="font-heading fs-12">MYR {{(Math.round((totalAmount - totalOutstanding + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-end m-b-10">
|
||||
<div class="col">
|
||||
<div class="font-heading all-caps fs-12">Floating Amount:</div>
|
||||
</div>
|
||||
<div class="col-auto text-right">
|
||||
<div class="font-heading fs-12">MYR {{(Math.round((totalFloating + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-end bold text-danger">
|
||||
<div class="col">
|
||||
<div class="font-heading all-caps fs-12">OutStanding Total:</div>
|
||||
</div>
|
||||
<div class="col-auto text-right">
|
||||
<div class="font-heading fs-12">MYR {{(Math.round((totalOutstanding + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-t-20" v-if="totalOutstanding - totalFloating > 0.009">
|
||||
<div class="col">
|
||||
<div class="btn btn-sm all-caps b-rad-none btn-success btn-block requestModal" data-type="makePayment">Make Payment</div>
|
||||
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="makePayment">
|
||||
<group-payment-form-component :section="section" :selectedIds="selectedIds" :sumAmount="totalOutstanding.toFixed(2)"></group-payment-form-component>
|
||||
</modal-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
},
|
||||
invoice_status: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
section:{
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
storage:{
|
||||
type: Object
|
||||
},
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
items: this.data,
|
||||
parameters: {
|
||||
packing_list_id: null,
|
||||
transaction_details: [],
|
||||
},
|
||||
expanded: true,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data: function() {
|
||||
this.items = this.data;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalAmount() {
|
||||
return this.items.reduce((sum, item) => sum + item.amount, 0);
|
||||
},
|
||||
totalOutstanding() {
|
||||
return this.items.reduce((sum, item) => sum + item.outstanding, 0);
|
||||
},
|
||||
totalFloating() {
|
||||
return this.items.reduce((sum, item) => sum + item.floating, 0);
|
||||
},
|
||||
selectedIds() {
|
||||
const ids = [];
|
||||
this.items.forEach(item => {
|
||||
ids.push(item.id);
|
||||
});
|
||||
return ids;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getLatestComment(item) {
|
||||
let questions = item.remarks;
|
||||
return questions.slice().reverse()[0];
|
||||
},
|
||||
successHandler(response){
|
||||
this.item = response.payload.data;
|
||||
},
|
||||
groupTransactionsExist(items, groups) {
|
||||
return groups.some(group =>
|
||||
group.transactions_ids.length === items.length &&
|
||||
group.transactions_ids.every(transaction =>
|
||||
items.some(item => item.id === transaction.transaction_id)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="row m-b-15 m-l-5 m-r-10">
|
||||
<div class="col bg-white rounded b-a" :class="{'b-white': !selected, 'b-primary': selected, 'bg-primary-lighter': selected}">
|
||||
<div v-for="item in items" class="row" v-if="$store.getters.isAdmin || item.order">
|
||||
<div class="col padding-20">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-auto pointer align-items-center" style="min-width:70px;" @click="activate()" v-if="!['customerPaidGroupInvoiceComponent', 'customerGroupPaymentInProgressInvoiceComponent'].includes(section)">
|
||||
<i v-show="((item.type === 1 && !isPaidInvoices) || isPaidInvoices)" class="fa fs-30 fa-fw" :class="{'fa-square-o': !selected, 'fa-check-square': selected, 'text-primary':selected}" ></i>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="no-margin fs-10 all-caps">Invoice No</p>
|
||||
<div>{{ item.bill_no }}</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="no-margin fs-10 all-caps">Order</p>
|
||||
<div v-if="item.order"><a :href="route('order.show', item.order.reference)">{{item.order.reference}}</a></div>
|
||||
<div class="text-danger" v-else>Error in retrieving order</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="no-margin fs-10 all-caps">Invoice Date</p>
|
||||
<div>{{ item.created_at }}</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<p class="no-margin fs-10 all-caps">Amount</p>
|
||||
<div>MYR {{ item.amount.toFixed(2) }}</div>
|
||||
</div>
|
||||
<div class="col" v-if="['customerPaidInvoiceComponent', 'customerPaidGroupInvoiceComponent'].includes(section)">
|
||||
<p class="no-margin fs-10 all-caps">Payment Date</p>
|
||||
<div>{{ item.payment_history[item.payment_history.length-1].created_at }}</div>
|
||||
</div>
|
||||
<div class="col" v-if="['customerPaidInvoiceComponent', 'customerPaidGroupInvoiceComponent'].includes(section)">
|
||||
<p class="no-margin fs-10 all-caps">Bill Number</p>
|
||||
<div>{{ item.payment_history[item.payment_history.length-1].payment_reference }}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div v-if="item.documents.length">
|
||||
<div v-for="file in item.documents[0].files" v-bind:key="file.id" class="col-auto no-padding">
|
||||
<document-file-viewer-component :file="file">
|
||||
<template slot="button">
|
||||
<div class="btn no-border muted" :class="{'btn-info': section === 'customerPaidInvoiceComponent', 'btn-success': section !== 'customerPaidInvoiceComponent'}">
|
||||
<i class="fa fa-file-pdf-o"></i>
|
||||
</div>
|
||||
</template>
|
||||
</document-file-viewer-component>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="btn bg-grey no-border muted invisible">
|
||||
<i class="fa fa-file-pdf-o"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
selectedInvoice: {
|
||||
type: Array,
|
||||
required: false,
|
||||
},
|
||||
section:{
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
invoices: {
|
||||
type: Array,
|
||||
},
|
||||
isPaidInvoices :{
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
items: this.invoices,
|
||||
expanded: false,
|
||||
selectedValue: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
activate(){
|
||||
// this.select = !this.select;
|
||||
this.$emit('input', this.invoices);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
invoices: function() {
|
||||
this.items = this.invoices;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selected() {
|
||||
var response = false;
|
||||
this.selectedInvoice.forEach((value, index) => {
|
||||
this.items.forEach(item => {
|
||||
if (item.id === value.id) {
|
||||
response = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -164,7 +164,7 @@
|
||||
},
|
||||
company_module_id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
// required: true,
|
||||
},
|
||||
selectedIds: {
|
||||
type: Array,
|
||||
|
||||
@@ -25,12 +25,21 @@
|
||||
@if($status === 2)
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
@if(!$result)
|
||||
<div class="row m-b-10">
|
||||
<div class="col">
|
||||
<h5 class="semi-bold text-success">Your Payment is Unsuccessful</h5>
|
||||
<p class="hint-text">Please contact our customer service.</p>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="row m-b-10">
|
||||
<div class="col">
|
||||
<h5 class="semi-bold text-success">Your Payment is Successful</h5>
|
||||
<p class="hint-text">Thank you for your payment. The amount paid has been successfully applied to your invoice.</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
<div class="row m-b-20">
|
||||
<div class="col">
|
||||
@if($marking)
|
||||
|
||||
@@ -42,6 +42,7 @@ Route::group(['prefix' => 'transactions', 'namespace' => 'Transactions', 'as' =>
|
||||
Route::group(['prefix' => 'groups', 'as' => 'group.'], function () {
|
||||
Route::post('/{transaction_ids}/create', 'CreateGroupsController@create')->name('create');
|
||||
Route::get('/list', 'ListGroupsController@list')->name('list');
|
||||
// Route::get('/list', 'ListGroupsController@list')->middleware('storage.invoice.check.bygroup')->name('list');
|
||||
Route::delete('/{id}/delete', 'DeleteGroupController@delete')->name('delete');
|
||||
Route::put('/{id}/update', 'UpdateGroupController@update')->name('update');
|
||||
// Route::post('/{id}/approve', 'CreateBulkPurchaseOrderDocumentController@aprove')->name('approve');
|
||||
|
||||
Reference in New Issue
Block a user