diff --git a/app/Classes/General/Eloquent/Filters/OrderTrackingId.php b/app/Classes/General/Eloquent/Filters/OrderTrackingId.php new file mode 100644 index 00000000..137ed17b --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/OrderTrackingId.php @@ -0,0 +1,20 @@ +where('order_tracking_id', $value); + } + +} diff --git a/app/Classes/General/Eloquent/Filters/TrackingNo.php b/app/Classes/General/Eloquent/Filters/TrackingNo.php new file mode 100644 index 00000000..d41655bf --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/TrackingNo.php @@ -0,0 +1,23 @@ +where('tracking_no', $value) + ->where(function ($query) { + $query->whereNotNull('tracking_english') + ->orWhereNotNull('remark_english'); + }); + } +} diff --git a/app/Classes/Jobs/Commands/V2/YD/FetchByTrakingNoYdPortalV2CommandJob.php b/app/Classes/Jobs/Commands/V2/YD/FetchTrakingInfoYDByPackingListV2CommandJob.php similarity index 83% rename from app/Classes/Jobs/Commands/V2/YD/FetchByTrakingNoYdPortalV2CommandJob.php rename to app/Classes/Jobs/Commands/V2/YD/FetchTrakingInfoYDByPackingListV2CommandJob.php index 78a4451f..dc1423ea 100644 --- a/app/Classes/Jobs/Commands/V2/YD/FetchByTrakingNoYdPortalV2CommandJob.php +++ b/app/Classes/Jobs/Commands/V2/YD/FetchTrakingInfoYDByPackingListV2CommandJob.php @@ -11,7 +11,7 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Log; -class FetchByTrakingNoYdPortalV2CommandJob implements ShouldQueue +class FetchTrakingInfoYDByPackingListV2CommandJob implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; @@ -24,7 +24,7 @@ class FetchByTrakingNoYdPortalV2CommandJob implements ShouldQueue public function handle() { - Log::info('Processing FetchByTrakingNoYdPortalV2CommandJob'); + Log::info('Processing FetchTrakingInfoYDByPackingListV2CommandJob'); $processor = app(FetchByTrakingNoYdPortalV2Processor::class); $processor->execute($this->packingList); } diff --git a/app/Classes/Jobs/Commands/V2/YD/FetchTrakingInfoYDByTrackingNoV2CommandJob.php b/app/Classes/Jobs/Commands/V2/YD/FetchTrakingInfoYDByTrackingNoV2CommandJob.php new file mode 100644 index 00000000..7c6993a6 --- /dev/null +++ b/app/Classes/Jobs/Commands/V2/YD/FetchTrakingInfoYDByTrackingNoV2CommandJob.php @@ -0,0 +1,30 @@ +trackingNo = $trackingNo; + } + + public function handle() + { + Log::info('Processing FetchTrakingInfoYDByTrackingNoV2CommandJob'); + $processor = app(FetchByTrakingNoYdPortalV2Processor::class); + $processor->processTrackingNo($this->trackingNo); + } +} diff --git a/app/Classes/Modules/Orders/ControllersLogic/ListOrderTrackingLogic.php b/app/Classes/Modules/Orders/ControllersLogic/ListOrderTrackingLogic.php new file mode 100644 index 00000000..85bc2bda --- /dev/null +++ b/app/Classes/Modules/Orders/ControllersLogic/ListOrderTrackingLogic.php @@ -0,0 +1,52 @@ + 'Retrieved Order Tracking', + 'message' => 'You have successfully retrieved order tracking info' + ]; + } + + /** @var ListsOrderTracking */ + private $listsOrderTracking; + + /** + * ListOrderTrackingLogic constructor. + * @param ListsOrderTracking $listsOrderTracking + */ + public function __construct(ListsOrderTracking $listsOrderTracking) + { + $this->listsOrderTracking = $listsOrderTracking; + } + + public function logic(Request $request) : JsonResponse + { + $query = $this->listsOrderTracking->execute($this->listsOrderTracking->deserializeFilters($request->input('filters'))); + + $filters = json_decode($request->input('filters'), true); + $trackingNo = $filters['tracking_no']; + if ($trackingNo) { + Artisan::call('process-yd-by-traking-no-data-command', [ + 'trackingNo' => $trackingNo + ]); + } + + return $this->collectionResponse(OrderTrackingResource::collection($query)); + } +} diff --git a/app/Classes/Modules/Orders/Services/ListsOrderTracking.php b/app/Classes/Modules/Orders/Services/ListsOrderTracking.php new file mode 100644 index 00000000..b402d83b --- /dev/null +++ b/app/Classes/Modules/Orders/Services/ListsOrderTracking.php @@ -0,0 +1,31 @@ +repository = $repository; + } + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/PackingLists/Processors/V2/FetchByTrakingNoYdPortalV2Processor.php b/app/Classes/Modules/PackingLists/Processors/V2/FetchByTrakingNoYdPortalV2Processor.php index c0d31e12..ac8e34ae 100644 --- a/app/Classes/Modules/PackingLists/Processors/V2/FetchByTrakingNoYdPortalV2Processor.php +++ b/app/Classes/Modules/PackingLists/Processors/V2/FetchByTrakingNoYdPortalV2Processor.php @@ -40,8 +40,6 @@ class FetchByTrakingNoYdPortalV2Processor Log::info('FetchByTrakingNoYdPortalV2Processor starts with packingList id ' . $packingList->id); // return; - $time_start = microtime(true); - $reference = $packingList->reference; Log::info('reference ' . $reference); @@ -56,15 +54,29 @@ class FetchByTrakingNoYdPortalV2Processor return; } + $this->processTrackingNo($reference); + + Log::info('FetchByTrakingNoYdPortalV2Processor ends with packingList id ' . $packingList->id); + Log::info('---'); + } + + /** + * @param string $reference + * @return void + * @throws MalformedRequestException + */ + public function processTrackingNo(string $reference){ + $time_start = microtime(true); + $check2 = YDOrderTracking::where('tracking_no', $reference) ->where('tracking', '第三方提货') ->first(); - if ($check2) { - Log::info('FetchByTrakingNoYdPortalV2Processor SKIPPED: 第三方提货'); - Log::info('---'); - return; - } + // if ($check2) { + // Log::info('FetchByTrakingNoYdPortalV2Processor SKIPPED: 第三方提货'); + // Log::info('---'); + // return; + // } $trackingRequest = $this->fetchesDataFRomYDPortal->clientRequest('http://www.yd-wl.com/api/ApiTracking.ashx', 'GET', [ 'trakingno' => $reference @@ -78,11 +90,10 @@ class FetchByTrakingNoYdPortalV2Processor $execution_time = ($time_end - $time_start)/60; - Log::info('FetchByTrakingNoYdPortalV2Processor ends with packingList id ' . $packingList->id . ' => Total Execution Time: '.$execution_time.' Mins.'); - Log::info('---'); + Log::info('FetchByTrakingNoYdPortalV2Processor ends with reference ' . $reference . ' => Total Execution Time: '.$execution_time.' Mins.'); } - public function processData($jsonData, $reference) + private function processData($jsonData, $reference) { $data = json_decode($jsonData, true); @@ -93,23 +104,65 @@ class FetchByTrakingNoYdPortalV2Processor ->where('tracking_no', $reference) ->first(); + // Log::info('tracking: '.$item['tracking']); + // Log::info('remark: '.$item['remark']); + if (!$existingRecord) { $etdEta = $this->parseEtdEta($item['remark'], $item['tracking'], $item['trackingtime']); + $isTrackingTranslatedPreviouly = $this->checkIfTranslationAlreadyExist(!empty($item['tracking']) ? $item['tracking'] : ""); + $isRemarkTranslatedPreviouly = $this->checkIfTranslationAlreadyExist(!empty($item['remark']) ? $item['remark'] : ""); YDOrderTracking::create([ 'order_tracking_id' => $item['Id'], 'tracking_no' => $reference, 'package_id' => $item['package_id'], 'tracking' => $item['tracking'] ?? null, + 'tracking_english' => $isTrackingTranslatedPreviouly ? $isTrackingTranslatedPreviouly : $this->translateTracking($item['tracking']), 'tracking_time' => $item['trackingtime'] ?? null, 'add_time' => $item['addtime'] ?? null, 'remark' => $item['remark'] ?? null, + 'remark_english' => $item['remark'] ? ($isRemarkTranslatedPreviouly ? $isRemarkTranslatedPreviouly : $this->translateTracking($item['remark'])) : null, 'etd' => $etdEta['etd'], 'eta' => $etdEta['eta'] ]); Log::info('FetchByTrakingNoYdPortalV2Processor RECORD CREATED'); } else{ - Log::info('FetchByTrakingNoYdPortalV2Processor RECORD EXIST'); + $isUpdated = false; + if($item['tracking']){ + $isTranslatedPreviouly = $this->checkIfTranslationAlreadyExist(!empty($item['tracking']) ? $item['tracking'] : ""); + $result = $isTranslatedPreviouly; + if (!$isTranslatedPreviouly){ + $result = $this->translateTracking($item['tracking']); + } + if ($result && $existingRecord->tracking_english !== $result) { + $existingRecord->update([ + 'tracking_english' => $result + ]); + $isUpdated = true; + } + } + + if($item['remark']) { + $isTranslatedPreviouly = $this->checkIfTranslationAlreadyExist(!empty($item['remark']) ? $item['remark'] : ""); + $result = $isTranslatedPreviouly; + if (!$isTranslatedPreviouly){ + $result = $this->translateTracking($item['remark']); + } + + if ($result && $existingRecord->remark_english !== $result) { + $existingRecord->update([ + 'remark_english' => $result + ]); + $isUpdated = true; + } + } + + if($isUpdated){ + Log::info('FetchByTrakingNoYdPortalV2Processor RECORD UPDATED'); + } + else{ + Log::info('FetchByTrakingNoYdPortalV2Processor RECORD EXIST'); + } } } } @@ -232,5 +285,44 @@ class FetchByTrakingNoYdPortalV2Processor 'eta' => $eta ? date('Y-m-d', strtotime($eta)) : null, ]; } + + private function checkIfTranslationAlreadyExist($textToTranslate) + { + $returnText = ""; + Log::info('textToTranslate: '.$textToTranslate); + if(!$textToTranslate) return $returnText; + + $translationFound = YDOrderTracking::where('tracking', $textToTranslate)->whereNotNull('tracking_english')->first(); + if(!$translationFound){ + $translationFound = YDOrderTracking::where('remark', $textToTranslate)->whereNotNull('remark_english')->first(); + if($translationFound){ + $returnText = $translationFound->remark_english; + } + } + else{ + $returnText = $translationFound->tracking_english; + } + Log::info('FOUND EXISTING TRANSLATION: '.$returnText); + return $returnText; + } + + private function translateTracking($textToTranslate) + { + $userPrompt = "Please translate mandarin to english and return result in this format, e.g. {'mandarin': '.$textToTranslate.', 'english': ''}. ".$textToTranslate." "; + Log::info('prompt: '.$userPrompt); + $result = $this->createsChatGPTResponse->execute($userPrompt); + Log::info('result (json_encoded): '.json_encode($result)); + $content = $result['choices'][0]['message']['content'] ?? null; + Log::info('content (json_encoded):'. json_encode($content)); + $parsedContent = json_decode(str_replace("'", '"', $content), true); + + if($parsedContent && $parsedContent['english']){ + Log::info('Translated Text: ' . $parsedContent['english']); + return $parsedContent['english']; + } + else{ + return null; + } + } } diff --git a/app/Console/Commands/V2/ProcessYDByTrakingNoDataV2Command.php b/app/Console/Commands/V2/ProcessYDByTrakingNoDataV2Command.php index a4ee8582..b7a12a1c 100644 --- a/app/Console/Commands/V2/ProcessYDByTrakingNoDataV2Command.php +++ b/app/Console/Commands/V2/ProcessYDByTrakingNoDataV2Command.php @@ -3,7 +3,8 @@ namespace App\Console\Commands\V2; -use App\Classes\Jobs\Commands\V2\YD\FetchByTrakingNoYdPortalV2CommandJob; +use App\Classes\Jobs\Commands\V2\YD\FetchTrakingInfoYDByPackingListV2CommandJob; +use App\Classes\Jobs\Commands\V2\YD\FetchTrakingInfoYDByTrackingNoV2CommandJob; use App\Classes\ValueObjects\Constants\PackingListType; use App\Models\PackingList; use Carbon\Carbon; @@ -17,7 +18,7 @@ class ProcessYDByTrakingNoDataV2Command extends Command * * @var string */ - protected $signature = 'process-yd-by-traking-no-data-command'; + protected $signature = 'process-yd-by-traking-no-data-command {trackingNo?}'; /** * The console command description. @@ -33,11 +34,19 @@ class ProcessYDByTrakingNoDataV2Command extends Command */ public function handle() { - $jobs = $this->fetchByTrakingNoYdPortalV2CommandJob(); + $trackingNo = $this->argument('trackingNo'); - foreach ($jobs as $index => $job) { - $delayInSeconds = intdiv($index, 2); - dispatch($job)->delay(now()->addSeconds($delayInSeconds)); + if ($trackingNo) { + dispatch(new FetchTrakingInfoYDByTrackingNoV2CommandJob($trackingNo)); + Log::info("Job dispatched for tracking number: {$trackingNo}"); + } + else { + $jobs = $this->fetchByTrakingNoYdPortalV2CommandJob(); + + foreach ($jobs as $index => $job) { + $delayInSeconds = intdiv($index, 2); + dispatch($job)->delay(now()->addSeconds($delayInSeconds)); + } } } @@ -48,9 +57,13 @@ class ProcessYDByTrakingNoDataV2Command extends Command $packingLists = PackingList::where('type', PackingListType::SHIPPING_PACKING_LIST)->whereDate('created_at', '>=', $maxFetchTime)->get(); $count = 0; foreach ($packingLists as $packingList) { - $jobs[] = new FetchByTrakingNoYdPortalV2CommandJob($packingList); + $jobs[] = new FetchTrakingInfoYDByPackingListV2CommandJob($packingList); Log::info('Dispatched FetchByTrakingNoYdPortalV2CommandJob for packingList with id ' . $packingList->id . ' reference ' . $packingList->reference . ' created_at ' . $packingList->created_at); $count++; + // Log::info('Dispatched count '. $count); + // if ($count === 50) { + // break; + // } } Log::info('Total FetchByTrakingNoYdPortalV2CommandJob dispatched: ' . $count); diff --git a/app/Http/Controllers/Orders/ListOrderTrackingController.php b/app/Http/Controllers/Orders/ListOrderTrackingController.php new file mode 100644 index 00000000..78152965 --- /dev/null +++ b/app/Http/Controllers/Orders/ListOrderTrackingController.php @@ -0,0 +1,22 @@ +execute($request); + } +} diff --git a/app/Http/Resources/OrderTrackingResource.php b/app/Http/Resources/OrderTrackingResource.php new file mode 100644 index 00000000..d68c3b92 --- /dev/null +++ b/app/Http/Resources/OrderTrackingResource.php @@ -0,0 +1,25 @@ + $this->id, + 'tracking_no' => $this->tracking_no, + 'tracking' => $this->tracking_english . ($this->remark_english ? " (" . $this->remark_english . ")" : ''), + 'tracking_time' => $this->tracking_time, + ]; + } +} diff --git a/app/Models/YDOrderTracking.php b/app/Models/YDOrderTracking.php index d265462e..a9f68de6 100644 --- a/app/Models/YDOrderTracking.php +++ b/app/Models/YDOrderTracking.php @@ -16,9 +16,11 @@ class YDOrderTracking extends Model 'tracking_no', 'package_id', 'tracking', + 'tracking_english', 'tracking_time', 'add_time', 'remark', + 'remark_english', 'eta', 'etd', ]; diff --git a/database/migrations/2025_03_19_132042_add_tracking_english_and_remark_english_to_yd_order_tracking_table.php b/database/migrations/2025_03_19_132042_add_tracking_english_and_remark_english_to_yd_order_tracking_table.php new file mode 100644 index 00000000..b728ce1e --- /dev/null +++ b/database/migrations/2025_03_19_132042_add_tracking_english_and_remark_english_to_yd_order_tracking_table.php @@ -0,0 +1,34 @@ +string('tracking_english')->nullable()->after('tracking'); + $table->text('remark_english')->nullable()->after('remark'); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('yd_order_tracking', function (Blueprint $table) { + $table->dropColumn(['tracking_english', 'remark_english']); + }); + } +} diff --git a/resources/assets/vue/components/orders/elements/PackageComponent.vue b/resources/assets/vue/components/orders/elements/PackageComponent.vue index 579ad87e..a502c3b1 100644 --- a/resources/assets/vue/components/orders/elements/PackageComponent.vue +++ b/resources/assets/vue/components/orders/elements/PackageComponent.vue @@ -22,7 +22,11 @@

Reference

-

{{item.reference}}

+

+ + {{ item.reference }} + +

diff --git a/resources/assets/vue/components/paymentsBilling/elements/CustomerPaymentsBillingComponent.vue b/resources/assets/vue/components/paymentsBilling/elements/CustomerPaymentsBillingComponent.vue index 39893e3b..dd3122a5 100644 --- a/resources/assets/vue/components/paymentsBilling/elements/CustomerPaymentsBillingComponent.vue +++ b/resources/assets/vue/components/paymentsBilling/elements/CustomerPaymentsBillingComponent.vue @@ -98,7 +98,11 @@

Packinglist Reference

-
{{ item.packing_list_reference }}
+
+ + {{ item.packing_list_reference }} + +
diff --git a/resources/assets/vue/components/tracking/elements/OrderTrackingComponent.vue b/resources/assets/vue/components/tracking/elements/OrderTrackingComponent.vue new file mode 100644 index 00000000..117cc470 --- /dev/null +++ b/resources/assets/vue/components/tracking/elements/OrderTrackingComponent.vue @@ -0,0 +1,51 @@ + + + diff --git a/resources/assets/vue/components/tracking/sections/TrackingSectionComponent.vue b/resources/assets/vue/components/tracking/sections/TrackingSectionComponent.vue new file mode 100644 index 00000000..b4c7bc8f --- /dev/null +++ b/resources/assets/vue/components/tracking/sections/TrackingSectionComponent.vue @@ -0,0 +1,89 @@ + + + diff --git a/resources/views/pages/tracking/search.blade.php b/resources/views/pages/tracking/search.blade.php new file mode 100644 index 00000000..eaa00f13 --- /dev/null +++ b/resources/views/pages/tracking/search.blade.php @@ -0,0 +1,8 @@ +@extends('layouts.base_portal') +@section('inner_content') +
+
+ +
+
+@endsection diff --git a/routes/order.php b/routes/order.php index 4023400f..af7a4620 100644 --- a/routes/order.php +++ b/routes/order.php @@ -26,4 +26,6 @@ Route::group(['prefix' => 'order', 'as' => 'order.', 'namespace' => 'Orders'], f Route::post('/{id}/supplier-tax-rebate/create', 'CreateSupplierTaxRebateController@create')->name('tax_rebate.create'); Route::post('/supplier-tax-rebate/{id}/delete', 'DeleteSupplierTaxRebateController@delete')->name('tax_rebate.delete'); + Route::get('/tracking/list', 'ListOrderTrackingController@list')->name('tracking.list'); + }); diff --git a/routes/web.php b/routes/web.php index cd7e0787..2225081a 100644 --- a/routes/web.php +++ b/routes/web.php @@ -46,6 +46,7 @@ use Illuminate\Support\Facades\Route; use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf; use Illuminate\Support\Facades\Storage; use App\Classes\General\AWSS3Helper; +use Illuminate\Support\Facades\Artisan; /* |-------------------------------------------------------------------------- @@ -445,6 +446,29 @@ Route::get('/yd', function (Request $request){ // (App()->make(FetchPackingListFromVTPortalProcessor::class))->execute(); })->name('yd.refresh'); + +Route::get('/yd-track', function (Request $request){ + $trackingNo = $request->input('tracking_no'); + + if ($trackingNo) { + Artisan::call('process-yd-by-traking-no-data-command', [ + 'trackingNo' => $trackingNo + ]); + + return response()->json([ + 'message' => 'Tracking process started successfully with tracking number', + 'trackingNo' => $trackingNo + ]); + } else { + Artisan::call('process-yd-by-traking-no-data-command'); + + return response()->json([ + 'message' => 'Tracking process started using packing list' + ]); + } +})->name('yd.track'); + + Route::get('/container/{reference}/refresh/', function (string $reference){ $container = Container::where('reference', $reference)->first(); $supplier = in_array($container->owner_id, [3, 4]) ? 'VT' : 'YD'; @@ -1442,3 +1466,6 @@ Route::get('/error-test', function () { throw new \Exception("Intentional Exception for testing stack trace"); }); +Route::get('/tracking/{tracking_no?}', function ($tracking_no = null) { + return view('pages.tracking.search', ['tracking_no' => $tracking_no]); +})->name('tracking');