From dcc1962127d373b067286496ab8b81078a069bae Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Wed, 14 Jan 2026 22:47:09 +0800 Subject: [PATCH] 1688 PO Automation Project --- ...TLPurchaseOrderTransactionV2CommandJob.php | 2 +- .../ImportPurchaseOrderTransactionLogic.php | 2 +- .../ETLPDFPurchaseOrderClaudeProcessor.php | 7 ++- ...LPurchaseOrderTransactionCompleteEvent.php | 44 +++++++++++++++++++ composer.json | 1 + package.json | 2 + resources/assets/vue/app.js | 13 ++++++ .../UploadPurchaseOrderPdfV2FormComponent.vue | 30 +++++++++++-- 8 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 app/Events/ETLPurchaseOrderTransactionCompleteEvent.php diff --git a/app/Classes/Jobs/Commands/V2/ETLPurchaseOrderTransactionV2CommandJob.php b/app/Classes/Jobs/Commands/V2/ETLPurchaseOrderTransactionV2CommandJob.php index 072fdd85..54c5f7a3 100644 --- a/app/Classes/Jobs/Commands/V2/ETLPurchaseOrderTransactionV2CommandJob.php +++ b/app/Classes/Jobs/Commands/V2/ETLPurchaseOrderTransactionV2CommandJob.php @@ -73,7 +73,7 @@ class ETLPurchaseOrderTransactionV2CommandJob implements ShouldQueue 'file_count' => count($this->files), ]); - (App()->make(ETLPDFPurchaseOrderClaudeProcessor::class))->execute($this->files, $this->bookingId, $this->batchIndex !== 1); + (App()->make(ETLPDFPurchaseOrderClaudeProcessor::class))->execute($this->files, $this->bookingId, $this->batchIndex !== 1, $this->isLast); $end = new Carbon(); $elapsedTime = $start->diff($end)->format('%H:%I:%S'); diff --git a/app/Classes/Modules/Transactions/ControllersLogic/ImportPurchaseOrderTransactionLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/ImportPurchaseOrderTransactionLogic.php index 56972f5d..1ce9a99a 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/ImportPurchaseOrderTransactionLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/ImportPurchaseOrderTransactionLogic.php @@ -166,7 +166,7 @@ class ImportPurchaseOrderTransactionLogic extends AbstractControllerLogic if ($totalSize > $maxSize) { // ETLPurchaseOrderTransactionV2CommandJob::dispatch($base64FilesEN, $request->route('id')); $this->runAsJob($base64FilesEN, $request->route('id')); - $this->notificationMessage = "Please refresh page in a few minutes"; + $this->notificationMessage = "Please don’t refresh the page. The page will automatically update when the process is done."; return $this->response([]); } else{ diff --git a/app/Classes/Modules/Transactions/Processors/ETLPDFPurchaseOrderClaudeProcessor.php b/app/Classes/Modules/Transactions/Processors/ETLPDFPurchaseOrderClaudeProcessor.php index 2549c10f..5dbd1474 100644 --- a/app/Classes/Modules/Transactions/Processors/ETLPDFPurchaseOrderClaudeProcessor.php +++ b/app/Classes/Modules/Transactions/Processors/ETLPDFPurchaseOrderClaudeProcessor.php @@ -14,6 +14,7 @@ use App\Classes\ValueObjects\Constants\KVPKey; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\PaymentMethodType; use App\Classes\ValueObjects\Constants\TransactionType; +use App\Events\ETLPurchaseOrderTransactionCompleteEvent; use App\Models\KeyValuePair; use App\Models\Booking; use Illuminate\Support\Facades\Log; @@ -47,7 +48,7 @@ class ETLPDFPurchaseOrderClaudeProcessor * @param bool $append * @return array|null */ - public function execute(array $files, ?int $bookingId = null, bool $append = false): ?array + public function execute(array $files, ?int $bookingId = null, bool $append = false, bool $broadcast = false): ?array { $tempPaths = []; @@ -146,6 +147,10 @@ class ETLPDFPurchaseOrderClaudeProcessor ); $this->createPurchaseOrderTransactionProcessor->execute($booking, $object, $append); + + if($broadcast){ + event(new ETLPurchaseOrderTransactionCompleteEvent(true, $bookingId)); + } } return $data; diff --git a/app/Events/ETLPurchaseOrderTransactionCompleteEvent.php b/app/Events/ETLPurchaseOrderTransactionCompleteEvent.php new file mode 100644 index 00000000..d06e2ff4 --- /dev/null +++ b/app/Events/ETLPurchaseOrderTransactionCompleteEvent.php @@ -0,0 +1,44 @@ +update = $update; // true or false + $this->bookingId = $bookingId; // unique booking identifier + } + + /** + * Get the channels the event should broadcast on. + * + * @return \Illuminate\Broadcasting\Channel|array + */ + public function broadcastOn() + { + return new Channel('admin.pdf.1688.processing.' . $this->bookingId); + } +} diff --git a/composer.json b/composer.json index c9975f33..2f2fd261 100644 --- a/composer.json +++ b/composer.json @@ -28,6 +28,7 @@ "maatwebsite/excel": "^3.1", "maxbanton/cwh": "^2.0", "mpdf/mpdf": "^8.1", + "pusher/pusher-php-server": "^7.2", "rinvex/countries": "^6.1", "rspective/voucherify": " v2.0.*", "smalot/pdfparser": "^2.2", diff --git a/package.json b/package.json index fc006695..63b136c9 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,12 @@ "jquery": "^3.7.0", "jquery.scrollbar": "^0.2.11", "jwt-decode": "^3.1.2", + "laravel-echo": "^1.19.0", "laravel-vapor": "^0.6.0", "noty": "^3.2.0-beta", "perfect-scrollbar": "^1.5.0", "popper.js": "^1.12", + "pusher-js": "^8.4.0", "sass-loader": "10.1.0", "select2": "^4.0.6-rc.1", "v-money": "^0.8.1", diff --git a/resources/assets/vue/app.js b/resources/assets/vue/app.js index 19d403c5..1d14a36a 100644 --- a/resources/assets/vue/app.js +++ b/resources/assets/vue/app.js @@ -32,6 +32,19 @@ import Vapor from 'laravel-vapor'; // Vapor.withBaseAssetUrl(import.meta.env.VITE_VAPOR_ASSET_URL); window.Vapor = Vapor; +import Echo from 'laravel-echo'; +import Pusher from 'pusher-js'; + +window.Pusher = Pusher; + +window.Echo = new Echo({ + broadcaster: 'pusher', + key: process.env.MIX_PUSHER_APP_KEY, + cluster: process.env.MIX_PUSHER_APP_CLUSTER, + forceTLS: true +}); + + /** Application Injections */ Vue.use(vuelidate); Vue.use(VueTheMask); diff --git a/resources/assets/vue/components/bookings/forms/UploadPurchaseOrderPdfV2FormComponent.vue b/resources/assets/vue/components/bookings/forms/UploadPurchaseOrderPdfV2FormComponent.vue index 535909e7..03fe81c6 100644 --- a/resources/assets/vue/components/bookings/forms/UploadPurchaseOrderPdfV2FormComponent.vue +++ b/resources/assets/vue/components/bookings/forms/UploadPurchaseOrderPdfV2FormComponent.vue @@ -102,11 +102,33 @@ } return str; }, + resetFileInput() { + this.parameters.files = []; + this.$v.parameters.files.$reset(); + this.fileInputKey++; + }, + onComplete(data) { + this.$store.dispatch('reloadList', {'name': 'bookingDetailSection'}); + this.fileInputKey += 1; + const bookingChannel = `admin.pdf.1688.processing.${data.bookingId}`; + window.Echo.leave(bookingChannel); + }, successHandler(response, section){ if(this.section + 'PDFImport' === section){ - this.$store.dispatch('reloadList', {'name': 'bookingDetailSection'}); - this.isImporting = false; - this.fileInputKey += 1; + if (Array.isArray(response.payload) && response.payload.length === 0) { + const bookingChannel = `admin.pdf.1688.processing.${this.bookingId}`; + + window.Echo.channel(bookingChannel) + .listen('ETLPurchaseOrderTransactionCompleteEvent', this.onComplete); + + this.isImporting = false; + this.resetFileInput(); + } + else{ + this.$store.dispatch('reloadList', {'name': 'bookingDetailSection'}); + this.isImporting = false; + this.fileInputKey += 1; + } } else if(this.section + 'PDFExport' === section){ try { @@ -143,7 +165,7 @@ errorHandler(response, statusCode, section){ this.isExporting = false; this.isImporting = false; - } + }, }, mixins: [formHandler] }