Laravel Vapor - YD API integration code rewrite to fix eta/etd

This commit is contained in:
Dillon Ngo
2024-11-11 12:48:52 +08:00
parent 3e3728a089
commit 26812f723a
13 changed files with 916 additions and 12 deletions
@@ -0,0 +1,31 @@
<?php
namespace App\Classes\Jobs\Commands\V2\YD;
use App\Classes\Modules\PackingLists\Processors\V2\FetchByTrakingNoYdPortalV2Processor;
use App\Models\PackingList;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class FetchByTrakingNoYdPortalV2CommandJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $packingList;
public function __construct(PackingList $packingList)
{
$this->packingList = $packingList;
}
public function handle()
{
Log::info('Processing FetchByTrakingNoYdPortalV2CommandJob');
$processor = app(FetchByTrakingNoYdPortalV2Processor::class);
$processor->execute($this->packingList);
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Classes\Jobs\Commands\V3\YD;
use App\Classes\Modules\PackingLists\Processors\V3\FetchContainersUpdatesYdPortalV3Processor;
use App\Models\Container;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class FetchContainersUpdatesYdPortalV3CommandJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $container;
public function __construct(Container $container)
{
$this->container = $container;
}
public function handle()
{
Log::info('Processing FetchContainersUpdatesYdPortalV3CommandJob');
$processor = app(FetchContainersUpdatesYdPortalV3Processor::class);
$processor->execute($this->container);
}
}
@@ -0,0 +1,32 @@
<?php
namespace App\Classes\Jobs\Commands\V3\YD;
use App\Classes\Modules\PackingLists\Processors\V3\FetchContainersYdPortalV3Processor;
use App\Models\PackingList;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class FetchContainersYdPortalV3CommandJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $packingList;
public function __construct(PackingList $packingList)
{
$this->packingList = $packingList;
}
public function handle()
{
Log::info('Processing FetchContainersYdPortalV3CommandJob');
$processor = app(FetchContainersYdPortalV3Processor::class);
$processor->execute($this->packingList);
}
}
@@ -0,0 +1,49 @@
<?php
namespace App\Classes\Modules\OpenAI\Services;
use Illuminate\Support\Facades\Http;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Exceptions\ConnectionErrorException;
use Illuminate\Support\Facades\Log;
class CreatesChatGPTResponse
{
/**
* @param string $userPrompt
* @return null|object
* @throws MalformedRequestException
*/
public function execute(string $userPrompt) {
try {
$data = [
'model' => 'gpt-4-turbo', //gpt-4-turbo, gpt-4o-mini
'messages' => [
[
'role' => 'user',
'content' => $userPrompt
]
]
];
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . config('openai.api_key')
])->post(config('openai.base_url') . '/v1/chat/completions', $data);
if ($response->successful()) {
$data = $response->json();
return $data;
} else {
Log::info('CreatesChatGPTResponse: ' . $response);
return null;
}
}
catch (\Illuminate\Http\Client\ConnectionException $exception) {
$error = 'Failed to connect to ChatGPT API';
throw new ConnectionErrorException($error, $exception->getMessage(), $userPrompt, $exception->getTraceAsString());
}
catch (\Exception $exception) {
throw new MalformedRequestException('Unable to get correct response from ChatGPT API: ' . $exception->getMessage());
}
}
}
@@ -0,0 +1,236 @@
<?php
namespace App\Classes\Modules\PackingLists\Processors\V2;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Modules\Orders\Services\FetchesDataFromYDPortal;
use App\Classes\Modules\OpenAI\Services\CreatesChatGPTResponse;
use App\Models\PackingList;
use App\Models\YDOrderTracking;
use Carbon\Carbon;
use DateTime;
use Illuminate\Support\Facades\Log;
class FetchByTrakingNoYdPortalV2Processor
{
/** @var FetchesDataFromYDPortal */
private $fetchesDataFRomYDPortal;
/** @var CreatesChatGPTResponse */
private $createsChatGPTResponse;
/**
* @param FetchesDataFromYDPortal $fetchesDataFRomYDPortal
* @param CreatesChatGPTResponse $createsChatGPTResponse
*/
public function __construct(FetchesDataFromYDPortal $fetchesDataFRomYDPortal, CreatesChatGPTResponse $createsChatGPTResponse)
{
$this->fetchesDataFRomYDPortal = $fetchesDataFRomYDPortal;
$this->createsChatGPTResponse = $createsChatGPTResponse;
}
/**
* @param PackingList $packingList
* @return void
* @throws MalformedRequestException
*/
public function execute(PackingList $packingList)
{
Log::info('FetchByTrakingNoYdPortalV2Processor starts with packingList id ' . $packingList->id);
// return;
$time_start = microtime(true);
$reference = $packingList->reference;
Log::info('reference ' . $reference);
$oneHourAgo = Carbon::now()->subHour();
$check1 = YDOrderTracking::where('tracking_no', $reference)
->orderBy('updated_at', 'desc')
->first();
if ($check1 && $check1->updated_at >= $oneHourAgo) {
Log::info('FetchByTrakingNoYdPortalV2Processor SKIPPED: Record updated within the last hour');
Log::info('---');
return;
}
$check2 = YDOrderTracking::where('tracking_no', $reference)
->where('tracking', '第三方提货')
->first();
if ($check2) {
Log::info('FetchByTrakingNoYdPortalV2Processor SKIPPED: 第三方提货');
Log::info('---');
return;
}
$trackingRequest = $this->fetchesDataFRomYDPortal->clientRequest('http://www.yd-wl.com/api/ApiTracking.ashx', 'GET', [
'trakingno' => $reference
]);
$rows = $this->fetchesDataFRomYDPortal->getResponseBody($trackingRequest);
$this->processData(json_encode($rows), $reference);
$time_end = microtime(true);
$execution_time = ($time_end - $time_start)/60;
Log::info('FetchByTrakingNoYdPortalV2Processor ends with packingList id ' . $packingList->id . ' => Total Execution Time: '.$execution_time.' Mins.');
Log::info('---');
}
public function processData($jsonData, $reference)
{
$data = json_decode($jsonData, true);
if ($data['res'] == 1 && isset($data['data'])) {
Log::info('package_id: '.$data['data'][0]['package_id']);
foreach (array_reverse($data['data']) as $item) {
$existingRecord = YDOrderTracking::where('order_tracking_id', $item['Id'])
->where('tracking_no', $reference)
->first();
if (!$existingRecord) {
$etdEta = $this->parseEtdEta($item['remark'], $item['tracking'], $item['trackingtime']);
YDOrderTracking::create([
'order_tracking_id' => $item['Id'],
'tracking_no' => $reference,
'package_id' => $item['package_id'],
'tracking' => $item['tracking'] ?? null,
'tracking_time' => $item['trackingtime'] ?? null,
'add_time' => $item['addtime'] ?? null,
'remark' => $item['remark'] ?? null,
'etd' => $etdEta['etd'],
'eta' => $etdEta['eta']
]);
Log::info('FetchByTrakingNoYdPortalV2Processor RECORD CREATED');
}
else{
Log::info('FetchByTrakingNoYdPortalV2Processor RECORD EXIST');
}
}
}
}
public function parseEtdEta($remark, $tracking, $trackingtime)
{
$etd = null;
$eta = null;
$etaString = '';
$etdString = '';
if($remark && $tracking != '第三方提货'){
if (preg_match('/(?:预计|延)(\d+\.\d+)(?:号)?开/', $remark, $matches)) {
$etdString = $matches[1];
}
if (preg_match('/(?:预计)?(\d+\.\d+)(?:号)?到港/', $remark, $matches)) {
$etaString = $matches[1];
}
Log::info('remark: '.$remark);
$currentYear = Carbon::now()->year;
$trackingDate = Carbon::parse($trackingtime);
$trackingYear = $trackingDate->year;
if($remark && preg_match('/\b\d{1,2}[-\/.]\d{1,2}\b/', $remark) && $eta === null && $etd === null){
$existingRecord = YDOrderTracking::where('remark', $remark)->where(function ($query) {
$query->whereNotNull('eta')
->orWhereNotNull('etd');
})
->first();
if($existingRecord){
Log::info('FetchByTrakingNoYdPortalV2Processor SAVER');
return [
'etd' => $existingRecord->etd,
'eta' => $existingRecord->eta,
];
}
$userPrompt = "Show me the answer in json string without escape, e.g. {'ETA': '', 'ETD': ''}. ".$remark." What is the ETA and ETD?";
Log::info('prompt: '.$userPrompt);
$result = $this->createsChatGPTResponse->execute($userPrompt);
Log::info(json_encode($result));
$content = $result['choices'][0]['message']['content'] ?? null;
Log::info('content:'. json_encode($content));
if ($content) {
$parsedContent = json_decode(str_replace("'", '"', $content), true);
if (is_array($parsedContent) && isset($parsedContent['ETA'], $parsedContent['ETD'])) {
Log::info('parsedContent ETA:'. $parsedContent['ETA']);
Log::info('parsedContent ETD:'. $parsedContent['ETD']);
$etaString = $parsedContent['ETA'];
$etdString = $parsedContent['ETD'];
$dateParts = explode('-', $etaString);
if (count($dateParts) === 3) {
$etaString = $dateParts[1] . '-' . $dateParts[2];
}
$dateParts = explode('-', $etdString);
if (count($dateParts) === 3) {
$etdString = $dateParts[1] . '-' . $dateParts[2];
}
}
}
}
if (!empty($etaString) && preg_match('/\b\d{1,2}[-\/.]\d{1,2}\b/', $etaString)) {
$etaString = str_replace(['.', '-', '/'], '/', $etaString);
$etaParts = explode('/', $etaString);
$etaMonth = intval($etaParts[0]);
$etaDay = intval($etaParts[1]);
$etaDate = DateTime::createFromFormat("Y/m/d", "$currentYear/$etaString");
if($trackingYear < $currentYear){
$etaDate = DateTime::createFromFormat("Y/m/d", ($trackingYear) . "/$etaString");
}
else{
$etaYear = $currentYear;
if ($etaMonth <= 2 && $trackingDate->month == 12) {
$etaYear = $trackingDate->year + 1;
}
$etaDate = Carbon::createFromDate($etaYear, $etaMonth, $etaDay);
}
if($etaDate){
$eta = $etaDate->format('Y-m-d');
}
Log::info('ETA: '.$eta);
Log::info('ETD: '.$etd);
}
if (!empty($etdString) && preg_match('/\b\d{1,2}[-\/.]\d{1,2}\b/', $etdString)) {
$etdString = str_replace(['.', '-', '/'], '/', $etdString);
$etdParts = explode('/', $etdString);
$etdMonth = intval($etdParts[0]);
$etdDay = intval($etdParts[1]);
$etdDate = DateTime::createFromFormat("Y/m/d", "$currentYear/$etdString");
if($trackingYear < $currentYear){
$etdDate = DateTime::createFromFormat("Y/m/d", ($trackingYear) . "/$etdString");
}
else{
$etdYear = $currentYear;
if ($etdMonth <= 1 && $trackingDate->month == 12) {
$etdYear = $trackingDate->year + 1;
}
$etdDate = Carbon::createFromDate($etdYear, $etdMonth, $etdDay);
}
if($etdDate){
$etd = $etdDate->format('Y-m-d');
}
Log::info('ETD: '.$etd);
Log::info('ETA: '.$eta);
}
}
return [
'etd' => $etd ? date('Y-m-d', strtotime($etd)) : null,
'eta' => $eta ? date('Y-m-d', strtotime($eta)) : null,
];
}
}
@@ -0,0 +1,190 @@
<?php
namespace App\Classes\Modules\PackingLists\Processors\V3;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Modules\Schedules\DataTransferObjects\ScheduleObject;
use App\Classes\Modules\Schedules\Services\CreatesSchedule;
use App\Classes\Modules\Unity\Services\UpdatesContractObligation;
use App\Classes\Modules\PerfexCRM\Processors\PackingListToPerfexCRMProcessor;
use App\Classes\Notifications\ShipmentRescheduleEmail;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\OrderRoleTypes;
use App\Classes\ValueObjects\Constants\PerfexCRMTasksYDStages;
use App\Models\Container;
use App\Models\Order;
use App\Models\PackingList;
use App\Models\YDOrderTracking;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
class FetchContainersUpdatesYdPortalV3Processor
{
/** @var UpdatesContractObligation */
private $updatesContractObligations;
/** @var CreatesSchedule */
private $createsSchedule;
/** @var PackingListToPerfexCRMProcessor */
private $packingListToPerfexCRMProcessor;
/**
* @param UpdatesContractObligation $updatesContractObligations
* @param CreatesSchedule $createsSchedule
* @param PackingListToPerfexCRMProcessor $packingListToPerfexCRMProcessor
*/
public function __construct(UpdatesContractObligation $updatesContractObligations, CreatesSchedule $createsSchedule, PackingListToPerfexCRMProcessor $packingListToPerfexCRMProcessor)
{
$this->updatesContractObligations = $updatesContractObligations;
$this->createsSchedule = $createsSchedule;
$this->packingListToPerfexCRMProcessor = $packingListToPerfexCRMProcessor;
}
/**
* @param Container $container
* @return void
* @throws MalformedRequestException
*/
public function execute(Container $container)
{
Log::info('FetchContainersUpdatesFromYdPortalV2Processor starts with container id ' . $container->id . ' created_at ' . $container->created_at);
// return;
$time_start = microtime(true);
$packingList = $container->packingLists()->first();
$reference = $packingList->reference;
Log::info('FetchContainersUpdatesFromYdPortalV2Processor starts with packingList id ' . $packingList->id . ' created_at ' . $packingList->created_at);
Log::info('FetchContainersUpdatesFromYdPortalV2Processor starts with reference ' . $reference);
if (!$container->created_at->greaterThanOrEqualTo(Carbon::now()->subYear())) {
return;
}
$trackingRows = YDOrderTracking::where('tracking_no', $reference)->get();
Log::info('YDContainerUpdates trackingRows ' . (count($trackingRows) > 0 ? json_encode($trackingRows) : 'NOT FOUND'));
Log::info('YDContainerUpdates order ' . json_encode($packingList->owner));
$unstuffingDate = null;
$delayDate = null;
$checkShipSailDate = null;
$checkShipArrivalDate = null;
foreach ($trackingRows as $trackingRow) {
if($trackingRow->eta){
if(!$delayDate){
$delayDate = Carbon::parse($trackingRow->eta);
}
if(Carbon::parse($trackingRow->eta) > $delayDate){
$delayDate = Carbon::parse($trackingRow->eta);
}
}
}
foreach ($trackingRows as $trackingRow) {
if ($trackingRow->tracking === '到港') {
Log::info('YDContainerUpdates - 到港 delayDate ' . $delayDate);
Log::info('YDContainerUpdates - 到港 $trackingRow->tracking_time ' . $trackingRow->tracking_time);
$delayDate = Carbon::parse($trackingRow->tracking_time);
$checkShipArrivalDate = Carbon::parse($trackingRow->tracking_time);
}
if ($trackingRow->tracking === '已开船') {
Log::info('YDContainerUpdates - 已开船 delayDate ' . $delayDate);
if(!$delayDate){
Log::info('YDContainerUpdates - 已开船 $trackingRow->tracking_time add 5 days ' . $trackingRow->tracking_time);
$delayDate = Carbon::parse($trackingRow->tracking_time)->addDays('5');
}
$checkShipSailDate = Carbon::parse($trackingRow->tracking_time);
}
if ($trackingRow->tracking === '货物已进目的港仓库') {
Log::info('YDContainerUpdates - 货物已进目的港仓库 delayDate ' . $delayDate);
$unstuffingDate = Carbon::parse($trackingRow->tracking_time);
}
}
if($checkShipSailDate){
//check if ship has sailed
if($checkShipSailDate->isPast()){
foreach ($container->packingLists as $packingList){
if(config('perfexcrm.is_enabled') == 'true'){
$this->packingListToPerfexCRMProcessor->execute($packingList, PerfexCRMTasksYDStages::SHIP_SAILED);
}
}
}
}
if($checkShipArrivalDate){
//check if ship has arrived at destination
if($checkShipArrivalDate->isPast()){
foreach ($container->packingLists as $packingList){
if(config('perfexcrm.is_enabled') == 'true'){
$this->packingListToPerfexCRMProcessor->execute($packingList, PerfexCRMTasksYDStages::SHIP_ARRIVED);
}
}
}
}
if($delayDate){
Log::info('YDContainerUpdates delayDate ' . $delayDate);
$transport = $container->transports()->first();
Log::info('YDContainerUpdates transport ' . json_encode($transport));
if(!$transport->schedules()->whereDate('eta', '>=', $delayDate)->first()) { //If no record
$etd = $transport->schedules()->where('status', '=', ApprovalStatus::APPROVED)->first()->etd;
Log::info('YDContainerUpdates etd ' . json_encode($etd));
$transport->schedules()->update(['status' => ApprovalStatus::EXPIRED]);
$this->createsSchedule->execute($transport, new ScheduleObject($etd, $delayDate, ApprovalStatus::APPROVED));
foreach ($container->packingLists as $packingList){
if(!($packingList->owner instanceof Order)) continue;
$user = $packingList->owner->companyModule->employees()->first();
if(app()->environment(['production'])) {
$user->notify(new ShipmentRescheduleEmail($user, $packingList));
}
}
}
else{
Log::info('YDContainerUpdates NO DELAY');
}
}
if($unstuffingDate){
$container->update(['status' => ApprovalStatus::COMPLETED]);
$container->transports()->first()->update(['drop_date' => $unstuffingDate, 'status' => ApprovalStatus::COMPLETED]);
/** @var PackingList $packingList */
foreach($container->packingLists as $packingList){
if($packingList->status === ApprovalStatus::PENDING_VERIFICATION){
$packingList->status = ApprovalStatus::APPROVED;
$packingList->save();
}
if(!($packingList->owner instanceof Order)) continue;
$signature = $packingList->owner->orderRoles()->where('role_id', '=', OrderRoleTypes::SUPERVISOR)->first()->appointee->entity_sigiture;
foreach($packingList->steps()->where('reference', '!=', 'DELIVERY')->get() as $step){
if(app()->environment(['production'])) {
$this->updatesContractObligations->execute($signature, $step->obligation_hash_id);
}
$step->update(['status' => ApprovalStatus::COMPLETED]);
}
if(config('perfexcrm.is_enabled') == 'true'){
$this->packingListToPerfexCRMProcessor->execute($packingList, PerfexCRMTasksYDStages::UNSTUFFING);
}
}
}
$time_end = microtime(true);
$execution_time = ($time_end - $time_start)/60;
Log::info('FetchContainersUpdatesFromYdPortalV2Processor ends with container id ' . $container->id . ' => Total Execution Time: '.$execution_time.' Mins.');
Log::info('---');
}
}
@@ -0,0 +1,169 @@
<?php
namespace App\Classes\Modules\PackingLists\Processors\V3;
use App\Classes\Exceptions\AccessForbiddenException;
use App\Classes\Exceptions\MalformedRequestException;
use App\Classes\Exceptions\RequestValidationException;
use App\Classes\Exceptions\ResourceNotFoundException;
use App\Classes\Modules\PackingLists\Processors\CreateContainerProcessor;
use App\Classes\Modules\PackingLists\DataTransferObjects\ContainerObject;
use App\Classes\Modules\PackingLists\Services\Containers\FetchesContainer;
use App\Classes\Modules\Schedules\DataTransferObjects\ScheduleObject;
use App\Classes\Modules\Schedules\Services\CreatesSchedule;
use App\Classes\Modules\Transports\DataTransferObjects\TransportObject;
use App\Classes\Modules\Transports\Services\CreatesTransport;
use App\Classes\Modules\PerfexCRM\Processors\PackingListToPerfexCRMProcessor;
use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\ContainerTypes;
use App\Classes\ValueObjects\Constants\OrderRoleTypes;
use App\Classes\ValueObjects\Constants\TransportType;
use App\Classes\ValueObjects\Constants\PerfexCRMTasksYDStages;
use App\Models\Container;
use App\Models\Order;
use App\Models\PackingList;
use App\Models\Transport;
use App\Models\YDOrderTracking;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
class FetchContainersYdPortalV3Processor
{
/** @var CreatesTransport */
private $createsTransport;
/** @var CreatesSchedule */
private $createsSchedule;
/** @var FetchesContainer */
private $fetchesContainer;
/** @var CreateContainerProcessor */
private $createContainerProcessor;
/** @var PackingListToPerfexCRMProcessor */
private $packingListToPerfexCRMProcessor;
/**
* @param CreatesTransport $createsTransport
* @param CreatesSchedule $createsSchedule
* @param FetchesContainer $fetchesContainer
* @param CreateContainerProcessor $createContainerProcessor
* @param PackingListToPerfexCRMProcessor $packingListToPerfexCRMProcessor
*/
public function __construct(CreatesTransport $createsTransport, CreatesSchedule $createsSchedule, FetchesContainer $fetchesContainer, CreateContainerProcessor $createContainerProcessor, PackingListToPerfexCRMProcessor $packingListToPerfexCRMProcessor)
{
$this->createsTransport = $createsTransport;
$this->createsSchedule = $createsSchedule;
$this->fetchesContainer = $fetchesContainer;
$this->createContainerProcessor = $createContainerProcessor;
$this->packingListToPerfexCRMProcessor = $packingListToPerfexCRMProcessor;
}
/**
* @param PackingList $packingList
* @return void
* @throws MalformedRequestException
* @throws AccessForbiddenException
* @throws RequestValidationException
*/
public function execute(PackingList $packingList)
{
Log::info('FetchContainersFromYdPortalV2Processor starts with packingList id '. $packingList->id);
// return;
$time_start = microtime(true);
$containerReference = null;
$loadingDate = null;
$etd = null;
$eta = null;
$reference = $packingList->reference;
Log::info('YDContainer reference ' . $reference);
$trackingRows = YDOrderTracking::where('tracking_no', $reference)->get();
Log::info('YDContainer trackingRows ' . json_encode($trackingRows));
foreach ($trackingRows as $trackingRow) {
if (strpos($trackingRow->tracking, '货物装柜完成。') !== false) {
$tracking = explode(':', $trackingRow->tracking);
$containerReference = explode('预计到港时间', $tracking[1])[0];
$loadingDate = Carbon::parse($trackingRow->tracking_time);
$etd = Carbon::parse($tracking[2])->subDays(5);
$eta = Carbon::parse($tracking[2]);
}
}
foreach ($trackingRows as $trackingRow) {
if($trackingRow->etd){
Log::info('YDContainer etd DB ' . $trackingRow->etd);
Log::info('YDContainer etd DB Carbon ' . Carbon::parse($trackingRow->etd));
Log::info('YDContainer etd existing ' . $etd);
if(Carbon::parse($trackingRow->etd) > $etd){
$etd = Carbon::parse($trackingRow->etd);
}
}
if($trackingRow->eta){
Log::info('YDContainer eta DB ' . $trackingRow->eta);
Log::info('YDContainer eta DB Carbon ' . Carbon::parse($trackingRow->eta));
Log::info('YDContainer eta existing ' . $eta);
if(Carbon::parse($trackingRow->eta) > $eta){
$eta = Carbon::parse($trackingRow->eta);
}
}
}
Log::info('YDContainer containerReference ' . json_encode($containerReference));
if($containerReference) {
try {
Log::info('YDContainer try');
$container = $this->fetchesContainer->execute(['reference' => $containerReference]);
$container->packingLists()->detach($packingList);
$container->packingLists()->attach($packingList);
} catch (ResourceNotFoundException $exception){
Log::info('YDContainer catch');
if(!($packingList->owner instanceof Order)) return;
$originWarehouse = $packingList->owner->orderRoles()->where('role_id', '=', OrderRoleTypes::ORIGIN_WAREHOUSE)->first()->appointee;
$containerObject = new ContainerObject($containerReference, '', '', ContainerTypes::FORTY_FEET_DRY_CONTAINER, $loadingDate, ApprovalStatus::PENDING_VERIFICATION);
/** @var Container $container */
$container = $this->createContainerProcessor->execute($containerObject, $originWarehouse);
$container->packingLists()->detach($packingList);
$container->packingLists()->attach($packingList);
if(config('perfexcrm.is_enabled') == 'true'){
$this->packingListToPerfexCRMProcessor->execute($packingList, PerfexCRMTasksYDStages::LOAD_CONTAINER);
}
$transport = $container->transports()->first();
if(!$transport){
$transportObject = new TransportObject(TransportType::SEA, null, null, $etd, null, ApprovalStatus::APPROVED);
/** @var Transport $transport */
$transport = $this->createsTransport->execute($transportObject, $container);
$this->createsSchedule->execute($transport, new ScheduleObject($etd, $eta, ApprovalStatus::APPROVED));
}
}
Log::info('YDContainer containerReference ' . json_encode($containerReference));
Log::info('YDContainer loadingDate ' . $loadingDate);
Log::info('YDContainer etd ' . $etd);
Log::info('YDContainer eta ' . $eta);
}
$time_end = microtime(true);
$execution_time = ($time_end - $time_start)/60;
Log::info('FetchContainersFromYdPortalV2Processor ends with packingList id '. $packingList->id . ' => Total Execution Time: '.$execution_time.' Mins.');
Log::info('---');
}
}
@@ -0,0 +1,58 @@
<?php
namespace App\Console\Commands\V2;
use App\Classes\Jobs\Commands\V2\YD\FetchByTrakingNoYdPortalV2CommandJob;
use App\Classes\ValueObjects\Constants\PackingListType;
use App\Models\PackingList;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
class ProcessYDByTrakingNoDataV2Command extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'process-yd-by-traking-no-data-command';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Process data from YD Portal by traking no';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$jobs2 = $this->fetchByTrakingNoYdPortalV2CommandJob();
foreach ($jobs2 as $index => $job) {
dispatch($job)->delay(now()->addSeconds($index * 3));
}
}
private function fetchByTrakingNoYdPortalV2CommandJob(){
$jobs = [];
$maxFetchTime = Carbon::now()->subMonths(4);
$packingLists = PackingList::where('type', PackingListType::SHIPPING_PACKING_LIST)->whereDate('created_at', '>=', $maxFetchTime)->get();
$count = 0;
foreach ($packingLists as $packingList) {
$jobs[] = new FetchByTrakingNoYdPortalV2CommandJob($packingList);
Log::info('Dispatched FetchByTrakingNoYdPortalV2CommandJob for packingList with id ' . $packingList->id . ' reference ' . $packingList->reference . ' created_at ' . $packingList->created_at);
$count++;
}
Log::info('Total FetchByTrakingNoYdPortalV2CommandJob dispatched: ' . $count);
return $jobs;
}
}
@@ -7,6 +7,8 @@ use App\Classes\Jobs\Commands\V2\YD\FetchContainersFromYdPortalV2CommandJob;
use App\Classes\Jobs\Commands\V2\YD\FetchContainersUpdatesFromYdPortalV2CommandJob;
use App\Classes\Jobs\Commands\V2\YD\FetchDeliveryUpdatesFromYdPortalV2CommandJob;
use App\Classes\Jobs\Commands\V2\YD\FetchOrderListsFromYdPortalV2CommandJob;
use App\Classes\Jobs\Commands\V3\YD\FetchContainersYdPortalV3CommandJob;
use App\Classes\Jobs\Commands\V3\YD\FetchContainersUpdatesYdPortalV3CommandJob;
use App\Models\PackingList;
use App\Models\Container;
@@ -42,10 +44,10 @@ class ProcessYDPortalDataV2Command extends Command
public function handle()
{
$jobs = [];
$jobs = array_merge($jobs, $this->fetchPackingListsFromYdPortalV2CommandJobs());
$jobs = array_merge($jobs, $this->fetchContainersFromYdPortalV2CommandJobs());
$jobs2 = $this->fetchContainersUpdatesFromYdPortalV2CommandJob();
$jobs3 = $this->fetchDeliveryUpdatesFromYdPortalV2CommandJobs();
// $jobs = array_merge($jobs, $this->fetchPackingListsFromYdPortalV2CommandJobs()); //cief todo
$jobs = array_merge($jobs, $this->fetchContainersYdPortalV3CommandJobs());
$jobs2 = $this->fetchContainersUpdatesYdPortalV3CommandJob();
// $jobs3 = $this->fetchDeliveryUpdatesFromYdPortalV2CommandJobs(); //cief todo
// The following job is intentionally excluded
////$jobs = array_merge($jobs, $this->fetchOrderListsFromYdPortalV2CommandJob());
@@ -53,18 +55,20 @@ class ProcessYDPortalDataV2Command extends Command
// Bus::chain($jobs)->dispatch();
foreach ($jobs as $index => $job) {
dispatch($job)->delay(now()->addSeconds($index * 2));
// dispatch($job)->delay(now()->addSeconds($index * 2));
dispatch($job);
}
foreach ($jobs2 as $index => $job) {
dispatch($job)->delay(now()->addSeconds($index * 1));
}
foreach ($jobs3 as $index => $job) {
$delayInSeconds = intdiv($index, 2);
dispatch($job)->delay(now()->addSeconds($delayInSeconds));
// dispatch($job)->delay(now()->addSeconds($index * 1));
dispatch($job);
}
//cief todo
// foreach ($jobs3 as $index => $job) {
// $delayInSeconds = intdiv($index, 2);
// dispatch($job)->delay(now()->addSeconds($delayInSeconds));
// }
}
private function fetchPackingListsFromYdPortalV2CommandJobs(){
@@ -138,6 +142,37 @@ class ProcessYDPortalDataV2Command extends Command
return $jobs;
}
private function fetchContainersYdPortalV3CommandJobs(){
$jobs = [];
$maxFetchTime = Carbon::now()->subMonths(4);
$packingLists = PackingList::where('type', PackingListType::SHIPPING_PACKING_LIST)->whereDate('created_at', '>=', $maxFetchTime)->doesntHave('containers')->get();
$count = 0;
foreach ($packingLists as $packingList) {
$jobs[] = new FetchContainersYdPortalV3CommandJob($packingList);
Log::info('Dispatched FetchContainersYdPortalV3CommandJob for packingList with id ' . $packingList->id . ' created_at ' . $packingList->created_at);
$count++;
}
Log::info('Total fetchContainersYdPortalV3CommandJobs dispatched: ' . $count);
return $jobs;
}
private function fetchContainersUpdatesYdPortalV3CommandJob(){
$jobs = [];
$containers = Container::where('status', ApprovalStatus::PENDING_VERIFICATION)->get();
$count = 0;
foreach ($containers as $container) {
$jobs[] = new FetchContainersUpdatesYdPortalV3CommandJob($container);
Log::info('Dispatched fetchContainersUpdatesYdPortalV3CommandJob for container with id ' . $container->id);
$count++;
}
Log::info('Total fetchContainersUpdatesYdPortalV3CommandJob dispatched: ' . $count);
return $jobs;
}
private function fetchDeliveryUpdatesFromYdPortalV2CommandJobs(){
$jobs = [];
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class YDOrderTracking extends Model
{
use HasFactory;
protected $table = 'yd_order_tracking';
protected $fillable = [
'order_tracking_id',
'tracking_no',
'package_id',
'tracking',
'tracking_time',
'add_time',
'remark',
'eta',
'etd',
];
}
+7
View File
@@ -0,0 +1,7 @@
<?php
return [
'base_url' => env('OPENAI_BASE_URL', 'https://api.openai.com'),
'api_key' => env('OPENAI_API_KEY', ''),
'is_enabled' => env('OPENAI_IS_ENABLED', 'true'),
];
+1 -1
View File
@@ -1,7 +1,7 @@
<?php
return [
'base_url' => env('PERFEXCRM_BASE_URL', 'http://192.168.1.100:8084'), //cief todo: Update crm api domain here
'base_url' => env('PERFEXCRM_BASE_URL', 'http://192.168.1.100:8084'),
'api_key' => env('PERFEXCRM_API_KEY', ''),
'is_enabled' => env('PERFEXCRM_IS_ENABLED', 'true'),
];
@@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateYdOrderTrackingTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('yd_order_tracking', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('order_tracking_id');
$table->string('tracking_no');
$table->bigInteger('package_id')->index();
$table->string('tracking')->nullable();
$table->dateTime('tracking_time');
$table->dateTime('add_time');
$table->text('remark')->nullable();
$table->date('eta')->nullable();
$table->date('etd')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('yd_order_tracking');
}
}