mirror of
https://gitlab.com/CIEFWorldwideSdnBhd/exchange-2.0.git
synced 2026-08-21 21:43:57 +00:00
Merge branch 'dillon/90-e-invoice-f' into vapor/staging
This commit is contained in:
@@ -87,4 +87,71 @@ class Helper
|
||||
return strtoupper('ringgit ' . $ringgitWords . ' and ' . $centsWords . ' cents only');
|
||||
}
|
||||
|
||||
public static function getStateCodeByName($name)
|
||||
{
|
||||
$path = resource_path('data/lhdn/StateCodes.json');
|
||||
|
||||
if (!file_exists($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$json = file_get_contents($path);
|
||||
$data = json_decode($json, true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$name = strtolower(trim($name));
|
||||
|
||||
// Step 1: Try exact match
|
||||
foreach ($data as $item) {
|
||||
if (
|
||||
isset($item['State']) &&
|
||||
strtolower(trim($item['State'])) === $name
|
||||
) {
|
||||
return $item['Code'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Try partial match
|
||||
foreach ($data as $item) {
|
||||
if (
|
||||
isset($item['State']) &&
|
||||
str_contains(strtolower($item['State']), $name)
|
||||
) {
|
||||
return $item['Code'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getMsicDescriptionByCode($code)
|
||||
{
|
||||
$path = resource_path('data/lhdn/MSICSubCategoryCodes.json');
|
||||
|
||||
if (!file_exists($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$json = file_get_contents($path);
|
||||
$data = json_decode($json, true);
|
||||
|
||||
if (!is_array($data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($data as $item) {
|
||||
if (
|
||||
isset($item['Code']) &&
|
||||
$item['Code'] === $code
|
||||
) {
|
||||
return $item['Description'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Jobs\Commands\V2;
|
||||
|
||||
use App\Classes\Modules\Bookings\Processors\RegenerateInvoiceBookingProcessor;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use App\Models\Booking;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
|
||||
class ProcessBookingForEInvoiceV2CommandJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/** @var Booking */
|
||||
private $booking;
|
||||
|
||||
/**
|
||||
* ProcessBookingForEInvoiceV2CommandJob constructor.
|
||||
* @param Booking $booking
|
||||
*/
|
||||
public function __construct(Booking $booking)
|
||||
{
|
||||
$this->booking = $booking;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
Log::info(Carbon::now() . ': Start job - Processing single booking for E-Invoice.');
|
||||
$start = new Carbon();
|
||||
|
||||
(App()->make(RegenerateInvoiceBookingProcessor::class))->execute($this->booking);
|
||||
|
||||
$end = new Carbon();
|
||||
$elapsedTime = $start->diff($end)->format('%H:%I:%S');
|
||||
Log::info(Carbon::now() . ': End job - Processing single booking for E-Invoice. ElapsedTime: ' . $elapsedTime . '.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Bookings\ControllersLogic;
|
||||
|
||||
|
||||
use App\Classes\General\Abstracts\AbstractControllerLogic;
|
||||
use App\Classes\Jobs\Commands\V2\ProcessBookingForEInvoiceV2CommandJob;
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Models\Booking;
|
||||
use App\Classes\Modules\Bookings\Processors\RegenerateInvoiceBookingProcessor;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class BatchBookingsGenerateEInvoiceLogic extends AbstractControllerLogic
|
||||
{
|
||||
|
||||
protected int $processedCount = 0;
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function notification(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'Generate Bookings E-Invoices',
|
||||
'message' => sprintf(
|
||||
'You have successfully submitted %d booking%s for E-Invoices.',
|
||||
$this->processedCount,
|
||||
$this->processedCount === 1 ? '' : 's'
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
/** @var RegenerateInvoiceBookingProcessor */
|
||||
private $regenerateInvoiceBookingProcessor;
|
||||
|
||||
/**
|
||||
* BatchBookingsGenerateEInvoiceLogic constructor.
|
||||
* @param RegenerateInvoiceBookingProcessor $regenerateInvoiceBookingProcessor
|
||||
*/
|
||||
public function __construct(
|
||||
RegenerateInvoiceBookingProcessor $regenerateInvoiceBookingProcessor
|
||||
) {
|
||||
$this->regenerateInvoiceBookingProcessor = $regenerateInvoiceBookingProcessor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'startDate' => 'nullable|date_format:d-m-Y',
|
||||
'endDate' => 'nullable|date_format:d-m-Y|after_or_equal:startDate',
|
||||
]);
|
||||
|
||||
$startDate = null;
|
||||
$endDate = null;
|
||||
|
||||
if (isset($validated['startDate']) && $validated['startDate']) {
|
||||
$startDate = Carbon::createFromFormat('d-m-Y', $validated['startDate'])->startOfDay();
|
||||
} else {
|
||||
$startDate = Carbon::now()->subMonths(1)->startOfDay();
|
||||
}
|
||||
|
||||
if (isset($validated['endDate']) && $validated['endDate']) {
|
||||
$endDate = Carbon::createFromFormat('d-m-Y', $validated['endDate'])->endOfDay();
|
||||
} else {
|
||||
$endDate = Carbon::now()->endOfDay();
|
||||
}
|
||||
|
||||
$startDate = $startDate ? Carbon::parse($startDate)->startOfDay() : Carbon::now()->subMonths(1);
|
||||
$endDate = $endDate ? Carbon::parse($endDate)->endOfDay() : Carbon::now();
|
||||
|
||||
$bookings = Booking::where('status', ApprovalStatus::COMPLETED)
|
||||
->whereBetween('created_at', [$startDate, $endDate])
|
||||
->whereHas('attributesKVP', function (Builder $query) {
|
||||
$query->where('key', 'AUTOCOUNT_DOCNO');
|
||||
})
|
||||
->with(['attributesKVP' => function ($query) {
|
||||
$query->where('key', 'AUTOCOUNT_DOCNO');
|
||||
}])
|
||||
->get();
|
||||
|
||||
foreach ($bookings as $booking) {
|
||||
// $autocountValue = optional($booking->attributesKVP->first())->value;
|
||||
//Log::info('Booking ID: ' . $booking->marking . ' | AUTOCOUNT_DOCNO: ' . $autocountValue);
|
||||
// $this->regenerateInvoiceBookingProcessor->execute($booking);
|
||||
ProcessBookingForEInvoiceV2CommandJob::dispatch($booking);
|
||||
}
|
||||
$this->processedCount = count($bookings);
|
||||
|
||||
return $this->resourceResponse(JsonResource::collection(collect([])));
|
||||
}
|
||||
}
|
||||
@@ -5,19 +5,11 @@ namespace App\Classes\Modules\Bookings\ControllersLogic;
|
||||
use App\Classes\General\Abstracts\AbstractControllerLogic;
|
||||
use App\Classes\Modules\Bookings\Services\FetchesBooking;
|
||||
use App\Classes\Modules\Bookings\Standards\Rules\CanFetchBooking;
|
||||
use App\Classes\Modules\Bookings\Services\UpdatesBookingStatus;
|
||||
use App\Classes\Modules\Transactions\Services\DeletesTransaction;
|
||||
use App\Classes\Modules\Documents\Services\DeletesDocument;
|
||||
use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionProcessor;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Classes\ValueObjects\Constants\DocumentType;
|
||||
use App\Classes\Modules\Bookings\Processors\RegenerateInvoiceBookingProcessor;
|
||||
use App\Http\Resources\BookingResource;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Classes\ValueObjects\Constants\TransactionType;
|
||||
use App\Models\Transaction;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class RegenerateInvoiceBookingLogic extends AbstractControllerLogic
|
||||
{
|
||||
@@ -39,41 +31,23 @@ class RegenerateInvoiceBookingLogic extends AbstractControllerLogic
|
||||
/** @var FetchesBooking */
|
||||
private $fetchesBooking;
|
||||
|
||||
/** @var DeletesTransaction */
|
||||
private $deletesTransaction;
|
||||
|
||||
/** @var UpdatesBookingStatus */
|
||||
private $updatesBookingStatus;
|
||||
|
||||
/** @var DeletesDocument */
|
||||
private $deletesDocument;
|
||||
|
||||
/** @var CreateInvoiceTransactionProcessor */
|
||||
private $createInvoiceTransactionProcessor;
|
||||
/** @var RegenerateInvoiceBookingProcessor */
|
||||
private $regenerateInvoiceBookingProcessor;
|
||||
|
||||
/**
|
||||
* FetchBookingLogic constructor.
|
||||
* RegenerateInvoiceBookingLogic constructor.
|
||||
* @param CanFetchBooking $canFetchBooking
|
||||
* @param FetchesBooking $fetchesBooking
|
||||
* @param DeletesTransaction $deletesTransaction
|
||||
* @param UpdatesBookingStatus $updatesBookingStatus
|
||||
* @param DeletesDocument $deletesDocument
|
||||
* @param CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor
|
||||
* @param RegenerateInvoiceBookingProcessor $regenerateInvoiceBookingProcessor
|
||||
*/
|
||||
public function __construct(
|
||||
CanFetchBooking $canFetchBooking,
|
||||
FetchesBooking $fetchesBooking,
|
||||
DeletesTransaction $deletesTransaction,
|
||||
UpdatesBookingStatus $updatesBookingStatus,
|
||||
DeletesDocument $deletesDocument,
|
||||
CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor
|
||||
RegenerateInvoiceBookingProcessor $regenerateInvoiceBookingProcessor
|
||||
) {
|
||||
$this->canFetchBooking = $canFetchBooking;
|
||||
$this->fetchesBooking = $fetchesBooking;
|
||||
$this->deletesTransaction = $deletesTransaction;
|
||||
$this->updatesBookingStatus = $updatesBookingStatus;
|
||||
$this->deletesDocument = $deletesDocument;
|
||||
$this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor;
|
||||
$this->regenerateInvoiceBookingProcessor = $regenerateInvoiceBookingProcessor;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,46 +70,7 @@ class RegenerateInvoiceBookingLogic extends AbstractControllerLogic
|
||||
]
|
||||
);
|
||||
|
||||
$this->updatesBookingStatus->execute($booking, ApprovalStatus::APPROVED);
|
||||
|
||||
$firstInvoice = $booking->transactions()
|
||||
->whereIn('type', [TransactionType::INVOICE])
|
||||
->withTrashed()
|
||||
->orderBy('created_at', 'asc')
|
||||
->first();
|
||||
|
||||
// get the first bill_no
|
||||
$firstBillNo = $firstInvoice->bill_no;
|
||||
if (strpos($firstBillNo, '-deleted') !== false) {
|
||||
$firstBillNo = substr($firstBillNo, 0, strpos($firstBillNo, '-deleted'));
|
||||
}
|
||||
|
||||
// update currentInvoice bill_no to '-deleted-'
|
||||
$currentInvoice = $booking->transactions()->where('type', TransactionType::INVOICE)->first();
|
||||
if($currentInvoice){
|
||||
$currentInvoice->bill_no = $currentInvoice->bill_no ."-deleted-" . (string)(Carbon::now()->timestamp);
|
||||
$currentInvoice->save();
|
||||
}
|
||||
|
||||
$transactionWithSameBillNo = Transaction::where('bill_no', $firstBillNo)->withTrashed()->get();
|
||||
if ($transactionWithSameBillNo) {
|
||||
foreach ($transactionWithSameBillNo as $transaction) {
|
||||
$transaction->bill_no = $transaction->bill_no . "-deleted-" . Str::random(10);
|
||||
$transaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
$transaction = $booking->transactions()->whereIn('type', [TransactionType::INVOICE, TransactionType::SUPPLIER_DELIVER])->get();
|
||||
foreach ($transaction as $key => $row) {
|
||||
$this->deletesTransaction->execute($row);
|
||||
}
|
||||
|
||||
$document = $booking->documents()->whereIn('document_type', [DocumentType::PURCHASE_ORDER, DocumentType::INVOICE, DocumentType::DELIVER_ORDER, DocumentType::SUPPLIER_DELIVER_ORDER])->get();
|
||||
foreach ($document as $key => $row) {
|
||||
$this->deletesDocument->execute($row);
|
||||
}
|
||||
|
||||
$this->createInvoiceTransactionProcessor->execute($booking, $firstBillNo, true);
|
||||
$this->regenerateInvoiceBookingProcessor->execute($booking);
|
||||
|
||||
return $this->resourceResponse(new BookingResource($booking));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Bookings\Processors;
|
||||
|
||||
|
||||
use App\Classes\Modules\Bookings\Services\UpdatesBookingStatus;
|
||||
use App\Classes\Modules\Transactions\Services\DeletesTransaction;
|
||||
use App\Classes\Modules\Documents\Services\DeletesDocument;
|
||||
use App\Classes\Modules\Transactions\Processors\CreateInvoiceTransactionProcessor;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Classes\ValueObjects\Constants\DocumentType;
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Classes\ValueObjects\Constants\TransactionType;
|
||||
use App\Models\Booking;
|
||||
use App\Models\Transaction;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class RegenerateInvoiceBookingProcessor
|
||||
{
|
||||
/** @var DeletesTransaction */
|
||||
private $deletesTransaction;
|
||||
|
||||
/** @var UpdatesBookingStatus */
|
||||
private $updatesBookingStatus;
|
||||
|
||||
/** @var DeletesDocument */
|
||||
private $deletesDocument;
|
||||
|
||||
/** @var CreateInvoiceTransactionProcessor */
|
||||
private $createInvoiceTransactionProcessor;
|
||||
|
||||
/**
|
||||
* RegenerateInvoiceBookingProcessor constructor.
|
||||
* @param DeletesTransaction $deletesTransaction
|
||||
* @param UpdatesBookingStatus $updatesBookingStatus
|
||||
* @param DeletesDocument $deletesDocument
|
||||
* @param CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor
|
||||
*/
|
||||
public function __construct(
|
||||
DeletesTransaction $deletesTransaction,
|
||||
UpdatesBookingStatus $updatesBookingStatus,
|
||||
DeletesDocument $deletesDocument,
|
||||
CreateInvoiceTransactionProcessor $createInvoiceTransactionProcessor
|
||||
) {
|
||||
$this->deletesTransaction = $deletesTransaction;
|
||||
$this->updatesBookingStatus = $updatesBookingStatus;
|
||||
$this->deletesDocument = $deletesDocument;
|
||||
$this->createInvoiceTransactionProcessor = $createInvoiceTransactionProcessor;
|
||||
}
|
||||
|
||||
public function execute(Booking $booking)
|
||||
{
|
||||
$this->updatesBookingStatus->execute($booking, ApprovalStatus::APPROVED);
|
||||
|
||||
$firstInvoice = $booking->transactions()
|
||||
->whereIn('type', [TransactionType::INVOICE])
|
||||
->withTrashed()
|
||||
->orderBy('created_at', 'asc')
|
||||
->first();
|
||||
|
||||
// get the first bill_no
|
||||
if($firstInvoice){
|
||||
$firstBillNo = $firstInvoice->bill_no;
|
||||
if (strpos($firstBillNo, '-deleted') !== false) {
|
||||
$firstBillNo = substr($firstBillNo, 0, strpos($firstBillNo, '-deleted'));
|
||||
}
|
||||
|
||||
// update currentInvoice bill_no to '-deleted-'
|
||||
$currentInvoice = $booking->transactions()->where('type', TransactionType::INVOICE)->first();
|
||||
if($currentInvoice){
|
||||
$currentInvoice->bill_no = $currentInvoice->bill_no ."-deleted-" . (string)(Carbon::now()->timestamp);
|
||||
$currentInvoice->save();
|
||||
}
|
||||
|
||||
$transactionWithSameBillNo = Transaction::where('bill_no', $firstBillNo)->withTrashed()->get();
|
||||
if ($transactionWithSameBillNo) {
|
||||
foreach ($transactionWithSameBillNo as $transaction) {
|
||||
$transaction->bill_no = $transaction->bill_no . "-deleted-" . Str::random(10);
|
||||
$transaction->save();
|
||||
}
|
||||
}
|
||||
|
||||
$transaction = $booking->transactions()->whereIn('type', [TransactionType::INVOICE, TransactionType::SUPPLIER_DELIVER])->get();
|
||||
foreach ($transaction as $key => $row) {
|
||||
$this->deletesTransaction->execute($row);
|
||||
}
|
||||
|
||||
$document = $booking->documents()->whereIn('document_type', [DocumentType::PURCHASE_ORDER, DocumentType::INVOICE, DocumentType::DELIVER_ORDER, DocumentType::SUPPLIER_DELIVER_ORDER])->get();
|
||||
foreach ($document as $key => $row) {
|
||||
$this->deletesDocument->execute($row);
|
||||
}
|
||||
|
||||
$this->createInvoiceTransactionProcessor->execute($booking, $firstBillNo, true);
|
||||
}
|
||||
else {
|
||||
$this->createInvoiceTransactionProcessor->execute($booking);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Exports\Services;
|
||||
|
||||
use App\Classes\General\Helper;
|
||||
use App\Classes\ValueObjects\Constants\DocumentType;
|
||||
use App\Models\Company;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Maatwebsite\Excel\Concerns\Exportable;
|
||||
use Maatwebsite\Excel\Concerns\FromQuery;
|
||||
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||
|
||||
class ExportsCompanies implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize
|
||||
{
|
||||
use Exportable;
|
||||
|
||||
protected $startDate;
|
||||
protected $endDate;
|
||||
|
||||
public function __construct($startDate = null, $endDate = null) {
|
||||
$this->startDate = $startDate ? Carbon::parse($startDate)->startOfDay() : Carbon::now()->subMonths(1);
|
||||
$this->endDate = $endDate ? Carbon::parse($endDate)->endOfDay() : Carbon::now();
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
return [
|
||||
'TIN',
|
||||
'IdentityNo',
|
||||
'Name',
|
||||
'IdentityType',
|
||||
'TaxClassification',
|
||||
'MSICCode',
|
||||
'BusinessActivityDesc',
|
||||
'DebtorCode',
|
||||
'TradeName',
|
||||
'Address',
|
||||
'PostCode',
|
||||
'Phone',
|
||||
'EmailAddress',
|
||||
'City',
|
||||
'CountryCode',
|
||||
'StateCode'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Support\Collection|mixed
|
||||
*/
|
||||
public function query()
|
||||
{
|
||||
return Company::whereBetween('e_invoice_requested_at', [$this->startDate, $this->endDate]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Company $company
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function map($company): array
|
||||
{
|
||||
$employee = $company->employees()->first();
|
||||
$lastBooking = $company->bookings()->orderByDesc('id')->first();
|
||||
$identityNo = $company->documents->where('document_type', DocumentType::IDENTITY_CARD)->first();
|
||||
$identityType = DocumentType::IDENTITY_CARD;
|
||||
if(!$identityNo){
|
||||
$identityNo = $company->documents->where('document_type', DocumentType::SSM_REGISTRATION)->first();
|
||||
$identityType = DocumentType::SSM_REGISTRATION;
|
||||
}
|
||||
$identityReference = "";
|
||||
if($identityNo){
|
||||
$identityReference = preg_replace('/\s*\(.*?\)/', '', $identityNo->reference);
|
||||
}
|
||||
$address = $company->addresses()->where('e_invoice', '=', true)->latest()->first();
|
||||
if(!$address){
|
||||
$address = $company->addresses()->where('billing', '=', true)->first();
|
||||
}
|
||||
|
||||
return [
|
||||
$company->tin, // 'TIN',
|
||||
$identityReference, // 'IdentityNo',
|
||||
$company->name, // 'Name',
|
||||
$identityType === DocumentType::IDENTITY_CARD ? 'MyKAD' : '', // 'IdentityType',
|
||||
$company->type !== null ? (string) $company->type : '0', // 'TaxClassification',
|
||||
$company->msic_code, // 'MSICCode',
|
||||
$company->msic_code ? Helper::getMsicDescriptionByCode($company->msic_code) : '', // 'BusinessActivityDesc',
|
||||
$company->debtor, // 'DebtorCode',
|
||||
$company->name, // 'TradeName',
|
||||
$address ? $address->street_one . ',' . $address->street_two : '',// 'Address',
|
||||
$address ? $address->postcode : '', // 'PostCode',
|
||||
$company->contacts()->first() ? $company->contacts()->first()->phone : '', // 'Phone',
|
||||
$employee ? $employee->email : '', // 'EmailAddress',
|
||||
$address ? $address->district()->first()->name : '', // 'City',
|
||||
'MYS', // 'CountryCode',
|
||||
$address ? Helper::getStateCodeByName($address->state()->first()->name) : '', // 'StateCode'
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Exports\Services;
|
||||
|
||||
use App\Classes\ValueObjects\Constants\PaymentMethodType;
|
||||
use App\Classes\ValueObjects\Constants\TransactionType;
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Models\Booking;
|
||||
use App\Models\Transaction;
|
||||
use Maatwebsite\Excel\Concerns\Exportable;
|
||||
use Maatwebsite\Excel\Concerns\FromQuery;
|
||||
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadingRow;
|
||||
use Maatwebsite\Excel\Concerns\WithHeadings;
|
||||
use Maatwebsite\Excel\Concerns\WithMapping;
|
||||
use Illuminate\Http\Request;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ExportsSalesInvoiceReport implements FromQuery, WithHeadings, WithHeadingRow, WithMapping, ShouldAutoSize
|
||||
{
|
||||
use Exportable;
|
||||
|
||||
protected $startDate;
|
||||
protected $endDate;
|
||||
|
||||
public function __construct($startDate = null, $endDate = null) {
|
||||
$this->startDate = $startDate ? Carbon::parse($startDate)->startOfDay() : Carbon::now()->subMonths(1);
|
||||
$this->endDate = $endDate ? Carbon::parse($endDate)->endOfDay() : Carbon::now();
|
||||
}
|
||||
|
||||
public function headings(): array
|
||||
{
|
||||
return [
|
||||
'DocNo',
|
||||
'DocDate',
|
||||
'DebtorCode',
|
||||
'Ref',
|
||||
'ShipInfo',
|
||||
'AccNo',
|
||||
'DetailDescription',
|
||||
'FurtherDescription',
|
||||
'Classification',
|
||||
'DeptNo',
|
||||
'Qty',
|
||||
'UnitPrice',
|
||||
'submiteinvoice',
|
||||
'ConsolidatedEinvoice',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Support\Collection|mixed
|
||||
*/
|
||||
public function query()
|
||||
{
|
||||
// $query = Transaction::query();
|
||||
// $query->where('type', TransactionType::PAYMENT)->where('payment_method', '!=', PaymentMethodType::WALLET);
|
||||
// $query->whereIn('status', [ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED]);
|
||||
// $query->whereHas('booking.transactions', function ($query) {
|
||||
// $query->where('type', TransactionType::PURCHASE_ORDER)->complete();
|
||||
// });
|
||||
|
||||
return Booking::where('status', ApprovalStatus::COMPLETED)->whereBetween('created_at', [$this->startDate, $this->endDate]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Booking $booking
|
||||
* @return array
|
||||
*/
|
||||
public function map($booking): array
|
||||
{
|
||||
$records = [];
|
||||
|
||||
$purchaseOrder = $booking->transactions()->where('type', TransactionType::PURCHASE_ORDER)->first();
|
||||
$company = $booking->company()->first();
|
||||
|
||||
$lastPaymentTransaction = $booking->transactions()->where('type', TransactionType::PAYMENT)->whereIn('status', [ApprovalStatus::COMPLETED, ApprovalStatus::APPROVED])->latest()->first();
|
||||
if(!$lastPaymentTransaction){
|
||||
return $records;
|
||||
}
|
||||
$documentDate = $lastPaymentTransaction->created_at;
|
||||
if(Carbon::parse($booking->updated_at)->isAfter($lastPaymentTransaction->created_at)){
|
||||
$documentDate = $booking->updated_at;
|
||||
}
|
||||
$formattedDocumentDate = Carbon::parse($documentDate)->format('m/d/Y');
|
||||
|
||||
$firstItem = true;
|
||||
$transactionDetails = $purchaseOrder->transactionDetails;
|
||||
foreach ($transactionDetails as $detail) {
|
||||
$records[] = [
|
||||
$firstItem ? '<<New>>' : '',
|
||||
$formattedDocumentDate,
|
||||
$company->debtor,
|
||||
$booking->marking,
|
||||
$booking->marking,
|
||||
'500-0000',
|
||||
'PRODUCT NAME :',
|
||||
$detail->product_name,
|
||||
'022',
|
||||
'C',
|
||||
$detail->quantity,
|
||||
number_format($detail->price, 2),
|
||||
$firstItem ? 'T' : '',
|
||||
$company->e_invoice ? 'F' : 'T'
|
||||
];
|
||||
|
||||
if($firstItem) {
|
||||
$firstItem = false;
|
||||
}
|
||||
}
|
||||
return $records;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes\Modules\Imports\ControllersLogic;
|
||||
|
||||
|
||||
use App\Classes\Exceptions\MalformedRequestException;
|
||||
use App\Classes\General\Abstracts\AbstractControllerLogic;
|
||||
use App\Classes\Modules\Accounts\Services\CreatesKeyValuePair;
|
||||
use App\Classes\Modules\Accounts\Services\UpdatesKeyValuePair;
|
||||
use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject;
|
||||
use App\Classes\Modules\Accounts\DataTransferObjects\KeyValuePairObject;
|
||||
use App\Classes\ValueObjects\Constants\ApprovalStatus;
|
||||
use App\Models\Booking;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Maatwebsite\Excel\Facades\Excel;
|
||||
|
||||
class ImportExcelLogic extends AbstractControllerLogic
|
||||
{
|
||||
|
||||
/** @var CreatesKeyValuePair */
|
||||
private $createsKeyValuePair;
|
||||
|
||||
/** @var UpdatesKeyValuePair */
|
||||
private $updatesKeyValuePair;
|
||||
|
||||
/**
|
||||
* ImportExcelLogic constructor.
|
||||
* @param CreatesKeyValuePair $createsKeyValuePair
|
||||
* @param UpdatesKeyValuePair $updatesKeyValuePair
|
||||
*/
|
||||
public function __construct(CreatesKeyValuePair $createsKeyValuePair, UpdatesKeyValuePair $updatesKeyValuePair)
|
||||
{
|
||||
$this->createsKeyValuePair = $createsKeyValuePair;
|
||||
$this->updatesKeyValuePair = $updatesKeyValuePair;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function notification():array {
|
||||
return [
|
||||
'title' => 'Import Excel',
|
||||
'message' => 'You have successfully imported and updated booking details'
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return JsonResponse
|
||||
* @throws MalformedRequestException
|
||||
*/
|
||||
public function logic(Request $request) : JsonResponse
|
||||
{
|
||||
$reportType = $request->input('report_type');
|
||||
|
||||
$object = new DocumentObject('', $request->input('files'), '', ApprovalStatus::APPROVED, 'imports');
|
||||
foreach ($object->getFiles() as $file){
|
||||
$collection = Excel::toCollection(null, json_decode($file)->file_info->original->file, null, null, true);
|
||||
|
||||
$sheet = $collection->first();
|
||||
$header = $sheet->first()->toArray();
|
||||
|
||||
$normalizedHeader = array_map(fn($h) => strtolower(trim($h)), $header);
|
||||
$salesInvoiceHeader = [
|
||||
'docno', 'docdate', 'debtorcode', 'ref', 'shipinfo', 'accno',
|
||||
'detaildescription', 'furtherdescription', 'classification',
|
||||
'deptno', 'qty', 'unitprice', 'submiteinvoice', 'consolidatedeinvoice'
|
||||
];
|
||||
$customersReportHeader = [
|
||||
'tin', 'identityno', 'name', 'identitytype', 'taxclassification', 'msiccode',
|
||||
'businessactivitydesc', 'debtorcode', 'tradename', 'address', 'postcode',
|
||||
'phone', 'emailaddress', 'city', 'countrycode', 'statecode'
|
||||
];
|
||||
|
||||
if ($reportType === 'Sales Invoice Report') {
|
||||
$optionalColumn = 'einvoicevalidationlink';
|
||||
|
||||
if (
|
||||
$normalizedHeader !== $salesInvoiceHeader &&
|
||||
$normalizedHeader !== [...$salesInvoiceHeader, $optionalColumn]
|
||||
) {
|
||||
throw new MalformedRequestException('Uploaded Excel file format is incorrect. Column headers do not match expected format.');
|
||||
}
|
||||
|
||||
} elseif ($reportType === 'Customers Report' && $normalizedHeader !== $customersReportHeader) {
|
||||
throw new MalformedRequestException('Uploaded Excel file format is incorrect. Column headers do not match expected format.');
|
||||
}
|
||||
|
||||
$rows = $sheet->skip(1);
|
||||
|
||||
foreach ($rows as $index => $details) {
|
||||
$docNo = $details[0] ?? null;
|
||||
$docDate = $details[1] ?? null;
|
||||
$debtorCode = $details[2] ?? null;
|
||||
$ref = $details[3] ?? null;
|
||||
$shipInfo = $details[4] ?? null;
|
||||
$accNo = $details[5] ?? null;
|
||||
$detailDescription = $details[6] ?? null;
|
||||
$furtherDescription = $details[7] ?? null;
|
||||
$classification = $details[8] ?? null;
|
||||
$deptNo = $details[9] ?? null;
|
||||
$qty = $details[10] ?? null;
|
||||
$unitPrice = $details[11] ?? null;
|
||||
$submitEinvoice = $details[12] ?? null;
|
||||
$consolidatedEinvoice = $details[13] ?? null;
|
||||
$eInvoiceValidationLink = $details[14] ?? null; // Safe access for the new column
|
||||
|
||||
Log::info("Row {$index} Details:", [
|
||||
'DocNo' => $docNo,
|
||||
'DocDate' => $docDate,
|
||||
'DebtorCode' => $debtorCode,
|
||||
'Ref' => $ref,
|
||||
'ShipInfo' => $shipInfo,
|
||||
'AccNo' => $accNo,
|
||||
'DetailDescription' => $detailDescription,
|
||||
'FurtherDescription' => $furtherDescription,
|
||||
'Classification' => $classification,
|
||||
'DeptNo' => $deptNo,
|
||||
'Qty' => $qty,
|
||||
'UnitPrice' => $unitPrice,
|
||||
'SubmitEinvoice' => $submitEinvoice,
|
||||
'ConsolidatedEinvoice' => $consolidatedEinvoice,
|
||||
'EInvoiceValidationLink' => $eInvoiceValidationLink,
|
||||
]);
|
||||
|
||||
$booking = Booking::where('marking', $ref)->first();
|
||||
if($docNo != "<<New>>"){
|
||||
$this->updateOrCreateKeyValuePair($booking, "AUTOCOUNT_DOCNO", $docNo);
|
||||
}
|
||||
if($eInvoiceValidationLink){
|
||||
$this->updateOrCreateKeyValuePair($booking, "AUTOCOUNT_EINVOICE_VALIDATION_LINK", $eInvoiceValidationLink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->response([]);
|
||||
}
|
||||
|
||||
private function updateOrCreateKeyValuePair($booking, $key, $value)
|
||||
{
|
||||
$keyValuePairObject = new KeyValuePairObject($key, $value);
|
||||
$metadata = $booking->attributesKVP()->where('key', $key)->first();
|
||||
|
||||
if ($metadata) {
|
||||
$this->updatesKeyValuePair->execute($metadata, $keyValuePairObject);
|
||||
} else {
|
||||
$this->createsKeyValuePair->execute($booking, $keyValuePairObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,8 @@ class CreateInvoiceDocumentProcessor
|
||||
$brn = $supplier->documents->where('document_type', DocumentType::SSM_REGISTRATION)->first();
|
||||
$documentDate = $supplier->segments->whereIn('id', [23])->first() ? \Carbon\Carbon::now() : $booking->created_at;
|
||||
$eInvoiceStartDate = Carbon::parse(env('E_INVOICE_START_DATE', '2025-07-01 00:00:00'));
|
||||
$autoCountInvoiceId = '';
|
||||
$autoCountEInvoiceValidationLink = 'CIEF';
|
||||
|
||||
if ($booking) {
|
||||
$bookingCreatedDate = Carbon::parse($booking->created_at);
|
||||
@@ -66,6 +68,14 @@ class CreateInvoiceDocumentProcessor
|
||||
}
|
||||
|
||||
if($document_type === DocumentType::EINVOICE){
|
||||
$metadata = $booking->attributesKVP()->where('key', 'AUTOCOUNT_DOCNO')->first();
|
||||
if($metadata){
|
||||
$autoCountInvoiceId = $metadata->value;
|
||||
}
|
||||
$metadata = $booking->attributesKVP()->where('key', 'AUTOCOUNT_EINVOICE_VALIDATION_LINK')->first();
|
||||
if($metadata){
|
||||
$autoCountEInvoiceValidationLink = $metadata->value;
|
||||
}
|
||||
$lastDayOfMonth = $documentDate->copy()->endOfMonth();
|
||||
$documentDate = $lastDayOfMonth;
|
||||
}
|
||||
@@ -77,7 +87,19 @@ 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, 'document_date' => $documentDate, 'brn' => $brn, 'autocountId' => null, 'booking' => $booking]); //cief todo: 90 - autocount id to be updated
|
||||
$order_pdf = LaravelMpdf::loadView('pages.pdfs.' . $lowercaseDocumentType,
|
||||
[
|
||||
'transaction' => $transaction,
|
||||
'po_order_transaction' => $purchaseOrder,
|
||||
'supplier' => $supplier,
|
||||
'voucher_redemption' => $voucherRedemption,
|
||||
'current_paid_amount' => $currentPaidAmount,
|
||||
'document_date' => $documentDate,
|
||||
'brn' => $brn,
|
||||
'booking' => $booking,
|
||||
'autocountId' => $autoCountInvoiceId,
|
||||
'autocountEInvoiceValidationLink' => $autoCountEInvoiceValidationLink,
|
||||
]);
|
||||
|
||||
if($purchaseOrder && $purchaseOrder->booking->service_id === 4) {
|
||||
$purchaseOrderDocuments = $purchaseOrder->booking->documents()->where('document_type', DocumentType::ECOMMERCE_PURCHASE_ORDER)->get();
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace App\Http\Controllers\Bookings;
|
||||
|
||||
use App\Classes\Modules\Bookings\ControllersLogic\RegenerateInvoiceBookingLogic;
|
||||
use App\Classes\Modules\Bookings\ControllersLogic\BatchBookingsGenerateEInvoiceLogic;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
|
||||
class RegenerateBookingEInvoiceController
|
||||
{
|
||||
/**
|
||||
@@ -17,4 +17,13 @@ class RegenerateBookingEInvoiceController
|
||||
public function regenerate(Request $request, RegenerateInvoiceBookingLogic $logic): JsonResponse {
|
||||
return $logic->execute($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param BatchBookingsGenerateEInvoiceLogic $logic
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function batchProcess(Request $request, BatchBookingsGenerateEInvoiceLogic $logic): JsonResponse {
|
||||
return $logic->execute($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Exports;
|
||||
|
||||
|
||||
use App\Classes\Modules\Exports\Services\ExportsSalesInvoiceReport;
|
||||
use Illuminate\Http\Request;
|
||||
use Maatwebsite\Excel\Excel;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Classes\General\AWSS3Helper;
|
||||
use App\Classes\Modules\Exports\Services\ExportsCompanies;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class ExportController
|
||||
{
|
||||
public function salesInvoices(Request $request){
|
||||
$validated = $request->validate([
|
||||
'startDate' => 'nullable|date_format:d-m-Y',
|
||||
'endDate' => 'nullable|date_format:d-m-Y|after_or_equal:startDate',
|
||||
]);
|
||||
|
||||
$startDate = null;
|
||||
$endDate = null;
|
||||
|
||||
if (isset($validated['startDate']) && $validated['startDate']) {
|
||||
$startDate = Carbon::createFromFormat('d-m-Y', $validated['startDate'])->startOfDay();
|
||||
} else {
|
||||
$startDate = Carbon::now()->subMonths(1)->startOfDay();
|
||||
}
|
||||
|
||||
if (isset($validated['endDate']) && $validated['endDate']) {
|
||||
$endDate = Carbon::createFromFormat('d-m-Y', $validated['endDate'])->endOfDay();
|
||||
} else {
|
||||
$endDate = Carbon::now()->endOfDay();
|
||||
}
|
||||
|
||||
|
||||
$exportsTransactions = new ExportsSalesInvoiceReport($startDate, $endDate);
|
||||
|
||||
$exportFileName = 'Exchange - Sales Invoice Report.xls';
|
||||
$filesystemDriver = Storage::getDefaultDriver();
|
||||
if($filesystemDriver === 's3'){
|
||||
return response([ 'src' => AWSS3Helper::S3Exportable($exportFileName, $exportsTransactions) ]);
|
||||
}
|
||||
else{
|
||||
$response = $exportsTransactions->download($exportFileName, Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
|
||||
ob_end_clean();
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function companies(Request $request){
|
||||
$validated = $request->validate([
|
||||
'startDate' => 'nullable|date_format:d-m-Y',
|
||||
'endDate' => 'nullable|date_format:d-m-Y|after_or_equal:startDate',
|
||||
]);
|
||||
|
||||
$startDate = null;
|
||||
$endDate = null;
|
||||
|
||||
if (isset($validated['startDate']) && $validated['startDate']) {
|
||||
$startDate = Carbon::createFromFormat('d-m-Y', $validated['startDate'])->startOfDay();
|
||||
} else {
|
||||
$startDate = Carbon::now()->subMonths(1)->startOfDay();
|
||||
}
|
||||
|
||||
if (isset($validated['endDate']) && $validated['endDate']) {
|
||||
$endDate = Carbon::createFromFormat('d-m-Y', $validated['endDate'])->endOfDay();
|
||||
} else {
|
||||
$endDate = Carbon::now()->endOfDay();
|
||||
}
|
||||
|
||||
$exportsCompanies = new ExportsCompanies($startDate, $endDate);
|
||||
|
||||
$exportFileName = 'Exchange - Customers Data Report.xls';
|
||||
$filesystemDriver = Storage::getDefaultDriver();
|
||||
if($filesystemDriver === 's3'){
|
||||
return response([ 'src' => AWSS3Helper::S3Exportable($exportFileName, $exportsCompanies) ]);
|
||||
}
|
||||
else{
|
||||
$response = $exportsCompanies->download($exportFileName, Excel::XLS, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
|
||||
ob_end_clean();
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Imports;
|
||||
|
||||
use App\Classes\Modules\Imports\ControllersLogic\ImportExcelLogic;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ImportController extends Controller
|
||||
{
|
||||
|
||||
public function salesInvoices(Request $request, ImportExcelLogic $logic): JsonResponse
|
||||
{
|
||||
return $logic->execute($request);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<div class="row m-b-20">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row m-l-0 m-r-0 parentContainer">
|
||||
<div class="col-12 col-md mb-2 mb-md-0 p-l-3 p-r-3 p-md-0">
|
||||
<validation-wrapper-component :validator="$v.parameters.startDate">
|
||||
<label class="all-caps">Start Date</label>
|
||||
<date-picker-component :parameters="parameters" v-model.lazy="parameters.startDate"></date-picker-component>
|
||||
</validation-wrapper-component>
|
||||
</div>
|
||||
<div class="col-12 col-md mb-2 mb-md-0 p-l-3 p-r-3 p-md-0">
|
||||
<validation-wrapper-component :validator="$v.parameters.endDate">
|
||||
<label class="all-caps">End Date</label>
|
||||
<date-picker-component :parameters="parameters" v-model.lazy="parameters.endDate"></date-picker-component>
|
||||
</validation-wrapper-component>
|
||||
</div>
|
||||
<div class="col-12 col-md mb-2 mb-md-0 p-l-3 p-r-3 p-md-0">
|
||||
<validation-wrapper-component selectable :validator="$v.parameters.reportType">
|
||||
<label class="all-caps">Report Type</label>
|
||||
<select-component :options="options()" v-model="parameters.reportType"></select-component>
|
||||
</validation-wrapper-component>
|
||||
</div>
|
||||
<div class="col-12 col-md-auto">
|
||||
<div class="btn btn-lg btn-primary fs-11 w-100 h-100 d-flex justify-content-center align-items-center" :class="{ disabled: isDownloading }" @click="handleExportClick">
|
||||
<span>
|
||||
Export
|
||||
</span>
|
||||
<span v-if="isDownloading" class="spinner"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-auto pl-md-0">
|
||||
<button
|
||||
class="btn btn-lg fs-11 w-100 h-100 d-flex justify-content-center align-items-center"
|
||||
:class="parameters.reportType === 'Sales Invoice Report' ? 'btn-primary requestModal pointer' : 'btn-secondary'"
|
||||
:disabled="parameters.reportType !== 'Sales Invoice Report'"
|
||||
:data-type="parameters.reportType === 'Sales Invoice Report' ? 'uploadDocumentModel' : null"
|
||||
>
|
||||
<span>Import</span>
|
||||
</button>
|
||||
<modal-component type="uploadDocumentModel">
|
||||
<upload-component :section="section" :report-type="parameters.reportType"></upload-component>
|
||||
</modal-component>
|
||||
</div>
|
||||
<div class="col-12 col-md-auto p-md-0">
|
||||
<button
|
||||
class="btn btn-lg fs-11 w-100 h-100 d-flex justify-content-center align-items-center"
|
||||
:class="parameters.reportType === 'Sales Invoice Report' ? 'btn-primary' : 'btn-secondary'"
|
||||
:disabled="parameters.reportType !== 'Sales Invoice Report'"
|
||||
@click="() => { if (parameters.reportType === 'Sales Invoice Report') handleGenerateEInvoiceClick() }"
|
||||
>
|
||||
<span>Process E-Invoices</span>
|
||||
</button>
|
||||
<modal-component
|
||||
class="animate__animated animate__fast animate__fadeIn"
|
||||
styleType="fill-in" size="large"
|
||||
id="modal-generate-einvoice">
|
||||
<general-confirmation-form-component
|
||||
contentText="Are you sure you want to batch process for E-Invoices? (Only applicable for bookings with DocNo)"
|
||||
modalType="confirm"
|
||||
class="text-center bg-white padding-40 b-rad-lg"
|
||||
:apiRoute="generateEInvoicesUrl"
|
||||
apiMethod="get"
|
||||
:section="section"
|
||||
v-if="generateEInvoicesUrl"
|
||||
>
|
||||
</general-confirmation-form-component>
|
||||
</modal-component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import componentHandler from "../../../general/mixins/componentHandler";
|
||||
import { required } from "vuelidate/lib/validators";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
section:{
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
parameters: {
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
reportType: '',
|
||||
},
|
||||
isDownloading: false,
|
||||
generateEInvoicesUrl: null,
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
switch(this.section) {
|
||||
case 'paymentsReportSection':
|
||||
this.parameters.reportType = 'Sales Invoice Report'
|
||||
break;
|
||||
}
|
||||
},
|
||||
validations: {
|
||||
parameters: {
|
||||
startDate: {
|
||||
required
|
||||
},
|
||||
endDate: {
|
||||
required
|
||||
},
|
||||
reportType: {
|
||||
required
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
options() {
|
||||
return [
|
||||
'Sales Invoice Report',
|
||||
'Customers Report',
|
||||
];
|
||||
},
|
||||
handleExportClick(){
|
||||
if (!this.validate()) return;
|
||||
|
||||
const reportType = this.parameters.reportType;
|
||||
|
||||
const routesMap = {
|
||||
'Sales Invoice Report': route('api.export.bookings.sales-invoices'),
|
||||
'Customers Report': route('api.export.companies.customers-data'),
|
||||
};
|
||||
|
||||
let url = `${routesMap[reportType]}?startDate=${this.parameters.startDate}&endDate=${this.parameters.endDate}`;
|
||||
this.isDownloading = true;
|
||||
if(window.LARAVEL_VAPOR_ENABLED){
|
||||
this.submit(url, 'get', this.section, false, false);
|
||||
}
|
||||
else{
|
||||
window.open(url, '_blank');
|
||||
this.isDownloading = false;
|
||||
}
|
||||
},
|
||||
handleGenerateEInvoiceClick(){
|
||||
if (!this.validate()) return;
|
||||
|
||||
const reportType = this.parameters.reportType;
|
||||
const routesMap = {
|
||||
'Sales Invoice Report': route('api.booking.batch.process'),
|
||||
};
|
||||
|
||||
const selectedRoute = routesMap[reportType];
|
||||
|
||||
if (!selectedRoute) {
|
||||
throw new Error(`No route mapping found for report type: ${reportType}`);
|
||||
}
|
||||
|
||||
this.generateEInvoicesUrl = `${selectedRoute}?startDate=${this.parameters.startDate}&endDate=${this.parameters.endDate}`;
|
||||
|
||||
$('#modal-generate-einvoice').modal('show');
|
||||
},
|
||||
successHandler(response) {
|
||||
if(window.LARAVEL_VAPOR_ENABLED){
|
||||
if (response.payload !== undefined && response.payload.src) {
|
||||
window.open(response.payload.src, '_blank');
|
||||
}
|
||||
else if (response.src) {
|
||||
window.open(response.src, '_blank');
|
||||
}
|
||||
}
|
||||
this.isDownloading = false;
|
||||
},
|
||||
errorHandler(error) {
|
||||
this.isDownloading = false;
|
||||
},
|
||||
},
|
||||
mixins: [componentHandler]
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.spinner {
|
||||
border: 2px solid rgba(0, 0, 0, 0.1);
|
||||
border-left-color: #000;
|
||||
border-radius: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
animation: spin 1s linear infinite;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,12 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="col-12 p-l-0">
|
||||
<small class="all-caps muted fs-10">Export Report (New)</small>
|
||||
<download-upload-component section="exportDataSection"></download-upload-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-8 p-l-0">
|
||||
<small class="all-caps muted fs-10">Export Payment Transactions CSV</small>
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="row" @keyup.enter="submitForm">
|
||||
<div class="col">
|
||||
<loading-component style="height: 200px; top: 0;" key="1" color="success" v-show="$store.getters.isLoading(section)"></loading-component>
|
||||
<div class="row" v-show="!$store.getters.isLoading(section)">
|
||||
<div class="col">
|
||||
<div class="row m-b-10">
|
||||
<div class="col">
|
||||
<div class="font-heading fs-16 all-caps bold m-b-15">Upload: {{ reportType }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<error-message-component class="m-b-20" :error="error"></error-message-component>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<file-input-component :validator="$v.files" v-model="files">
|
||||
<template slot="label">
|
||||
<div class="font-heading fs-11 text-primary all-caps">.xls file ONLY</div>
|
||||
</template>
|
||||
<template slot="tips">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="row m-b-10">
|
||||
<div class="col">
|
||||
<div class="font-heading fs-11 text-warning m-b-10">Please double check the Report Type selected before proceed upload.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</file-input-component>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row m-t-20">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-sm bg-master-lighter p-t-10 p-b-10 p-r-35 p-l-35 btn-default b-rad-none" data-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
<div class="col text-right">
|
||||
<button type="button" class="btn btn-sm p-t-10 p-b-10 p-r-35 p-l-35 btn-success b-rad-none" @click="submitForm">Upload</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ModalFromHandler from '../../../general/mixins/modalFormHandler'
|
||||
import { required } from "vuelidate/lib/validators";
|
||||
export default {
|
||||
props: {
|
||||
reportType: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
files: [],
|
||||
parameters: {}
|
||||
}
|
||||
},
|
||||
validations: {
|
||||
files: {
|
||||
required
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submitForm(){
|
||||
this.parameters = {
|
||||
files: this.files,
|
||||
report_type: this.reportType
|
||||
};
|
||||
|
||||
this.submit(this.route('api.import.sales-invoices'), 'post', this.section, true, true);
|
||||
}
|
||||
},
|
||||
mixins: [ModalFromHandler]
|
||||
|
||||
}
|
||||
</script>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
[
|
||||
{
|
||||
"Code": "01",
|
||||
"State": "Johor"
|
||||
},
|
||||
{
|
||||
"Code": "02",
|
||||
"State": "Kedah"
|
||||
},
|
||||
{
|
||||
"Code": "03",
|
||||
"State": "Kelantan"
|
||||
},
|
||||
{
|
||||
"Code": "04",
|
||||
"State": "Melaka"
|
||||
},
|
||||
{
|
||||
"Code": "05",
|
||||
"State": "Negeri Sembilan"
|
||||
},
|
||||
{
|
||||
"Code": "06",
|
||||
"State": "Pahang"
|
||||
},
|
||||
{
|
||||
"Code": "07",
|
||||
"State": "Pulau Pinang"
|
||||
},
|
||||
{
|
||||
"Code": "08",
|
||||
"State": "Perak"
|
||||
},
|
||||
{
|
||||
"Code": "09",
|
||||
"State": "Perlis"
|
||||
},
|
||||
{
|
||||
"Code": "10",
|
||||
"State": "Selangor"
|
||||
},
|
||||
{
|
||||
"Code": "11",
|
||||
"State": "Terengganu"
|
||||
},
|
||||
{
|
||||
"Code": "12",
|
||||
"State": "Sabah"
|
||||
},
|
||||
{
|
||||
"Code": "13",
|
||||
"State": "Sarawak"
|
||||
},
|
||||
{
|
||||
"Code": "14",
|
||||
"State": "Wilayah Persekutuan Kuala Lumpur"
|
||||
},
|
||||
{
|
||||
"Code": "15",
|
||||
"State": "Wilayah Persekutuan Labuan"
|
||||
},
|
||||
{
|
||||
"Code": "16",
|
||||
"State": "Wilayah Persekutuan Putrajaya"
|
||||
},
|
||||
{
|
||||
"Code": "17",
|
||||
"State": "Not Applicable"
|
||||
}
|
||||
]
|
||||
@@ -97,12 +97,12 @@
|
||||
<tbody>
|
||||
<tr align="center">
|
||||
<td>
|
||||
<img src="{{ url(config('qr.qr_code_img_url') . 'http://e-invoice uuid link') }}" style="width: 230px; height: 230px;" />
|
||||
<img src="{{ url(config('qr.qr_code_img_url') . $autocountEInvoiceValidationLink ) }}" style="width: 230px; height: 230px;" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr align="center">
|
||||
<td>
|
||||
<h2 style="margin: 0 !important;"><strong>http://e-invoice uuid link</strong></h2>
|
||||
<h2 style="margin: 0 !important;"><strong>{{ $autocountEInvoiceValidationLink }}</strong></h2>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -74,6 +74,8 @@ Route::group(['middleware' => 'api', 'prefix' => 'v1', 'as' => 'api.'], function
|
||||
|
||||
require __DIR__ . '/rule.php';
|
||||
|
||||
require __DIR__ . '/export.php';
|
||||
|
||||
// require __DIR__ . '/rate.php';
|
||||
// require __DIR__ . '/receipt.php';
|
||||
});
|
||||
|
||||
@@ -50,4 +50,7 @@ Route::group(['prefix' => 'booking', 'as' => 'booking.', 'namespace' => 'Booking
|
||||
Route::group(['prefix' => '{id}/einvoice', 'as' => 'einvoice.'], function () {
|
||||
Route::post('/', [RegenerateBookingEInvoiceController::class, 'regenerate'])->name('regenerate');
|
||||
});
|
||||
Route::group(['prefix' => 'batch/einvoice', 'as' => 'batch.'], function () {
|
||||
Route::get('/process', [RegenerateBookingEInvoiceController::class, 'batchProcess'])->name('process');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Exports\ExportController;
|
||||
use App\Http\Controllers\Imports\ImportController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
|
||||
Route::group(['prefix' => 'export', 'as' => 'export.', 'namespace' => 'Exports'], function () {
|
||||
Route::group(['prefix' => 'bookings', 'as' => 'bookings.'], function () {
|
||||
Route::get('/sales-invoices', [ExportController::class, 'salesInvoices'])->name('sales-invoices');
|
||||
});
|
||||
Route::group(['prefix' => 'companies', 'as' => 'companies.'], function () {
|
||||
Route::get('/customers-data', [ExportController::class, 'companies'])->name('customers-data');
|
||||
});
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'import', 'as' => 'import.', 'namespace' => 'Imports'], function () {
|
||||
Route::post('/import', [ImportController::class, 'salesInvoices'])->name('sales-invoices');
|
||||
});
|
||||
Reference in New Issue
Block a user