Merge branch 'clear-booking-backlog' of gitlab.com:CIEFWorldwideSdnBhd/exchange-2.0 into refund-booking

This commit is contained in:
JiaSheng
2024-01-10 21:15:37 +08:00
3 changed files with 274 additions and 0 deletions
@@ -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,153 @@
<?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::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}");
}
}
// 3. Cancel fully refunded payment & cancel booking
$transactions = Transaction::where('type', TransactionType::CREDIT_NOTE)->get();
foreach ($transactions as $transaction) {
// get the booking marking
$marking = substr($transaction->payment_reference, -5);
// for a special payment reference on transaction id: 152013
if (!is_numeric($marking)) {
$payment_reference = explode(" ", trim($transaction->payment_reference));
if (count($payment_reference) > 1) {
$marking = $payment_reference[count($payment_reference) - 2];
}
}
if (is_numeric($marking)) {
$booking = Booking::where('marking', $marking)->first();
if ($booking) {
if ($booking->status === ApprovalStatus::APPROVED) {
$bookingPayment = $booking->transactions()->payments()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->first();
if (!$bookingPayment) {
$bookingPayment = $booking->transactions()->payments()->count();
Log::info("Credit note transaction id: {$transaction->id}, there are {$bookingPayment} payment for the booking.");
$bookingPayment = $booking->transactions()->payments()->whereIn('status', [ApprovalStatus::EXPIRED, ApprovalStatus::REJECTED])->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);
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}");
}
} else {
$status = ApprovalStatus::APPROVAL_STATUS_ID[$booking->status];
Log::info("Credit note transaction id: {$transaction->id}, booking status is {$status}");
}
} 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}");
}
}
}
}