diff --git a/app/Classes/Jobs/Commands/V2/CreateInvoiceTransactionV2CommandJob.php b/app/Classes/Jobs/Commands/V2/CreateInvoiceTransactionV2CommandJob.php new file mode 100644 index 00000000..08576f16 --- /dev/null +++ b/app/Classes/Jobs/Commands/V2/CreateInvoiceTransactionV2CommandJob.php @@ -0,0 +1,42 @@ +booking = $booking; + } + + public function handle() + { + Log::info(Carbon::now() . ': Start job - Creating invoice for single booking.'); + $start = new Carbon(); + + (App()->make(CreateInvoiceTransactionV2Processor::class))->execute($this->booking); + + $end = new Carbon(); + $elapsedTime = $start->diff($end)->format('%H:%I:%S'); + Log::info(Carbon::now() . ': End job - Creating invoice for single booking. ElapsedTime: ' . $elapsedTime . '.'); + } + +} diff --git a/app/Classes/Modules/Billplzs/ControllersLogic/CallbackBillplzLogic.php b/app/Classes/Modules/Billplzs/ControllersLogic/CallbackBillplzLogic.php index b6537420..42726384 100644 --- a/app/Classes/Modules/Billplzs/ControllersLogic/CallbackBillplzLogic.php +++ b/app/Classes/Modules/Billplzs/ControllersLogic/CallbackBillplzLogic.php @@ -2,31 +2,32 @@ namespace App\Classes\Modules\Billplzs\ControllersLogic; -use App\Classes\Modules\Wallets\DataTransferObjects\WalletObject; -use App\Classes\Modules\Wallets\Services\UpdatesWallet; +use App\Classes\Exceptions\MalformedRequestException; +use App\Classes\Exceptions\ResourceNotFoundException; +use App\Classes\General\Abstracts\AbstractControllerLogic; +use App\Classes\Jobs\Commands\V2\CreateInvoiceTransactionV2CommandJob; +use App\Classes\Modules\Billplzs\DataTransferObjects\BillplzXSignatureObject; +use App\Classes\Modules\Billplzs\Services\GetBillplzBill; use App\Classes\Modules\Transactions\Processors\CreateCashBackTransactionProcessor; use App\Classes\Modules\Transactions\Processors\CreateReceiptVoucherTransactionProcessor; - -use App\Classes\Exceptions\ResourceNotFoundException; +use App\Classes\Modules\Transactions\Services\FetchesTransaction; +use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; +use App\Classes\Modules\Wallets\DataTransferObjects\WalletObject; +use App\Classes\Modules\Wallets\Services\RecalculatesWalletBalance; +use App\Classes\Modules\Wallets\Services\UpdatesWallet; use App\Classes\Modules\Wallets\Services\UpdatesWalletBalance; +use App\Classes\ValueObjects\Constants\ApprovalStatus; +use App\Classes\ValueObjects\Constants\TransactionType; use App\Http\Resources\TransactionResource; + use App\Models\Booking; use App\Models\User; use App\Models\Wallet; -use Illuminate\Http\Request; -use Illuminate\Http\JsonResponse; -use App\Classes\Exceptions\MalformedRequestException; -use App\Classes\ValueObjects\Constants\ApprovalStatus; -use App\Classes\ValueObjects\Constants\TransactionType; -use App\Classes\Modules\Billplzs\Services\GetBillplzBill; -use App\Classes\Modules\Billplzs\DataTransferObjects\BillplzXSignatureObject; -use App\Classes\General\Abstracts\AbstractControllerLogic; -use App\Classes\Modules\Transactions\Services\FetchesTransaction; -use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; +use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; -use App\Classes\Modules\Wallets\Services\RecalculatesWalletBalance; class CallbackBillplzLogic { @@ -117,6 +118,10 @@ class CallbackBillplzLogic $this->createReceiptVoucherTransactionProcessor->execute($booking, $transaction); } + if($booking){ + CreateInvoiceTransactionV2CommandJob::dispatch($booking); + } + // if ($transaction->type == TransactionType::PAYMENT) { // $cash_back_transaction = $this->createCashBackTransactionProcessor->execute($transaction); // } diff --git a/app/Classes/Modules/Bookings/ControllersLogic/ApprovePurchaseOrderLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/ApprovePurchaseOrderLogic.php index 7422d6ca..3360946a 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/ApprovePurchaseOrderLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/ApprovePurchaseOrderLogic.php @@ -67,7 +67,13 @@ class ApprovePurchaseOrderLogic extends AbstractControllerLogic $this->updatesTransactionStatus->execute($purchaseOrder, ApprovalStatus::APPROVED); - $this->createInvoiceTransactionProcessor->execute($booking); + $this->createInvoiceTransactionProcessor->execute($booking, "", null, [ + 'generateEInvoice' => false, + 'generateEInvoiceWithNormalInvoiceTemplate' => false, + 'generateEInvoiceRefund' => false, + 'bookingOriginalStatus' => $booking->status + ] + ); return $this->response([]); } diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php index f67fa669..e8c31bae 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php @@ -5,6 +5,7 @@ namespace App\Classes\Modules\Bookings\ControllersLogic; use App\Classes\Exceptions\CriteriaNotFulfilledException; use App\Classes\Exceptions\MalformedRequestException; use App\Classes\General\Abstracts\AbstractControllerLogic; +use App\Classes\Jobs\Commands\V2\CreateInvoiceTransactionV2CommandJob; use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding; use App\Classes\Modules\Bookings\Services\FetchesBookingQuotation; use App\Classes\Modules\Companies\Services\FetchesCompanyPaymentAttemptLimit; @@ -238,6 +239,10 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic $this->createReceiptVoucherTransactionProcessor->execute($booking, $transaction); } + if($booking){ + CreateInvoiceTransactionV2CommandJob::dispatch($booking); + } + return $this->resourceResponse(new TransactionResource($transaction)); } diff --git a/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountLogic.php index fd4af7f8..73985848 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountLogic.php @@ -2,25 +2,24 @@ namespace App\Classes\Modules\Bookings\ControllersLogic; +use App\Classes\Exceptions\MalformedRequestException; +use App\Classes\General\Abstracts\AbstractControllerLogic; +use App\Classes\Jobs\Commands\V2\CreateInvoiceTransactionV2CommandJob; +use App\Classes\Modules\Bookings\DataTransferObjects\BookingObject; +use App\Classes\Modules\Bookings\DataTransferObjects\UpdateBookingInvoiceStatusObject; +use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding; +use App\Classes\Modules\Bookings\Services\FetchesBooking; +use App\Classes\Modules\Bookings\Services\UpdatesBookingInvoiceStatus; use App\Classes\Modules\Bookings\Services\UpdatesBookingFixedAmount; +use App\Classes\Modules\Bookings\Standards\Rules\CanUpdateBooking; +use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionV2Processor; use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\TransactionType; use App\Http\Resources\BookingResource; - -use App\Classes\General\Abstracts\AbstractControllerLogic; - -use App\Classes\Modules\Bookings\Services\FetchesBooking; - -use App\Classes\Modules\Bookings\Standards\Rules\CanUpdateBooking; -use App\Classes\Modules\Bookings\DataTransferObjects\BookingObject; -use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding; - -use ErrorException; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; -use App\Classes\Exceptions\MalformedRequestException; use Illuminate\Support\Facades\Log; class UpdateBookingAmountLogic extends AbstractControllerLogic @@ -52,6 +51,12 @@ class UpdateBookingAmountLogic extends AbstractControllerLogic /** @var UpdatesTransactionStatus */ private $updatesTransactionStatus; + /** @var UpdatesBookingInvoiceStatus */ + private $updatesBookingInvoiceStatus; + + /** @var CreateInvoiceTransactionV2Processor */ + private $createInvoiceTransactionProcessor; + /** * UpdateBookingAmountLogic constructor. * @param CanUpdateBooking $canUpdateBooking @@ -59,14 +64,18 @@ class UpdateBookingAmountLogic extends AbstractControllerLogic * @param FetchesBooking $fetchesBooking * @param CalculatesBookingOutstanding $calculatesBookingOutstanding * @param UpdatesTransactionStatus $updatesTransactionStatus + * @param UpdatesBookingInvoiceStatus $updatesBookingInvoiceStatus + * @param CreateInvoiceTransactionV2Processor $createInvoiceTransactionProcessor */ - public function __construct(CanUpdateBooking $canUpdateBooking, UpdatesBookingFixedAmount $updatesBookingFixedAmount, FetchesBooking $fetchesBooking, CalculatesBookingOutstanding $calculatesBookingOutstanding, UpdatesTransactionStatus $updatesTransactionStatus) + public function __construct(CanUpdateBooking $canUpdateBooking, UpdatesBookingFixedAmount $updatesBookingFixedAmount, FetchesBooking $fetchesBooking, CalculatesBookingOutstanding $calculatesBookingOutstanding, UpdatesTransactionStatus $updatesTransactionStatus, UpdatesBookingInvoiceStatus $updatesBookingInvoiceStatus, CreateInvoiceTransactionV2Processor $createInvoiceTransactionProcessor) { $this->canUpdateBooking = $canUpdateBooking; $this->updatesBookingFixedAmount = $updatesBookingFixedAmount; $this->fetchesBooking = $fetchesBooking; $this->calculatesBookingOutstanding = $calculatesBookingOutstanding; $this->updatesTransactionStatus = $updatesTransactionStatus; + $this->updatesBookingInvoiceStatus = $updatesBookingInvoiceStatus; + $this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor; } /** @@ -81,7 +90,7 @@ class UpdateBookingAmountLogic extends AbstractControllerLogic $input_amount = number_format( floatval(str_replace(',', '', $request->input('fix_amount', $booking->fix_amount))), 5, '.', ''); $minimum_amount = $booking->fix_amount - $this->calculatesBookingOutstanding->execute($booking); - + if (((float)$input_amount + 0.01) < (float)$minimum_amount) { $input_amount = 0; // throw new MalformedRequestException('Booking Amount cannot be less than '. $minimum_amount .'.'); @@ -97,7 +106,15 @@ class UpdateBookingAmountLogic extends AbstractControllerLogic $booking = $this->updatesBookingFixedAmount->execute($booking, $input_amount); + if($booking->invoice_status === ApprovalStatus::APPROVED){ + $updateBookingInvoiceStatusObject = new UpdateBookingInvoiceStatusObject($booking->id, ApprovalStatus::PENDING_VERIFICATION); + $this->updatesBookingInvoiceStatus->execute($booking, $updateBookingInvoiceStatusObject); + } + else{ + $this->createInvoiceTransactionProcessor->execute($booking); + } + return $this->resourceResponse(new BookingResource($booking)); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountOnHoldLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountOnHoldLogic.php index 39dc6674..3588909f 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountOnHoldLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountOnHoldLogic.php @@ -14,6 +14,7 @@ use App\Classes\ValueObjects\Constants\TransactionType; use App\Classes\Exceptions\MalformedRequestException; use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Accounts\DataTransferObjects\KeyValuePairObject; +use App\Classes\ValueObjects\Constants\KVPKey; use App\Http\Resources\BookingResource; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -90,9 +91,8 @@ class UpdateBookingAmountOnHoldLogic extends AbstractControllerLogic throw new MalformedRequestException('Booking Amount cannot be less than '. $minimum_amount .'.'); } - $key = "BOOKING_AMOUNT_UPDATE"; - $keyValuePairObject = new KeyValuePairObject($key, $input_amount); - $metadata = $booking->attributesKVP()->where('key', $key)->first(); + $keyValuePairObject = new KeyValuePairObject(KVPKey::BOOKING_AMOUNT_UPDATE, $input_amount); + $metadata = $booking->attributesKVP()->where('key', KVPKey::BOOKING_AMOUNT_UPDATE)->first(); if($metadata){ $this->updatesKeyValuePair->execute($metadata, $keyValuePairObject); } diff --git a/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountWithPOLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountWithPOLogic.php index 4def81f7..48cc7d94 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountWithPOLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/UpdateBookingAmountWithPOLogic.php @@ -10,6 +10,11 @@ use App\Classes\Modules\Bookings\Services\UpdatesBookingFixedAmount; use App\Classes\Modules\Bookings\Services\CalculatesBookingOutstanding; use App\Classes\Modules\Bookings\Services\CalculatesBookingPayableAmount; use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount; +use App\Classes\Modules\Bookings\Services\UpdatesBookingInvoiceStatus; +use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionV2Processor; +use App\Classes\Modules\Bookings\DataTransferObjects\UpdateBookingInvoiceStatusObject; +use App\Classes\ValueObjects\Constants\ApprovalStatus; +use App\Classes\ValueObjects\Constants\KVPKey; use App\Classes\ValueObjects\Constants\TransactionType; use App\Http\Resources\BookingResource; use App\Models\Booking; @@ -45,6 +50,12 @@ class UpdateBookingAmountWithPOLogic extends AbstractControllerLogic /** @var CalculatesBookingRefundAmount */ private $calculatesBookingRefundAmount; + /** @var UpdatesBookingInvoiceStatus */ + private $updatesBookingInvoiceStatus; + + /** @var CreateInvoiceTransactionV2Processor */ + private $createInvoiceTransactionProcessor; + /** * UpdateBookingAmountWithPOLogic constructor. * @param FetchesBooking $fetchesBooking @@ -52,14 +63,18 @@ class UpdateBookingAmountWithPOLogic extends AbstractControllerLogic * @param CalculatesBookingOutstanding $calculatesBookingOutstanding * @param CalculatesBookingPayableAmount $calculatesBookingPayableAmount * @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount + * @param UpdatesBookingInvoiceStatus $updatesBookingInvoiceStatus + * @param CreateInvoiceTransactionV2Processor $createInvoiceTransactionProcessor */ - public function __construct(FetchesBooking $fetchesBooking, UpdatesBookingFixedAmount $updatesBookingFixedAmount, CalculatesBookingOutstanding $calculatesBookingOutstanding, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingRefundAmount $calculatesBookingRefundAmount) + public function __construct(FetchesBooking $fetchesBooking, UpdatesBookingFixedAmount $updatesBookingFixedAmount, CalculatesBookingOutstanding $calculatesBookingOutstanding, CalculatesBookingPayableAmount $calculatesBookingPayableAmount, CalculatesBookingRefundAmount $calculatesBookingRefundAmount, UpdatesBookingInvoiceStatus $updatesBookingInvoiceStatus, CreateInvoiceTransactionV2Processor $createInvoiceTransactionProcessor) { $this->fetchesBooking = $fetchesBooking; $this->updatesBookingFixedAmount = $updatesBookingFixedAmount; $this->calculatesBookingOutstanding = $calculatesBookingOutstanding; $this->calculatesBookingPayableAmount = $calculatesBookingPayableAmount; $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount; + $this->updatesBookingInvoiceStatus = $updatesBookingInvoiceStatus; + $this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor; } /** @@ -74,7 +89,7 @@ class UpdateBookingAmountWithPOLogic extends AbstractControllerLogic /** @var Booking $booking */ $booking = $this->fetchesBooking->execute(['id' => $request->route('id') ?? $id]); $outstandingAmount = $this->calculatesBookingOutstanding->execute($booking); - $bookingAttribute = $booking->attributesKVP()->where('key', "BOOKING_AMOUNT_UPDATE")->first(); + $bookingAttribute = $booking->attributesKVP()->where('key', KVPKey::BOOKING_AMOUNT_UPDATE)->first(); // cief todo: 90 - BEFORE REVERT @@ -110,6 +125,14 @@ class UpdateBookingAmountWithPOLogic extends AbstractControllerLogic $booking->transactions()->where('type', TransactionType::PROFORMA)->delete(); $booking = $this->updatesBookingFixedAmount->execute($booking, $bookingAmountUpdate); + if($booking->invoice_status === ApprovalStatus::APPROVED){ + $updateBookingInvoiceStatusObject = new UpdateBookingInvoiceStatusObject($booking->id, ApprovalStatus::PENDING_VERIFICATION); + $this->updatesBookingInvoiceStatus->execute($booking, $updateBookingInvoiceStatusObject); + } + else{ + $this->createInvoiceTransactionProcessor->execute($booking); + } + $request->merge(['is_privilleged_update' => true]); $bookingAttribute->delete(); diff --git a/app/Classes/Modules/Bookings/DataTransferObjects/UpdateBookingInvoiceStatusObject.php b/app/Classes/Modules/Bookings/DataTransferObjects/UpdateBookingInvoiceStatusObject.php new file mode 100644 index 00000000..6b04a695 --- /dev/null +++ b/app/Classes/Modules/Bookings/DataTransferObjects/UpdateBookingInvoiceStatusObject.php @@ -0,0 +1,42 @@ +id = $id; + $this->invoiceStatus = $invoiceStatus; + } + + /** + * @return int + */ + public function getId(): int + { + return $this->id; + } + + /** + * @return int + */ + public function getInvoiceStatus(): int + { + return $this->invoiceStatus; + } +} diff --git a/app/Classes/Modules/Bookings/Processors/CreatePurchaseOrderFor1688OrderProcessor.php b/app/Classes/Modules/Bookings/Processors/CreatePurchaseOrderFor1688OrderProcessor.php index 8a24a5ab..83b25386 100644 --- a/app/Classes/Modules/Bookings/Processors/CreatePurchaseOrderFor1688OrderProcessor.php +++ b/app/Classes/Modules/Bookings/Processors/CreatePurchaseOrderFor1688OrderProcessor.php @@ -88,7 +88,13 @@ class CreatePurchaseOrderFor1688OrderProcessor if($purchaseOrder->amount === $booking->fix_amount){ $this->updatesTransactionStatus->execute($purchaseOrder, ApprovalStatus::APPROVED); - $this->createInvoiceTransactionProcessor->execute($booking); + $this->createInvoiceTransactionProcessor->execute($booking, "", null, [ + 'generateEInvoice' => false, + 'generateEInvoiceWithNormalInvoiceTemplate' => false, + 'generateEInvoiceRefund' => false, + 'bookingOriginalStatus' => ApprovalStatus::COMPLETED + ] + ); } } diff --git a/app/Classes/Modules/Bookings/Services/UpdatesBookingInvoiceStatus.php b/app/Classes/Modules/Bookings/Services/UpdatesBookingInvoiceStatus.php new file mode 100644 index 00000000..97342170 --- /dev/null +++ b/app/Classes/Modules/Bookings/Services/UpdatesBookingInvoiceStatus.php @@ -0,0 +1,23 @@ +invoice_status = $object->getInvoiceStatus(); + return $this->handler($model); + } +} diff --git a/app/Classes/Modules/Companies/ControllersLogic/FetchCompanyEInvoiceInfoLogic.php b/app/Classes/Modules/Companies/ControllersLogic/FetchCompanyEInvoiceInfoLogic.php index ebb2d275..2d886967 100644 --- a/app/Classes/Modules/Companies/ControllersLogic/FetchCompanyEInvoiceInfoLogic.php +++ b/app/Classes/Modules/Companies/ControllersLogic/FetchCompanyEInvoiceInfoLogic.php @@ -58,6 +58,7 @@ class FetchCompanyEInvoiceInfoLogic extends AbstractControllerLogic $eInvoiceInfo->tin = $company->tin; $eInvoiceInfo->msic_code = $company->msic_code; $eInvoiceInfo->e_invoice = $company->e_invoice; + $eInvoiceInfo->e_invoice_requested_at = $company->e_invoice_requested_at; } else{ return $this->response([]); diff --git a/app/Classes/Modules/Exports/Services/ExportsSalesInvoiceReport.php b/app/Classes/Modules/Exports/Services/ExportsSalesInvoiceReport.php index 3142441b..a7c170b7 100644 --- a/app/Classes/Modules/Exports/Services/ExportsSalesInvoiceReport.php +++ b/app/Classes/Modules/Exports/Services/ExportsSalesInvoiceReport.php @@ -2,6 +2,7 @@ namespace App\Classes\Modules\Exports\Services; +use App\Classes\Jobs\Commands\V2\CreateInvoiceTransactionV2CommandJob; use App\Classes\ValueObjects\Constants\TransactionType; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Models\Booking; @@ -71,7 +72,8 @@ class ExportsSalesInvoiceReport implements FromQuery, WithHeadings, WithHeadingR 'transactions.voucherRedemption', 'transactions.voucherRedemption.voucher.campaign' ]) - ->where('status', ApprovalStatus::COMPLETED) + // ->whereIn('status', [ApprovalStatus::COMPLETED]) + ->whereIn('invoice_status', [ApprovalStatus::APPROVED]) ->whereHas('transactions', function ($query) use ($startDate, $endDate) { $query->payments() ->complete() @@ -115,6 +117,11 @@ class ExportsSalesInvoiceReport implements FromQuery, WithHeadings, WithHeadingR } $invoiceTransaction = $booking->transactions->where('type', TransactionType::INVOICE)->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->first(); + if(!$invoiceTransaction){ + Log::info('ExportsSalesInvoiceReport booking with NO invoice: ' . json_encode($booking)); + // CreateInvoiceTransactionV2CommandJob::dispatch($booking); + return $records; + } $currencyId = $booking->fix_currency_id; diff --git a/app/Classes/Modules/Rules/ControllersLogic/CheckEInvoiceAmountLimitLogic.php b/app/Classes/Modules/Rules/ControllersLogic/CheckEInvoiceAmountLimitLogic.php new file mode 100644 index 00000000..bc9f18ae --- /dev/null +++ b/app/Classes/Modules/Rules/ControllersLogic/CheckEInvoiceAmountLimitLogic.php @@ -0,0 +1,67 @@ + '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("
- ", $result->messages())); + } + + return $this->resourceResponse(new RuleResource((object)$result)); + } +} diff --git a/app/Classes/Modules/Rules/DataTransferObjects/CheckEInvoiceAmountLimitRuleDTO.php b/app/Classes/Modules/Rules/DataTransferObjects/CheckEInvoiceAmountLimitRuleDTO.php new file mode 100644 index 00000000..b28e78f1 --- /dev/null +++ b/app/Classes/Modules/Rules/DataTransferObjects/CheckEInvoiceAmountLimitRuleDTO.php @@ -0,0 +1,36 @@ +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, + ]; + } +} diff --git a/app/Classes/Modules/Rules/Standards/Rules/CanPassMustEInvoiceAmountLimitRule.php b/app/Classes/Modules/Rules/Standards/Rules/CanPassMustEInvoiceAmountLimitRule.php new file mode 100644 index 00000000..d077aab7 --- /dev/null +++ b/app/Classes/Modules/Rules/Standards/Rules/CanPassMustEInvoiceAmountLimitRule.php @@ -0,0 +1,102 @@ +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; + + // $totalPayments = 0; + + // if(isset($object->amount)){ + // $totalPayments = $booking->transactions()->payments()->whereIn('transactions.status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->sum('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(); + // $bookingAmountInMYR = $bookingAmountInMYR + $totalPayments; + + // if($bookingAmountInMYR >= 10000 && (!$company->e_invoice || !$company->tin) ){ + // throw new CriteriaNotFulfilledException("RM10,000 and above must opt-in for E-Invoice."); + // } + + //1 MYR, 2 CNY, 3 USD + if((($booking->fix_amount >= 9000 && $booking->fix_currency_id === 1) || + ($booking->fix_amount >= 12600 && $booking->fix_currency_id === 2) || + ($booking->fix_amount >= 1920 && $booking->fix_currency_id === 3)) + && (!$company->e_invoice || !$company->tin) ){ + throw new CriteriaNotFulfilledException("Booking amount above limit, must opt-in for E-Invoice."); + } + + return true; + } +} \ No newline at end of file diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php index fdaf5e75..a1d12768 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/CreatePaymentProofDocumentLogic.php @@ -90,7 +90,13 @@ class CreatePaymentProofDocumentLogic extends AbstractControllerLogic $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED); - $this->createInvoiceTransactionProcessor->execute($transaction->owner->booking); + $this->createInvoiceTransactionProcessor->execute($transaction->owner->booking, "", null, [ + 'generateEInvoice' => false, + 'generateEInvoiceWithNormalInvoiceTemplate' => false, + 'generateEInvoiceRefund' => false, + 'bookingOriginalStatus' => ApprovalStatus::COMPLETED + ] + ); // send email to customer // todo: a function to send a proof to the receipiant, they have to give us a email of the receipiant and also need to submiited purchase order diff --git a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionV2Processor.php b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionV2Processor.php index 5684ba18..230296e4 100644 --- a/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionV2Processor.php +++ b/app/Classes/Modules/Transactions/Processors/CreateInvoiceTransactionV2Processor.php @@ -104,14 +104,20 @@ class CreateInvoiceTransactionV2Processor $generateEInvoice = $options['generateEInvoice'] ?? false; $generateEInvoiceWithNormalInvoiceTemplate = $options['generateEInvoiceWithNormalInvoiceTemplate'] ?? false; $generateEInvoiceRefund = $options['generateEInvoiceRefund'] ?? false; - $bookingOriginalStatus = $options['bookingOriginalStatus'] ?? ApprovalStatus::COMPLETED; + $bookingOriginalStatus = $options['bookingOriginalStatus'] ?? ApprovalStatus::APPROVED; + + // Log::info('CreateInvoiceTransactionV2Processor generateEInvoice:' . json_encode($generateEInvoice)); + // Log::info('CreateInvoiceTransactionV2Processor generateEInvoiceWithNormalInvoiceTemplate: ' . json_encode($generateEInvoiceWithNormalInvoiceTemplate)); + // Log::info('CreateInvoiceTransactionV2Processor generateEInvoiceRefund: ' . json_encode($generateEInvoiceRefund)); + // Log::info('CreateInvoiceTransactionV2Processor bookingOriginalStatus: ' . json_encode($bookingOriginalStatus)); if($generateEInvoiceRefund){ $generateEInvoice = true; //With or without refund, these 2 flags are meant to Generate E-Invoice, so cannot have opposite indicator } - if ($booking->status === ApprovalStatus::COMPLETED && !$generateEInvoice) { - return; + if ($booking->status === ApprovalStatus::COMPLETED && !$generateEInvoiceRefund) { + Log::info('CreateInvoiceTransactionV2Processor Check 1 Bypass New Business Logic Update for booking ' . $booking->id); + // return; } $payable_amount = $this->calculatesBookingPayableAmount->execute($booking, $booking->fix_currency_id, $generateEInvoiceRefund); @@ -128,31 +134,27 @@ class CreateInvoiceTransactionV2Processor } // confirm that all payments has been transferred if ($this->calculatesBookingTransferredAmount->execute($booking) !== $this->calculatesBookingPaidAmount->execute($booking)) { - return; + Log::info('CreateInvoiceTransactionV2Processor Check 2 Bypass New Business Logic Update for booking ' . $booking->id); + // return; } - if($generateEInvoiceRefund){ + $purchaseOrder = $booking->transactions() + ->where('type', TransactionType::PURCHASE_ORDER) + ->complete() + ->first(); + + if(!$purchaseOrder){ + //was use for $generateEInvoiceRefund true $purchaseOrder = $booking->transactions() ->where('type', TransactionType::PURCHASE_ORDER) ->where('status', ApprovalStatus::PENDING_SUBMISSION) ->first(); } - else{ - $purchaseOrder = $booking->transactions() - ->where('type', TransactionType::PURCHASE_ORDER) - ->complete() - ->first(); - } $constants = SegmentConstant::where('reference', SegmentConstants::SERVICE_TYPE)->where('detail->id', $booking->service->id)->first(); - - if ($constants->detail->is_billable && !$purchaseOrder && !$generateEInvoiceRefund) { - return; - } - - // $transaction = $booking->transactions() - // ->where('type', TransactionType::PAYMENT) - // ->first(); + // if ($constants->detail->is_billable && !$purchaseOrder && !$generateEInvoiceRefund) { + // return; + // } $transaction = $booking->transactions() ->where('type', TransactionType::PAYMENT) @@ -163,7 +165,8 @@ class CreateInvoiceTransactionV2Processor $eInvoice = false; $eInvoiceStartDate = Carbon::parse(env('E_INVOICE_START_DATE', '2025-07-01 00:00:00')); $bookingCreatedDate = Carbon::parse($booking->created_at); - if ($bookingCreatedDate->isAfter($eInvoiceStartDate) && $supplier->e_invoice === 1) { + $eInvoiceRequestedDate = Carbon::parse($supplier->e_invoice_requested_at); + if ($bookingCreatedDate->isAfter($eInvoiceStartDate) && $bookingCreatedDate->isAfter($eInvoiceRequestedDate) && $supplier->e_invoice === 1) { $eInvoice = true; } $kvp = $booking->attributesKVP()->where('key', KVPKey::BOOKING_EINVOICE_ELIGIBLE)->first(); @@ -211,84 +214,20 @@ class CreateInvoiceTransactionV2Processor ->sum('tax'); } - - - $transaction_object = new TransactionObject( - $billNumber, - TransactionType::INVOICE, - $transaction->issuer, - $transaction->receiver, - $transaction->recipient_bank_account_id, - $transaction->payment_method, - $payable_amount, - $booking_amount, - $transaction->currency_id, - $transaction->original_currency_id, - $booking_currency_average_rate, - $total_tax, - $total_service_charge, - null, - ApprovalStatus::APPROVED - ); - $invoice_transaction = $this->createsTransaction->execute($purchaseOrder->booking ?? $booking, $transaction_object); - - if ($kvpCopies) { - foreach ($kvpCopies as $kvp) { - $invoice_transaction->attributesKVP()->save($kvp); - } + $invoice_transaction = null; + if($booking->status === ApprovalStatus::APPROVED){ + //NEW INVOICE for non-einvoice user applicable only when booking is completed + $invoice_transaction = $booking->transactions() + ->where('type', TransactionType::INVOICE) + ->complete() + ->latest() + ->first(); } - $voucherRedemption = $transaction->voucherRedemption; - - // purchase order - if(!$generateEInvoiceRefund){ - $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::PURCHASE_ORDER, $voucherRedemption, $generateEInvoiceWithNormalInvoiceTemplate); - } - - // deliver order - if(!$generateEInvoiceRefund){ - $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::DELIVER_ORDER, $voucherRedemption, $generateEInvoiceWithNormalInvoiceTemplate); - } - - // e-invoice - if ($eInvoice) - { - if($generateEInvoice){ - $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::EINVOICE, $voucherRedemption, $generateEInvoiceWithNormalInvoiceTemplate, $generateEInvoiceRefund); - } - } - // invoice - else - { - $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::INVOICE, $voucherRedemption, $generateEInvoiceWithNormalInvoiceTemplate, $generateEInvoiceRefund); - } - - if(!$generateEInvoiceRefund){ - $billNumber = $this->generatesTransactionBillNumber->execute('SPDO-'); - - $booking_currency_average_rate = $this->calculatesBookingCurrencyAverageRate->execute($booking, TransactionType::BILL, $generateEInvoiceRefund); - - $paymentTransaction = $booking->transactions()->payments()->where('status', ApprovalStatus::COMPLETED)->first(); - - $transaction = null; - if($paymentTransaction){ - $transaction = $paymentTransaction->transactions()->where('type', TransactionType::BILL)->first(); - } - else{ // Special handling for refund cases (When a refund is deleted via DeleteRefundTransactionLogic, a booking payment transaction is set to ApprovalStatus::APPROVED) - $temp = $booking->transactions()->payments()->where('status', ApprovalStatus::APPROVED)->first(); - // Lets check if there is a refund case - $refund = $temp->transactions()->refunds()->where('status', ApprovalStatus::APPROVED)->first(); - if($refund){ - $transaction = $temp; - } - else{ - throw new Exception("No payment found for booking '$booking->id'."); - } - } - + if(!$invoice_transaction) { $transaction_object = new TransactionObject( $billNumber, - TransactionType::SUPPLIER_DELIVER, + TransactionType::INVOICE, $transaction->issuer, $transaction->receiver, $transaction->recipient_bank_account_id, @@ -303,19 +242,101 @@ class CreateInvoiceTransactionV2Processor null, ApprovalStatus::APPROVED ); - $supplier_deliver_order_transaction = $this->createsTransaction->execute($purchaseOrder->booking, $transaction_object); + $invoice_transaction = $this->createsTransaction->execute($purchaseOrder->booking ?? $booking, $transaction_object); + } - // supply deliver order - $this->invoiceDocumentProcessor->execute($supplier_deliver_order_transaction, $purchaseOrder, $supplier, DocumentType::SUPPLIER_DELIVER_ORDER, null); + //Update booking table, used on Export Sales Invoice Report + $booking->invoice_status = ApprovalStatus::APPROVED; + $booking->save(); + + if ($kvpCopies) { + foreach ($kvpCopies as $kvp) { + $invoice_transaction->attributesKVP()->save($kvp); + } + } + + $voucherRedemption = $transaction->voucherRedemption; + + if($purchaseOrder){ + // purchase order + if(!$generateEInvoiceRefund){ + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::PURCHASE_ORDER, $voucherRedemption, $generateEInvoiceWithNormalInvoiceTemplate); + } + + // deliver order + if(!$generateEInvoiceRefund){ + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::DELIVER_ORDER, $voucherRedemption, $generateEInvoiceWithNormalInvoiceTemplate); + } + + // e-invoice + if ($eInvoice) + { + if($generateEInvoice){ + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::EINVOICE, $voucherRedemption, $generateEInvoiceWithNormalInvoiceTemplate, $generateEInvoiceRefund); + } + } + // invoice + else + { + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::INVOICE, $voucherRedemption, $generateEInvoiceWithNormalInvoiceTemplate, $generateEInvoiceRefund); + } + + if(!$generateEInvoiceRefund){ + $billNumber = $this->generatesTransactionBillNumber->execute('SPDO-'); + + $booking_currency_average_rate = $this->calculatesBookingCurrencyAverageRate->execute($booking, TransactionType::BILL, $generateEInvoiceRefund); + + $paymentTransaction = $booking->transactions()->payments()->where('status', ApprovalStatus::COMPLETED)->first(); + + $transactionTypeBill= null; + if($paymentTransaction){ + $transactionTypeBill = $paymentTransaction->transactions()->where('type', TransactionType::BILL)->first(); + } + else{ // Special handling for refund cases (When a refund is deleted via DeleteRefundTransactionLogic, a booking payment transaction is set to ApprovalStatus::APPROVED) + $paymentTransactionTemp = $booking->transactions()->payments()->where('status', ApprovalStatus::APPROVED)->first(); + // Lets check if there is a refund case + $refund = $paymentTransactionTemp->transactions()->refunds()->where('status', ApprovalStatus::APPROVED)->first(); + if($refund){ + $transactionTypeBill = $paymentTransactionTemp->transactions()->where('type', TransactionType::BILL)->first(); + } + else{ + Log::info("CreateInvoiceTransactionV2Processor NO completed payment transaction nor refund transaction found for booking '$booking->id'."); + } + } + + if($transactionTypeBill){ + $transaction_object = new TransactionObject( + $billNumber, + TransactionType::SUPPLIER_DELIVER, + $transactionTypeBill->issuer, + $transactionTypeBill->receiver, + $transactionTypeBill->recipient_bank_account_id, + $transactionTypeBill->payment_method, + $payable_amount, + $booking_amount, + $transactionTypeBill->currency_id, + $transactionTypeBill->original_currency_id, + $booking_currency_average_rate, + $total_tax, + $total_service_charge, + null, + ApprovalStatus::APPROVED + ); + $supplier_deliver_order_transaction = $this->createsTransaction->execute($purchaseOrder->booking, $transaction_object); + + // supply deliver order + $this->invoiceDocumentProcessor->execute($supplier_deliver_order_transaction, $purchaseOrder, $supplier, DocumentType::SUPPLIER_DELIVER_ORDER, null); + } + } + + // update perfex crm + // if(config('perfexcrm.is_enabled') == 'true'){ + // CreatePerfexCRMInvoice::dispatch($invoice_transaction, $purchaseOrder, $supplier); + // } } //if(!$generateEInvoiceRefund){ $this->updatesBookingStatus->execute($booking, $bookingOriginalStatus); //} - - // update perfex crm - // if(config('perfexcrm.is_enabled') == 'true'){ - // CreatePerfexCRMInvoice::dispatch($invoice_transaction, $purchaseOrder, $supplier); - // } } } diff --git a/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php index b514b404..42a21d71 100644 --- a/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/CreateSupplierTransactionProcessor.php @@ -86,6 +86,7 @@ class CreateSupplierTransactionProcessor $original_amount_after_refund = $payment->original_amount - $totalRefund; $this->updatesTransactionStatus->execute($payment, ApprovalStatus::COMPLETED); + $billNumber = $this->generatesTransactionBillNumber->execute('SPLR-'); $constant = SegmentConstant::where('reference', SegmentConstants::SERVICE_CHARGE)->where('detail->id', $supplier->id)->first(); diff --git a/app/Classes/ValueObjects/Constants/ApprovalStatus.php b/app/Classes/ValueObjects/Constants/ApprovalStatus.php index ba7660d4..987dbc6a 100644 --- a/app/Classes/ValueObjects/Constants/ApprovalStatus.php +++ b/app/Classes/ValueObjects/Constants/ApprovalStatus.php @@ -29,5 +29,5 @@ final class ApprovalStatus { self::SUSPENDED => "Suspended", self::EXPIRED => "Expired", self::REFUNDED => "Refunded", - ]; + ]; } diff --git a/app/Classes/ValueObjects/Constants/KVPKey.php b/app/Classes/ValueObjects/Constants/KVPKey.php index bc889729..958d1710 100644 --- a/app/Classes/ValueObjects/Constants/KVPKey.php +++ b/app/Classes/ValueObjects/Constants/KVPKey.php @@ -30,4 +30,7 @@ class KVPKey public const CHATGPT_PROMPT_PREFIX = 'CHATGPT_PROMPT_'; public const CLAUDE_PROMPT_PREFIX = 'CLAUDE_PROMPT_'; + + public const BOOKING_AMOUNT_UPDATE = 'BOOKING_AMOUNT_UPDATE'; + } diff --git a/app/Console/Commands/V2/OneTimeProcessBookingForEInvoiceV2Command.php b/app/Console/Commands/V2/OneTimeProcessBookingForEInvoiceV2Command.php new file mode 100644 index 00000000..c001f69b --- /dev/null +++ b/app/Console/Commands/V2/OneTimeProcessBookingForEInvoiceV2Command.php @@ -0,0 +1,62 @@ +get(); + + $count = 0; + foreach ($bookings as $booking) { + ProcessBookingForEInvoiceV2CommandJob::dispatch($booking); + $count++; + Log::info('Processed: ' . $count); + Log::info('Booking ID: ' . $booking->marking . ' for E-Invoice '); + } + } +} diff --git a/app/Http/Controllers/Rules/CheckRuleController.php b/app/Http/Controllers/Rules/CheckRuleController.php index 59b24cd0..b0a175c1 100644 --- a/app/Http/Controllers/Rules/CheckRuleController.php +++ b/app/Http/Controllers/Rules/CheckRuleController.php @@ -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); + } } diff --git a/app/Http/Resources/BookingResource.php b/app/Http/Resources/BookingResource.php index fef78809..e118f2e9 100644 --- a/app/Http/Resources/BookingResource.php +++ b/app/Http/Resources/BookingResource.php @@ -13,6 +13,7 @@ use App\Classes\ValueObjects\Constants\DocumentType; use App\Classes\ValueObjects\Constants\KVPKey; use Carbon\Carbon; use Illuminate\Http\Resources\Json\JsonResource; +use Illuminate\Support\Facades\Log; class BookingResource extends JsonResource { @@ -30,7 +31,7 @@ class BookingResource extends JsonResource $bookingCreatedDate = Carbon::parse($this->created_at); $eInvoiceRequestedDate = Carbon::parse($this->company->e_invoice_requested_at); //cief todo: 90 - for testing - if ($bookingCreatedDate->isAfter($eInvoiceStartDate) && $this->company->e_invoice === 1) { //&& $bookingCreatedDate->diffInMinutes($eInvoiceRequestedDate) <= 480 cief todo: 90 + if ($bookingCreatedDate->isAfter($eInvoiceStartDate) && $bookingCreatedDate->isAfter($eInvoiceRequestedDate) && $this->company->e_invoice === 1) { //&& $bookingCreatedDate->diffInMinutes($eInvoiceRequestedDate) <= 480 cief todo: 90 $eInvoice = true; } @@ -58,15 +59,13 @@ class BookingResource extends JsonResource 'convertible_currency' => new CurrencyResource($this->convertibleCurrency), 'conversion_currency' => new CurrencyResource($this->conversionCurrency), 'documents' => [ - 'purchase_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::PURCHASE_ORDER)->first()), - 'delivery_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::DELIVER_ORDER)->first()), - 'invoice' => new DocumentResource($this->documents()->where('document_type', DocumentType::INVOICE)->first()), + 'purchase_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::PURCHASE_ORDER)->latest()->first()), + 'delivery_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::DELIVER_ORDER)->latest()->first()), + 'invoice' => new DocumentResource($this->documents()->where('document_type', DocumentType::INVOICE)->latest()->first()), 'e_invoice' => new DocumentResource($this->documents()->where('document_type', DocumentType::EINVOICE)->latest()->first()), - 'supplier_delivery_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::SUPPLIER_DELIVER_ORDER)->first()), - 'proforma_invoice' => new DocumentResource($this->documents()->where('document_type', DocumentType::PROFORMA_INVOICE)->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::EXPIRED])->orderByDesc('id')->first()), - 'ecommerce_purchase_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->first()), - 'banking_invoice' => new DocumentResource($this->documents()->where('document_type', DocumentType::BANKING_INVOICE)->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::EXPIRED])->orderByDesc('id')->first()), - 'delivery_order_banking' => new DocumentResource($this->documents()->where('document_type', DocumentType::DELIVER_ORDER_BANKING)->orderByDesc('id')->first()), + 'supplier_delivery_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::SUPPLIER_DELIVER_ORDER)->latest()->first()), + 'proforma_invoice' => new DocumentResource($this->documents()->where('document_type', DocumentType::PROFORMA_INVOICE)->whereNotIn('status', [ApprovalStatus::REJECTED, ApprovalStatus::EXPIRED])->latest()->first()), + 'ecommerce_purchase_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->latest()->first()), ], 'order_reference_no' => $this->modelAttributes()->where('name', BookingAttributeNames::ORDER_REFERENCE_NO)->get()->map(function ($attr) { return [ @@ -100,6 +99,7 @@ class BookingResource extends JsonResource })->latest()->get()) ]), 'einvoice' => $eInvoice, + 'invoice_status' => $this->invoice_status, ]; } } diff --git a/app/Http/Resources/EInvoiceInfoResource.php b/app/Http/Resources/EInvoiceInfoResource.php index 97483dd3..a4126eef 100644 --- a/app/Http/Resources/EInvoiceInfoResource.php +++ b/app/Http/Resources/EInvoiceInfoResource.php @@ -2,6 +2,7 @@ namespace App\Http\Resources; +use Carbon\Carbon; use Illuminate\Http\Resources\Json\JsonResource; class EInvoiceInfoResource extends JsonResource @@ -26,6 +27,7 @@ class EInvoiceInfoResource extends JsonResource 'msic_code' => (string) $this->msic_code, 'tin' => (string) $this->tin, 'e_invoice' => (int) $this->e_invoice, + 'e_invoice_requested_date' => $this->e_invoice_requested_at ? Carbon::parse($this->e_invoice_requested_at)->format('d-m-Y') : null, ]; } } diff --git a/database/migrations/2025_11_27_120102_add_invoice_status_to_bookings_table.php b/database/migrations/2025_11_27_120102_add_invoice_status_to_bookings_table.php new file mode 100644 index 00000000..124800b4 --- /dev/null +++ b/database/migrations/2025_11_27_120102_add_invoice_status_to_bookings_table.php @@ -0,0 +1,37 @@ +unsignedTinyInteger('invoice_status') + ->default(0) + ->after('status'); + + $table->index('invoice_status', 'bookings_invoice_status_index'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('bookings', function (Blueprint $table) { + $table->dropIndex('bookings_invoice_status_index'); + $table->dropColumn('invoice_status'); + }); + } +} diff --git a/database/migrations/2025_11_27_120102_add_is_invoice_generated_to_bookings_table.php b/database/migrations/2025_11_27_120102_add_is_invoice_generated_to_bookings_table.php new file mode 100644 index 00000000..b2af1a22 --- /dev/null +++ b/database/migrations/2025_11_27_120102_add_is_invoice_generated_to_bookings_table.php @@ -0,0 +1,37 @@ +boolean('is_invoice_generated') + ->default(false) + ->after('status'); + + $table->index('is_invoice_generated', 'bookings_is_invoice_generated_index'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('bookings', function (Blueprint $table) { + $table->dropIndex('bookings_is_invoice_generated_index'); + $table->dropColumn('is_invoice_generated'); + }); + } +} diff --git a/database/migrations/2025_11_27_121256_add_invoice_status_to_booking_logs_table.php b/database/migrations/2025_11_27_121256_add_invoice_status_to_booking_logs_table.php new file mode 100644 index 00000000..d0b21f09 --- /dev/null +++ b/database/migrations/2025_11_27_121256_add_invoice_status_to_booking_logs_table.php @@ -0,0 +1,34 @@ +boolean('invoice_status') + ->default(false) + ->after('status'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('booking_logs', function (Blueprint $table) { + $table->dropColumn('invoice_status'); + }); + } +} diff --git a/database/migrations/2025_11_27_121256_add_is_invoice_generated_to_booking_logs_table.php b/database/migrations/2025_11_27_121256_add_is_invoice_generated_to_booking_logs_table.php new file mode 100644 index 00000000..e04b01d0 --- /dev/null +++ b/database/migrations/2025_11_27_121256_add_is_invoice_generated_to_booking_logs_table.php @@ -0,0 +1,34 @@ +boolean('is_invoice_generated') + ->default(false) + ->after('status'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('booking_logs', function (Blueprint $table) { + $table->dropColumn('is_invoice_generated'); + }); + } +} diff --git a/database/seeds/DummyDataSeeder.php b/database/seeds/DummyDataSeeder.php index a752ee6f..328dcc15 100644 --- a/database/seeds/DummyDataSeeder.php +++ b/database/seeds/DummyDataSeeder.php @@ -679,8 +679,13 @@ class DummyDataSeeder extends Seeder // once the invoice is generated the transaction table will include 2 new transaction type TransactionType::INVOICE, TransactionType::SUPPLIER_DELIVERY // and for documents will be generated and attached to the booking. // once this process is complete the booking status will update to ApprovalStatus::COMPLETED - $this->createInvoiceTransactionProcessor->execute($booking); - + $this->createInvoiceTransactionProcessor->execute($booking, "", null, [ + 'generateEInvoice' => false, + 'generateEInvoiceWithNormalInvoiceTemplate' => false, + 'generateEInvoiceRefund' => false, + 'bookingOriginalStatus' => ApprovalStatus::COMPLETED + ] + ); } } diff --git a/resources/assets/vue/components/accounts/forms/RegistrationFormComponent.vue b/resources/assets/vue/components/accounts/forms/RegistrationFormComponent.vue index d6f05b1b..a046a7eb 100644 --- a/resources/assets/vue/components/accounts/forms/RegistrationFormComponent.vue +++ b/resources/assets/vue/components/accounts/forms/RegistrationFormComponent.vue @@ -169,6 +169,7 @@ +< diff --git a/resources/assets/vue/components/bookings/forms/confirmBookingComponent.vue b/resources/assets/vue/components/bookings/forms/confirmBookingComponent.vue index d0a92e59..6fc7e203 100644 --- a/resources/assets/vue/components/bookings/forms/confirmBookingComponent.vue +++ b/resources/assets/vue/components/bookings/forms/confirmBookingComponent.vue @@ -11,6 +11,7 @@ + @endif +@endpush \ No newline at end of file diff --git a/routes/rule.php b/routes/rule.php index d5330176..4918b4d0 100644 --- a/routes/rule.php +++ b/routes/rule.php @@ -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'); });