E-Invoice - EXC3000: Exchange Rules of RM10K single INV

This commit is contained in:
Dillon Ngo
2025-12-23 15:24:05 +08:00
parent 002418dcac
commit 408baab0c6
7 changed files with 268 additions and 24 deletions
@@ -0,0 +1,67 @@
<?php
namespace App\Classes\Modules\Rules\ControllersLogic;
use App\Classes\Exceptions\CriteriaNotFulfilledException;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use App\Classes\Modules\Rules\DataTransferObjects\CheckEInvoiceAmountLimitRuleDTO;
use App\Classes\Modules\Rules\Services\RuleEvaluator;
use App\Classes\Modules\Rules\Standards\Rules\CanPassMustEInvoiceAmountLimitRule;
use App\Http\Resources\RuleResource;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class CheckEInvoiceAmountLimitLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Rule Check E-Invoice Amount Limit',
'message' => 'You have successfully passed all rules evaluated'
];
}
/** @var RuleEvaluator */
private $ruleEvaluator;
/** @var CanPassMustEInvoiceAmountLimitRule */
private $canPassMustEInvoiceAmountLimitRule;
/**
* CheckEInvoiceAmountLimitLogic constructor.
* @param RuleEvaluator $ruleEvaluator
* @param CanPassMustEInvoiceAmountLimitRule $canPassMustEInvoiceAmountLimitRule
*/
public function __construct(RuleEvaluator $ruleEvaluator, CanPassMustEInvoiceAmountLimitRule $canPassMustEInvoiceAmountLimitRule)
{
$this->ruleEvaluator = $ruleEvaluator;
$this->canPassMustEInvoiceAmountLimitRule = $canPassMustEInvoiceAmountLimitRule;
}
/**
* @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 CheckEInvoiceAmountLimitRuleDTO($request->all());
$result = $this->ruleEvaluator->evaluate([
$this->canPassMustEInvoiceAmountLimitRule
], $dto);
if ($result->failed()) {
throw new CriteriaNotFulfilledException("- " . implode("<br>- ", $result->messages()));
}
return $this->resourceResponse(new RuleResource((object)$result));
}
}
@@ -0,0 +1,36 @@
<?php
namespace App\Classes\Modules\Rules\DataTransferObjects;
use App\Classes\General\Interfaces\DataTransferObject;
class CheckEInvoiceAmountLimitRuleDTO implements DataTransferObject
{
public int $bookingId;
public int $companyId;
public ?string $amount;
public ?string $paymentMethod;
public ?string $voucherCode;
public function __construct(array $data)
{
$this->bookingId = $data['booking_id'];
$this->companyId = $data['company_id'];
$this->amount = $data['amount'] ?? null;
$this->paymentMethod = $data['payment_method'] ?? null;
$this->voucherCode = $data['voucherCode'] ?? null;
}
public function toArray(): array
{
return [
'booking_id' => $this->bookingId,
'company_id' => $this->companyId,
'amount' => $this->amount,
'payment_method' => $this->paymentMethod,
'voucher_code' => $this->voucherCode,
];
}
}
@@ -0,0 +1,87 @@
<?php
namespace App\Classes\Modules\Rules\Standards\Rules;
use App\Classes\Exceptions\CriteriaNotFulfilledException;
use App\Classes\General\Abstracts\AbstractRule;
use App\Classes\Modules\Bookings\Services\FetchesBooking;
use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding;
use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation;
use App\Classes\Modules\Currencies\DataTransferObjects\CurrencyConversionObject;
use App\Classes\ValueObjects\Constants\PaymentMethodType;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
class CanPassMustEInvoiceAmountLimitRule extends AbstractRule
{
/** @var FetchesBooking */
private $fetchesBooking;
/** @var FetchesBookingQuotation */
private $fetchesBookingQuotation;
/** @var CalculatesBookingOutstanding */
private $calculatesBookingOutstanding;
/**
* CanPassMustEInvoiceAmountLimitRule constructor.
* @param FetchesBooking $fetchesBooking
* @param FetchesBookingQuotation $fetchesBookingQuotation
* @param CalculatesBookingOutstanding $calculatesBookingOutstanding
*/
public function __construct(FetchesBooking $fetchesBooking, FetchesBookingQuotation $fetchesBookingQuotation, CalculatesBookingOutstanding $calculatesBookingOutstanding)
{
$this->fetchesBooking = $fetchesBooking;
$this->fetchesBookingQuotation = $fetchesBookingQuotation;
$this->calculatesBookingOutstanding = $calculatesBookingOutstanding;
}
/**
* @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 booking amount is RM10,000 or more, than must opt-in E-Invoice
$booking = $this->fetchesBooking->execute(['id' => $object->bookingId]);
$company = $booking->company;
if(isset($object->amount)){
$conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', floatval(str_replace(',', '', $object->amount)))), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0:1, PaymentMethodType::PAYMENT_METHODS[$object->paymentMethod]);
$voucherCode = $object->voucherCode ?? null;
$configurations = $this->fetchesBookingQuotation->execute($booking->company, $conversionObject, $voucherCode, null, $booking);
}
else{
$outstanding = $this->calculatesBookingOutstanding->execute($booking);
$conversionObject = new CurrencyConversionObject(floatval(str_replace(',', '', $outstanding)), $booking->convertible_currency_id, $booking->service_id, $booking->fix_currency_id === 1 ? 0 : 1, PaymentMethodType::CASH);
$configurations = $this->fetchesBookingQuotation->execute($booking->company, $conversionObject);
}
$bookingAmountInMYR = $configurations->getTotal();
if($bookingAmountInMYR >= 10000 && (!$company->e_invoice || !$company->tin) ){
throw new CriteriaNotFulfilledException("RM10,000 and above must opt-in for E-Invoice.");
}
return true;
}
}
@@ -5,6 +5,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 App\Classes\Modules\Rules\ControllersLogic\CheckEInvoiceAmountLimitLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -36,4 +37,13 @@ class CheckRuleController
public function checkTransferRule(Request $request, CheckTransferRuleLogic $logic): JsonResponse {
return $logic->execute($request);
}
/**
* @param Request $request
* @param CheckEInvoiceAmountLimitLogic $logic
* @return JsonResponse
*/
public function checkEInvoiceAmountLimitRule(Request $request, CheckEInvoiceAmountLimitLogic $logic): JsonResponse {
return $logic->execute($request);
}
}
@@ -162,29 +162,6 @@
</modal-component>
</div>
</div>
<modal-component
id="modal-einvoice-request"
class="animate__animated animate__fast animate__fadeIn"
styleType="fill-in" type="requestEInvoiceA" size="large">
<e-invoice-request-form-component
class="text-center"
:section="section"
:company-id="data.company.id"
@choice-made="handleEInvoiceRequestRespond"
/>
</modal-component>
<modal-component
id="modal-einvoice-info"
class="animate__animated animate__fast animate__fadeIn"
styleType="fill-in" type="requestEInvoiceB" size="large">
<e-invoice-info-form-component
:section="section"
:company-id="data.company.id"
:company-type="data.company.type"
v-on:eInvoiceInfoUpdated="updatedEInvoiceInfo($event)"
v-on:eInvoiceChangeOfMindRequest="changeOfMindEInvoiceRequest()">
</e-invoice-info-form-component>
</modal-component>
<!-- E-Invoice - end -->
</div>
</div>
@@ -622,7 +599,7 @@
<!-- E-Invoice -->
<button id="lock-booking" class="btn btn-sm all-caps b-rad-none btn-success btn-block"
v-if="paymentMethod.id !== 'wallet' || walletOutstanding >= 0"
@click.prevent="checkPurchaseOrderRule">Lock Booking</button>
@click.prevent="checkEInvoiceAmountLimitRule">Lock Booking</button>
</div>
<modal-component id="confirm-booking-modal" v-if="calculation.total > 0" class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" type="paymentSummary">
<confirm-quotation-form-component v-on:cancelQuotation="cancelQuotation()" :calculation="calculation" :section="section" :id="item.id" :payment_method="paymentMethod.id" :bank_code="onlinePayment.id" :amount="amount" :company-id="item.company.id"></confirm-quotation-form-component>
@@ -630,6 +607,31 @@
</div>
</div>
</div>
<!-- E-Invoice - start -->
<modal-component
id="modal-einvoice-request"
class="animate__animated animate__fast animate__fadeIn"
styleType="fill-in" type="requestEInvoiceA" size="large">
<e-invoice-request-form-component
class="text-center"
:section="section"
:company-id="data.company.id"
@choice-made="handleEInvoiceRequestRespond"
/>
</modal-component>
<modal-component
id="modal-einvoice-info"
class="animate__animated animate__fast animate__fadeIn"
styleType="fill-in" type="requestEInvoiceB" size="large">
<e-invoice-info-form-component
:section="section"
:company-id="data.company.id"
:company-type="data.company.type"
v-on:eInvoiceInfoUpdated="updatedEInvoiceInfo($event)"
v-on:eInvoiceChangeOfMindRequest="changeOfMindEInvoiceRequest()">
</e-invoice-info-form-component>
</modal-component>
<!-- E-Invoice - end -->
</div>
</div>
</div>
@@ -740,6 +742,9 @@
$('#confirm-booking-modal').modal('show');
}
}
else if(section === this.section + 'CheckEInvoiceAmountLimitRule'){
this.checkPurchaseOrderRule();
}
else if(section === this.section + 'ChangeOfMind'){
this.$store.dispatch('reloadList', {'name': "bookingDetailSection"});
}
@@ -767,6 +772,14 @@
if(section === this.section + 'CheckEInvoiceRule' && statusCode === 422){
$('#modal-einvoice-info').modal('show');
}
else if(section === this.section + 'CheckEInvoiceAmountLimitRule' && statusCode === 422){
if(!this.data.company.e_invoice){
$('#modal-einvoice-request').modal('show');
}
else if(!this.data.company.tin){
$('#modal-einvoice-info').modal('show');
}
}
this.error = error.message;
},
makePayment(){ //E-Invoice
@@ -837,6 +850,17 @@
};
this.submit(route('api.rule.check.einvoice'), 'post', this.section + 'CheckEInvoiceRule', false, true);
},
checkEInvoiceAmountLimitRule(){
this.error = '';
this.parameters = {
booking_id: this.data.id,
company_id: this.data.company.id,
payment_method: this.paymentMethod.id,
voucherCode: this.voucherCode,
amount: this.amount
};
this.submit(route('api.rule.check.einvoice.amount-limit'), 'post', this.section + 'CheckEInvoiceAmountLimitRule', false, true);
},
checkPurchaseOrderRule(){
this.error = '';
this.parameters = {
@@ -432,6 +432,9 @@
this.checkEInvoiceRule();
}
else if(section === this.section + 'CheckEInvoiceRule'){
this.checkEInvoiceAmountLimitRule();
}
else if(section === this.section + 'CheckEInvoiceAmountLimitRule'){
if(response.payload.data.isPassed){
if(this.data.service.id === 14 || this.data.service.id === 15 || this.data.service.id === 16 || this.data.service.id === 17 || this.data.service.id === 18 || this.data.service.id === 19){
this.submit(route('api.booking.banking.create', this.data.id), 'post', this.section, true, true);
@@ -452,6 +455,14 @@
if(section === this.section + 'CheckEInvoiceRule' && statusCode === 422){
$('#modal-einvoice-info').modal('show');
}
else if(section === this.section + 'CheckEInvoiceAmountLimitRule' && statusCode === 422){
if(!this.data.company.e_invoice){
$('#modal-einvoice-request').modal('show');
}
else if(!this.data.company.tin){
$('#modal-einvoice-info').modal('show');
}
}
},
addProduct(){
this.products.push({
@@ -518,6 +529,14 @@
};
this.submit(route('api.rule.check.einvoice'), 'post', this.section + 'CheckEInvoiceRule', false, true);
},
checkEInvoiceAmountLimitRule(){
this.error = '';
this.parameters = {
booking_id: this.data.id,
company_id: this.data.company.id,
};
this.submit(route('api.rule.check.einvoice.amount-limit'), 'post', this.section + 'CheckEInvoiceAmountLimitRule', false, true);
},
updatedEInvoiceInfo(info){
this.$store.dispatch('reloadList', {'name': "bookingDetailSection"});
},
+1
View File
@@ -9,4 +9,5 @@ Route::prefix('rule')
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');
Route::post('/check/e-invoice/amount-limit', [CheckRuleController::class, 'checkEInvoiceAmountLimitRule'])->name('check.einvoice.amount-limit');
});