E-Invoice - Amendment for storage invoice to generate E-Invoice (partial completion)

This commit is contained in:
Dillon Ngo
2025-09-25 17:46:33 +08:00
parent 0e9ef67121
commit fcd6f2d6a8
10 changed files with 288 additions and 16 deletions
@@ -36,6 +36,7 @@ class RegenerateShippingInvoiceTransactionLogic extends AbstractControllerLogic
public function logic(Request $request) : JsonResponse
{
return $this->response([]); //The business logic to generate normal invoice and e-invoice no longer so straight forward
$orders = Order::where('company_module_id', $request->route('company_module_id'))->get();
@@ -0,0 +1,53 @@
<?php
namespace App\Classes\Modules\Transactions\ControllersLogic;
use App\Classes\General\Abstracts\AbstractControllerLogic;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Processors\CreateStorageEInvoiceDocProcessor;
use App\Classes\ValueObjects\Constants\DocumentType;
class RegenerateSingleStorageEInvoiceLogic extends AbstractControllerLogic
{
/**
* @return array
*/
protected function notification():array {
return [
'title' => 'Regenerate Storage E-Invoice',
'message' => 'You have successfully regenerated storage e-invoice'
];
}
/** @var FetchesTransaction */
private $fetchesTransaction;
/** @var CreateStorageEInvoiceDocProcessor */
private $createStorageEInvoiceDocProcessor;
/**
* @param FetchesTransaction $fetchesTransaction
* @param CreateStorageEInvoiceDocProcessor $createStorageEInvoiceDocProcessor
*/
public function __construct(FetchesTransaction $fetchesTransaction, CreateStorageEInvoiceDocProcessor $createStorageEInvoiceDocProcessor)
{
$this->fetchesTransaction = $fetchesTransaction;
$this->createStorageEInvoiceDocProcessor = $createStorageEInvoiceDocProcessor;
}
public function logic(Request $request) : JsonResponse
{
$invoice = $this->fetchesTransaction->execute(['id' => $request->route('invoice_id')]);
$invoice->documents()->where('document_type', DocumentType::STORAGE_EINVOICE)->delete();
$this->createStorageEInvoiceDocProcessor->execute($invoice);
return $this->response([]);
}
}
@@ -8,9 +8,10 @@ use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Transactions\Services\FetchesTransaction;
use App\Classes\Modules\Transactions\Processors\CreateStorageInvoiceDocTransactionFixProcessor;
use App\Classes\Modules\Transactions\Processors\CreateStorageInvoiceTransactionDocProcessor;
use App\Classes\Modules\Transactions\Standards\Rules\CanRegenerateStorageInvoice;
use App\Classes\ValueObjects\Constants\TransactionType;
use App\Classes\ValueObjects\Constants\DocumentType;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
@@ -30,19 +31,21 @@ class RegenerateSingleStorageInvoiceTransactionLogic extends AbstractControllerL
/** @var FetchesTransaction */
private $fetchesTransaction;
/** @var CreateStorageInvoiceDocTransactionFixProcessor */
private $createStorageInvoiceDocTransactionProcessor;
/** @var CreateStorageInvoiceTransactionDocProcessor */
private $createStorageInvoiceTransactionDocProcessor;
/** @var CanRegenerateStorageInvoice */
private $canRegenerateStorageInvoice;
/**
* @param CreatesDocument $createsDocument
* @param FetchesTransaction $fetchesTransaction
* @param CreateStorageInvoiceTransactionDocProcessor $createStorageInvoiceTransactionDocProcessor
* @param CanRegenerateStorageInvoice $canRegenerateStorageInvoice
*/
public function __construct(FetchesTransaction $fetchesTransaction, CreateStorageInvoiceDocTransactionFixProcessor $createStorageInvoiceDocTransactionProcessor, CanRegenerateStorageInvoice $canRegenerateStorageInvoice)
public function __construct(FetchesTransaction $fetchesTransaction, CreateStorageInvoiceTransactionDocProcessor $createStorageInvoiceTransactionDocProcessor, CanRegenerateStorageInvoice $canRegenerateStorageInvoice)
{
$this->fetchesTransaction = $fetchesTransaction;
$this->createStorageInvoiceDocTransactionProcessor = $createStorageInvoiceDocTransactionProcessor;
$this->createStorageInvoiceTransactionDocProcessor = $createStorageInvoiceTransactionDocProcessor;
$this->canRegenerateStorageInvoice = $canRegenerateStorageInvoice;
}
@@ -51,10 +54,13 @@ class RegenerateSingleStorageInvoiceTransactionLogic extends AbstractControllerL
$this->canRegenerateStorageInvoice->passes();
$invoice = $this->fetchesTransaction->execute(['id' => $request->route('invoice_id')]);
$packingList = $invoice->owner;
$invoice->documents()->delete();
$invoice->documents()->where('document_type', DocumentType::STORAGE_INVOICE)->delete();
/** @var Transaction $invoice_transaction */
// $invoice_transaction = $packingList->transactions()->where('transactions.type', TransactionType::STORAGE_INVOICE)->whereIn('status', [ApprovalStatus::COMPLETED])->first();
$this->createStorageInvoiceTransactionDocProcessor->execute($invoice);
$this->createStorageInvoiceDocTransactionProcessor->execute($packingList, true);
Log::info(Auth::user()->email." regenerate storage invoice for transaction with id ".$request->route('invoice_id'));
return $this->response([]);
@@ -0,0 +1,95 @@
<?php
namespace App\Classes\Modules\Transactions\Processors;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Modules\Accounts\DataTransferObjects\KeyValuePairObject;
use App\Models\Document;
use App\Models\Transaction;
use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Accounts\Services\CreatesKeyValuePair;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\ValueObjects\Constants\KVPKey;
class CreateStorageEInvoiceDocProcessor
{
/** @var CreatesDocument */
private $createsDocument;
/** @var CreatesFiles */
private $createsFiles;
/** @var CreatesKeyValuePair */
private $createsKeyValuePair;
/**
* @param CreatesDocument $createsDocument
* @param CreatesFiles $createsFiles
* @param CreatesKeyValuePair $createsKeyValuePair
*/
public function __construct(CreatesDocument $createsDocument, CreatesFiles $createsFiles, CreatesKeyValuePair $createsKeyValuePair)
{
$this->createsDocument = $createsDocument;
$this->createsFiles = $createsFiles;
$this->createsKeyValuePair = $createsKeyValuePair;
}
/**
* @throws MalformedRequestException
*/
public function execute(Transaction $invoice_transaction)
{
$view = 'pages.pdfs.shipping_einvoice';
$companyModule = $invoice_transaction->owner->owner->companyModule;
$user = $companyModule->employees()->first();
$brn = $companyModule->company->documents->where('document_type', DocumentType::SSM_REGISTRATION)->first();
$lastDayOfMonth = $invoice_transaction->created_at->copy()->endOfMonth();
$documentDate = $lastDayOfMonth;
$documentIdentifierPrefix = 'EI#: ';
$autoCountInvoiceId = '';
$autoCountEInvoiceValidationLink = 'CIEF';
$metadata = $invoice_transaction->attributesKVP()->where('key', KVPKey::AUTOCOUNT_DOCNO_INVOICE)->first();
if($metadata){
$autoCountInvoiceId = $metadata->value;
}
$metadata = $invoice_transaction->attributesKVP()->where('key', KVPKey::AUTOCOUNT_EINVOICE_VALIDATION_LINK)->first();
if($metadata){
$autoCountEInvoiceValidationLink = $metadata->value;
}
$transaction_invoice_pdf = LaravelMpdf::loadView($view, [
'invoice_transaction' => $invoice_transaction,
'brn' => $brn,
'document_date' => $documentDate,
'document_identifier' => $autoCountInvoiceId,
'document_identifier_prefix' => $documentIdentifierPrefix,
'autocountEInvoiceValidationLink' => $autoCountEInvoiceValidationLink,
]);
$document_object = new DocumentObject(
DocumentType::STORAGE_EINVOICE,
[chunk_split('data:application/pdf;base64,'.base64_encode($transaction_invoice_pdf->output()))],
'',
ApprovalStatus::COMPLETED,
'shipping_einvoice'
);
/** @var Document $document */
$document =$this->createsDocument->execute($invoice_transaction, $document_object);
$this->createsFiles->execute($document, $document_object);
$keyValuePairObject = new KeyValuePairObject(KVPKey::EINVOICE_ISSUED, 1);
$this->createsKeyValuePair->execute($invoice_transaction, $keyValuePairObject);
}
}
@@ -0,0 +1,91 @@
<?php
namespace App\Classes\Modules\Transactions\Processors;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Exceptions\ResourceNotFoundException;
use App\Models\Document;
use App\Models\Transaction;
use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf;
use App\Classes\ValueObjects\Constants\DocumentType;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\Modules\Documents\Services\CreatesFiles;
use App\Classes\Modules\Documents\Services\CreatesDocument;
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;
class CreateStorageInvoiceTransactionDocProcessor
{
/** @var CreatesDocument */
private $createsDocument;
/** @var CreatesFiles */
private $createsFiles;
/**
* @param CreatesDocument $createsDocument
* @param CreatesFiles $createsFiles
*/
public function __construct(CreatesDocument $createsDocument, CreatesFiles $createsFiles)
{
$this->createsDocument = $createsDocument;
$this->createsFiles = $createsFiles;
}
/**
* @param Transaction $invoice_transaction
* @throws MalformedRequestException
*/
public function execute(Transaction $invoice_transaction)
{
if($invoice_transaction->status !== ApprovalStatus::COMPLETED){
throw new ResourceNotFoundException();
}
$view = 'pages.pdfs.shipping_invoice';
$dateToCompare = Carbon::parse(env('SST_START_DATE', '2024-04-01 00:00:00'));
Log::info('$dateToCompare 2: '.$dateToCompare);
$storageInvoiceTransactionCreatedDate = Carbon::parse($invoice_transaction->created_at);
Log::info('$storageInvoiceTransactionCreatedDate 2: '.$storageInvoiceTransactionCreatedDate);
if ($storageInvoiceTransactionCreatedDate->isAfter($dateToCompare) && $invoice_transaction->tax > 0) {
$view = 'pages.pdfs.shipping_invoice_sst';
}
$eInvoiceStartDate = Carbon::parse(env('E_INVOICE_START_DATE', '2025-07-01 00:00:00'));
if ($storageInvoiceTransactionCreatedDate->isAfter($eInvoiceStartDate)) {
$view = 'pages.pdfs.shipping_invoice_sst_v2';
}
$companyModule = $invoice_transaction->owner->owner->companyModule;
$brn = $companyModule->company->documents->where('document_type', DocumentType::SSM_REGISTRATION)->first();
$documentDate = $invoice_transaction->created_at;
$documentIdentifierPrefix = 'Invoice No: ';
$documentIdentifier = $invoice_transaction->bill_no;
$transaction_invoice_pdf = LaravelMpdf::loadView($view, ['invoice_transaction' => $invoice_transaction, 'brn' => $brn, 'document_date' => $documentDate, 'document_identifier' => $documentIdentifier, 'document_identifier_prefix' => $documentIdentifierPrefix]);
$document_object = new DocumentObject(
DocumentType::STORAGE_INVOICE,
[chunk_split('data:application/pdf;base64,'.base64_encode($transaction_invoice_pdf->output()))],
'',
ApprovalStatus::COMPLETED,
'storage_invoice'
);
// $invouce_transaction_document = $invoice_transaction->documents()->where('document_type', DocumentType::STORAGE_INVOICE)->first();
// if(!$invouce_transaction_document){
/** @var Document $document */
$document = $this->createsDocument->execute($invoice_transaction, $document_object);
$this->createsFiles->execute($document, $document_object);
// $user = $packingList->owner->companyModule->employees()->first();
// if(app()->environment(['production'])) {
// $user->notify(new InvoiceIssuedEmail($user, $packingList));
// }
// }
}
}
@@ -27,4 +27,5 @@ final class DocumentType {
public const STORAGE_INVOICE = 'STORAGE_INVOICE';
public const SALES_ORDER = 'SALES_ORDER';
public const SHIPPING_EINVOICE = 'SHIPPING_EINVOICE';
public const STORAGE_EINVOICE = 'STORAGE_EINVOICE';
}
@@ -6,6 +6,7 @@ namespace App\Http\Controllers\Transactions;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Classes\Modules\Transactions\ControllersLogic\RegenerateSingleStorageInvoiceTransactionLogic;
use App\Classes\Modules\Transactions\ControllersLogic\RegenerateSingleStorageEInvoiceLogic;
class RegenerateSingleStorageInvoiceTransactionController
@@ -18,4 +19,13 @@ class RegenerateSingleStorageInvoiceTransactionController
public function regenerate(Request $request, RegenerateSingleStorageInvoiceTransactionLogic $logic) : JsonResponse {
return $logic->execute($request);
}
/**
* @param Request $request
* @param RegenerateSingleStorageEInvoiceLogic $logic
* @return JsonResponse
*/
public function regenerateEInvoice(Request $request, RegenerateSingleStorageEInvoiceLogic $logic) : JsonResponse {
return $logic->execute($request);
}
}
@@ -1,7 +1,7 @@
<template>
<div class="row">
<div class="col">
<div v-if="$store.getters.isAdmin" class="btn btn-sm btn-danger pointer m-t-10 m-b-15" @click="submit(route('api.transaction.invoice.company.regenerate', company_module_id), 'post', section, true , true)">Regenerate Invoice</div>
<!-- <div v-if="$store.getters.isAdmin" class="btn btn-sm btn-danger pointer m-t-10 m-b-15" @click="submit(route('api.transaction.invoice.company.regenerate', company_module_id), 'post', section, true , true)">Regenerate Invoice</div> -->
<div class="row flex-nowrap">
<div class="col">
<div class="row tabsContainer">
@@ -15,7 +15,8 @@
<div class="row padding-20 align-items-center">
<div class="col-auto">
<p class="no-margin fs-10 all-caps">Invoice Date</p>
<div> {{ item.created_at }}</div>
<div v-if="item.type === 1 || (item.type === 16 && item.status === 3)">{{ item.created_at }}</div>
<div v-else>-</div>
</div>
<div class="col-auto">
<p class="no-margin fs-10 all-caps">Status</p>
@@ -107,8 +108,8 @@
<!-- PDF - EINV -->
<div class="col-auto" v-if="isEinvoiceApplicable">
<div v-if="item.documents.length && item.documents.some(d => d.document_type === 'SHIPPING_EINVOICE')">
<div v-for="doc in item.documents.filter(d => d.document_type === 'SHIPPING_EINVOICE')" :key="doc.id">
<div v-if="item.documents.length && item.documents.some(d => d.document_type === 'SHIPPING_EINVOICE' || d.document_type === 'STORAGE_EINVOICE')">
<div v-for="doc in item.documents.filter(d => d.document_type === 'SHIPPING_EINVOICE' || d.document_type === 'STORAGE_EINVOICE')" :key="doc.id">
<div v-for="file in doc.files" v-bind:key="file.id" class="col-auto no-padding">
<document-file-viewer-component :file="file">
<template slot="button">
@@ -188,7 +189,7 @@
</div>
</div>
<div class="col-auto d-flex justify-content-center align-items-center" v-if="$store.getters.isSuperAdmin">
<div class="btn btn-sm btn block all-caps b-rad-none btn-danger pointer requestModal m-l-20" :data-type="'regenerateStorageInvoice-' + item.id" v-if="item.type === 16 && item.status === 3">Regenerate Storage Invoice PDF</div>
<div class="btn btn-sm btn block all-caps b-rad-none btn-danger pointer requestModal m-l-20" :data-type="'regenerateStorageInvoice-' + item.id" v-if="item.type === 16 && item.status === 3 && !isEinvoiceApplicable">Regenerate Storage Invoice PDF</div>
<div class="btn btn-sm btn block all-caps b-rad-none btn-danger pointer requestModal m-l-20" :data-type="'regenerateShippingInvoice-' + item.id" v-if="item.type === 1 && !isEinvoiceApplicable">Regenerate Shipping Invoice PDF</div>
@@ -222,7 +223,7 @@
</general-confirmation-form-component>
</modal-component>
<div class="btn btn-sm btn block all-caps b-rad-none btn-danger pointer requestModal m-l-20" :data-type="'regenerateEInvoice-' + item.id" v-if="item.type === 1 && isEinvoiceApplicable">Regenerate E-Invoice PDF</div>
<div class="btn btn-sm btn block all-caps b-rad-none btn-danger pointer requestModal m-l-20" :data-type="'regenerateEInvoice-' + item.id" v-if="item.type === 1 && item.status === 3 && isEinvoiceApplicable">Regenerate E-Invoice PDF</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" :type="'regenerateEInvoice-' + item.id">
<general-confirmation-form-component
contentText="Are you sure you want to regenerate this E-Invoice PDF document?"
@@ -249,6 +250,20 @@
>
</general-confirmation-form-component>
</modal-component>
<div class="btn btn-sm btn block all-caps b-rad-none btn-danger pointer requestModal m-l-20" :data-type="'regenerateStorageEInvoice-' + item.id" v-if="item.type === 16 && item.status === 3 && isEinvoiceApplicable">Regenerate Storage E-Invoice PDF</div>
<modal-component class="animate__animated animate__fast animate__fadeIn" styleType="fill-in" :type="'regenerateStorageEInvoice-' + item.id">
<general-confirmation-form-component
contentText="Are you sure you want to regenerate this E-Invoice PDF document?"
modalType="delete"
buttonText="Regenerate"
class="text-center"
:apiRoute="route('api.transaction.storage.einvoice.regenerate', item.id)"
apiMethod="post"
:section="section"
>
</general-confirmation-form-component>
</modal-component>
</div>
</div>
</div>
+1 -1
View File
@@ -14,7 +14,7 @@ Route::group(['prefix' => 'transactions', 'namespace' => 'Transactions', 'as' =>
Route::post('/e-invoice/{invoice_id}/regenerate', 'RegenerateSingleShippingInvoiceTransactionController@regenerateEInvoice')->name('einvoice.regenerate');
Route::post('/storage-invoice/{invoice_id}/regenerate', 'RegenerateSingleStorageInvoiceTransactionController@regenerate')->name('storage.invoice.regenerate');
Route::post('/sales-order/{invoice_id}/regenerate', 'RegenerateSingleShippingInvoiceTransactionController@regenerateSalesOrder')->name('sales.order.regenerate');
Route::post('/storage-e-invoice/{invoice_id}/regenerate', 'RegenerateSingleStorageInvoiceTransactionController@regenerateEInvoice')->name('storage.einvoice.regenerate');
Route::get('wallet/list', 'ListWalletTransactionsController@list')->name('wallet.list');