diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php
index ce8bec20..aa9f9670 100644
--- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php
+++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php
@@ -24,6 +24,7 @@ use App\Classes\Modules\Transactions\Processors\CreateCashBackTransactionProcess
use App\Classes\Modules\Vouchers\Processors\Voucherify\BookingToVoucherifyProcessor;
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\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use App\Classes\ValueObjects\Constants\TransactionType;
@@ -131,6 +132,7 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic
{
$dto = new ConfirmBookingDTO($request->all());
$result = $this->ruleEvaluator->evaluate([
+ App()->make(CanPassOrderDurationLimitRule::class),
App()->make(CanPassEInvoicePromptedRule::class),
App()->make(CanPassTINRule::class),
App()->make(CanPassPurchaseOrderRule::class),
diff --git a/app/Classes/Modules/Rules/ControllersLogic/CheckPurchaseOrderRuleLogic.php b/app/Classes/Modules/Rules/ControllersLogic/CheckPurchaseOrderRuleLogic.php
index 41f24c22..a1c74aee 100644
--- a/app/Classes/Modules/Rules/ControllersLogic/CheckPurchaseOrderRuleLogic.php
+++ b/app/Classes/Modules/Rules/ControllersLogic/CheckPurchaseOrderRuleLogic.php
@@ -8,6 +8,7 @@ use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Rules\DataTransferObjects\CheckPurchaseOrderRuleDTO;
use App\Classes\Modules\Rules\Services\RuleEvaluator;
use App\Classes\Modules\Rules\Standards\Rules\CanPassEInvoicePromptedRule;
+use App\Classes\Modules\Rules\Standards\Rules\CanPassOrderDurationLimitRule;
use App\Classes\Modules\Rules\Standards\Rules\CanPassPurchaseOrderRule;
use App\Http\Resources\RuleResource;
use Illuminate\Http\JsonResponse;
@@ -49,6 +50,7 @@ class CheckPurchaseOrderRuleLogic extends AbstractControllerLogic
$dto = new CheckPurchaseOrderRuleDTO($request->all());
$result = $this->ruleEvaluator->evaluate([
+ App()->make(CanPassOrderDurationLimitRule::class),
App()->make(CanPassEInvoicePromptedRule::class),
App()->make(CanPassPurchaseOrderRule::class),
], $dto);
diff --git a/app/Classes/Modules/Rules/ControllersLogic/CheckTransferRuleLogic.php b/app/Classes/Modules/Rules/ControllersLogic/CheckTransferRuleLogic.php
new file mode 100644
index 00000000..b17d8ed0
--- /dev/null
+++ b/app/Classes/Modules/Rules/ControllersLogic/CheckTransferRuleLogic.php
@@ -0,0 +1,60 @@
+ 'Rule Check Transfer',
+ 'message' => 'You have successfully passed all rules evaluated'
+ ];
+ }
+
+ /** @var RuleEvaluator */
+ private $ruleEvaluator;
+
+ /**
+ * CheckTransferRuleLogic constructor.
+ */
+ public function __construct(RuleEvaluator $ruleEvaluator)
+ {
+ $this->ruleEvaluator = $ruleEvaluator;
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ * @throws \App\Classes\Exceptions\AccessForbiddenException
+ * @throws \App\Classes\Exceptions\MalformedRequestException
+ * @throws \App\Classes\Exceptions\RequestValidationException
+ * @throws \App\Classes\Exceptions\CriteriaNotFulfilledException
+ */
+ public function logic(Request $request) : JsonResponse
+ {
+ $dto = new CheckTransferRuleDTO($request->all());
+
+ $result = $this->ruleEvaluator->evaluate([
+ App()->make(CanPassOrderDurationLimitRule::class),
+ ], $dto);
+
+ if ($result->failed()) {
+ throw new CriteriaNotFulfilledException("- " . implode("
- ", $result->messages()));
+ }
+
+ return $this->resourceResponse(new RuleResource((object)$result));
+ }
+}
diff --git a/app/Classes/Modules/Rules/DataTransferObjects/CheckTransferRuleDTO.php b/app/Classes/Modules/Rules/DataTransferObjects/CheckTransferRuleDTO.php
new file mode 100644
index 00000000..35f630f2
--- /dev/null
+++ b/app/Classes/Modules/Rules/DataTransferObjects/CheckTransferRuleDTO.php
@@ -0,0 +1,25 @@
+bookingId = $data['booking_id'];
+ $this->companyId = $data['company_id'];
+ }
+
+ public function toArray(): array
+ {
+ return [
+ 'booking_id' => $this->bookingId,
+ 'company_id' => $this->companyId,
+ ];
+ }
+}
diff --git a/app/Classes/Modules/Rules/Standards/Rules/CanPassOrderDurationLimitRule.php b/app/Classes/Modules/Rules/Standards/Rules/CanPassOrderDurationLimitRule.php
new file mode 100644
index 00000000..46739d2b
--- /dev/null
+++ b/app/Classes/Modules/Rules/Standards/Rules/CanPassOrderDurationLimitRule.php
@@ -0,0 +1,113 @@
+fetchesBooking = $fetchesBooking;
+ $this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit;
+ }
+
+ /**
+ * @return bool
+ */
+ protected function authorized($object): bool
+ {
+ return true;
+
+ }
+
+ /**
+ * @return bool
+ */
+ protected function validators($object): bool
+ {
+ return true;
+
+ }
+
+
+ /**
+ * @return bool
+ */
+ 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}");
+ }
+ }
+
+ if($isExpired){
+ throw new CriteriaNotFulfilledException("Transfer has already expired.");
+ }
+ return true;
+ }
+}
diff --git a/app/Http/Controllers/Rules/CheckRuleController.php b/app/Http/Controllers/Rules/CheckRuleController.php
index 881c6abe..59b24cd0 100644
--- a/app/Http/Controllers/Rules/CheckRuleController.php
+++ b/app/Http/Controllers/Rules/CheckRuleController.php
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Rules;
use App\Classes\Modules\Rules\ControllersLogic\CheckEInvoiceRuleLogic;
use App\Classes\Modules\Rules\ControllersLogic\CheckPurchaseOrderRuleLogic;
+use App\Classes\Modules\Rules\ControllersLogic\CheckTransferRuleLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -26,4 +27,13 @@ class CheckRuleController
public function checkPurchaseOrderRule(Request $request, CheckPurchaseOrderRuleLogic $logic): JsonResponse {
return $logic->execute($request);
}
+
+ /**
+ * @param Request $request
+ * @param CheckTransferRuleLogic $logic
+ * @return JsonResponse
+ */
+ public function checkTransferRule(Request $request, CheckTransferRuleLogic $logic): JsonResponse {
+ return $logic->execute($request);
+ }
}
diff --git a/app/Http/Resources/V2/BookingV2Resource.php b/app/Http/Resources/V2/BookingV2Resource.php
index a41d7e35..e1735081 100644
--- a/app/Http/Resources/V2/BookingV2Resource.php
+++ b/app/Http/Resources/V2/BookingV2Resource.php
@@ -53,15 +53,19 @@ class BookingV2Resource extends JsonResource
'created_at' => Carbon::parse($this->created_at)->format('d-m-Y'),
'created_at_with_time' => Carbon::parse($this->created_at)->format('d-m-Y h:i:s A'),
$this->mergeWhen($this->relationLoaded('transactions'), [
- 'purchase_order' => new V2\TransactionV2Resource($this->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first()),
+ 'purchase_order' => new V2\TransactionV2Resource(
+ $this->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first()),
'payment_attempts' => V2\TransactionV2Resource::collection(
$this->transactions()
->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION)
->whereDate('expires_on', '>=', Carbon::now())
->get()
),
- 'expired_payment_attempts' => V2\TransactionV2Resource::collection($this->transactions()->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION)->whereDate('expires_on', '>=', Carbon::now())->where('expires_on', '>', Carbon::now()->toTimeString())->get()),
- 'payment_history' => V2\TransactionV2Resource::collection($this->transactions()->where(function($query){
+ 'expired_payment_attempts' => V2\TransactionV2Resource::collection(
+ $this->transactions()->payments()->where('status', ApprovalStatus::PENDING_SUBMISSION)->whereDate('expires_on', '>=', Carbon::now())->where('expires_on', '>', Carbon::now()->toTimeString())->get()
+ ),
+ 'payment_history' => V2\TransactionV2Resource::collection(
+ $this->transactions()->where(function($query){
$query->where(function($query){
$query->payments()->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::COMPLETED, ApprovalStatus::REJECTED]);
})->orWhere(function($query){
diff --git a/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationV2Component.vue b/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationV2Component.vue
index b5858ee3..f9267240 100644
--- a/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationV2Component.vue
+++ b/resources/assets/vue/components/bookings/forms/BookingPaymentQuotationV2Component.vue
@@ -708,7 +708,10 @@
this.calculation = null;
},
successHandler(response, section){ //E-Invoice
- if(section === this.section + 'CheckEInvoiceRule'){
+ if(section === this.section + 'CheckTransferRule'){
+ this.checkEInvoiceRule();
+ }
+ else if(section === this.section + 'CheckEInvoiceRule'){
if(response.payload.data.isPassed){
this.amount = (Math.round((this.data.outstanding_amount + Number.EPSILON) * 100) / 100).toFixed(2);
this.expandPayment = true;
@@ -751,7 +754,8 @@
$('#modal-einvoice-request').modal('show');
}
else{
- this.checkEInvoiceRule();
+ this.checkTransferRule();
+ // this.checkEInvoiceRule();
}
},
cancelQuotation(){
@@ -799,6 +803,14 @@
}
},
//E-Invoice - Starts
+ checkTransferRule(){
+ this.error = '';
+ this.parameters = {
+ booking_id: this.data.id,
+ company_id: this.data.company.id,
+ };
+ this.submit(route('api.rule.check.transfer'), 'post', this.section + 'CheckTransferRule', false, true);
+ },
checkEInvoiceRule(){
this.error = '';
this.parameters = {
@@ -807,7 +819,6 @@
this.submit(route('api.rule.check.einvoice'), 'post', this.section + 'CheckEInvoiceRule', false, true);
},
checkPurchaseOrderRule(){
- this.error = '';
this.error = '';
this.parameters = {
booking_id: this.data.id,
diff --git a/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue b/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue
index 4636db2a..942a30e4 100644
--- a/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue
+++ b/resources/assets/vue/components/bookings/forms/PurchaseOrderFormComponent.vue
@@ -194,6 +194,7 @@
+
diff --git a/routes/rule.php b/routes/rule.php
index b2ea057b..d5330176 100644
--- a/routes/rule.php
+++ b/routes/rule.php
@@ -8,4 +8,5 @@ Route::prefix('rule')
->group(function () {
Route::post('/check/eInvoice', [CheckRuleController::class, 'checkEInvoiceRule'])->name('check.einvoice');
Route::post('/check/purchase-order', [CheckRuleController::class, 'checkPurchaseOrderRule'])->name('check.purchase.order');
+ Route::post('/check/tranfer', [CheckRuleController::class, 'checkTransferRule'])->name('check.transfer');
});