From cd80950d65bb5552d921a1c0e44be6611d89dbe6 Mon Sep 17 00:00:00 2001 From: JiaSheng Date: Sat, 23 Sep 2023 11:56:21 +0800 Subject: [PATCH 1/6] expired booking --- .../Commands/ExpiredBookingCommand.php | 95 +++++++++++++++++++ app/Console/Kernel.php | 4 + 2 files changed, 99 insertions(+) create mode 100644 app/Console/Commands/ExpiredBookingCommand.php diff --git a/app/Console/Commands/ExpiredBookingCommand.php b/app/Console/Commands/ExpiredBookingCommand.php new file mode 100644 index 00000000..2598b380 --- /dev/null +++ b/app/Console/Commands/ExpiredBookingCommand.php @@ -0,0 +1,95 @@ +updatesBookingStatus = $updatesBookingStatus; + } + + /** + * Execute the console command. + * + * @return int + */ + public function handle() + { + // 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}"); + } + } + + // 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}"); + } + } + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index cb9cf060..0355aea1 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -43,6 +43,10 @@ class Kernel extends ConsoleKernel ->everyMinute() ->appendOutputTo(storage_path().'/logs/regenerateInvoice.log') ->withoutOverlapping(); + + $schedule->command('booking:expired') + ->dailyAt('02:00') + ->withoutOverlapping(); } /** From 32602cab31318eb8813696fdd238a17181cfd38b Mon Sep 17 00:00:00 2001 From: JiaSheng Date: Sat, 23 Sep 2023 13:00:04 +0800 Subject: [PATCH 2/6] auto fill purchase order command --- .../Commands/AutoFillPurchaseOrderCommand.php | 112 ++++++++++++++++++ .../Commands/ExpiredBookingCommand.php | 4 +- 2 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 app/Console/Commands/AutoFillPurchaseOrderCommand.php diff --git a/app/Console/Commands/AutoFillPurchaseOrderCommand.php b/app/Console/Commands/AutoFillPurchaseOrderCommand.php new file mode 100644 index 00000000..059232d2 --- /dev/null +++ b/app/Console/Commands/AutoFillPurchaseOrderCommand.php @@ -0,0 +1,112 @@ +generatesPurchaseOrderProducts = $generatesPurchaseOrderProducts; + $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; + $this->createPurchaseOrderTransactionProcessor = $createPurchaseOrderTransactionProcessor; + } + + /** + * Execute the console command. + * + * @return int + */ + public function handle() + { + $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); + } + } +} diff --git a/app/Console/Commands/ExpiredBookingCommand.php b/app/Console/Commands/ExpiredBookingCommand.php index 2598b380..37fe8612 100644 --- a/app/Console/Commands/ExpiredBookingCommand.php +++ b/app/Console/Commands/ExpiredBookingCommand.php @@ -55,7 +55,7 @@ class ExpiredBookingCommand extends Command ->orWhereDoesntHave('transactions', function($transaction) { return $transaction->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]); }); - })->get(); + })->get(); foreach ($bookings as $booking) { $this->updatesBookingStatus->execute($booking, ApprovalStatus::EXPIRED); @@ -78,7 +78,7 @@ class ExpiredBookingCommand extends Command })->whereHas('transactions', function($transaction) { return $transaction->where('type', TransactionType::PURCHASE_ORDER); }); - })->get(); + })->get(); foreach ($bookings as $booking) { $this->updatesBookingStatus->execute($booking, ApprovalStatus::EXPIRED); From f1c23d46dde54f6cbcd768df15370d4972624b54 Mon Sep 17 00:00:00 2001 From: JiaSheng Date: Tue, 26 Sep 2023 21:30:51 +0800 Subject: [PATCH 3/6] update --- app/Console/Commands/AutoFillPurchaseOrderCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/Console/Commands/AutoFillPurchaseOrderCommand.php b/app/Console/Commands/AutoFillPurchaseOrderCommand.php index 059232d2..65a6fa93 100644 --- a/app/Console/Commands/AutoFillPurchaseOrderCommand.php +++ b/app/Console/Commands/AutoFillPurchaseOrderCommand.php @@ -16,7 +16,7 @@ use App\Classes\ValueObjects\Constants\PaymentMethodType; use App\Models\Transaction; use Illuminate\Support\Facades\DB; -class ExpiredBookingCommand extends Command +class AutoFillPurchaseOrderCommand extends Command { /** * The name and signature of the console command. @@ -61,6 +61,7 @@ class ExpiredBookingCommand extends Command */ 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) { From 75f44fae74126045ebf5ac58108e374596b7045e Mon Sep 17 00:00:00 2001 From: JiaSheng Date: Tue, 26 Sep 2023 21:31:09 +0800 Subject: [PATCH 4/6] update --- app/Console/Commands/ExpiredBookingCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/Console/Commands/ExpiredBookingCommand.php b/app/Console/Commands/ExpiredBookingCommand.php index 37fe8612..c5b4081c 100644 --- a/app/Console/Commands/ExpiredBookingCommand.php +++ b/app/Console/Commands/ExpiredBookingCommand.php @@ -47,7 +47,7 @@ class ExpiredBookingCommand extends Command */ public function handle() { - // Cancel booking without payment & purchase order (1 month) + // 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) { @@ -69,7 +69,7 @@ class ExpiredBookingCommand extends Command } } - // Cancel booking without payment but with purchase order (2 month) + // 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) { From 4be3b11a505c663a78f6f6abc42223f5fe0e1bc5 Mon Sep 17 00:00:00 2001 From: JiaSheng Date: Tue, 26 Sep 2023 21:32:20 +0800 Subject: [PATCH 5/6] add auto fill purchase order job inside kernel --- app/Console/Kernel.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 0355aea1..46e77f3a 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -47,6 +47,10 @@ class Kernel extends ConsoleKernel $schedule->command('booking:expired') ->dailyAt('02:00') ->withoutOverlapping(); + + $schedule->command('purchaseOrder:autoFill') + ->dailyAt('03:00') + ->withoutOverlapping(); } /** From a84cbdde6a09d0fecc4342f1014e868fae868f36 Mon Sep 17 00:00:00 2001 From: JiaSheng Date: Wed, 27 Sep 2023 14:12:32 +0800 Subject: [PATCH 6/6] add cancel booking for refunded payment --- .../Commands/ExpiredBookingCommand.php | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/app/Console/Commands/ExpiredBookingCommand.php b/app/Console/Commands/ExpiredBookingCommand.php index c5b4081c..d79b7b9c 100644 --- a/app/Console/Commands/ExpiredBookingCommand.php +++ b/app/Console/Commands/ExpiredBookingCommand.php @@ -9,6 +9,7 @@ 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 { @@ -91,5 +92,62 @@ class ExpiredBookingCommand extends Command 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}"); + } + } } }