mirror of
https://gitlab.com/CIEFWorldwideSdnBhd/exchange-2.0.git
synced 2026-08-22 05:53:58 +00:00
Merge branch 'refund-booking' of gitlab.com:CIEFWorldwideSdnBhd/exchange-2.0 into development
# Conflicts: # resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\General\Eloquent\Filters;
|
||||
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class IsNotFullyRefunded implements Filter
|
||||
{
|
||||
|
||||
/**
|
||||
* @param Builder $builder
|
||||
* @param $value
|
||||
* @return Builder|mixed
|
||||
*/
|
||||
public static function apply(Builder $builder, $value)
|
||||
{
|
||||
return $builder->withSum(['transactions as total_refund_amount' => function($q) {
|
||||
$q->refunds()->where('status', ApprovalStatus::APPROVED);
|
||||
}], 'original_amount')
|
||||
->having('total_refund_amount', '<', DB::raw('original_amount'));
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount;
|
||||
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
|
||||
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
|
||||
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionRefundCalculationObject;
|
||||
use App\Classes\ValueObjects\Constants\PaymentMethodType;
|
||||
|
||||
class CreateBookingRefundLogic extends AbstractControllerLogic
|
||||
{
|
||||
@@ -80,25 +81,30 @@ class CreateBookingRefundLogic extends AbstractControllerLogic
|
||||
|
||||
$transaction = $this->fetchesTransaction->execute(['id' => $request->route('payment_id')]);
|
||||
|
||||
if ($transaction->transactions()->bills()->first()) {
|
||||
throw new MalformedRequestException('Booking under white form cannot request for refund');
|
||||
}
|
||||
|
||||
$booking = $transaction->owner;
|
||||
|
||||
$billNumber = $this->generatesTransactionBillNumber->execute('RFD-');
|
||||
|
||||
$refund = $transaction->transactions()->refunds()->sum('amount');
|
||||
$refund = $transaction->transactions()->refunds()->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED])->sum('original_amount');
|
||||
|
||||
if($refund + $request->input('amount') > $transaction->original_amount) throw new MalformedRequestException('Your refund must not be greater than '. $transaction->original_amount .'.');
|
||||
|
||||
$amount = $transaction->booking->fix_currency_id == 1 ? $request->input('amount') : $request->input('amount') / $transaction->currency_rate;
|
||||
// $transactionRefundCalculationObject = new TransactionRefundCalculationObject($booking, $transaction, $request->input('amount'));
|
||||
// $transactionRefundCalculationObject->init();
|
||||
|
||||
|
||||
$transactionRefundCalculationObject = new TransactionRefundCalculationObject($booking, $transaction, $amount);
|
||||
$transactionRefundCalculationObject->init();
|
||||
$refundAmount = bcdiv($request->input('amount'), $transaction->currency_rate, 7);
|
||||
// refund service charges if is fully refund
|
||||
$refundTotal = ($refund + $request->input('amount')) == $transaction->original_amount ? $refundAmount + $transaction->service_charge + $transaction->tax : $refundAmount;
|
||||
|
||||
$object = new TransactionObject($billNumber, TransactionType::REFUND, 1, $booking->company->id,
|
||||
1, $transactionRefundCalculationObject->getConversionObject()->getPaymentMethod(),
|
||||
$transactionRefundCalculationObject->getRefundTotalAmount(), $transactionRefundCalculationObject->getAmount(), 1,
|
||||
$transactionRefundCalculationObject->getConversionObject()->getCurrencyId(), $transactionRefundCalculationObject->getTransaction()->currency_rate,
|
||||
$transactionRefundCalculationObject->getRefundTax(), $transactionRefundCalculationObject->getRefundServiceCharge(), null, ApprovalStatus::PENDING_VERIFICATION, [], $transaction->bill_no);
|
||||
1, PaymentMethodType::CASH,
|
||||
$refundTotal, $request->input('amount'), 1,
|
||||
$transaction->original_currency_id, $transaction->currency_rate,
|
||||
0, 0, null, ApprovalStatus::PENDING_VERIFICATION, [], $transaction->bill_no);
|
||||
|
||||
$transaction = $this->createsTransaction->execute($transaction, $object);
|
||||
|
||||
|
||||
@@ -125,6 +125,8 @@ class ExportsInvoiceTransactions implements FromQuery, WithHeadings, WithHeading
|
||||
$textToAppend = Carbon::now()->format('[Y-m-d H:i:s]') . ' Shipping Portal Respnose ' . json_encode($row) . PHP_EOL;
|
||||
file_put_contents($errorFilePath, $textToAppend, FILE_APPEND);
|
||||
|
||||
$row = $row[0];
|
||||
|
||||
return [
|
||||
'<<New>>',
|
||||
Carbon::parse($row['created_at'])->format('m/d/Y H:m'),
|
||||
|
||||
+24
-1
@@ -3,6 +3,7 @@
|
||||
|
||||
namespace App\Classes\Modules\Transactions\ControllersLogic;
|
||||
|
||||
use App\Classes\Exceptions\MalformedRequestException;
|
||||
use App\Classes\Modules\Transactions\Processors\CreateSupplierTransactionProcessor;
|
||||
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
|
||||
use App\Models\Document;
|
||||
@@ -18,6 +19,7 @@ use App\Classes\General\Abstracts\AbstractControllerLogic;
|
||||
use App\Classes\Modules\Companies\Services\FetchesCompany;
|
||||
use App\Classes\Modules\Documents\Services\CreatesDocument;
|
||||
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
|
||||
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
|
||||
|
||||
class CreateSupplierTransactionLogic extends AbstractControllerLogic
|
||||
{
|
||||
@@ -48,6 +50,9 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic
|
||||
/** @var GeneratesTransactionBillNumber */
|
||||
private $generatesTransactionBillNumber;
|
||||
|
||||
/** @var FetchesTransaction */
|
||||
private $fetchesTransaction;
|
||||
|
||||
|
||||
/**
|
||||
* CreateSupplierTransactionLogic constructor.
|
||||
@@ -56,14 +61,16 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic
|
||||
* @param CreatesDocument $createsDocument
|
||||
* @param CreatesFiles $createsFile
|
||||
* @param GeneratesTransactionBillNumber $generatesTransactionBillNumber
|
||||
* @param FetchesTransaction $fetchesTransaction
|
||||
*/
|
||||
public function __construct(FetchesCompany $fetchesCompany, CreateSupplierTransactionProcessor $createSupplierTransactionProcessor, CreatesDocument $createsDocument, CreatesFiles $createsFile, GeneratesTransactionBillNumber $generatesTransactionBillNumber)
|
||||
public function __construct(FetchesCompany $fetchesCompany, CreateSupplierTransactionProcessor $createSupplierTransactionProcessor, CreatesDocument $createsDocument, CreatesFiles $createsFile, GeneratesTransactionBillNumber $generatesTransactionBillNumber, FetchesTransaction $fetchesTransaction)
|
||||
{
|
||||
$this->fetchesCompany = $fetchesCompany;
|
||||
$this->createSupplierTransactionProcessor = $createSupplierTransactionProcessor;
|
||||
$this->createsDocument = $createsDocument;
|
||||
$this->createsFile = $createsFile;
|
||||
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
|
||||
$this->fetchesTransaction = $fetchesTransaction;
|
||||
}
|
||||
|
||||
public function logic(Request $request) : JsonResponse
|
||||
@@ -75,6 +82,22 @@ class CreateSupplierTransactionLogic extends AbstractControllerLogic
|
||||
|
||||
$payments = $request->input('payments');
|
||||
|
||||
foreach($payments as $payment){
|
||||
$payment = $this->fetchesTransaction->execute(['id' => $payment['id']]);
|
||||
|
||||
$pendingRefundRequest = $payment->transactions()->refunds()->where('status', ApprovalStatus::PENDING_VERIFICATION)->first();
|
||||
|
||||
if ($pendingRefundRequest) {
|
||||
throw new MalformedRequestException('Unable to create supplier order for pending refund request payment');
|
||||
}
|
||||
|
||||
$totalRefund = $payment->transactions()->refunds()->where('status', ApprovalStatus::APPROVED)->sum('original_amount');
|
||||
|
||||
if ($payment->original_amount - $totalRefund <= 0) {
|
||||
throw new MalformedRequestException('Unable to create supplier order for fully refunded payment');
|
||||
}
|
||||
}
|
||||
|
||||
$this->createSupplierTransactionProcessor->execute($supplier, $rate, $payments);
|
||||
|
||||
if(!count($this->createSupplierTransactionProcessor->getBills())) return $this->response([]);
|
||||
|
||||
+3
-1
@@ -73,7 +73,9 @@ class UpdateRefundTransactionStatusLogic extends AbstractControllerLogic
|
||||
|
||||
$booking = $transaction->owner->owner;
|
||||
|
||||
$reference = 'Credit Voucher for Overpaid for Ref. '.$booking->marking;
|
||||
$paymentTransaction = $transaction->owner;
|
||||
|
||||
$reference = $transaction->amount == $paymentTransaction->amount ? 'Fully Refund for Ref. ' . $booking->marking : 'Partially Refund for Ref. ' . $booking->marking;
|
||||
|
||||
if ($transaction->status == ApprovalStatus::APPROVED) {
|
||||
$this->creditWalletProcessor->execute($booking->company, $transaction->type, $transaction->amount, $reference);
|
||||
|
||||
@@ -81,15 +81,19 @@ class CreateSupplierTransactionProcessor
|
||||
|
||||
if($payment->status !== ApprovalStatus::APPROVED) continue;
|
||||
|
||||
$totalRefund = $payment->transactions()->refunds()->where('status', ApprovalStatus::APPROVED)->sum('original_amount');
|
||||
|
||||
$original_amount_after_refund = $payment->original_amount - $totalRefund;
|
||||
|
||||
$this->updatesTransactionStatus->execute($payment, ApprovalStatus::COMPLETED);
|
||||
$billNumber = $this->generatesTransactionBillNumber->execute('SPLR-');
|
||||
$constant = SegmentConstant::where('reference', SegmentConstants::SERVICE_CHARGE)->where('detail->id', $supplier->id)->first();
|
||||
|
||||
$serviceCharge = $this->calculatesTransactionServiceCharge->execute($payment->original_amount, $rate, $constant);
|
||||
$serviceCharge = $this->calculatesTransactionServiceCharge->execute($original_amount_after_refund, $rate, $constant);
|
||||
|
||||
$object = new TransactionObject($billNumber, TransactionType::BILL, $supplier->id, 1,
|
||||
$supplier->banks()->where('default', true)->first()->id, PaymentMethodType::CASH,
|
||||
$payment->original_amount * (1 / $rate), $payment->original_amount, 1, $payment->original_currency_id,
|
||||
$original_amount_after_refund * (1 / $rate), $original_amount_after_refund, 1, $payment->original_currency_id,
|
||||
$rate, 0, $serviceCharge, null, ApprovalStatus::PENDING_SUBMISSION);
|
||||
|
||||
/** @var Transaction $billTransaction */
|
||||
@@ -101,7 +105,7 @@ class CreateSupplierTransactionProcessor
|
||||
$transferFee = $this->calculatesTransactionTransferFee->execute($billTransaction->original_amount, $constant);
|
||||
$object = new TransactionObject($transferFeeNumber, TransactionType::TRANSFER_FEE, 1, $supplier->id,
|
||||
$supplier->banks()->where('default', true)->first()->id, PaymentMethodType::CASH,
|
||||
$payment->original_amount, $payment->original_amount, $payment->original_currency_id, $payment->original_currency_id,
|
||||
$original_amount_after_refund, $original_amount_after_refund, $payment->original_currency_id, $payment->original_currency_id,
|
||||
1, 0, $transferFee, null, ApprovalStatus::PENDING_VERIFICATION);
|
||||
|
||||
$this->pushTransferFee($this->createsTransaction->execute($billTransaction, $object));
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Classes\ValueObjects\Constants\TransactionType;
|
||||
use App\Models\Booking;
|
||||
use Illuminate\Console\Command;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Classes\Modules\Transactions\Processors\CreatePurchaseOrderTransactionProcessor;
|
||||
use App\Classes\Modules\Transactions\Services\GeneratesPurchaseOrderProducts;
|
||||
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
|
||||
use App\Classes\ValueObjects\Constants\PaymentMethodType;
|
||||
use App\Models\Transaction;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class AutoFillPurchaseOrderCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'purchaseOrder:autoFill';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Auto fill up the purchase order for booking that have payment';
|
||||
|
||||
/** @var GeneratesPurchaseOrderProducts */
|
||||
private $generatesPurchaseOrderProducts;
|
||||
|
||||
/** @var GeneratesTransactionBillNumber */
|
||||
private $generatesTransactionBillNumber;
|
||||
|
||||
/** @var CreatePurchaseOrderTransactionProcessor */
|
||||
private $createPurchaseOrderTransactionProcessor;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(GeneratesPurchaseOrderProducts $generatesPurchaseOrderProducts, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatePurchaseOrderTransactionProcessor $createPurchaseOrderTransactionProcessor)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->generatesPurchaseOrderProducts = $generatesPurchaseOrderProducts;
|
||||
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
|
||||
$this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// 5. If purchase order not fill up in 2 month, auto fill up it
|
||||
$bookings = Booking::where('status', ApprovalStatus::APPROVED)
|
||||
->where('created_at', '<', now()->subDays(60)->endOfDay())
|
||||
->whereHas('transactions', function($transaction) {
|
||||
return $transaction->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
|
||||
})
|
||||
->whereDoesntHave('transactions', function($transaction){
|
||||
$transaction->where('type', TransactionType::PURCHASE_ORDER);
|
||||
$transaction->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED]);
|
||||
})->get();
|
||||
|
||||
foreach ($bookings as $booking) {
|
||||
$po = Transaction::where('type', TransactionType::PURCHASE_ORDER)
|
||||
->where('status', ApprovalStatus::APPROVED)->where('issuer', $booking->company_id)
|
||||
->select('*', DB::raw('abs(amount - ' . $booking->fix_amount . ') as nearest_price'))->orderBy('nearest_price')->first();
|
||||
|
||||
|
||||
if (!$po) {
|
||||
$po = Transaction::where('type', TransactionType::PURCHASE_ORDER)
|
||||
->where('status', ApprovalStatus::APPROVED)->select('*', DB::raw('abs(amount - ' . $booking->fix_amount . ') as nearest_price'))->orderBy('nearest_price')->first();
|
||||
}
|
||||
|
||||
$products = $this->generatesPurchaseOrderProducts->execute($po, $booking->fix_amount);
|
||||
|
||||
$deference = $booking->fix_amount - $products->sum('total');
|
||||
|
||||
if($deference > -150 && $deference < 150 && $deference != 0) {
|
||||
|
||||
$products->push([
|
||||
'description' => $deference < 0 ? 'Discount':'Shipping Fee',
|
||||
'quantity' => 1,
|
||||
'stockCode' => '',
|
||||
'total' => $deference,
|
||||
'unit_price' => $deference
|
||||
]);
|
||||
}
|
||||
|
||||
$billNumber = $this->generatesTransactionBillNumber->execute('XPO-');
|
||||
|
||||
$total = $products->sum('total');
|
||||
|
||||
$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->toArray());
|
||||
|
||||
$this->createPurchaseOrderTransactionProcessor->execute($booking, $object);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Classes\ValueObjects\Constants\TransactionType;
|
||||
use App\Models\Booking;
|
||||
use Illuminate\Console\Command;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Classes\Modules\Bookings\Services\UpdatesBookingStatus;
|
||||
use App\Models\Transaction;
|
||||
|
||||
class ExpiredBookingCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'booking:expired';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Expiring booking that do not have further action by user';
|
||||
|
||||
/** @var UpdatesBookingStatus */
|
||||
private $updatesBookingStatus;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(UpdatesBookingStatus $updatesBookingStatus)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->updatesBookingStatus = $updatesBookingStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// 1. Cancel booking without payment & purchase order (1 month)
|
||||
$bookings = Booking::where('status', ApprovalStatus::APPROVED)
|
||||
->where('created_at', '<', now()->subDays(30)->endOfDay())
|
||||
->where(function ($query) {
|
||||
$query->whereDoesntHave('transactions')
|
||||
->orWhereDoesntHave('transactions', function($transaction) {
|
||||
return $transaction->where('type', TransactionType::PURCHASE_ORDER)->orWhere(function ($q) {
|
||||
$q->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
|
||||
});
|
||||
});
|
||||
})->get();
|
||||
|
||||
foreach ($bookings as $booking) {
|
||||
$this->updatesBookingStatus->execute($booking, ApprovalStatus::EXPIRED);
|
||||
Log::info("Expired Booking without payment & purchase order, booking id: " . $booking->id);
|
||||
$transactions = $booking->transactions;
|
||||
|
||||
foreach ($transactions as $transaction) {
|
||||
$transaction->status = ApprovalStatus::EXPIRED;
|
||||
$transaction->save();
|
||||
Log::info("Expired Transaction id: {$transaction->id} from Booking id: {$booking->id}");
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Cancel booking without payment but with purchase order (2 month)
|
||||
$bookings = Booking::where('status', ApprovalStatus::APPROVED)
|
||||
->where('created_at', '<', now()->subDays(60)->endOfDay())
|
||||
->where(function ($query) {
|
||||
$query->whereDoesntHave('transactions', function($transaction) {
|
||||
return $transaction->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
|
||||
})->whereHas('transactions', function($transaction) {
|
||||
return $transaction->where('type', TransactionType::PURCHASE_ORDER);
|
||||
});
|
||||
})->get();
|
||||
|
||||
foreach ($bookings as $booking) {
|
||||
$this->updatesBookingStatus->execute($booking, ApprovalStatus::EXPIRED);
|
||||
Log::info("Expired Booking without payment but with purchase order, booking id: " . $booking->id);
|
||||
$transactions = $booking->transactions;
|
||||
|
||||
foreach ($transactions as $transaction) {
|
||||
$transaction->status = ApprovalStatus::EXPIRED;
|
||||
$transaction->save();
|
||||
Log::info("Expired Transaction id: {$transaction->id} from Booking id: {$booking->id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Classes\ValueObjects\Constants\TransactionType;
|
||||
use App\Models\Booking;
|
||||
use Illuminate\Console\Command;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Classes\Modules\Bookings\Services\UpdatesBookingStatus;
|
||||
use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject;
|
||||
use App\Classes\Modules\Transactions\Services\CreatesTransaction;
|
||||
use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber;
|
||||
use App\Classes\ValueObjects\Constants\PaymentMethodType;
|
||||
use App\Models\Transaction;
|
||||
|
||||
class ExpiredRefundedBookingCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'RefundedBooking:expired';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Expiring refunded booking';
|
||||
|
||||
/** @var UpdatesBookingStatus */
|
||||
private $updatesBookingStatus;
|
||||
|
||||
/** @var GeneratesTransactionBillNumber */
|
||||
private $generatesTransactionBillNumber;
|
||||
|
||||
/** @var CreatesTransaction */
|
||||
private $createsTransaction;
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(UpdatesBookingStatus $updatesBookingStatus, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->updatesBookingStatus = $updatesBookingStatus;
|
||||
$this->generatesTransactionBillNumber = $generatesTransactionBillNumber;
|
||||
$this->createsTransaction = $createsTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// 3. Cancel fully refunded payment & cancel booking
|
||||
$transactions = Transaction::where('type', TransactionType::CREDIT_NOTE)->where('payment_reference', 'LIKE', "%refund%")->get();
|
||||
|
||||
foreach ($transactions as $transaction) {
|
||||
// get the booking marking
|
||||
$payment_reference = explode(" ", trim($transaction->payment_reference));
|
||||
// $marking = substr($transaction->payment_reference, -5);
|
||||
$marking = trim(end($payment_reference));
|
||||
|
||||
if (!preg_match('/^[0-9]+$/', $marking)) {
|
||||
$payment_reference = explode(".", trim($transaction->payment_reference));
|
||||
$marking = trim(end($payment_reference));
|
||||
}
|
||||
|
||||
// for a special payment reference on transaction id: 140231
|
||||
if (!preg_match('/^[0-9]+$/', $marking)) {
|
||||
$payment_reference = explode("No", trim($transaction->payment_reference));
|
||||
$marking = end($payment_reference);
|
||||
}
|
||||
|
||||
// for a special payment reference on transaction id: 152013
|
||||
if (!preg_match('/^[0-9]+$/', $marking)) {
|
||||
$payment_reference = explode(" ", trim($transaction->payment_reference));
|
||||
$marking = end($payment_reference);
|
||||
$marking = prev($payment_reference);
|
||||
}
|
||||
|
||||
if (preg_match('/^[0-9]+$/', $marking)) {
|
||||
$booking = Booking::where('marking', $marking)->first();
|
||||
|
||||
if ($booking) {
|
||||
$bookingPayment = $booking->transactions()->payments()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->first();
|
||||
if (!$bookingPayment) {
|
||||
$bookingPaymentCount = $booking->transactions()->payments()->count();
|
||||
if ($bookingPaymentCount > 1) {
|
||||
Log::info("Credit note transaction id: {$transaction->id}, there are {$bookingPaymentCount} payment for the booking.");
|
||||
foreach ($booking->transactions()->payments()->get() as $bp) {
|
||||
if ($transaction->amount - $bp->amount < 0.01) {
|
||||
$bookingPayment = $bp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bookingPayment) {
|
||||
$bookingPayment = $booking->transactions()->payments()->whereIn('status', [ApprovalStatus::SUSPENDED, ApprovalStatus::EXPIRED, ApprovalStatus::REJECTED])->orderBy('id', 'DESC')->first();
|
||||
}
|
||||
$status = ApprovalStatus::APPROVAL_STATUS_ID[$bookingPayment->status];
|
||||
Log::info("Credit note transaction id: {$transaction->id}, the payment for the booking is in status {$status}");
|
||||
}
|
||||
$bookingPaymentAmount = $bookingPayment->amount;
|
||||
// check if the booking is fully refund
|
||||
$amountDifference = bcsub($transaction->amount, $bookingPaymentAmount, 7);
|
||||
|
||||
if (abs($amountDifference) < 0.01) {
|
||||
// rejecting booking payment transaction
|
||||
// $bookingPayment->status = ApprovalStatus::REJECTED;
|
||||
// $bookingPayment->save();
|
||||
|
||||
//expired booking
|
||||
// $this->updatesBookingStatus->execute($booking, ApprovalStatus::EXPIRED);
|
||||
Log::info("Credit note transaction id: {$transaction->id} is fully refunded, the refunded amount was {$transaction->amount} the payment reference is: {$transaction->payment_reference}");
|
||||
// Log::info("Credit note transaction id: {$transaction->id}, Rejected Booking Transaction Payment id: {$bookingPayment->id}, the payment amount was {$bookingPayment->amount}");
|
||||
// Log::info("Credit note transaction id: {$transaction->id}, Expired Booking id: {$booking->id}");
|
||||
} else {
|
||||
Log::info("Credit note transaction id: {$transaction->id} is not fully refunded, the refunded amount was {$transaction->amount}, the payment amount was {$bookingPayment->amount}, the payment reference is: {$transaction->payment_reference}");
|
||||
}
|
||||
|
||||
$refund = $bookingPayment->transactions()->refunds()->where('amount', $transaction->amount)->where('status', ApprovalStatus::APPROVED)->first();
|
||||
|
||||
$bookingInWhiteForm = $bookingPayment->transactions()->bills()->first();
|
||||
|
||||
if ($refund) {
|
||||
Log::info("Credit note transaction id: {$transaction->id}, already created same amount of refund transaction for same booking payment transaction");
|
||||
}
|
||||
|
||||
if ($bookingInWhiteForm) {
|
||||
Log::info("Credit note transaction id: {$transaction->id}, booking is in white form");
|
||||
}
|
||||
|
||||
if (!$refund && !$bookingInWhiteForm) {
|
||||
$billNumber = $this->generatesTransactionBillNumber->execute('RFD-');
|
||||
|
||||
$object = new TransactionObject($billNumber, TransactionType::REFUND, 1, $booking->company->id,
|
||||
1, PaymentMethodType::CASH,
|
||||
$transaction->amount, $transaction->amount * $bookingPayment->currency_rate, 1,
|
||||
$bookingPayment->original_currency_id, $bookingPayment->currency_rate,
|
||||
0, 0, null, ApprovalStatus::APPROVED, [], $bookingPayment->bill_no);
|
||||
|
||||
$transaction = $this->createsTransaction->execute($bookingPayment, $object);
|
||||
}
|
||||
} else {
|
||||
Log::info("Credit note transaction id: {$transaction->id}, booking marking not found, the payment reference is: {$transaction->payment_reference}");
|
||||
}
|
||||
} else {
|
||||
Log::info("Credit note transaction id: {$transaction->id} does not have booking marking, the payment reference is: {$transaction->payment_reference}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,14 @@ class Kernel extends ConsoleKernel
|
||||
->hourly()
|
||||
->appendOutputTo(storage_path().'/logs/delete-bulk-download-files.log')
|
||||
->withoutOverlapping();
|
||||
|
||||
$schedule->command('booking:expired')
|
||||
->dailyAt('02:00')
|
||||
->withoutOverlapping();
|
||||
|
||||
$schedule->command('purchaseOrder:autoFill')
|
||||
->dailyAt('03:00')
|
||||
->withoutOverlapping();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="col-auto p-l-0" v-if="totalRefunds !== 0">
|
||||
<div class="font-heading fs-8 muted all-caps">Refunded Amount</div>
|
||||
<div class="font-heading fs-10 bold text-danger">
|
||||
{{item.original_currency.short_code}} {{(Math.round((totalRefunds + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
|
||||
{{item.currency.short_code}} {{(Math.round((totalConvertRefunds + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -133,7 +133,13 @@
|
||||
<div class="font-heading all-caps fs-10">Requested Refund Amount</div>
|
||||
</div>
|
||||
<div class="col-auto text-right">
|
||||
<div class="font-heading fs-10">{{item.currency.short_code}} {{(Math.round((totalRequestedRefund + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
|
||||
<div class="font-heading fs-10">{{item.original_currency.short_code}} {{(Math.round((totalRequestedRefund + 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 bold text-danger" v-if="totalRequestedRefund != 0">
|
||||
<div class="col"></div>
|
||||
<div class="col-auto text-right">
|
||||
<div class="font-heading fs-10">{{item.currency.short_code}} {{(Math.round((totalRequestedConvertRefund + 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 bold text-danger" v-if="totalRefunds != 0">
|
||||
@@ -144,6 +150,12 @@
|
||||
<div class="font-heading fs-12">{{item.original_currency.short_code}} {{(Math.round((totalRefunds + 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 bold text-danger" v-if="totalRefunds != 0">
|
||||
<div class="col"></div>
|
||||
<div class="col-auto text-right">
|
||||
<div class="font-heading fs-12">{{item.currency.short_code}} {{(Math.round((totalConvertRefunds + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-end bold m-b-10 text-primary">
|
||||
<div class="col">
|
||||
<div class="font-heading all-caps fs-10">Rate</div>
|
||||
@@ -187,6 +199,14 @@
|
||||
<div class="font-heading fs-12">MYR {{(Math.round((item.amount + 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 bold text-success" v-if="totalRefunds != 0">
|
||||
<div class="col">
|
||||
<div class="font-heading all-caps fs-10">Your Payment After Refund</div>
|
||||
</div>
|
||||
<div class="col-auto text-right">
|
||||
<div class="font-heading fs-12">MYR {{(Math.round((item.amount - totalConvertRefunds + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="font-heading all-caps fs-10 m-b-5">Your Payment Proof</div>
|
||||
@@ -282,14 +302,55 @@
|
||||
</modal-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-t-10" v-show="[2, 3].includes(item.status) && totalRequestedConvertRefund < data.booking.amount">
|
||||
<div class="col">
|
||||
<button class="btn btn-xs all-caps b-rad-none bg-master-lighter btn-block no-border requestModal hide" data-type="transferSummary">Request Refund</button>
|
||||
<div class="row m-t-10" v-show="[2, 3].includes(item.status) && (totalRequestedRefund + totalRefunds) < data.booking.amount">
|
||||
<div class="col" v-if="!item.transaction_bill">
|
||||
<button class="btn btn-xs all-caps b-rad-none bg-master-lighter btn-block no-border requestModal" data-type="transferSummary">Request Refund</button>
|
||||
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="transferSummary" size="large">
|
||||
<refund-confirmation-component :data="data" :section="section" :totalRefunds="totalRequestedRefund + totalRefunds"></refund-confirmation-component>
|
||||
</modal-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-t-10" v-if="data.transaction_refunds.length > 0">
|
||||
<div class="col">
|
||||
<button class="btn btn-xs all-caps b-rad-none bg-master-lighter btn-block no-border" @click="clickExpandRefundTransactions">Show Refund Transactions</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" v-if="data.transaction_refunds.length > 0 && expandRefundTransactions">
|
||||
<div class="col bg-white padding-15">
|
||||
<div v-for="(refund, index) in data.transaction_refunds">
|
||||
<div class="row align-items-end m-b-10 bold">
|
||||
<div class="col">
|
||||
<div class="font-heading all-caps fs-10">{{ index + 1 }}. Refund updated on</div>
|
||||
</div>
|
||||
<div class="col-auto text-right">
|
||||
<div class="font-heading fs-10">{{ refund.updated_at }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-end m-b-10 bold">
|
||||
<div class="col">
|
||||
<div class="font-heading all-caps fs-10"> Refund status</div>
|
||||
</div>
|
||||
<div class="col-auto text-right">
|
||||
<div class="font-heading fs-10" :class="[{'text-warning': refund.status === 1}, {'text-success': refund.status === 2}, {'text-danger': refund.status === 4}]">{{ refund.status === 1 ? 'Pending Verification' : refund.status === 2 ? 'Approved' : 'Rejected'}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-end m-b-10 bold text-danger">
|
||||
<div class="col">
|
||||
<div class="font-heading all-caps fs-10"> Requested Refund Amount</div>
|
||||
</div>
|
||||
<div class="col-auto text-right">
|
||||
<div class="font-heading fs-10">{{refund.original_currency.short_code}} {{(Math.round((refund.original_amount + 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 bold text-danger">
|
||||
<div class="col"></div>
|
||||
<div class="col-auto text-right">
|
||||
<div class="font-heading fs-10">{{refund.currency.short_code}} {{(Math.round((refund.amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -302,6 +363,7 @@
|
||||
data(){
|
||||
return {
|
||||
expandPaymentDetails: false,
|
||||
expandRefundTransactions: false,
|
||||
amount: (Math.round(1000 * 100) / 100).toFixed(2),
|
||||
parameters: {
|
||||
amount: (Math.round(1000 * 100) / 100).toFixed(2),
|
||||
@@ -324,12 +386,12 @@
|
||||
let vm = this;
|
||||
var TotalRequestedRefund = 0;
|
||||
this.data.transaction_refunds.forEach(function(refunds) {
|
||||
TotalRequestedRefund += refunds.status === 1 ? refunds.original_amount : 0;
|
||||
TotalRequestedRefund += refunds.status === 1 ? refunds.amount : 0;
|
||||
});
|
||||
if (vm.data.booking.fixed_currency.id != 1 && this.data.transaction_refunds[0]) {
|
||||
TotalRequestedRefund = (TotalRequestedRefund * this.data.transaction_refunds[0].currency_rate);
|
||||
}
|
||||
return ((Math.round((TotalRequestedRefund + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","));
|
||||
// if (vm.data.booking.fixed_currency.id != 1 && this.data.transaction_refunds[0]) {
|
||||
// TotalRequestedRefund = (TotalRequestedRefund * this.data.transaction_refunds[0].currency_rate);
|
||||
// }
|
||||
return TotalRequestedRefund;
|
||||
},
|
||||
totalRefunds() {
|
||||
var TotalRequestedRefund = 0;
|
||||
@@ -337,11 +399,22 @@
|
||||
TotalRequestedRefund += refunds.status === 2 ? refunds.original_amount : 0;
|
||||
});
|
||||
return TotalRequestedRefund;
|
||||
},
|
||||
totalConvertRefunds() {
|
||||
var TotalRequestedRefund = 0;
|
||||
this.data.transaction_refunds.forEach(function(refunds) {
|
||||
TotalRequestedRefund += refunds.status === 2 ? refunds.amount : 0;
|
||||
});
|
||||
return TotalRequestedRefund;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickExpand(){
|
||||
this.expandPaymentDetails = !this.expandPaymentDetails;
|
||||
this.expandRefundTransactions = false;
|
||||
},
|
||||
clickExpandRefundTransactions(){
|
||||
this.expandRefundTransactions = !this.expandRefundTransactions;
|
||||
},
|
||||
trackShipment(){
|
||||
window.open(route('booking.track_shipping_order', this.data.booking.marking) + '?token=' + this.$store.getters.getAccessToken, '_blank');
|
||||
|
||||
+21
-2
@@ -56,6 +56,16 @@
|
||||
<span class="flag-icon" :class="'flag-icon-'+item.original_currency.country.short_code.toLowerCase()"></span> {{item.original_currency.short_code}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col text-right" v-if="totalRefunds !== 0">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="font-heading fs-10 muted all-caps">Refunded Amount</div>
|
||||
<div class="font-heading fs-14 text-danger bold">
|
||||
{{(Math.round((totalRefunds + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
@@ -95,7 +105,7 @@
|
||||
<div class="col text-right">
|
||||
<div class="font-heading fs-10 muted all-caps">Amount</div>
|
||||
<div class="font-heading fs-14 text-success bold">
|
||||
{{(Math.round((item.original_amount + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
|
||||
{{(Math.round((item.original_amount - totalRefunds + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,11 +134,20 @@
|
||||
active: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalRefunds() {
|
||||
var TotalRequestedRefund = 0;
|
||||
this.data.transaction_refunds.forEach(function(refunds) {
|
||||
TotalRequestedRefund += refunds.status === 2 ? refunds.original_amount : 0;
|
||||
});
|
||||
return TotalRequestedRefund;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
activate(){
|
||||
this.active = !this.active;
|
||||
this.$emit('input', this.item)
|
||||
}
|
||||
},
|
||||
},
|
||||
mixins: [componentHandler]
|
||||
}
|
||||
|
||||
@@ -115,7 +115,12 @@
|
||||
computed: {
|
||||
total(){
|
||||
return this.payments.reduce(function (total, currentValue) {
|
||||
return total + currentValue.original_amount;
|
||||
return total + currentValue.original_amount - currentValue.transaction_refunds.reduce(function (totalRefund, refundTransaction) {
|
||||
if (refundTransaction.status === 2) {
|
||||
return totalRefund + refundTransaction.original_amount;
|
||||
}
|
||||
return totalRefund;
|
||||
}, 0);
|
||||
}, 0);
|
||||
|
||||
},
|
||||
|
||||
+2
-2
@@ -117,7 +117,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<list-component ref="pendingOrdersList" section="pendingOrdersSection" :endpoint="route('api.transaction.list')" :options="{per_page: 5, status: 2, owner_type: 'App\\Models\\Booking', type: 1, original_currency_id_in: [selectedCurrency.id], transaction_service_id: selectedService.id}">
|
||||
<list-component ref="pendingOrdersList" section="pendingOrdersSection" :endpoint="route('api.transaction.list')" :options="{per_page: 5, status: 2, owner_type: 'App\\Models\\Booking', is_not_fully_refunded: true, type: 1, original_currency_id_in: [selectedCurrency.id], transaction_service_id: selectedService.id}">
|
||||
<template slot="list" slot-scope="{data}">
|
||||
<supplier-pending-order-component :data="data" v-on:input="updateOrder($event)"></supplier-pending-order-component>
|
||||
</template>
|
||||
@@ -195,7 +195,7 @@
|
||||
},
|
||||
updateList(){
|
||||
|
||||
this.$refs.pendingOrdersList.updateFilters({per_page: 10000, status: 2, type: 1, original_currency_id_in: [this.selectedCurrency.id], transaction_service_id: this.selectedService.id});
|
||||
this.$refs.pendingOrdersList.updateFilters({per_page: 10000, status: 2, type: 1, is_not_fully_refunded: true, original_currency_id_in: [this.selectedCurrency.id], transaction_service_id: this.selectedService.id});
|
||||
|
||||
this.selectedSupplier.status = false;
|
||||
this.currencyDropdownLaunch.status = false;
|
||||
|
||||
Reference in New Issue
Block a user