From b3693aa1e35e32b16b3f4b5a28e7ec7deb1be04d Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Sat, 24 May 2025 22:14:35 +0800 Subject: [PATCH] E-Invoice - Receipt Voucher --- .env.example | 2 + app/Classes/General/Helper.php | 23 +++- .../ControllersLogic/CallbackBillplzLogic.php | 16 ++- .../ApprovePaymentVerificationLogic.php | 71 ++++++----- .../CreateBookingPaymentLogic.php | 9 +- .../RegenerateBookingPaymentRVLogic.php | 81 +++++++++++++ .../CreateInvoiceDocumentProcessor.php | 22 +++- ...eateReceiptVoucherTransactionProcessor.php | 113 ++++++++++++++++++ .../ValueObjects/Constants/DocumentType.php | 2 + .../Constants/TransactionType.php | 4 +- .../RegenerateBookingPaymentRVController.php | 20 ++++ app/Http/Resources/BookingResource.php | 9 +- app/Http/Resources/TransactionResource.php | 2 + composer.json | 1 + .../elements/PaymentHistoryComponent.vue | 42 ++++++- .../RegenerateReceiptVoucherComponent.vue | 43 +++++++ .../BookingDetailsSectionComponent.vue | 2 +- .../elements/DocumentFileViewerComponent.vue | 7 ++ resources/views/pages/pdfs/invoice.blade.php | 2 +- .../pages/pdfs/receipt_voucher.blade.php | 113 ++++++++++++++++++ routes/booking.php | 5 + 21 files changed, 543 insertions(+), 46 deletions(-) create mode 100644 app/Classes/Modules/Bookings/ControllersLogic/RegenerateBookingPaymentRVLogic.php create mode 100644 app/Classes/Modules/Transactions/Processors/CreateReceiptVoucherTransactionProcessor.php create mode 100644 app/Http/Controllers/Bookings/RegenerateBookingPaymentRVController.php create mode 100644 resources/assets/vue/components/bookings/forms/RegenerateReceiptVoucherComponent.vue create mode 100644 resources/views/pages/pdfs/receipt_voucher.blade.php diff --git a/.env.example b/.env.example index 05b97dee..346b5b3b 100644 --- a/.env.example +++ b/.env.example @@ -75,3 +75,5 @@ LARAVEL_VAPOR_ENABLED=false COMMANDS_V2_ENABLED=false SENDING_EMAIL_ENABLED=false SENDING_EMAIL_WELCOME_VOUCHER_ENABLED=false + +E_INVOICE_START_DATE="2025-07-01 00:00:00" diff --git a/app/Classes/General/Helper.php b/app/Classes/General/Helper.php index 45c72f53..cb77d936 100644 --- a/app/Classes/General/Helper.php +++ b/app/Classes/General/Helper.php @@ -4,8 +4,8 @@ namespace App\Classes\General; use Illuminate\Http\Resources\Json\ResourceCollection; use Illuminate\Support\Facades\Log; - use Illuminate\Support\Str; +use NumberToWords\NumberToWords; class Helper { @@ -66,4 +66,25 @@ class Helper return json_decode($collection->response()->getContent(), true); } + /** + * Convert a given number to words based on the specified locale. + * + * @param int|float $number + * @param string $locale The locale to use for conversion (default is 'en'). + * @return string + */ + static function convert($number, $locale = 'en') + { + $numberToWords = new NumberToWords(); + $numberTransformer = $numberToWords->getNumberTransformer($locale); + + $number = number_format((float)$number, 2, '.', ''); + [$ringgit, $cents] = explode('.', $number); + + $ringgitWords = $numberTransformer->toWords((int)$ringgit); + $centsWords = $numberTransformer->toWords((int)$cents); + + return strtoupper('ringgit ' . $ringgitWords . ' and ' . $centsWords . ' cents only'); + } + } diff --git a/app/Classes/Modules/Billplzs/ControllersLogic/CallbackBillplzLogic.php b/app/Classes/Modules/Billplzs/ControllersLogic/CallbackBillplzLogic.php index 186ef3b6..b6537420 100644 --- a/app/Classes/Modules/Billplzs/ControllersLogic/CallbackBillplzLogic.php +++ b/app/Classes/Modules/Billplzs/ControllersLogic/CallbackBillplzLogic.php @@ -5,6 +5,7 @@ namespace App\Classes\Modules\Billplzs\ControllersLogic; use App\Classes\Modules\Wallets\DataTransferObjects\WalletObject; use App\Classes\Modules\Wallets\Services\UpdatesWallet; use App\Classes\Modules\Transactions\Processors\CreateCashBackTransactionProcessor; +use App\Classes\Modules\Transactions\Processors\CreateReceiptVoucherTransactionProcessor; use App\Classes\Exceptions\ResourceNotFoundException; use App\Classes\Modules\Wallets\Services\UpdatesWalletBalance; @@ -49,6 +50,9 @@ class CallbackBillplzLogic /** @var RecalculatesWalletBalance */ private $recalculatesWalletBalance; + /** @var CreateReceiptVoucherTransactionProcessor */ + private $createReceiptVoucherTransactionProcessor; + /** * CallbackBillplzLogic constructor. * @param GetBillplzBill $getBillplzBill @@ -56,8 +60,9 @@ class CallbackBillplzLogic * @param UpdatesTransactionStatus $updatesTransactionStatus * @param UpdatesWalletBalance $updatesWalletBalance * @param RecalculatesWalletBalance $recalculatesWalletBalance + * @param CreateReceiptVoucherTransactionProcessor $createReceiptVoucherTransactionProcessor */ - public function __construct(GetBillplzBill $getBillplzBill, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, UpdatesWalletBalance $updatesWalletBalance, CreateCashBackTransactionProcessor $createCashBackTransactionProcessor, RecalculatesWalletBalance $recalculatesWalletBalance) + public function __construct(GetBillplzBill $getBillplzBill, FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, UpdatesWalletBalance $updatesWalletBalance, CreateCashBackTransactionProcessor $createCashBackTransactionProcessor, RecalculatesWalletBalance $recalculatesWalletBalance, CreateReceiptVoucherTransactionProcessor $createReceiptVoucherTransactionProcessor) { $this->getBillplzBill = $getBillplzBill; $this->fetchesTransaction = $fetchesTransaction; @@ -65,6 +70,7 @@ class CallbackBillplzLogic $this->updatesWalletBalance = $updatesWalletBalance; $this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor; $this->recalculatesWalletBalance = $recalculatesWalletBalance; + $this->createReceiptVoucherTransactionProcessor = $createReceiptVoucherTransactionProcessor; } @@ -105,6 +111,12 @@ class CallbackBillplzLogic $this->updatesWalletBalance->execute($transaction->owner, $transaction->amount); } + $booking = $transaction->owner instanceof Booking ? $transaction->booking : (count($transaction->owner->owner->bookings()->get())? $transaction->owner->owner->bookings()->orderBy('id', 'DESC')->first(): null); + + if($transaction->owner instanceof Booking && $booking && $transaction){ + $this->createReceiptVoucherTransactionProcessor->execute($booking, $transaction); + } + // if ($transaction->type == TransactionType::PAYMENT) { // $cash_back_transaction = $this->createCashBackTransactionProcessor->execute($transaction); // } @@ -117,4 +129,4 @@ class CallbackBillplzLogic return $request->method() === 'POST' ? true : view('pages.payments_redirect', ['marking' => $marking ?? null, 'transaction' => $transaction, 'status' => $status]); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/ApprovePaymentVerificationLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/ApprovePaymentVerificationLogic.php index c20a44d2..7d32671e 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/ApprovePaymentVerificationLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/ApprovePaymentVerificationLogic.php @@ -11,42 +11,15 @@ use App\Classes\Modules\Transactions\Services\FetchesTransaction; use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; use App\Classes\Modules\Transactions\Processors\CreateCashBackTransactionProcessor; +use App\Classes\Modules\Transactions\Processors\CreateReceiptVoucherTransactionProcessor; use App\Classes\ValueObjects\Constants\ApprovalStatus; - +use App\Models\Booking; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; class ApprovePaymentVerificationLogic extends AbstractControllerLogic { - /** - * ApprovePaymentVerificationLogic constructor. - * @param FetchesTransaction $fetchesTransaction - * @param UpdatesTransactionStatus $updatesTransactionStatus - * @param ApprovesDocument $approvesDocument - * @param RejectsDocument $rejectsDocument - * @param FetchesDocument $fetchesDocument - * @param CreateCashBackTransactionProcessor $createCashBackTransactionProcessor - */ - public function __construct(FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, ApprovesDocument $approvesDocument, RejectsDocument $rejectsDocument, FetchesDocument $fetchesDocument, CreateCashBackTransactionProcessor $createCashBackTransactionProcessor) - { - $this->fetchesTransaction = $fetchesTransaction; - $this->updatesTransactionStatus = $updatesTransactionStatus; - $this->approvesDocument = $approvesDocument; - $this->rejectsDocument = $rejectsDocument; - $this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor; - } - - /** - * @return array - */ - protected function notification():array { - return [ - 'title' => 'Payment Status', - 'message' => 'You have successfully updated the payment status' - ]; - } - /** @var FetchesTransaction */ private $fetchesTransaction; @@ -62,6 +35,39 @@ class ApprovePaymentVerificationLogic extends AbstractControllerLogic /** @var CreateCashBackTransactionProcessor */ private $createCashBackTransactionProcessor; + /** @var CreateReceiptVoucherTransactionProcessor */ + private $createReceiptVoucherTransactionProcessor; + + /** + * ApprovePaymentVerificationLogic constructor. + * @param FetchesTransaction $fetchesTransaction + * @param UpdatesTransactionStatus $updatesTransactionStatus + * @param ApprovesDocument $approvesDocument + * @param RejectsDocument $rejectsDocument + * @param FetchesDocument $fetchesDocument + * @param CreateCashBackTransactionProcessor $createCashBackTransactionProcessor + * @param CreateReceiptVoucherTransactionProcessor $createReceiptVoucherTransactionProcessor + */ + public function __construct(FetchesTransaction $fetchesTransaction, UpdatesTransactionStatus $updatesTransactionStatus, ApprovesDocument $approvesDocument, RejectsDocument $rejectsDocument, FetchesDocument $fetchesDocument, CreateCashBackTransactionProcessor $createCashBackTransactionProcessor, CreateReceiptVoucherTransactionProcessor $createReceiptVoucherTransactionProcessor) + { + $this->fetchesTransaction = $fetchesTransaction; + $this->updatesTransactionStatus = $updatesTransactionStatus; + $this->approvesDocument = $approvesDocument; + $this->rejectsDocument = $rejectsDocument; + $this->createCashBackTransactionProcessor = $createCashBackTransactionProcessor; + $this->createReceiptVoucherTransactionProcessor = $createReceiptVoucherTransactionProcessor; + } + + /** + * @return array + */ + protected function notification():array { + return [ + 'title' => 'Payment Status', + 'message' => 'You have successfully updated the payment status' + ]; + } + /** * @param Request $request * @return JsonResponse @@ -77,9 +83,14 @@ class ApprovePaymentVerificationLogic extends AbstractControllerLogic $this->updatesTransactionStatus->execute($transaction, $status === 'approve' ? ApprovalStatus::APPROVED : ApprovalStatus::REJECTED); + $booking = $transaction->owner instanceof Booking ? $transaction->booking : null; + + if($booking && $transaction){ + $this->createReceiptVoucherTransactionProcessor->execute($booking, $transaction); + } // $this->createCashBackTransactionProcessor->execute($transaction); return $this->response([]); } -} \ No newline at end of file +} diff --git a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php index aa9f9670..56b8fff1 100644 --- a/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php +++ b/app/Classes/Modules/Bookings/ControllersLogic/CreateBookingPaymentLogic.php @@ -25,6 +25,7 @@ use App\Classes\Modules\Vouchers\Processors\Voucherify\BookingToVoucherifyProces 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\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\PaymentMethodType; use App\Classes\ValueObjects\Constants\TransactionType; @@ -89,6 +90,9 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic /** @var RuleEvaluator */ private $ruleEvaluator; + /** @var CreateReceiptVoucherTransactionProcessor */ + private $createReceiptVoucherTransactionProcessor; + /** * CreateBookingPaymentLogic constructor. * @param FetchesBookingQuotation $fetchBookingQuotation @@ -104,8 +108,9 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic * @param BookingToVoucherifyProcessor $bookingToVoucherifyProcessor * @param CalculatesBookingRefundAmount $calculatesBookingRefundAmount * @param RuleEvaluator $ruleEvaluator + * @param CreateReceiptVoucherTransactionProcessor $createReceiptVoucherTransactionProcessor */ - 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) + 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) { $this->fetchBookingQuotation = $fetchBookingQuotation; $this->fetchesCompanyPaymentAttemptLimit = $fetchesCompanyPaymentAttemptLimit; @@ -120,6 +125,7 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic $this->bookingToVoucherifyProcessor = $bookingToVoucherifyProcessor; $this->calculatesBookingRefundAmount = $calculatesBookingRefundAmount; $this->ruleEvaluator = $ruleEvaluator; + $this->createReceiptVoucherTransactionProcessor = $createReceiptVoucherTransactionProcessor; } /** @@ -200,6 +206,7 @@ class CreateBookingPaymentLogic extends AbstractControllerLogic if(PaymentMethodType::PAYMENT_METHODS[$request->input('payment_method')] == PaymentMethodType::WALLET){ $this->updatesTransactionStatus->execute($transaction, ApprovalStatus::APPROVED); + $this->createReceiptVoucherTransactionProcessor->execute($booking, $transaction); } return $this->resourceResponse(new TransactionResource($transaction)); diff --git a/app/Classes/Modules/Bookings/ControllersLogic/RegenerateBookingPaymentRVLogic.php b/app/Classes/Modules/Bookings/ControllersLogic/RegenerateBookingPaymentRVLogic.php new file mode 100644 index 00000000..8106eb8f --- /dev/null +++ b/app/Classes/Modules/Bookings/ControllersLogic/RegenerateBookingPaymentRVLogic.php @@ -0,0 +1,81 @@ + 'Regenerate Booking Payment Receipt Voucher', + 'message' => 'You have successfully regenerate booking payment receipt voucher' + ]; + } + + /** @var CanFetchBooking */ + private $canFetchBooking; + + /** @var FetchesBooking */ + private $fetchesBooking; + + /** @var FetchesTransaction */ + private $fetchesTransaction; + + /** @var CreateReceiptVoucherTransactionProcessor */ + private $createReceiptVoucherTransactionProcessor; + + /** + * RegenerateBookingPaymentRVLogic constructor. + * @param CanFetchBooking $canFetchBooking + * @param FetchesBooking $fetchesBooking + * @param FetchesTransaction $fetchesTransaction + * @param CreateReceiptVoucherTransactionProcessor $createReceiptVoucherTransactionProcessor + */ + public function __construct( + CanFetchBooking $canFetchBooking, + FetchesBooking $fetchesBooking, + FetchesTransaction $fetchesTransaction, + CreateReceiptVoucherTransactionProcessor $createReceiptVoucherTransactionProcessor + ) { + $this->canFetchBooking = $canFetchBooking; + $this->fetchesBooking = $fetchesBooking; + $this->fetchesTransaction = $fetchesTransaction; + $this->createReceiptVoucherTransactionProcessor = $createReceiptVoucherTransactionProcessor; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function logic(Request $request): JsonResponse + { + $this->canFetchBooking->passes(); + + $booking = $this->fetchesBooking->execute(['id' => $request->route('id')]); + $transaction = $this->fetchesTransaction->execute(['id' => $request->input('paymentId')]); + + if($booking && $transaction){ + $this->createReceiptVoucherTransactionProcessor->execute($booking, $transaction, true); + } + + return $this->resourceResponse(new BookingResource($booking)); + } +} diff --git a/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php index 1db15909..550e6148 100644 --- a/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/CreateInvoiceDocumentProcessor.php @@ -10,6 +10,7 @@ use App\Classes\ValueObjects\Constants\DocumentType; use App\Classes\ValueObjects\Constants\TransactionType; use App\Models\Booking; use App\Models\Document; +use Carbon\Carbon; use Illuminate\Support\Facades\Log; use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf; use Webklex\PDFMerger\Facades\PDFMergerFacade as PDFMerger; @@ -45,9 +46,16 @@ class CreateInvoiceDocumentProcessor public function execute($transaction, $purchaseOrder, $supplier, $document_type, $voucherRedemption = null) { // calculate current Paid Amount - $booking = $transaction->owner_type == Booking::class ? $transaction->owner : null; + $booking = $transaction->owner_type == Booking::class ? $transaction->owner : null; $currentPaidAmount = null; + $lastPaymentDate = $supplier->segments->whereIn('id', [23])->first() ? \Carbon\Carbon::now() : $purchaseOrder->booking->created_at; + $eInvoiceStartDate = Carbon::parse(env('E_INVOICE_START_DATE', '2025-07-01 00:00:00')); + if ($booking) { + $bookingCreatedDate = Carbon::parse($booking->created_at); + if ($bookingCreatedDate->isAfter($eInvoiceStartDate)) { + $lastPaymentDate = $booking->transactions()->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::COMPLETED, ApprovalStatus::APPROVED])->latest()->first()->created_at; + } $payment = $booking->transactions()->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::COMPLETED, ApprovalStatus::APPROVED])->first(); $refundAmount = $payment->transactions()->refunds()->whereIn('status', [ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED])->sum('amount'); $paymentAmount = $payment->amount; @@ -56,9 +64,9 @@ class CreateInvoiceDocumentProcessor $lowercaseDocumentType = strtolower($document_type); - $order_pdf = LaravelMpdf::loadView('pages.pdfs.' . $lowercaseDocumentType, ['transaction' => $transaction, 'po_order_transaction' => $purchaseOrder, 'supplier' => $supplier, 'voucher_redemption' => $voucherRedemption, 'current_paid_amount' => $currentPaidAmount]); + $order_pdf = LaravelMpdf::loadView('pages.pdfs.' . $lowercaseDocumentType, ['transaction' => $transaction, 'po_order_transaction' => $purchaseOrder, 'supplier' => $supplier, 'voucher_redemption' => $voucherRedemption, 'current_paid_amount' => $currentPaidAmount, 'last_payment_date' => $lastPaymentDate ]); - if($purchaseOrder->booking->service_id === 4) { + if($purchaseOrder && $purchaseOrder->booking->service_id === 4) { $purchaseOrderDocuments = $purchaseOrder->booking->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->get(); @@ -112,8 +120,12 @@ class CreateInvoiceDocumentProcessor ); /** @var Document $document */ - $document = $this->createsDocument->execute($purchaseOrder->booking, $document_object); + if($document_type === DocumentType::RECEIPT_VOUCHER){ + $document = $this->createsDocument->execute($transaction, $document_object); + } + else{ + $document = $this->createsDocument->execute($purchaseOrder->booking, $document_object); + } $this->createsFile->execute($document, $document_object); - } } diff --git a/app/Classes/Modules/Transactions/Processors/CreateReceiptVoucherTransactionProcessor.php b/app/Classes/Modules/Transactions/Processors/CreateReceiptVoucherTransactionProcessor.php new file mode 100644 index 00000000..16b37380 --- /dev/null +++ b/app/Classes/Modules/Transactions/Processors/CreateReceiptVoucherTransactionProcessor.php @@ -0,0 +1,113 @@ +createsTransaction = $createsTransaction; + $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; + $this->calculatesBookingCurrencyAverageRate = $calculatesBookingCurrencyAverageRate; + $this->fetchesCompany = $fetchesCompany; + $this->invoiceDocumentProcessor = $invoiceDocumentProcessor; + } + + /** + * @param Booking $booking + * @param Transaction $transaction + * @param bool $isRegenerate + * @return void + * @throws MalformedRequestException + */ + public function execute(Booking $booking, Transaction $transaction, bool $isRegenerate = false) + { + $purchaseOrder = $booking->transactions() + ->where('type', TransactionType::PURCHASE_ORDER) + // ->complete() + ->first(); + + if(!$transaction->type === TransactionType::PAYMENT){ + return; + } + + if(!($transaction->status === ApprovalStatus::APPROVED || $transaction->status === ApprovalStatus::COMPLETED)){ + return; + } + Log::info('CreateReceiptVoucherTransactionProcessor booking id: '. $booking->id); + Log::info('CreateReceiptVoucherTransactionProcessor transaction: '. json_encode($transaction)); + + //If payment receipt voucher already exists, return (unless you want to regenerate) + if($transaction->transactions()->where('type', TransactionType::RECEIPT_VOUCHER)->exists() && !$isRegenerate){ + return; + } + + $billNumber = $this->generatesTransactionBillNumber->execute('RV-'); + + $booking_currency_average_rate = $this->calculatesBookingCurrencyAverageRate->execute($booking, TransactionType::PAYMENT); + + $transaction_object = new TransactionObject( + $billNumber, + TransactionType::RECEIPT_VOUCHER, + $transaction->issuer, + $transaction->receiver, + $transaction->recipient_bank_account_id, + $transaction->payment_method, + $transaction->amount, + $transaction->original_amount, + $transaction->currency_id, + $transaction->original_currency_id, + $booking_currency_average_rate, + 0, + 0, + null, + ApprovalStatus::APPROVED + ); + $invoice_transaction = $this->createsTransaction->execute($transaction, $transaction_object); + + $voucherRedemption = $transaction->voucherRedemption; + + $supplier = $this->fetchesCompany->execute(['id' => $transaction->receiver]); + + // receipt voucher - Receipt is mandatory for customer who wants e-invoice and those who does not + $this->invoiceDocumentProcessor->execute($invoice_transaction, $purchaseOrder, $supplier, DocumentType::RECEIPT_VOUCHER, $voucherRedemption); + } +} diff --git a/app/Classes/ValueObjects/Constants/DocumentType.php b/app/Classes/ValueObjects/Constants/DocumentType.php index 42f8222b..4728acbe 100644 --- a/app/Classes/ValueObjects/Constants/DocumentType.php +++ b/app/Classes/ValueObjects/Constants/DocumentType.php @@ -27,4 +27,6 @@ final class DocumentType { public const BULK_PURCHASE_ORDER = 'BULK_PURCHASE_ORDER'; public const BILL_GROUP_PAYMENT_PROOF = 'BILL_GROUP_PAYMENT_PROOF'; + + public const RECEIPT_VOUCHER = 'RECEIPT_VOUCHER'; } diff --git a/app/Classes/ValueObjects/Constants/TransactionType.php b/app/Classes/ValueObjects/Constants/TransactionType.php index 09479729..efb11f5c 100644 --- a/app/Classes/ValueObjects/Constants/TransactionType.php +++ b/app/Classes/ValueObjects/Constants/TransactionType.php @@ -38,6 +38,8 @@ final class TransactionType { public const BILL_REFUND = 16; + public const RECEIPT_VOUCHER = 17; + public const ID_TO_NAME = [ self::PAYMENT_ATTEMPT => "PAYMENT_ATTEMPT", self::PAYMENT => "PAYMENT", @@ -56,5 +58,5 @@ final class TransactionType { self::SUPPLIER_PAYMENT => "SUPPLIER_PAYMENT", self::SUPPLIER_REFUND => "SUPPLIER_REFUND", ]; - + } diff --git a/app/Http/Controllers/Bookings/RegenerateBookingPaymentRVController.php b/app/Http/Controllers/Bookings/RegenerateBookingPaymentRVController.php new file mode 100644 index 00000000..29a75c70 --- /dev/null +++ b/app/Http/Controllers/Bookings/RegenerateBookingPaymentRVController.php @@ -0,0 +1,20 @@ +execute($request); + } +} diff --git a/app/Http/Resources/BookingResource.php b/app/Http/Resources/BookingResource.php index 83e53249..79d7c466 100644 --- a/app/Http/Resources/BookingResource.php +++ b/app/Http/Resources/BookingResource.php @@ -24,6 +24,12 @@ class BookingResource extends JsonResource */ public function toArray($request) { + $eInvoice = false; + $eInvoiceStartDate = Carbon::parse(env('E_INVOICE_START_DATE', '2025-07-01 00:00:00')); + $bookingCreatedDate = Carbon::parse($this->created_at); + if ($bookingCreatedDate->isAfter($eInvoiceStartDate) && $this->company->e_invoice === 1) { + $eInvoice = true; + } return [ 'id' => $this->id, 'company' => new CompanyResource($this->company), @@ -76,7 +82,8 @@ class BookingResource extends JsonResource }); }); })->latest()->get()) - ]) + ]), + 'einvoice' => $eInvoice, ]; } } diff --git a/app/Http/Resources/TransactionResource.php b/app/Http/Resources/TransactionResource.php index 4271f030..46c1f29b 100644 --- a/app/Http/Resources/TransactionResource.php +++ b/app/Http/Resources/TransactionResource.php @@ -8,6 +8,7 @@ use App\Classes\ValueObjects\Constants\TransactionType; use App\Models\Booking; use Carbon\Carbon; use Illuminate\Http\Resources\Json\JsonResource; +use Illuminate\Support\Facades\Log; class TransactionResource extends JsonResource { @@ -56,6 +57,7 @@ class TransactionResource extends JsonResource 'currency_rate' => (double) $this->currency_rate, 'status' => (int) $this->status, 'details' => TransactionDetailResource::collection($this->transactionDetails), + 'receipt_voucher' => $this->type === TransactionType::PAYMENT && $this->transactions()->where('type', TransactionType::RECEIPT_VOUCHER)->latest()->first() ? new DocumentResource($this->transactions()->where('type', TransactionType::RECEIPT_VOUCHER)->latest()->first()->documents()->first()) : null, 'documents' => new DocumentResource($this->documents()->first()), 'transaction_bill' => new TransactionResource($this->when((int) $this->type === TransactionType::PAYMENT, $this->transactions()->bills()->first())), 'transaction_refunds' => TransactionResource::collection($this->when((int) $this->type === TransactionType::PAYMENT, $this->transactions()->refunds()->get())), diff --git a/composer.json b/composer.json index 757d16f2..c9975f33 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ "fruitcake/laravel-cors": "^1.0", "guzzlehttp/guzzle": "^7.0.1", "intervention/image": "^2.5", + "kwn/number-to-words": "^2.11", "laravel/framework": "^8.0", "laravel/tinker": "^2.0", "laravel/vapor-cli": "^1.55", diff --git a/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue b/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue index f35f8573..8b776ce5 100644 --- a/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue +++ b/resources/assets/vue/components/bookings/elements/PaymentHistoryComponent.vue @@ -45,10 +45,23 @@ +
+ + + +
-
+
+

Payment Slip

@@ -83,7 +96,7 @@ {{item.original_currency.short_code}} {{(Math.round((totalRefunds + Number.EPSILON) * 100) / 100).toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}}
-
+
Service Type
{{item.booking.service.name}} ({{item.booking.service.id}}) @@ -114,12 +127,25 @@
+
+ + + +