From 1037c1c202135f3d791ccb529fadb98d5ec93944 Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Thu, 17 Sep 2026 10:45:19 +0100 Subject: [PATCH] M3 Security Fixes for pass 28 --- Modules/M3/Console/CallCloseAPI.php | 161 +++----- .../M3/Http/Controllers/API/TwoShipAPIs.php | 354 +++++------------- .../M3/Http/Controllers/userPickingStats5.php | 217 ++++++----- 3 files changed, 248 insertions(+), 484 deletions(-) diff --git a/Modules/M3/Console/CallCloseAPI.php b/Modules/M3/Console/CallCloseAPI.php index ff48f079..fa59acd4 100644 --- a/Modules/M3/Console/CallCloseAPI.php +++ b/Modules/M3/Console/CallCloseAPI.php @@ -12,129 +12,62 @@ use Str; class CallCloseAPI extends Command { - /** - * The name and signature of the console command. - * - * @var string - */ protected $signature = 'command:CallCloseAPI {user?} {manifest?} {co?} {whs?} '; - protected $description = 'Call Close API'; - public function __construct() - { - parent::__construct(); - } - public function handle() { - // $this->info('Start CallCloseAPI...'); - ini_set('max_execution_time', Config::get('arrays.PHPTimeOut')); - ini_set('max_input_time', Config::get('arrays.PHPTimeOut')); - ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOut')); - + self::configureTimeouts(); try { - // Get the Calling User - $userId = $this->argument('user'); - $SHUSER = isset($userId) ? $userId : (m3_user() !== '' ? Str::upper(trim(m3_user())) : ''); - - // Get the Manifest Number - $manifest = $this->argument('manifest'); - $MANIFESTNum = isset($manifest) ? $manifest : ''; - $co = $this->argument('co'); - $APCO = isset($co) ? $co : ''; - $whs = $this->argument('whs'); - $APWHS = isset($whs) ? $whs : ''; - - // if ($SHUSER!='') { - // Log::channel('dailyCron')->info("Cron...Start CallCloseAPI...For Manifest#: ".$MANIFESTNum); // ".$SHUSER." - - if ($MANIFESTNum == '') { - // Get Macpickers from ICM705M1 - $ICM705M1 = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1') - ->select('APMANIFEST', 'APCO', 'APWHS') - ->where('APSPARE2', '') - ->where('APMANIFEST', '!=', '0') - ->whereRaw('EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - APLDCLOZ)) / 60 > 15') - ->orderBy('APMANIFEST', 'desc') - ->get(); - // dump($ICM705M1); - - if (count($ICM705M1) > 0) { - // Loop the Results from $ICM705M1 - foreach ($ICM705M1 as $key => $ICM705M1R) { - $MANIFESTNum = $ICM705M1R->APMANIFEST; - - $USERCO = isset($ICM705M1R->APCO) ? $ICM705M1R->APCO : ''; - $USERWH = isset($ICM705M1R->APWHS) ? $ICM705M1R->APWHS : ''; - - // Update ICM705M1 and set APSPARE2 to P - $ICM705M1U = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1') - ->where('APMANIFEST', $MANIFESTNum) - ->where('APCO', $USERCO) - ->where('APWHS', $USERWH) - ->update(['APSPARE2' => 'P']); - - $myRequest = new Request; - $myRequest->setMethod('POST'); - $myRequest->request->add([ - 'WHS' => $USERWH, - 'COMPANY' => $USERCO, - 'TRANSTYPE' => 'M', - 'MANIFEST' => $MANIFESTNum, - ]); - $callCloseShipment = CloseShipment::CloseShipmentCoChargeAPI($myRequest); - $responseData = $callCloseShipment->getData(); - $jsonData = json_encode($responseData); - Log::channel('dailyCron')->info('Close Shipment API Response for Manifest:' . (string) (int) $MANIFESTNum . '-' . $jsonData); - } - } + $manifest = (string) ($this->argument('manifest') ?? ''); + if ($manifest !== '') { + return $this->closeSpecificManifest($manifest, (string) ($this->argument('co') ?? ''), (string) ($this->argument('whs') ?? '')); } - // Call Closing for a specific Manifest - else { - // Get Macpickers from ICM705M1 - $ICM705M1 = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1') - ->select('APMANIFEST', 'APCO', 'APWHS') - ->where('APSPARE2', '') - ->where('APCO', $APCO) - ->where('APWHS', $APWHS) - ->where('APMANIFEST', '=', $MANIFESTNum) - ->first(); - - // Update ICM705M1 and set APSPARE2 to P - $ICM705M1U = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1') - ->where('APMANIFEST', $MANIFESTNum) - ->where('APCO', $APCO) - ->where('APWHS', $APWHS) - ->update(['APSPARE2' => 'P']); - - $myRequest = new Request; - $myRequest->setMethod('POST'); - - $myRequest->request->add([ - 'WHS' => $APWHS, - 'COMPANY' => $APCO, - 'TRANSTYPE' => 'M', - 'MANIFEST' => $MANIFESTNum, - ]); - - $callCloseShipment = CloseShipment::CloseShipmentCoChargeAPI($myRequest); - $responseData = $callCloseShipment->getData(); - $jsonData = json_encode($responseData); - Log::channel('dailyCron')->info('Close Shipment API Response for Manifest:' . $MANIFESTNum . '-' . $jsonData); - - return 1; - } - // } - // else{ - // Log::channel('dailyCron')->error('A user must be defined...'); - - // $this->error('A user must be defined...'); - // } - + $this->closePendingManifests(); + return self::SUCCESS; } catch (\Throwable $th) { - // Log::error($th); Log::channel('dailyCron')->error($th); + return self::FAILURE; } } + + private static function configureTimeouts(): void + { + $timeout = Config::get('arrays.PHPTimeOut'); + ini_set('max_execution_time', $timeout); + ini_set('max_input_time', $timeout); + ini_set('default_socket_timeout', $timeout); + } + + private function closePendingManifests(): void + { + $rows = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1') + ->select('APMANIFEST', 'APCO', 'APWHS')->where('APSPARE2', '')->where('APMANIFEST', '!=', '0') + ->whereRaw('EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - APLDCLOZ)) / 60 > 15')->orderBy('APMANIFEST', 'desc')->get(); + foreach ($rows as $row) { + $this->closeManifest((string) $row->APMANIFEST, (string) ($row->APCO ?? ''), (string) ($row->APWHS ?? '')); + } + } + + private function closeSpecificManifest(string $manifest, string $co, string $whs): int + { + $exists = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1') + ->where('APSPARE2', '')->where('APCO', $co)->where('APWHS', $whs)->where('APMANIFEST', $manifest)->exists(); + if (! $exists) { + Log::channel('dailyCron')->warning('Manifest not available for close', compact('manifest', 'co', 'whs')); + return self::FAILURE; + } + $this->closeManifest($manifest, $co, $whs); + return self::SUCCESS; + } + + private function closeManifest(string $manifest, string $co, string $whs): void + { + DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1') + ->where('APMANIFEST', $manifest)->where('APCO', $co)->where('APWHS', $whs)->update(['APSPARE2' => 'P']); + $request = Request::create('', 'POST', ['WHS' => $whs, 'COMPANY' => $co, 'TRANSTYPE' => 'M', 'MANIFEST' => $manifest]); + $response = CloseShipment::CloseShipmentCoChargeAPI($request); + $json = json_encode($response->getData()); + Log::channel('dailyCron')->info('Close Shipment API Response for Manifest:' . $manifest . '-' . $json); + } } diff --git a/Modules/M3/Http/Controllers/API/TwoShipAPIs.php b/Modules/M3/Http/Controllers/API/TwoShipAPIs.php index e5fa21d3..940ef37f 100644 --- a/Modules/M3/Http/Controllers/API/TwoShipAPIs.php +++ b/Modules/M3/Http/Controllers/API/TwoShipAPIs.php @@ -4,280 +4,116 @@ namespace Modules\M3\Http\Controllers\API; use Config; use DB; +use Illuminate\Http\Client\Response as HttpResponse; use Illuminate\Http\Request; -use Illuminate\Http\Response; -use Log; +use Illuminate\Support\Facades\Http; use Modules\M3\Helpers\Helper; use Modules\M3\Http\Controllers\Controller; use Str; class TwoShipAPIs extends Controller { - // TwoShipUpdateHold with URL Params + private const SOURCE = 'TwoShipUpdateHold'; + private const RESPONSE_TYPE = 'AggStopRsp'; + public function TwoShipUpdateHold(Request $request) { - ini_set('max_execution_time', Config::get('arrays.PHPTimeOut')); - ini_set('max_input_time', Config::get('arrays.PHPTimeOut')); - ini_set('default_socket_timeout', Config::get('arrays.PHPTimeOut')); + $this->configureTimeouts(); + $user = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser'; + $params = $request->all(); + $whs = (string) ($params['WHS'] ?? ''); + $co = (string) ($params['COMPANY'] ?? ''); + $macpkr = (string) ($params['MACPKR'] ?? ''); - // dd($request->all()); - - // Log::info( $request->all() ); - // Get The Calling User - $SHUSER = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser'; - - $SHSOURCE = 'TwoShipUpdateHold'; - $TwoShipErrors = ''; - - $urlParams = count($request->all()) > 0 ? $request->all() : ''; - $WHS = isset($urlParams['WHS']) ? $urlParams['WHS'] : ''; - $CO = isset($urlParams['COMPANY']) ? $urlParams['COMPANY'] : ''; - $MACPKR = isset($urlParams['MACPKR']) ? $urlParams['MACPKR'] : ''; + if ($params === []) { + $this->log($macpkr ?: '000000', $co, $whs, $user, 'X', 'No URL Parameters Sent'); + return response()->json(['code' => '405', 'status' => 'Error', 'message' => 'No URL Parameters Sent']); + } try { - if ($urlParams != '') { - $IncomingParams = ['CO' => $CO, 'WHS' => $WHS, 'MACPKR' => $MACPKR]; - $IncomingParams = json_encode($IncomingParams); - - // Write to Logs : Params In - $data = ['SHMACPKR' => $MACPKR, - 'SHDT' => date('Y-m-d'), - 'SHTIME' => date('H:i:s'), - 'SHERROR' => '', - 'SHINTBOX' => '', - 'SHTYPE' => 'AggStopReq', - 'SHDATA' => $IncomingParams, - 'SHUSER' => $SHUSER, - 'SHSOURCE' => $SHSOURCE, - 'SHCO' => $CO, - 'SHWHS' => $WHS, - ]; - Helper::logToICM699M($data); - - // First Step : Get MACPKRs list if available - // select a4macpkr from icm688m1 where a4shpaggcd in (select a4shpaggcd from icm688m1 where a4macpkr=233709) and a4shpaggcd<>0 and a4concmplt<>'y' and a4co=410 and a4whs=111 - $A4MACPKRSel = DB::connection(config('m3.connection', 'pgsql'))->select( - "select a4macpkr from icm688m1 where a4shpaggcd in (select a4shpaggcd from icm688m1 where a4macpkr={$MACPKR}) - and a4shpaggcd<>0 and a4concmplt<>'y' and a4co='{$CO}' and a4whs='{$WHS}' ", - ); - // dd($A4MACPKRSel); - - if (count($A4MACPKRSel) > 0) { - foreach ($A4MACPKRSel as $key => $A4MACPKRR) { - $A4MACPKRArr[] = $A4MACPKRR->A4MACPKR; - } - } else { - $A4MACPKRArr[] = $MACPKR; - } - - // dump($A4MACPKRArr); - - // Call 2ship API with Params - $WS_key = Config::get('arrays.2Ship.TwoShip_Key'); - $end_point = Config::get('arrays.2Ship.end_point'); - $API = Config::get('arrays.2Ship.hold_update'); - - $ShipOnHoldOrder_url = $end_point . $API; - - // Build Json Data - $data = [ - 'WS_Key' => $WS_key, - 'OrderNumbers' => [implode(',', $A4MACPKRArr)], - // 'ShipmentId' => 3, - 'FindBy' => 'ByMultipleOrderNumbers', - 'DataToBeUpdated' => [ - 'UpdateAggregationSettings' => true, - 'OrderAggregationSettingsInfo' => [ - 'NotConsideredAggregation' => true, - ], - ], - ]; - - $json_string = json_encode($data); - - // dump($json_string); - - // ###### - // Initialise the cURL var - $ch = curl_init(); - - // Get the response from cURL - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); - - curl_setopt($ch, CURLOPT_URL, $ShipOnHoldOrder_url); - curl_setopt($ch, CURLOPT_VERBOSE, false); - - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); - // When CURLOPT_SSL_VERIFYPEER is enabled (value=1), and the verification fails to prove that the certificate is authentic, the connection fails. - // When the option is zero, the peer certificate verification succeeds regardless. - - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); - // When CURLOPT_SSL_VERIFYHOST is 2, that certificate must indicate that the server is the server to which you meant to connect, - // or the connection fails. Simply put, it means it has to have the same name in the certificate as is in the URL you operate against. - - // Set data content type - curl_setopt( - $ch, - CURLOPT_HTTPHEADER, - [ - 'Content-Type: application/json', - 'Content-Length: ' . strlen($json_string), - ], - ); - - // Post data - curl_setopt($ch, CURLOPT_POSTFIELDS, $json_string); - - // Set timeout - curl_setopt($ch, CURLOPT_TIMEOUT, Config::get('arrays.PHPTimeOut')); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, Config::get('arrays.PHPTimeOut')); - - // if set true, cURL will follow redirects - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - - // Execute the request - $response = curl_exec($ch); - - // Verifying error - $info = curl_getinfo($ch); - - // Get status - $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); - - // Output the error code - // $CurlError= curl_error($ch); - $CurlError = str_replace("'", ' ', curl_error($ch)); - - // Close the session - unset($ch); - - // dump($response); - // dump($status); - // dump($CurlError); - - if ($status == '200' && $CurlError == '') { - $responseJSON = json_decode($response); - // dump($responseJSON); - if ($responseJSON->Success == false) { - $TwoShipErrors = '2Ship Error: ' . $responseJSON->GlobalError; - } - } else { - $TwoShipErrors = 'Error: No Response from 2Ship'; - $TwoShipErrors .= ' -Curl Error: ' . $CurlError; - } - - // Validate response From 2Ship - // Error - if ($TwoShipErrors != '') { - // dump($TwoShipErrors); - - // Write to Logs : Error - $data = ['SHMACPKR' => $MACPKR, - 'SHDT' => date('Y-m-d'), - 'SHTIME' => date('H:i:s'), - 'SHERROR' => 'X', - 'SHINTBOX' => '', - 'SHTYPE' => 'AggStopRsp', - 'SHDATA' => $status . '-' . $TwoShipErrors, - 'SHUSER' => $SHUSER, - 'SHSOURCE' => $SHSOURCE, - 'SHCO' => $CO, - 'SHWHS' => $WHS, - ]; - Helper::logToICM699M($data); - - // Send Response - return response()->json([ - 'code' => '405', - 'status' => 'error', - 'message' => 'Status: ' . $status . '-' . $TwoShipErrors, - ]); - } - // Success - else { - // Write to Logs : JSON - $data = ['SHMACPKR' => $MACPKR, - 'SHDT' => date('Y-m-d'), - 'SHTIME' => date('H:i:s'), - 'SHERROR' => '', - 'SHINTBOX' => '', - 'SHTYPE' => 'AggStopRsp', - 'SHDATA' => $response, - 'SHUSER' => $SHUSER, - 'SHSOURCE' => $SHSOURCE, - 'SHCO' => $CO, - 'SHWHS' => $WHS, - ]; - Helper::logToICM699M($data); - - // Update ICM688M1 set a4concmplt to 'y' - DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1') - ->whereIn('A4MACPKR', $A4MACPKRArr) - ->where('A4CO', $CO) - ->where('A4WHS', $WHS) - ->update(['A4CONCMPLT' => 'Y']); - - } - - // Send Response - return response()->json([ - 'code' => '200', - 'status' => 'Success', - ]); + $this->logRequest($macpkr, $co, $whs, $user); + $orders = $this->aggregationOrders($macpkr, $co, $whs); + $response = $this->callTwoShip($orders); + $error = $this->responseError($response); + if ($error !== '') { + $this->log($macpkr, $co, $whs, $user, 'X', $response->status() . '-' . $error); + return response()->json(['code' => '405', 'status' => 'error', 'message' => 'Status: ' . $response->status() . '-' . $error]); } - // Error : No URL params - else { - // Write to Logs Out : Error - $MACPKR = isset($MACPKR) ? $MACPKR : '000000'; - $data = ['SHMACPKR' => $MACPKR, - 'SHDT' => date('Y-m-d'), - 'SHTIME' => date('H:i:s'), - 'SHERROR' => 'X', - 'SHINTBOX' => '', - 'SHTYPE' => 'AggStopRsp', - 'SHDATA' => 'No URL Parameters Sent', - 'SHUSER' => $SHUSER, - 'SHSOURCE' => $SHSOURCE, - 'SHCO' => $CO, - 'SHWHS' => $WHS, - ]; - Helper::logToICM699M($data); - // Return Error - return response()->json([ - 'code' => '405', - 'status' => 'Error', - 'message' => 'No URL Parameters Sent', - ]); - } + $this->log($macpkr, $co, $whs, $user, '', $response->body()); + $this->markAggregationComplete($orders, $co, $whs); + return response()->json(['code' => '200', 'status' => 'Success']); } catch (\Throwable $th) { - // Write to Logs Out : Error - $getCurlErrors = str_contains($th->getMessage(), 'cURL error'); - if ($getCurlErrors) { - // dd($getCurlErrors); - // Write to Logs Out : Failed - $data = ['SHMACPKR' => $MACPKR, - 'SHDT' => date('Y-m-d'), - 'SHTIME' => date('H:i:s'), - 'SHERROR' => 'X', - 'SHINTBOX' => '', - 'SHTYPE' => 'AggStopRsp', - 'SHDATA' => 'Curl Error: Communication with 2Ship Timed Out' . ' 2Ship-JSN-out' . $IncomingParams, - 'SHUSER' => $SHUSER, - 'SHSOURCE' => $SHSOURCE, - 'SHCO' => $CO, - 'SHWHS' => $WHS, - ]; - Helper::logToICM699M($data); - - return response()->json([ - 'code' => '405', - 'status' => 'error', - 'message' => 'Curl Error: Communication with 2Ship Timed Out.', - ]); - } + $message = str_contains($th->getMessage(), 'cURL error') + ? 'Curl Error: Communication with 2Ship Timed Out.' + : '2Ship communication failed.'; + $this->log($macpkr, $co, $whs, $user, 'X', $message); + return response()->json(['code' => '405', 'status' => 'error', 'message' => $message]); } } - // End TwoShipUpdateHold + + private function configureTimeouts(): void + { + $timeout = Config::get('arrays.PHPTimeOut'); + ini_set('max_execution_time', $timeout); + ini_set('max_input_time', $timeout); + ini_set('default_socket_timeout', $timeout); + } + + private function logRequest(string $macpkr, string $co, string $whs, string $user): void + { + $data = json_encode(['CO' => $co, 'WHS' => $whs, 'MACPKR' => $macpkr]); + Helper::logToICM699M($this->logData($macpkr, $co, $whs, $user, '', (string) $data, 'AggStopReq')); + } + + private function aggregationOrders(string $macpkr, string $co, string $whs): array + { + $aggregation = DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1') + ->where('A4MACPKR', $macpkr)->value('A4SHPAGGCD'); + if (empty($aggregation) || (string) $aggregation === '0') { + return [$macpkr]; + } + $orders = DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1') + ->where('A4SHPAGGCD', $aggregation)->where('A4SHPAGGCD', '<>', 0)->where('A4CONCMPLT', '<>', 'y') + ->where('A4CO', $co)->where('A4WHS', $whs)->pluck('A4MACPKR')->all(); + return $orders !== [] ? $orders : [$macpkr]; + } + + private function callTwoShip(array $orders): HttpResponse + { + $payload = [ + 'WS_Key' => Config::get('arrays.2Ship.TwoShip_Key'), + 'OrderNumbers' => [implode(',', $orders)], + 'FindBy' => 'ByMultipleOrderNumbers', + 'DataToBeUpdated' => ['UpdateAggregationSettings' => true, 'OrderAggregationSettingsInfo' => ['NotConsideredAggregation' => true]], + ]; + return Http::acceptJson()->asJson()->timeout((int) Config::get('arrays.PHPTimeOut')) + ->post(Config::get('arrays.2Ship.end_point') . Config::get('arrays.2Ship.hold_update'), $payload); + } + + private function responseError(HttpResponse $response): string + { + if (! $response->successful()) { + return 'Error: No Response from 2Ship'; + } + $body = $response->json(); + return ($body['Success'] ?? true) === false ? '2Ship Error: ' . ($body['GlobalError'] ?? 'Unknown error') : ''; + } + + private function markAggregationComplete(array $orders, string $co, string $whs): void + { + DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1') + ->whereIn('A4MACPKR', $orders)->where('A4CO', $co)->where('A4WHS', $whs)->update(['A4CONCMPLT' => 'Y']); + } + + private function log(string $macpkr, string $co, string $whs, string $user, string $error, string $message): void + { + Helper::logToICM699M($this->logData($macpkr, $co, $whs, $user, $error, $message, self::RESPONSE_TYPE)); + } + + private function logData(string $macpkr, string $co, string $whs, string $user, string $error, string $message, string $type): array + { + return ['SHMACPKR' => $macpkr, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => $error, + 'SHINTBOX' => '', 'SHTYPE' => $type, 'SHDATA' => $message, 'SHUSER' => $user, 'SHSOURCE' => self::SOURCE, 'SHCO' => $co, 'SHWHS' => $whs]; + } } diff --git a/Modules/M3/Http/Controllers/userPickingStats5.php b/Modules/M3/Http/Controllers/userPickingStats5.php index a3578efe..347ea7b7 100644 --- a/Modules/M3/Http/Controllers/userPickingStats5.php +++ b/Modules/M3/Http/Controllers/userPickingStats5.php @@ -4,7 +4,7 @@ namespace Modules\M3\Http\Controllers; use DateTime; use DB; -use Illuminate\Support\Facades\Http; // added this for curl call to v4 +use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Schema; use Symfony\Component\HttpFoundation\Request; @@ -13,133 +13,128 @@ class userPickingStats5 extends Controller { public static function index(Request $request) { + [$userArray, $co, $whs] = self::loadUsers(); + [$starting, $ending, $days] = self::dateRange($request); + $user = $request->user; + $statistics = []; + $totals = self::emptyTotals(); - // 618 - // Get Logged User CO & WH. Legacy M3 profile data may not yet exist in PostgreSQL. - $co = ''; - $whs = ''; - $user_array = []; - try { - if (Schema::connection(config('m3.connection', 'pgsql'))->hasTable('icm428m')) { - $profile = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m') - ->select('R428US', 'R428CO', 'R428WH') - ->where('R428US', strtoupper((string) m3_user())) - ->first(); - $co = trim((string) ($profile->R428CO ?? '')); - $whs = trim((string) ($profile->R428WH ?? '')); - - if ($co !== '' && $whs !== '') { - $rows = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m') - ->selectRaw("R428US as userprf, (trim(R428FIRST) || ' ' || trim(R428FAMILY)) as name, R428EMPCDE as empno") - ->where('R428EMPCDE', '!=', 0) - ->where('R428CO', $co) - ->where('R428WH', $whs) - ->get(); - foreach ($rows as $row) { - $user_array[] = [ - 'userprf' => trim((string) $row->userprf), - 'name' => trim((string) $row->name), - 'empno' => $row->empno, - ]; - } - array_multisort(array_column($user_array, 'name'), SORT_ASC, $user_array); - } - } - } catch (\Throwable $e) { - Log::warning('M3 picking user list unavailable', ['exception' => $e->getMessage()]); - $user_array = []; + if (isset($request->dates)) { + [$statistics, $totals] = self::collectDailyStatistics($request, $starting, $days, $totals); } - // Filter by date + if ($request->format === 'json') { + return response()->json($statistics); + } + + $statistic5 = $statistics; + $totals_array = $totals; + $user_array = $userArray; + + return view('m3::liberty-theme.pages.userPickingStats5', compact( + 'statistic5', 'totals_array', 'user_array', 'user', 'starting', 'ending', 'request' + )); + } + + private static function loadUsers(): array + { + $users = []; + $co = ''; + $whs = ''; + + try { + $connection = config('m3.connection', 'pgsql'); + if (! Schema::connection($connection)->hasTable('icm428m')) { + return [$users, $co, $whs]; + } + + $profile = DB::connection($connection)->table('icm428m') + ->select('R428CO', 'R428WH') + ->where('R428US', strtoupper((string) m3_user())) + ->first(); + $co = trim((string) ($profile->R428CO ?? '')); + $whs = trim((string) ($profile->R428WH ?? '')); + if ($co === '' || $whs === '') { + return [$users, $co, $whs]; + } + + $rows = DB::connection($connection)->table('icm428m') + ->selectRaw("R428US as userprf, (trim(R428FIRST) || ' ' || trim(R428FAMILY)) as name, R428EMPCDE as empno") + ->where('R428EMPCDE', '!=', 0)->where('R428CO', $co)->where('R428WH', $whs)->get(); + foreach ($rows as $row) { + $users[] = ['userprf' => trim((string) $row->userprf), 'name' => trim((string) $row->name), 'empno' => $row->empno]; + } + array_multisort(array_column($users, 'name'), SORT_ASC, $users); + } catch (\Throwable $e) { + Log::warning('M3 picking user list unavailable', ['exception' => $e->getMessage()]); + } + + return [$users, $co, $whs]; + } + + private static function dateRange(Request $request): array + { if (isset($request->dates)) { - $date = explode(' - ', $request->dates); - $start = str_replace([' ', '-'], '', $date[0]); - $end = str_replace([' ', '-'], '', $date[1]); - $starting = $date[0]; - $ending = $date[1]; + $dates = explode(' - ', (string) $request->dates); + $starting = $dates[0] ?? date('Y-m-d'); + $ending = $dates[1] ?? $starting; } else { - $start = date('Ymd', strtotime('-5 days')); - $end = date('Ymd'); $starting = date('Y-m-d', strtotime('-5 days')); - // $starting = "2021-11-01"; $ending = date('Y-m-d'); } - // calculate day difference between 2 dates - $str_date = new DateTime($starting); - $end_date = new DateTime($ending); - $interval = $str_date->diff($end_date); - $days = $interval->format('%a'); // retrieve number of days %a - // echo "diff=$days
"; + $days = (int) (new DateTime($starting))->diff(new DateTime($ending))->format('%a'); + return [$starting, $ending, $days]; + } - $user = $request->user; + private static function emptyTotals(): array + { + return ['performance' => 0, 'blendedTargetLPH' => 0, 'actualLPH' => 0, 'grossHours' => 0]; + } - // retrieve session user and pw, also the HTTP server host address and port - $username = $request->header('php-auth-user'); - $pw = $request->header('php-auth-pw'); - $http_host = $request->server('HTTP_HOST'); - $urlStats4 = "http://$http_host/userPickingStats4"; + private static function collectDailyStatistics(Request $request, string $starting, int $days, array $totals): array + { + $statistics = []; + $count = 0; + $url = 'http://' . $request->server('HTTP_HOST') . '/userPickingStats4'; - // initialize arrays - $statistic5 = $totals_array = []; - // initialize totals - $totals_array['performance'] = 0; - $totals_array['blendedTargetLPH'] = 0; - $totals_array['actualLPH'] = 0; - $totals_array['grossHours'] = 0; - $y = 0; - - // loop for the days difference and passing proper date to v4 to get json encoded response - if (isset($request->dates)) { - for ($x = 0; $x <= $days; $x++) { - - $next_date = date('Y-m-d', strtotime($starting . "+$x days")); - - $parameters = ['dates' => $next_date, 'user' => $request->user, 'format' => 'json']; - // echo "
"; print_r($parameters); echo "$urlStats4
"; - - $response = Http::withBasicAuth($username, $pw)->get($urlStats4, $parameters); - // echo $response; - $statusCode = $response->status(); - - // echo "$statusCode json return=".$response->getBody()."
"; - - $statistic5[$x] = json_decode($response->getBody(), true); - - $totals_array['performance'] += isset($statistic5[$x]['performance']) ? $statistic5[$x]['performance'] : 0; - $totals_array['blendedTargetLPH'] += isset($statistic5[$x]['blendedTargetLPH']) ? $statistic5[$x]['blendedTargetLPH'] : 0; - $totals_array['actualLPH'] += isset($statistic5[$x]['actualLPH']) ? $statistic5[$x]['actualLPH'] : 0; - $totals_array['grossHours'] += isset($statistic5[$x]['grossHours']) ? $statistic5[$x]['grossHours'] : 0; - - if (! isset($statistic5[$x]['day'])) { - $statistic5[$x]['day'] = $next_date; - } - if (isset($statistic5[$x]['performance'])) { - if ($statistic5[$x]['performance'] > 0) { - $y++; - } - } else { - $statistic5[$x]['performance'] = 0; - } + for ($day = 0; $day <= $days; $day++) { + $date = date('Y-m-d', strtotime($starting . "+$day days")); + $statistics[$day] = self::fetchStatistic($request, $url, $date); + self::addTotals($totals, $statistics[$day]); + if (($statistics[$day]['performance'] ?? 0) > 0) { + $count++; } } - if ($y > 0) { - $totals_array['performance'] /= $y; - $totals_array['blendedTargetLPH'] /= $y; - $totals_array['actualLPH'] /= $y; - $totals_array['grossHours'] /= $y; + if ($count > 0) { + foreach (array_keys($totals) as $key) { + $totals[$key] /= $count; + } } - if ($request->format == 'json') { - echo '
';
-            print_r($statistic5);
-            echo '
'; - } else { - // dd($statistic5); - // if($statistic5[1]!=null) - return view('m3::liberty-theme.pages.userPickingStats5', compact('statistic5', 'totals_array', 'user_array', 'user', 'starting', 'ending', 'request')); - } + return [$statistics, $totals]; + } + private static function fetchStatistic(Request $request, string $url, string $date): array + { + try { + $response = Http::withBasicAuth((string) $request->header('php-auth-user'), (string) $request->header('php-auth-pw')) + ->get($url, ['dates' => $date, 'user' => $request->user, 'format' => 'json']); + $data = $response->successful() ? (json_decode($response->body(), true) ?: []) : []; + } catch (\Throwable $e) { + Log::warning('M3 picking statistics unavailable', ['date' => $date, 'exception' => $e->getMessage()]); + $data = []; + } + $data['day'] = $data['day'] ?? $date; + $data['performance'] = $data['performance'] ?? 0; + return $data; + } + + private static function addTotals(array &$totals, array $statistic): void + { + foreach (array_keys($totals) as $key) { + $totals[$key] += (float) ($statistic[$key] ?? 0); + } } }