Merge branch 'dillon/90-e-invoice-c' into vapor/development

This commit is contained in:
Dillon Ngo
2025-06-10 02:29:14 +08:00
7 changed files with 145 additions and 65 deletions
@@ -27,6 +27,7 @@ use App\Classes\Modules\Wallets\Services\RecalculatesWalletBalance;
use App\Classes\Modules\Rules\Services\RuleEvaluator;
use App\Classes\Modules\Rules\Standards\Rules\CanPassOrderDurationLimitRule;
use App\Classes\Modules\Transactions\Processors\CreateReceiptVoucherTransactionProcessor;
use App\Classes\Modules\Transactions\Services\CalculatesTransactionExpiryDateTime;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
@@ -103,6 +104,9 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic
/** @var CanPassPurchaseOrderRule */
protected $canPassPurchaseOrderRule;
/** @var CalculatesTransactionExpiryDateTime */
protected $calculatesTransactionExpiryDateTime;
/**
* @param FetchesBookingQuotation $fetchBookingQuotation
* @param FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit
@@ -121,8 +125,9 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic
* @param CanPassEInvoicePromptedRule $canPassEInvoicePromptedRule
* @param CanPassTINRule $canPassTINRule
* @param CanPassPurchaseOrderRule $canPassPurchaseOrderRule
* @param CalculatesTransactionExpiryDateTime $calculatesTransactionExpiryDateTime
*/
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, RuleEvaluator $ruleEvaluator, CreateReceiptVoucherTransactionProcessor $createReceiptVoucherTransactionProcessor, CanPassOrderDurationLimitRule $canPassOrderDurationLimitRule, CanPassEInvoicePromptedRule $canPassEInvoicePromptedRule, CanPassTINRule $canPassTINRule, CanPassPurchaseOrderRule $canPassPurchaseOrderRule)
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, RuleEvaluator $ruleEvaluator, CreateReceiptVoucherTransactionProcessor $createReceiptVoucherTransactionProcessor, CanPassOrderDurationLimitRule $canPassOrderDurationLimitRule, CanPassEInvoicePromptedRule $canPassEInvoicePromptedRule, CanPassTINRule $canPassTINRule, CanPassPurchaseOrderRule $canPassPurchaseOrderRule, CalculatesTransactionExpiryDateTime $calculatesTransactionExpiryDateTime)
{
$this->fetchBookingQuotation = $fetchBookingQuotation;
$this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit;
@@ -141,6 +146,7 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic
$this->canPassEInvoicePromptedRule = $canPassEInvoicePromptedRule;
$this->canPassTINRule = $canPassTINRule;
$this->canPassPurchaseOrderRule = $canPassPurchaseOrderRule;
$this->calculatesTransactionExpiryDateTime = $calculatesTransactionExpiryDateTime;
}
/**
@@ -269,6 +275,9 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic
private function createTransactionObject(Booking $booking, CalculationObject $configurations, string $billNumber, ?string $paymentReference): TransactionObject
{
// $expiresOn = Carbon::now()->addMinutes($this->fetchesCompanyPaymentAttemptLimit->execute($booking->company));
$expiresOn = $this->calculatesTransactionExpiryDateTime->execute($booking->id);
return new TransactionObject(
$billNumber,
TransactionType::PAYMENT,
@@ -283,7 +292,7 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic
$configurations->getConfigurations()->getRate(),
$configurations->getTax(),
$configurations->getServiceCharge(),
Carbon::now()->addMinutes($this->fetchesCompanyPaymentAttemptLimit->execute($booking->company)),
$expiresOn,
ApprovalStatus::PENDING_SUBMISSION,
[],
$paymentReference
@@ -8,11 +8,13 @@ class CreatePaymentVerificationDocumentDTO implements DataTransferObject
{
public int $bookingId;
public int $companyId;
public int $paymentId;
public function __construct(array $data)
{
$this->bookingId = $data['booking_id'];
$this->companyId = $data['company_id'];
$this->paymentId = $data['payment_id'];
}
public function toArray(): array
@@ -20,6 +22,7 @@ class CreatePaymentVerificationDocumentDTO implements DataTransferObject
return [
'booking_id' => $this->bookingId,
'company_id' => $this->companyId,
'payment_id' => $this->paymentId,
];
}
}
@@ -6,6 +6,7 @@ use App\Classes\Exceptions\CriteriaNotFulfilledException;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Companies\Services\FetchesCompanyPaymentAttemptLimit;
use App\Classes\Modules\Transactions\Services\CalculatesTransactionExpiryDateTime;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
@@ -18,15 +19,20 @@ class CanPassOrderDurationLimitRule extends AbstractRule
/** @var FetchesCompanyPaymentAttemptLimit */
private $fetchesCompanyPaymentAttemptLimit;
/** @var CalculatesTransactionExpiryDateTime */
private $calculatesTransactionExpiryDateTime;
/**
* CanPassOrderDurationLimitRule constructor.
* @param FetchesBooking $fetchesBooking
* @param FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit
* @param CalculatesTransactionExpiryDateTime $calculatesTransactionExpiryDateTime
*/
public function __construct(FetchesBooking $fetchesBooking, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit)
public function __construct(FetchesBooking $fetchesBooking, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit, CalculatesTransactionExpiryDateTime $calculatesTransactionExpiryDateTime)
{
$this->fetchesBooking = $fetchesBooking;
$this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit;
$this->calculatesTransactionExpiryDateTime = $calculatesTransactionExpiryDateTime;
}
/**
@@ -54,57 +60,8 @@ class CanPassOrderDurationLimitRule extends AbstractRule
protected function criteria($object): bool
{
//Check if order is still valid (within duration limit, reused PAYMENT_ATTEMPT_DURATION_LIMIT)
$isExpired = false;
$booking = $this->fetchesBooking->execute(['id' => $object->bookingId]);
$paymentAttemptLimit = $this->fetchesCompanyPaymentAttemptLimit->execute($booking->company);
// $paymentAttemptLimit = 5; //Manual testing must pay in minutes
$createdAt = Carbon::parse($booking->created_at);
$bookingExpiresAt = $createdAt->addMinutes($paymentAttemptLimit);
$now = Carbon::now();
if ($now->greaterThan($bookingExpiresAt)) {
$isExpired = true;
}
$allPayments = $booking->transactions()
->payments()
->get();
if ($isExpired) {
$filteredPayments = $allPayments->filter(function ($payment) use ($bookingExpiresAt) {
return Carbon::parse($payment->created_at)->lessThanOrEqualTo($bookingExpiresAt);
});
if ($filteredPayments->isNotEmpty()) {
// Use the earlier payment to recalculate bookingExpiresAt
$earliestPayment = $filteredPayments->sortBy('created_at')->first();
$newBookingExpiresAt = Carbon::parse($earliestPayment->created_at)->addMinutes($paymentAttemptLimit);
$logDetails = [
'booking_id' => $booking->id,
'initial_created_at' => $booking->created_at,
'original_expiry' => $bookingExpiresAt->toDateTimeString(),
'new_expiry' => $newBookingExpiresAt->toDateTimeString(),
'valid_payments' => []
];
foreach ($filteredPayments as $payment) {
$logDetails['valid_payments'][] = [
'payment_id' => $payment->id,
'created_at' => $payment->created_at,
'amount' => $payment->amount,
];
}
Log::info("Booking initially expired, but found valid pending payment(s).", $logDetails);
$bookingExpiresAt = $newBookingExpiresAt;
$isExpired = Carbon::now()->greaterThan($bookingExpiresAt);
} else {
Log::info("Booking expired and no valid pending payments for booking ID: {$booking->id}");
}
}
$expiresOn = $this->calculatesTransactionExpiryDateTime->execute($object->bookingId);
$isExpired = Carbon::now()->greaterThan($expiresOn);
if($isExpired){
throw new CriteriaNotFulfilledException("Transfer has already expired.");
}
@@ -0,0 +1,91 @@
<?php
namespace App\Classes\Modules\Transactions\Services;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Companies\Services\FetchesCompanyPaymentAttemptLimit;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
class CalculatesTransactionExpiryDateTime
{
/** @var FetchesBooking */
private $fetchesBooking;
/** @var FetchesCompanyPaymentAttemptLimit */
private $fetchesCompanyPaymentAttemptLimit;
/**
* CalculatesTransactionExpiryDateTime constructor.
* @param FetchesBooking $fetchesBooking
* @param FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit
*/
public function __construct(FetchesBooking $fetchesBooking, FetchesCompanyPaymentAttemptLimit $fetchesCompanyPaymentAttemptLimit)
{
$this->fetchesBooking = $fetchesBooking;
$this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit;
}
/**
* @param int $bookingId
* @return Carbon|null $returnDateTime
*/
public function execute(int $bookingId)
{
$isExpired = false;
$booking = $this->fetchesBooking->execute(['id' => $bookingId]);
$paymentAttemptLimit = $this->fetchesCompanyPaymentAttemptLimit->execute($booking->company);
$createdAt = Carbon::parse($booking->created_at);
Log::info("1. Booking created at {$createdAt}.");
$bookingExpiresAt = $createdAt->addMinutes($paymentAttemptLimit);
Log::info("1. Booking expires at {$bookingExpiresAt}. (original)");
$newBookingExpiresAt = null;
$now = Carbon::now();
if ($now->greaterThan($bookingExpiresAt)) {
$isExpired = true;
}
$allPayments = $booking->transactions()
->payments()
->get();
// if ($isExpired) {
$filteredPayments = $allPayments->filter(function ($payment) use ($bookingExpiresAt) {
return Carbon::parse($payment->created_at)->lessThanOrEqualTo($bookingExpiresAt);
});
if ($filteredPayments->isNotEmpty()) {
// Use the earlier payment to recalculate bookingExpiresAt
$earliestPayment = $filteredPayments->sortBy('created_at')->first();
Log::info("2. Booking earliest payment : {$earliestPayment->id}, {$earliestPayment->created_at}");
$newBookingExpiresAt = Carbon::parse($earliestPayment->created_at)->addMinutes($paymentAttemptLimit);
Log::info("2. Booking expires at : {$newBookingExpiresAt}. (new)");
$logDetails = [
'booking_id' => $booking->id,
'initial_created_at' => $booking->created_at,
'original_expiry' => $bookingExpiresAt->toDateTimeString(),
'new_expiry' => $newBookingExpiresAt->toDateTimeString(),
'valid_payments' => []
];
foreach ($filteredPayments as $payment) {
$logDetails['valid_payments'][] = [
'payment_id' => $payment->id,
'created_at' => $payment->created_at,
'amount' => $payment->amount,
];
}
Log::info("2. Booking initially expired, but found valid pending payment(s).", $logDetails);
$isExpired = Carbon::now()->greaterThan($newBookingExpiresAt);
Log::info("2. Booking expired: {$isExpired}");
} else {
Log::info("Booking expired and no valid pending payments for booking ID: {$booking->id}");
}
// }
$returnDateTime = $newBookingExpiresAt ? $newBookingExpiresAt : $bookingExpiresAt;
return $returnDateTime;
}
}
@@ -118,11 +118,11 @@
},
methods: {
submitForm(){
this.parameters = {
files: this.files,
booking_id: this.id,
company_id: this.data.booking.company.id,
payment_id: this.data.id,
};
this.submit(this.route('api.booking.payment.verification.create', this.id, this.data.id), 'post', this.section, true, true);
@@ -395,8 +395,8 @@
</div>
</div>
</div>
<div class="col-auto p-l-5 p-r-5 bg-success pointer" v-if="item.payment_method === 5">
<a :href="route('billplz.bill', item.payment_reference)">
<div class="col-auto p-l-5 p-r-5 bg-success pointer" v-if="item.payment_method === 5" @click="checkTransferRule(item)">
<a href="javascript:void(0);">
<div class="row align-items-center h-100">
<div class="col">
<i class="fa fa-repeat fs-20 text-white p-l-10 p-r-10"></i>
@@ -538,6 +538,7 @@
attention: false,
bearShowing: false,
oneSixEightEightServiceIds: [4, 10],
parameters: {}
}
},
computed: {
@@ -580,13 +581,24 @@
this.isLoading = true;
this.submit(route('api.booking.show', this.marking), 'get', this.section, false, false)
},
successHandler(response){
this.$store.dispatch('completeList', {'name': this.section, 'data': []});
this.isLoading = false;
this.booking = response.payload.data;
successHandler(response, section, payload){
if(section === this.section + 'CheckTransferRule'){
window.location.href = route('billplz.bill', payload.payment_reference);
}
else
{
this.$store.dispatch('completeList', {'name': this.section, 'data': []});
this.isLoading = false;
this.booking = response.payload.data;
}
},
errorHandler(){
window.location.href = this.route('dashboard')
errorHandler(response, statusCode, section){
if(section === this.section + 'CheckTransferRule' && statusCode === 422){
console.log('Transfer expired, please create new transfer');
}
else{
window.location.href = this.route('dashboard');
}
},
updateDefaultAddress(){
this.addressEdit = false;
@@ -594,7 +606,15 @@
},
selectedID(id){
this.selected_id = id;
}
},
checkTransferRule(item){
this.parameters = {
booking_id: item.booking.id,
company_id: item.booking.company.id,
payment_reference: item.payment_reference,
};
this.submit(route('api.rule.check.transfer'), 'post', this.section + 'CheckTransferRule', false, true);
},
}
}
</script>
+1 -1
View File
@@ -41,7 +41,7 @@ export default {
}
successNotification ? this.$store.dispatch('createNotification', { title: response.title, message: response.message, type: 'success' }) : null;
this.successHandler(response, section)
this.successHandler(response, section, this.parameters);
});