mirror of
https://gitlab.com/CIEFWorldwideSdnBhd/shipping-portal.git
synced 2026-08-19 04:24:12 +00:00
Laravel Vapor - YD API integration code rewrite to fix eta/etd
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
+236
@@ -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,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
+190
@@ -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('---');
|
||||
}
|
||||
}
|
||||
|
||||
+169
@@ -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 = [];
|
||||
|
||||
|
||||
@@ -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',
|
||||
];
|
||||
}
|
||||
@@ -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,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');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user