From f2ba89d3e42cbd860444a767d6400687db6e42aa Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Sat, 22 Feb 2025 19:18:35 +0800 Subject: [PATCH] dmin Workflow - Fix Reported Issues - Automatically deduct funds from customer wallet when customer underpaid --- .../CreateBookingPaymentLogic.php | 142 +-------------- .../CreateBookingPaymentProcessor.php | 172 ++++++++++++++++++ .../UpdateNextQuestionV1AdminWFLogic.php | 16 +- ...NextQuestionMetadataV1AdminWFProcessor.php | 10 +- .../FetchNextQuestionV1AdminWFProcessor.php | 83 +++++---- .../SaveAnswerActionV1AdminWFProcessor.php | 39 +++- .../SaveAnswerV1AdminWFProcessor.php | 12 +- app/Http/Resources/QuestionResource.php | 7 +- database/seeds/QAWorkFlowSeeder.php | 74 ++++++-- .../forms/AdminWorkFlowFormComponent.vue | 50 +++-- 10 files changed, 385 insertions(+), 220 deletions(-) create mode 100644 app/Classes/Modules/Bookings/Processors/CreateBookingPaymentProcessor.php diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php index ab654675..48f1fd21 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php @@ -5,32 +5,11 @@ namespace App\Classes\Modules\Bookings\ControllersLogic; use App\Classes\Exceptions\MalformedRequestException; use App\Classes\General\Abstracts\AbstractControllerLogic; -use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding; -use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation; -use App\Classes\Modules\Companies\Services\FetchesCompanyPaymentAttemptLimit; -use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject; -use App\Classes\Modules\Transactions\Services\CreatesTransaction; -use App\Classes\Modules\Transactions\Services\GeneratesTransactionBillNumber; -use App\Classes\Modules\Billplzs\Services\CreatesBillplzBill; -use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; -use App\Classes\Modules\Wallets\Services\UpdatesWalletBalance; -use App\Classes\ValueObjects\Constants\ApprovalStatus; -use App\Classes\ValueObjects\Constants\PaymentMethodType; -use App\Classes\ValueObjects\Constants\TransactionType; -use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject; use App\Http\Resources\TransactionResource; -use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount; - -use App\Classes\Modules\Transactions\Processors\CreateCashBackTransactionProcessor; -use App\Classes\Modules\Vouchers\Processors\Voucherify\BookingToVoucherifyProcessor; - use App\Models\Booking; -use App\Models\Transaction; -use App\Models\Wallet; -use Carbon\Carbon; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use App\Classes\Modules\Wallets\Services\RecalculatesWalletBalance; +use App\Classes\Modules\Bookings\Processors\CreateBookingPaymentProcessor; class CreateBookingPaymentLogic extends AbstractControllerLogic { @@ -45,71 +24,18 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic ]; } - /** @var FetchesBookingQuotation */ - private $fetchBookingQuotation; + /** @var CreateBookingPaymentProcessor */ + private $createBookingPaymentProcessor; - /** @var FetchesCompanyPaymentAttemptLimit */ - private $fetchesCompanyPaymentAttemptLimit; - /** @var GeneratesTransactionBillNumber */ - private $generatesTransactionBillNumber; - - /** @var CreatesTransaction */ - private $createsTransaction; - - /** @var CalculatesBookingOutstanding */ - private $calculatesBookingOutstanding; - - /** @var CreatesBillplzBill */ - private $createsBillplzBill; - - /** @var UpdatesWalletBalance */ - private $updatesWalletBalance; - - /** @var UpdatesTransactionStatus */ - private $updatesTransactionStatus; - - /** @var CreateCashBackTransactionProcessor */ - private $createCashBackTransactionProcessor; - - /** @var RecalculatesWalletBalance */ - private $recalculatesWalletBalance; - - /** @var BookingToVoucherifyProcessor */ - private $bookingToVoucherifyProcessor; - - /** @var CalculatesBookingRefundAmount */ - private $calculatesBookingRefundAmount; /** * CreateBookingPaymentLogic constructor. - * @param FetchesBookingQuotation $fetchBookingQuotation - * @param FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit - * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber - * @param CreatesTransaction $createsTransaction - * @param CalculatesBookingOutstanding $calculatesBookingOutstanding - * @param CreatesBillplzBill $createsBillplzBill - * @param UpdatesWalletBalance $updatesWalletBalance - * @param UpdatesTransactionStatus $updatesTransactionStatus - * @param CreateCashBackTransactionProcessor $createCashBackTransactionProcessor - * @param RecalculatesWalletBalance $recalculatesWalletBalance - * @param BookingToVoucherifyProcessor $bookingToVoucherifyProcessor - * @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount + * @param CreateBookingPaymentProcessor $createBookingPaymentProcessor */ - public function __construct(FetchesBookingQuotation $fetchBookingQuotation, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit, GeneratesTransactionBillNumber $generatesTransactionBillNumber, CreatesTransaction $createsTransaction, CalculatesBookingOutstanding $calculatesBookingOutstanding, CreatesBillplzBill $createsBillplzBill, UpdatesWalletBalance $updatesWalletBalance, UpdatesTransactionStatus $updatesTransactionStatus, CreateCashBackTransactionProcessor $createCashBackTransactionProcessor, RecalculatesWalletBalance $recalculatesWalletBalance, BookingToVoucherifyProcessor $bookingToVoucherifyProcessor, CalculatesBookingRefundAmount $calculatesBookingRefundAmount) + public function __construct(CreateBookingPaymentProcessor $createBookingPaymentProcessor) { - $this->fetchBookingQuotation = $fetchBookingQuotation; - $this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit; - $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; - $this->createsTransaction = $createsTransaction; - $this->calculatesBookingOutstanding = $calculatesBookingOutstanding; - $this->createsBillplzBill = $createsBillplzBill; - $this->updatesWalletBalance = $updatesWalletBalance; - $this->updatesTransactionStatus = $updatesTransactionStatus; - $this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor; - $this->recalculatesWalletBalance = $recalculatesWalletBalance; - $this->bookingToVoucherifyProcessor = $bookingToVoucherifyProcessor; - $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount; + $this->createBookingPaymentProcessor = $createBookingPaymentProcessor; } /** @@ -123,61 +49,7 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic $booking = Booking::find($request->route('id')); - $conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $request->input('amount'))), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')]); - - $outstanding = $this->calculatesBookingOutstanding->execute($booking) + $this->calculatesBookingRefundAmount->execute($booking, $booking->fix_currency_id); - - if($conversionObject->getAmount() > round($outstanding, 2)) throw new MalformedRequestException('Your payment must not be greater than '. $outstanding .'.'); - - $configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject, $voucherCode); - - $paymentAttemptLimit = $this->fetchesCompanyPaymentAttemptLimit->execute($booking->company); - - $billNumber = $this->generatesTransactionBillNumber->execute('PYMT-'); - - $paymentReference = null; - - $amount = $configurations->getTotal(); - - if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::PAYMENT_GATEWAY){ - $billPlzBill = $this->createsBillplzBill->execute($booking->company->name, $request->user()->email, 'This payment is made for transfer ref. '.$booking->marking, $configurations->getTotal(), $billNumber, $request->input('bank_code')); - $paymentReference = $billPlzBill->id; - } - - if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::WALLET){ - /** @var Wallet $wallet */ - $wallet = $booking->company->wallets()->first(); - - if((float) number_format(($wallet->amount - $amount),2) < 0){ - throw new MalformedRequestException('Insufficient wallet balance. Please Top up your wallet.'); - } - - $transaction_object = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id, 1, PaymentMethodType::WALLET, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::APPROVED, [], ''); - $transaction = $this->createsTransaction->execute($wallet, $transaction_object); - - $paymentReference = $billNumber; - - $walletBalance = $this->recalculatesWalletBalance->execute($wallet); - $this->updatesWalletBalance->execute($wallet, $walletBalance); - } - - $billNumber = $this->generatesTransactionBillNumber->execute('PYMT-'); - - $object = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id, - $configurations->getConfigurations()->getBankId(), $configurations->getConversionObject()->getPaymentMethod(), - $configurations->getTotal(), $configurations->getForeignTotal(), 1, - $configurations->getConversionObject()->getCurrencyId(), $configurations->getConfigurations()->getRate(), - $configurations->getTax(), $configurations->getServiceCharge(), Carbon::now()->addMinutes($paymentAttemptLimit), ApprovalStatus::PENDING_SUBMISSION, [], $paymentReference); - - /** @var Transaction $transaction */ - $transaction = $this->createsTransaction->execute($booking, $object); -// $cash_back_transaction = $this->createCashBackTransactionProcessor->execute($transaction); - - $this->bookingToVoucherifyProcessor->execute($booking->company->employees()->first(), $transaction, $booking->company->id, $configurations->getSubTotal(), $configurations->getVoucherDiscountAmount(), $voucherCode); - - if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::WALLET){ - $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED); - } + $transaction = $this->createBookingPaymentProcessor->execute($booking, $request->input('amount'), $request->input('payment_method'), $voucherCode, $request->input('bank_code'), $request->user()->email); return $this->resourceResponse(new TransactionResource($transaction)); } diff --git a/app/Classes/Modules/Bookings/Processors/CreateBookingPaymentProcessor.php b/app/Classes/Modules/Bookings/Processors/CreateBookingPaymentProcessor.php new file mode 100644 index 00000000..93b1594a --- /dev/null +++ b/app/Classes/Modules/Bookings/Processors/CreateBookingPaymentProcessor.php @@ -0,0 +1,172 @@ +fetchBookingQuotation = $fetchBookingQuotation; + $this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit; + $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; + $this->createsTransaction = $createsTransaction; + $this->calculatesBookingOutstanding = $calculatesBookingOutstanding; + $this->createsBillplzBill = $createsBillplzBill; + $this->updatesWalletBalance = $updatesWalletBalance; + $this->updatesTransactionStatus = $updatesTransactionStatus; + $this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor; + $this->recalculatesWalletBalance = $recalculatesWalletBalance; + $this->bookingToVoucherifyProcessor = $bookingToVoucherifyProcessor; + $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount; + } + + /** + * Process booking payment. + * + * @param Booking $booking + * @param float $amount + * @param string $paymentMethod + * @param string|null $voucherCode + * @param string|null $bankCode + * @param string $email + * @return Transaction + * @throws MalformedRequestException + */ + public function execute(Booking $booking, string $amount, string $paymentMethod, ?string $voucherCode, ?string $bankCode, string $email) + { + $conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $amount)), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS[$paymentMethod]); + + $outstanding = $this->calculatesBookingOutstanding->execute($booking) + $this->calculatesBookingRefundAmount->execute($booking, $booking->fix_currency_id); + + if($conversionObject->getAmount() > round($outstanding, 2)) throw new MalformedRequestException('Your payment must not be greater than '. $outstanding .'.'); + + $configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject, $voucherCode); + + $paymentAttemptLimit = $this->fetchesCompanyPaymentAttemptLimit->execute($booking->company); + + $billNumber = $this->generatesTransactionBillNumber->execute('PYMT-'); + + $paymentReference = null; + + $amount = $configurations->getTotal(); + + if(PaymentMethodType::PAYMENT_METHODS[$paymentMethod] == PaymentMethodType::PAYMENT_GATEWAY){ + $billPlzBill = $this->createsBillplzBill->execute($booking->company->name, $email, 'This payment is made for transfer ref. '.$booking->marking, $configurations->getTotal(), $billNumber, $bankCode); + $paymentReference = $billPlzBill->id; + } + + if(PaymentMethodType::PAYMENT_METHODS[$paymentMethod] == PaymentMethodType::WALLET){ + /** @var Wallet $wallet */ + $wallet = $booking->company->wallets()->first(); + + if((float) number_format(($wallet->amount - $amount), 2) < 0){ + throw new MalformedRequestException('Insufficient wallet balance. Please Top up your wallet.'); + } + + $transaction_object = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id, 1, PaymentMethodType::WALLET, $amount, $amount, 1, 1, 1, 0, 0, null, ApprovalStatus::APPROVED, [], ''); + $transaction = $this->createsTransaction->execute($wallet, $transaction_object); + + $paymentReference = $billNumber; + + $walletBalance = $this->recalculatesWalletBalance->execute($wallet); + $this->updatesWalletBalance->execute($wallet, $walletBalance); + } + + $billNumber = $this->generatesTransactionBillNumber->execute('PYMT-'); + + $object = new TransactionObject($billNumber, TransactionType::PAYMENT, 1, $booking->company->id, + $configurations->getConfigurations()->getBankId(), $configurations->getConversionObject()->getPaymentMethod(), + $configurations->getTotal(), $configurations->getForeignTotal(), 1, + $configurations->getConversionObject()->getCurrencyId(), $configurations->getConfigurations()->getRate(), + $configurations->getTax(), $configurations->getServiceCharge(), Carbon::now()->addMinutes($paymentAttemptLimit), ApprovalStatus::PENDING_SUBMISSION, [], $paymentReference); + + /** @var Transaction $transaction */ + $transaction = $this->createsTransaction->execute($booking, $object); +// $cash_back_transaction = $this->createCashBackTransactionProcessor->execute($transaction); + + $this->bookingToVoucherifyProcessor->execute($booking->company->employees()->first(), $transaction, $booking->company->id, $configurations->getSubTotal(), $configurations->getVoucherDiscountAmount(), $voucherCode); + + if(PaymentMethodType::PAYMENT_METHODS[$paymentMethod] == PaymentMethodType::WALLET){ + $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED); + } + + return $transaction; + } +} diff --git a/app/Classes/Modules/Questionnaires/ControllersLogic/UpdateNextQuestionV1AdminWFLogic.php b/app/Classes/Modules/Questionnaires/ControllersLogic/UpdateNextQuestionV1AdminWFLogic.php index 67440946..366cb135 100644 --- a/app/Classes/Modules/Questionnaires/ControllersLogic/UpdateNextQuestionV1AdminWFLogic.php +++ b/app/Classes/Modules/Questionnaires/ControllersLogic/UpdateNextQuestionV1AdminWFLogic.php @@ -43,7 +43,7 @@ class UpdateNextQuestionV1AdminWFLogic extends AbstractControllerLogic private $saveAnswerActionProcessor; /** @var FetchNextQuestionV1AdminWFProcessor */ - private $fetchNextQuestionQAProcessor; + private $fetchNextQuestionV1AdminWFProcessor; /** @var FetchNextQuestionMetadataV1AdminWFProcessor */ private $fetchNextQuestionMetadataProcessor; @@ -61,18 +61,18 @@ class UpdateNextQuestionV1AdminWFLogic extends AbstractControllerLogic * UpdateNextQuestionV1AdminWFLogic constructor. * @param CanFetchQuestion $canFetchQuestion * @param SaveAnswerV1AdminWFProcessor $saveAnswerProcessor - * @param FetchNextQuestionV1AdminWFProcessor $fetchNextQuestionQAProcessor + * @param FetchNextQuestionV1AdminWFProcessor $fetchNextQuestionV1AdminWFProcessor * @param FetchFirstQuestionV1AdminWFProcessor $fetchFirstQuestionQAProcessor * @param ListsQuestionUserAnswerSelected $listsUserAnswerSelected * @param CreatesKeyValuePair $createsKeyValuePair * @param SaveAnswerActionV1AdminWFProcessor $saveAnswerActionProcessor * @param FetchNextQuestionMetadataV1AdminWFProcessor $fetchNextQuestionMetadataProcessor */ - public function __construct(CanFetchQuestion $canFetchQuestion, SaveAnswerV1AdminWFProcessor $saveAnswerProcessor, FetchNextQuestionV1AdminWFProcessor $fetchNextQuestionQAProcessor, FetchFirstQuestionV1AdminWFProcessor $fetchFirstQuestionQAProcessor, ListsQuestionUserAnswerSelected $listsUserAnswerSelected, CreatesKeyValuePair $createsKeyValuePair, SaveAnswerActionV1AdminWFProcessor $saveAnswerActionProcessor, FetchNextQuestionMetadataV1AdminWFProcessor $fetchNextQuestionMetadataProcessor) + public function __construct(CanFetchQuestion $canFetchQuestion, SaveAnswerV1AdminWFProcessor $saveAnswerProcessor, FetchNextQuestionV1AdminWFProcessor $fetchNextQuestionV1AdminWFProcessor, FetchFirstQuestionV1AdminWFProcessor $fetchFirstQuestionQAProcessor, ListsQuestionUserAnswerSelected $listsUserAnswerSelected, CreatesKeyValuePair $createsKeyValuePair, SaveAnswerActionV1AdminWFProcessor $saveAnswerActionProcessor, FetchNextQuestionMetadataV1AdminWFProcessor $fetchNextQuestionMetadataProcessor) { $this->canFetchQuestion = $canFetchQuestion; $this->saveAnswerProcessor = $saveAnswerProcessor; - $this->fetchNextQuestionQAProcessor = $fetchNextQuestionQAProcessor; + $this->fetchNextQuestionV1AdminWFProcessor = $fetchNextQuestionV1AdminWFProcessor; $this->fetchFirstQuestionQAProcessor = $fetchFirstQuestionQAProcessor; $this->listsUserAnswerSelected = $listsUserAnswerSelected; $this->createsKeyValuePair = $createsKeyValuePair; @@ -128,7 +128,7 @@ class UpdateNextQuestionV1AdminWFLogic extends AbstractControllerLogic if ($request->has('isPrevious')){ $answerInText = "go_back"; } - $answer = $this->saveAnswerProcessor->execute($userId, 0, $currentQuestion['id'], $currentQuestion['question_type'], $questionMetadata, $answerInText, $answerOptionId, $filesUpload, $timeUsedSeconds, $request->has('isPrevious'), $reference); + $this->saveAnswerProcessor->execute($userId, 0, $currentQuestion, $questionMetadata, $answerInText, $answerOptionId, $filesUpload, $timeUsedSeconds, $request->has('isPrevious'), $reference); if(!$request->has('isPrevious')){ $isNoGoingBackOverwrite = $this->saveAnswerActionProcessor->execute($booking, $questionMetadata, $answerOptionId, $currentQuestion, $filesUpload); } @@ -173,7 +173,7 @@ class UpdateNextQuestionV1AdminWFLogic extends AbstractControllerLogic } //Get returned question (previous or next) - $returnQuestion = $this->fetchNextQuestionQAProcessor->execute($request, $previousAnswer); + $returnQuestion = $this->fetchNextQuestionV1AdminWFProcessor->execute($request, $previousAnswer, $booking); if($returnQuestion && $returnQuestion->is_start){ $isNoGoingBack = 1; @@ -186,12 +186,12 @@ class UpdateNextQuestionV1AdminWFLogic extends AbstractControllerLogic $isNoGoingBack = $isNoGoingBackOverwrite; } - $returnQuestion = $this->fetchNextQuestionMetadataProcessor->execute($returnQuestion, $questionMetadata); + $questionMetadata = $this->fetchNextQuestionMetadataProcessor->execute($returnQuestion, $questionMetadata); //When a questionnaire ended, return back the first question if(is_null($returnQuestion)){ $returnQuestion = $this->fetchFirstQuestionQAProcessor->execute($request); } - return $this->resourceResponse(new QuestionResource($returnQuestion, $userId, $request->has('isPrevious'), $isNoGoingBack, $previousAnswer)); + return $this->resourceResponse(new QuestionResource($returnQuestion, $userId, $request->has('isPrevious'), $isNoGoingBack, $previousAnswer, $questionMetadata)); } } diff --git a/app/Classes/Modules/Questionnaires/Processors/FetchNextQuestionMetadataV1AdminWFProcessor.php b/app/Classes/Modules/Questionnaires/Processors/FetchNextQuestionMetadataV1AdminWFProcessor.php index 95d975b6..d7842e3d 100644 --- a/app/Classes/Modules/Questionnaires/Processors/FetchNextQuestionMetadataV1AdminWFProcessor.php +++ b/app/Classes/Modules/Questionnaires/Processors/FetchNextQuestionMetadataV1AdminWFProcessor.php @@ -4,12 +4,14 @@ namespace App\Classes\Modules\Questionnaires\Processors; class FetchNextQuestionMetadataV1AdminWFProcessor { - public function execute($returnQuestion, $questionMetadata){ + public function execute($returnQuestion, $questionMetadata) { - if($returnQuestion && $returnQuestion->question_number === '1688_order_verification'){ - $returnQuestion->question_description = $questionMetadata['amount_processed'] ." ".$questionMetadata['fixed_currency']['short_code'] . "/" . $questionMetadata['amount'] ." ".$questionMetadata['fixed_currency']['short_code']; + if($returnQuestion && $returnQuestion->question_number === '1688_underpaid_order_1'){ + $processed = $questionMetadata['amount_processed']; + $amount = $questionMetadata['amount']; + $questionMetadata['amount_to_be_deducted'] = $amount - $processed; } - return $returnQuestion; + return $questionMetadata; } } diff --git a/app/Classes/Modules/Questionnaires/Processors/FetchNextQuestionV1AdminWFProcessor.php b/app/Classes/Modules/Questionnaires/Processors/FetchNextQuestionV1AdminWFProcessor.php index 98d91a9b..879b8ef6 100644 --- a/app/Classes/Modules/Questionnaires/Processors/FetchNextQuestionV1AdminWFProcessor.php +++ b/app/Classes/Modules/Questionnaires/Processors/FetchNextQuestionV1AdminWFProcessor.php @@ -5,10 +5,16 @@ namespace App\Classes\Modules\Questionnaires\Processors; use App\Classes\Modules\Questionnaires\Services\FetchesQuestion; use App\Classes\Modules\Questionnaires\Services\ListsQuestions; -use Illuminate\Http\Request; +use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding; +use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount; +use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation; +use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject; +use App\Classes\ValueObjects\Constants\PaymentMethodType; use App\Classes\ValueObjects\Constants\QAType; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; +use Illuminate\Http\Request; +use App\Models\Booking; class FetchNextQuestionV1AdminWFProcessor { @@ -18,18 +24,33 @@ class FetchNextQuestionV1AdminWFProcessor /** @var ListsQuestions */ private $listsQuestions; + /** @var CalculatesBookingOutstanding */ + private $calculatesBookingOutstanding; + + /** @var CalculatesBookingRefundAmount */ + private $calculatesBookingRefundAmount; + + /** @var FetchesBookingQuotation */ + private $fetchBookingQuotation; + /** * FetchNextQuestionV1AdminWFProcessor constructor. * @param FetchesQuestion $fetchesQuestion * @param ListsQuestions $listsQuestions + * @param CalculatesBookingOutstanding $calculatesBookingOutstanding + * @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount + * @param FetchesBookingQuotation $fetchBookingQuotation */ - public function __construct(FetchesQuestion $fetchesQuestion, ListsQuestions $listsQuestions) + public function __construct(FetchesQuestion $fetchesQuestion, ListsQuestions $listsQuestions, CalculatesBookingOutstanding $calculatesBookingOutstanding, CalculatesBookingRefundAmount $calculatesBookingRefundAmount, FetchesBookingQuotation $fetchBookingQuotation) { $this->fetchesQuestion = $fetchesQuestion; $this->listsQuestions = $listsQuestions; + $this->calculatesBookingOutstanding = $calculatesBookingOutstanding; + $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount; + $this->fetchBookingQuotation = $fetchBookingQuotation; } - public function execute(Request $request, $previousAnswer){ + public function execute(Request $request, $previousAnswer, $booking){ $isEnd = 0; $questionId = 0; $nextQuestionNumber = ""; @@ -62,8 +83,6 @@ class FetchNextQuestionV1AdminWFProcessor return null; } - Log::info("Previous Answer question id: ".$previousAnswer['question_id']); - //Get previous question $previousQuestion = $this->fetchesQuestion->execute(['questionnaire_set_id' => $questionnaireSetId, 'id' => $previousAnswer['question_id']]); $returnQuestion = $previousQuestion; @@ -85,31 +104,39 @@ class FetchNextQuestionV1AdminWFProcessor if($question_number !== ""){ //Check if question exists $questions = $this->listsQuestions->execute(['questionnaire_set_id' => $questionnaireSetId, 'question_number' => $question_number]); - if($previousAnswer['answer'] === '1688_underpaid_overpaid' && count($questions) == 0){ $extra = $request->extra; $questionMetadata = $extra['questionMetadata'] ?? null; - $processed = round((floatval($questionMetadata['amount_processed']) + PHP_FLOAT_EPSILON) * 1000) / 1000; - $amount = round((floatval($questionMetadata['amount']) + PHP_FLOAT_EPSILON) * 1000) / 1000; + // $processed = round((floatval($questionMetadata['amount_processed']) + PHP_FLOAT_EPSILON) * 1000) / 1000; + // $amount = round((floatval($questionMetadata['amount']) + PHP_FLOAT_EPSILON) * 1000) / 1000; + $processed = $questionMetadata['amount_processed']; + $amount = $questionMetadata['amount']; + $wallet = $booking->company->wallets()->first(); + $walletAmount = $wallet->amount; - //1688_underpaid_order_1, 1688_underpaid_order_2,1688_overpaid_order if ($processed === $amount) { - Log::info('The amounts are exactly the same:', [ - 'amount_processed' => $questionMetadata['amount_processed'], - 'amount' => $questionMetadata['amount'] - ]); - $question_number = '1688_underpaid_order_1'; - } elseif ($processed < $amount) { - Log::info('The processed amount is under the expected amount:', [ - 'amount_processed' => $questionMetadata['amount_processed'], - 'amount' => $questionMetadata['amount'] - ]); - $question_number = '1688_underpaid_order_2'; + $question_number = '1688_order_paid_precisely'; + } + else if ($processed < $amount) { + $outstanding = $this->calculatesBookingOutstanding->execute($booking) + $this->calculatesBookingRefundAmount->execute($booking, $booking->fix_currency_id); + + $differenceUnder = $amount - $processed; + + $conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $differenceUnder)), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS['wallet']); + $configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject); + $underpay = $configurations->getTotal(); + + if($underpay > round($outstanding, 2)) { + // throw new MalformedRequestException('Your payment must not be greater than '. $outstanding .'.'); + $question_number = '1688_underpaid_order_3'; + } + else if($walletAmount > $underpay) { + $question_number = '1688_underpaid_order_1'; + } + else { + $question_number = '1688_underpaid_order_2'; + } } else { - Log::info('The processed amount is over the expected amount:', [ - 'amount_processed' => $questionMetadata['amount_processed'], - 'amount' => $questionMetadata['amount'] - ]); $question_number = '1688_overpaid_order'; } } @@ -117,14 +144,8 @@ class FetchNextQuestionV1AdminWFProcessor $nextQuestion = $this->fetchesQuestion->execute(['questionnaire_set_id' => $questionnaireSetId, 'question_number' => $question_number]); $returnQuestion = $nextQuestion; } - //cief todo: 74 - else if (array_key_exists('id', $request->input('question'))) { - dd('cbal todo'); - $nextQuestion = $this->fetchesQuestion->execute(['questionnaire_set_id' => $questionnaireSetId, 'id_next' => $questionId, 'next_main_question' => null]); - $returnQuestion = $nextQuestion; - } } return $returnQuestion; } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Questionnaires/Processors/SaveAnswerActionV1AdminWFProcessor.php b/app/Classes/Modules/Questionnaires/Processors/SaveAnswerActionV1AdminWFProcessor.php index 872e253c..3b729b11 100644 --- a/app/Classes/Modules/Questionnaires/Processors/SaveAnswerActionV1AdminWFProcessor.php +++ b/app/Classes/Modules/Questionnaires/Processors/SaveAnswerActionV1AdminWFProcessor.php @@ -5,10 +5,13 @@ namespace App\Classes\Modules\Questionnaires\Processors; use App\Classes\Modules\Bookings\Processors\ApprovePurchaseOrderProcessor; use App\Classes\Modules\Bookings\Processors\UploadPurchaseOrderProcessor; +use App\Classes\Modules\Bookings\Processors\CreateBookingPaymentProcessor; use App\Classes\Modules\Transactions\Processors\CreatePaymentProofDocumentProcessor; use App\Classes\Modules\Transactions\Services\FetchesTransaction; +use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation; +use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject; +use App\Classes\ValueObjects\Constants\PaymentMethodType; use App\Models\QAAnswerOptions; -use Illuminate\Support\Facades\Log; class SaveAnswerActionV1AdminWFProcessor { @@ -24,19 +27,29 @@ class SaveAnswerActionV1AdminWFProcessor /** @var FetchesTransaction */ private $fetchesTransaction; + /** @var FetchesBookingQuotation */ + private $fetchBookingQuotation; + + /** @var CreateBookingPaymentProcessor */ + private $createBookingPaymentProcessor; + /** * SaveAnswerActionV1AdminWFProcessor constructor. * @param ApprovePurchaseOrderProcessor $approvePurchaseOrderProcessor * @param UploadPurchaseOrderProcessor $uploadPurchaseOrderProcessor * @param CreatePaymentProofDocumentProcessor $createPaymentProofDocumentProcessor * @param FetchesTransaction $fetchesTransaction + * @param FetchesBookingQuotation $fetchBookingQuotation + * @param CreateBookingPaymentProcessor $createBookingPaymentProcessor */ - public function __construct(ApprovePurchaseOrderProcessor $approvePurchaseOrderProcessor, UploadPurchaseOrderProcessor $uploadPurchaseOrderProcessor, CreatePaymentProofDocumentProcessor $createPaymentProofDocumentProcessor, FetchesTransaction $fetchesTransaction) + public function __construct(ApprovePurchaseOrderProcessor $approvePurchaseOrderProcessor, UploadPurchaseOrderProcessor $uploadPurchaseOrderProcessor, CreatePaymentProofDocumentProcessor $createPaymentProofDocumentProcessor, FetchesTransaction $fetchesTransaction, FetchesBookingQuotation $fetchBookingQuotation, CreateBookingPaymentProcessor $createBookingPaymentProcessor) { $this->approvePurchaseOrderProcessor = $approvePurchaseOrderProcessor; $this->uploadPurchaseOrderProcessor = $uploadPurchaseOrderProcessor; $this->createPaymentProofDocumentProcessor = $createPaymentProofDocumentProcessor; $this->fetchesTransaction = $fetchesTransaction; + $this->fetchBookingQuotation = $fetchBookingQuotation; + $this->createBookingPaymentProcessor = $createBookingPaymentProcessor; } /** @@ -45,7 +58,7 @@ class SaveAnswerActionV1AdminWFProcessor public function execute($booking, $questionMetadata, $answerOptionId, $currentQuestion, $filesUpload){ $isNoGoingBack = 0; $answerOption = QAAnswerOptions::where('id', $answerOptionId)->first(); - if($answerOption && $answerOption->value === "approve_po_approved"){ + if($currentQuestion['question_number'] === 'approve_po' && $answerOption && $answerOption->value === "approve_po_approved"){ $this->approvePurchaseOrderProcessor->execute($questionMetadata['id']); $isNoGoingBack = 1; } @@ -69,6 +82,26 @@ class SaveAnswerActionV1AdminWFProcessor $isNoGoingBack = 1; } } + else if($currentQuestion['question_number'] === '1688_underpaid_order_1') + { + $wallet = $booking->company->wallets()->first(); + $processed = $questionMetadata['amount_processed']; + $amount = $questionMetadata['amount']; + if ($processed < $amount) { + $differenceUnder = $amount - $processed; + $conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $differenceUnder)), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS['wallet']); + $configurations = $this->fetchBookingQuotation->execute($booking->company, $conversionObject); + $underpay = $configurations->getTotal(); + + if($wallet->amount > $underpay){ + $company = $booking->company()->first(); + $employee = $company->employees()->first(); + $transaction = $this->createBookingPaymentProcessor->execute($booking, (string) $differenceUnder, 'wallet', "", "", $employee->email); + $isNoGoingBack = 1; + } + } + + } return $isNoGoingBack; } diff --git a/app/Classes/Modules/Questionnaires/Processors/SaveAnswerV1AdminWFProcessor.php b/app/Classes/Modules/Questionnaires/Processors/SaveAnswerV1AdminWFProcessor.php index 3d8f107a..cb772aa8 100644 --- a/app/Classes/Modules/Questionnaires/Processors/SaveAnswerV1AdminWFProcessor.php +++ b/app/Classes/Modules/Questionnaires/Processors/SaveAnswerV1AdminWFProcessor.php @@ -26,10 +26,13 @@ class SaveAnswerV1AdminWFProcessor /** * @return QAUserAnswerSelected|\Illuminate\Database\Eloquent\Model */ - public function execute($userId, $sourceId, $questionId, $questionType, $questionMetadata, $answerInText, $answerOptionId, $filesUpload, $timeUsedSeconds, $isPrevious, $reference = null){ + public function execute($userId, $sourceId, $currentQuestion, $questionMetadata, $answerInText, $answerOptionId, $filesUpload, $timeUsedSeconds, $isPrevious, $reference = null){ $answer = null; $attachments = []; + $questionId = $currentQuestion['id']; + $questionType = $currentQuestion['question_type']; + $questionNumber = $currentQuestion['question_number']; if(is_null($answer)) { @@ -89,8 +92,15 @@ class SaveAnswerV1AdminWFProcessor ]; $answer->answer = json_encode($structuredAnswer); } + else if($questionType === QAType::FLOAT_MONEY){ + $answer->answer = floatval(str_replace(',', '', $answerInText)); + } else { $answer->answer = $answerInText; + + if($questionNumber === '1688_underpaid_order_1_proceed' || $questionNumber === '1688_underpaid_order_1'){ + $answer->answer = $questionMetadata['amount_to_be_deducted']; + } } } else { diff --git a/app/Http/Resources/QuestionResource.php b/app/Http/Resources/QuestionResource.php index 45872f33..e2ad946c 100644 --- a/app/Http/Resources/QuestionResource.php +++ b/app/Http/Resources/QuestionResource.php @@ -26,12 +26,16 @@ class QuestionResource extends JsonResource /** @var integer */ private $previousAnswer; - public function __construct($question, $userId, $isPrevious = false, $isNoGoingBack = null, $previousAnswer = null) { + /** @var object */ + private $questionMetadata; + + public function __construct($question, $userId, $isPrevious = false, $isNoGoingBack = null, $previousAnswer = null, $questionMetadata = null) { $this->question = $question; $this->userId = $userId; $this->isPrevious = $isPrevious; $this->isNoGoingBack = $isNoGoingBack; $this->previousAnswer = $previousAnswer; + $this->questionMetadata = $questionMetadata; } /** * Transform the resource into an array. @@ -51,6 +55,7 @@ class QuestionResource extends JsonResource 'question_type' => $q->question_type, 'question_answers' => AnswerOptionsResource::collection(QAAnswerOptions::where('question_number', $q->question_number)->where('questionnaire_set_id', $q->questionnaire_set_id)->orderBy('order', 'ASC')->get()), 'questionnaire_set_id' => $q->questionnaire_set_id, + 'question_metadata'=> $this->questionMetadata, // 'questionnaire' => new QuestionnaireSetsResource($q->questionnaire), // 'questionnaire_answers' => $q->is_end ? QuestionnaireAnswersResource::collection(QAUserAnswerSelected::where('user_id', $this->userId)->get()) : null, 'next_nested_question' => $q->next_nested_question, diff --git a/database/seeds/QAWorkFlowSeeder.php b/database/seeds/QAWorkFlowSeeder.php index 6a1ad2d2..efad4c00 100644 --- a/database/seeds/QAWorkFlowSeeder.php +++ b/database/seeds/QAWorkFlowSeeder.php @@ -127,7 +127,7 @@ class QAWorkFlowSeeder extends Seeder ], [ 'question_number' => '1688_order_verify', - 'question_title' => 'Order Amount', + 'question_title' => 'Order Amount (CNY)', 'question_description' => 'Please key in the order amount', 'question_' => 'Order Amount', 'question_type' => QAType::FLOAT_MONEY, @@ -233,22 +233,22 @@ class QAWorkFlowSeeder extends Seeder ], 'group' => '1688', ], + // [ + // 'question_number' => '1688_insufficient_order', + // 'question_title' => 'Are You Sure this is the correct amount?', + // 'question_type' => QAType::MULTIPLE_CHOICES, + // 'is_start' => false, + // 'is_end' => false, + // 'answer_options' => [ + // ['display_text' => 'Underpaid Order', 'value' => 'underpaid_order_1', 'next_question_number' => '1688_issue_submit'], + // ['display_text' => 'Underpaid Order', 'value' => 'underpaid_order_2', 'next_question_number' => '1688_issue_submit'], + // ['display_text' => 'Overpaid Order', 'value' => 'underpaid_order_2', 'next_question_number' => '1688_issue_submit'], + // ], + // 'group' => '1688', + // ], [ - 'question_number' => '1688_insufficient_order', - 'question_title' => 'Are You Sure this is the correct amount?', - 'question_type' => QAType::MULTIPLE_CHOICES, - 'is_start' => false, - 'is_end' => false, - 'answer_options' => [ - ['display_text' => 'Underpaid Order', 'value' => 'underpaid_order_1', 'next_question_number' => '1688_issue_submit'], - ['display_text' => 'Underpaid Order', 'value' => 'underpaid_order_2', 'next_question_number' => '1688_issue_submit'], - ['display_text' => 'Overpaid Order', 'value' => 'underpaid_order_2', 'next_question_number' => '1688_issue_submit'], - ], - 'group' => '1688', - ], - [ - 'question_number' => '1688_underpaid_order_1', - 'question_title' => 'This order is underpaid 1', + 'question_number' => '1688_order_paid_precisely', + 'question_title' => 'Amount paid is same as recorded in system', 'question_type' => QAType::MULTIPLE_CHOICES, 'is_start' => false, 'is_end' => false, @@ -257,9 +257,46 @@ class QAWorkFlowSeeder extends Seeder ], 'group' => '1688', ], + [ + 'question_number' => '1688_underpaid_order_1', + 'question_title' => "This order is underpaid. Clicking 'Next' will deduct the amount short from wallet automatically. ", + 'question_type' => QAType::DEFAULT, + 'is_start' => false, + 'is_end' => false, + 'next_nested_question' => '1688_underpaid_order_1_proceed', + // 'answer_options' => [ + // ['display_text' => 'Proceed', 'value' => '1688_underpaid_order_1_proceed', 'next_question_number' => '1688_underpaid_order_1_proceed'], + // ], + 'group' => '1688', + ], + [ + 'question_number' => '1688_underpaid_order_1_proceed', + 'question_title' => "The missing amount has successfully been deducted from the customer’s wallet", + 'question_type' => QAType::DEFAULT, + 'is_start' => false, + 'is_end' => true, + 'next_nested_question' => '1688_issue_submit', + // 'answer_options' => [ + // ['display_text' => 'Done', 'value' => '1688_issue_submit', 'next_question_number' => '1688_issue_submit'], + // ], + 'group' => '1688', + ], [ 'question_number' => '1688_underpaid_order_2', - 'question_title' => 'This order is underpaid 2', + 'question_title' => 'Underpaid Order', + 'question_description' => 'Insufficient wallet balance. Refund request to be sent', + 'question_type' => QAType::MULTIPLE_CHOICES, + 'is_start' => false, + 'is_end' => false, + 'answer_options' => [ + ['display_text' => 'Done', 'value' => '1688_issue_submit', 'next_question_number' => '1688_issue_submit'], + ], + 'group' => '1688', + ], + [ + 'question_number' => '1688_underpaid_order_3', + 'question_title' => 'Underpaid Order?', + 'question_description' => 'There is no outstanding on record, please go back and check if the amount processed is entered correctly.', 'question_type' => QAType::MULTIPLE_CHOICES, 'is_start' => false, 'is_end' => false, @@ -270,7 +307,8 @@ class QAWorkFlowSeeder extends Seeder ], [ 'question_number' => '1688_overpaid_order', - 'question_title' => 'This order is overpaid', + 'question_title' => 'Overpaid Order', + 'question_description' => 'Exceeded amount to be refunded to customer’s wallet', 'question_type' => QAType::MULTIPLE_CHOICES, 'is_start' => false, 'is_end' => false, diff --git a/resources/assets/vue/components/general/forms/AdminWorkFlowFormComponent.vue b/resources/assets/vue/components/general/forms/AdminWorkFlowFormComponent.vue index 806ecb6f..23b848ee 100644 --- a/resources/assets/vue/components/general/forms/AdminWorkFlowFormComponent.vue +++ b/resources/assets/vue/components/general/forms/AdminWorkFlowFormComponent.vue @@ -144,6 +144,13 @@ --> +
+
Amount to be deducted from wallet:
+
+ {{(Math.round((externalApiResponse.data.amount_to_be_deducted + Number.EPSILON) * 1000) / 1000).toFixed(3)}} {{externalApiResponse.data.fixed_currency.short_code}} +
+
+
@@ -329,23 +336,23 @@
-
-

- {{ index + 1 + '. #' + attribute.value }} -

-
-
-
-
Total:
-
- - {{(Math.round((externalApiResponse.data.amount + Number.EPSILON) * 1000) / 1000).toFixed(3)}} {{externalApiResponse.data.fixed_currency.short_code}} -
+
+

+ {{ index + 1 + '. #' + attribute.value }} +

-
+
+
+
Total:
+
+ + {{(Math.round((externalApiResponse.data.amount + Number.EPSILON) * 1000) / 1000).toFixed(3)}} {{externalApiResponse.data.fixed_currency.short_code}} +
+
+
@@ -353,7 +360,7 @@
-
+
@@ -533,6 +540,10 @@ // this.answer = this.question.answer.answer; // } + if (this.question.question_metadata != null) { + this.externalApiResponse = { data: this.question.question_metadata }; + } + //if there is only 1 answer, select answer by default if(this.question.question_answers.length === 1){ this.answer = this.question.question_answers[0].value; @@ -546,7 +557,7 @@ this.answerId = this.question.answer.answer_option_id; } else if(this.question.question_type === 9){ //FLOAT_MONEY - this.answer = this.externalApiResponse.data.amount_processed; + this.answer = this.externalApiResponse.data.amount_processed * 100; } else if(this.question.question_type === 7){ //REMARKS_WITH_DOCUMENT_UPLOAD this.answer = JSON.parse(this.question.answer.answer).text; @@ -631,13 +642,14 @@ payment_history: this.externalApiResponse?.data?.payment_history, amount: this.externalApiResponse?.data?.amount, amount_processed: this.externalApiResponse?.data?.amount_processed, + amount_to_be_deducted: this.externalApiResponse?.data?.amount_to_be_deducted, }; let trimmedData = baseData; if (this.question.question_number === '1688_order_verify') { trimmedData = { ...trimmedData, // Spread existing attributes - amount_processed: this.answer, + amount_processed: parseFloat(this.answer.replace(/,/g, '')), }; this.externalApiResponse.data = trimmedData; }