From 1644efed9cd8c6a9fb5b136ffd9def58cddc5f8b Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Thu, 17 Sep 2026 10:37:56 +0100 Subject: [PATCH] m3 Security Fixes --- Modules/M3/Helpers/Helper.php | 126 ++--- .../Controllers/API/M3InventoryReport.php | 484 ------------------ .../M3/Http/Controllers/API/PdfStorage.php | 251 +++++---- .../Controllers/M3CallCloseAPIController.php | 177 ++++--- .../Controllers/M3ErrorsDispController.php | 139 ++--- 5 files changed, 356 insertions(+), 821 deletions(-) delete mode 100644 Modules/M3/Http/Controllers/API/M3InventoryReport.php diff --git a/Modules/M3/Helpers/Helper.php b/Modules/M3/Helpers/Helper.php index 51f9378a..e0c28099 100644 --- a/Modules/M3/Helpers/Helper.php +++ b/Modules/M3/Helpers/Helper.php @@ -159,74 +159,80 @@ class Helper // Read M3 JSON and Analyse it for Errors public static function checkM3Errors($responseResult) { - // dd($responseResult); - // M3 Response Types - $M3TypesResponse = ['CloseShipment', 'Move', 'COCharge', 'Replenish', 'MoveDock']; - // CloseShipment Types And Contents - $M3CloseShipmentResp = ['AddPackage', 'PickingListClose', 'TrailerNo', 'TrackingNo', 'ContentLabel', 'DeliveryNote', 'ShipLabel']; - // 'ContentLabel', 'DeliveryNote','ShipLabel' - $ErrorMessages = ''; + if (! is_object($responseResult)) { + return 'Invalid M3 response '; + } - $typesErrors = 0; + $messages = []; + $inactiveTypes = 0; + $closeShipmentInactive = self::collectCloseShipmentErrors($responseResult, $messages); - foreach ($M3TypesResponse as $key => $Type) { - // dump($responseResult->$Type); - if ($Type == 'CloseShipment') { - // dump($Type); - $CloseShipmentErr = 0; - $M3RespClose = $responseResult->$Type; - foreach ($M3CloseShipmentResp as $key => $closeType) { - $ErrorMessage = $M3RespClose->$closeType->ErrorMessage; - $ErrorMessage = $ErrorMessage == '' ? '' : $ErrorMessage; - $Result = $M3RespClose->$closeType->Result; - $ResultT = $M3RespClose->$closeType->Result; - $Result = $Result == 'N/A' || $Result == 'OK' ? '' : $Result; - if ($ErrorMessage != '' || $Result != '') { - $ErrorMessages .= $closeType . '-' . $Result . '-' . $ErrorMessage . ' '; - } - // dump($closeType);// dump($ErrorMessage);// dump($Result); + foreach (['Move', 'COCharge', 'Replenish', 'MoveDock'] as $type) { + $result = self::extractM3Result($responseResult, $type); + self::appendM3Error($messages, $type, $result); - if ($ResultT == 'N/A') { - $CloseShipmentErr++; - } - } - } elseif ($Type == 'MoveDock') { - $MoveDockRes = isset($responseResult->$Type->Move) ? $responseResult->$Type->Move : ''; - $ErrorMessage = isset($MoveDockRes->ErrorMessage) ? $MoveDockRes->ErrorMessage : ''; - $ErrorMessage = $ErrorMessage == '' ? '' : $ErrorMessage; - $Result = isset($MoveDockRes->Result) ? $MoveDockRes->Result : ''; - $ResultT = $Result; - $Result = $Result == 'N/A' || $Result == 'OK' ? '' : $Result; - if ($ErrorMessage != '' || $Result != '') { - $ErrorMessages .= $Type . '-' . $Result . '-' . $ErrorMessage . ' '; - } - // dump($Type);// dump($ErrorMessage);// dump($Result);// dump($responseResult->$Type); - if ($ResultT == 'N/A') { - $typesErrors++; - } - - } else { - $ErrorMessage = isset($responseResult->$Type->ErrorMessage) ? $responseResult->$Type->ErrorMessage : ''; - $ErrorMessage = $ErrorMessage == '' ? '' : $ErrorMessage; - $Result = isset($responseResult->$Type->Result) ? $responseResult->$Type->Result : ''; - $ResultT = $Result; - $Result = $Result == 'N/A' || $Result == 'OK' ? '' : $Result; - if ($ErrorMessage != '' || $Result != '') { - $ErrorMessages .= $Type . '-' . $Result . '-' . $ErrorMessage . ' '; - } - // dump($Type); // dump($ErrorMessage); // dump($Result); // dump($responseResult->$Type); - if ($ResultT == 'N/A') { - $typesErrors++; - } + if ($result['rawResult'] === 'N/A') { + $inactiveTypes++; } } - // dd($ErrorMessages); - if ($CloseShipmentErr == count($M3CloseShipmentResp) && $typesErrors == count($M3TypesResponse) - 1) { - $ErrorMessages .= '-M3 did not call any method or type '; + if ($closeShipmentInactive === 7 && $inactiveTypes === 4) { + $messages[] = '-M3 did not call any method or type'; } - return $ErrorMessages; + return $messages === [] ? '' : implode(' ', $messages) . ' '; + } + + private static function collectCloseShipmentErrors(object $responseResult, array &$messages): int + { + $closeShipment = $responseResult->CloseShipment ?? null; + if (! is_object($closeShipment)) { + return 7; + } + + $inactive = 0; + foreach (['AddPackage', 'PickingListClose', 'TrailerNo', 'TrackingNo', 'ContentLabel', 'DeliveryNote', 'ShipLabel'] as $type) { + $result = self::extractResultNode($closeShipment->{$type} ?? null); + self::appendM3Error($messages, $type, $result); + + if ($result['rawResult'] === 'N/A') { + $inactive++; + } + } + + return $inactive; + } + + private static function extractM3Result(object $responseResult, string $type): array + { + $node = $responseResult->{$type} ?? null; + if ($type === 'MoveDock' && is_object($node)) { + $node = $node->Move ?? null; + } + + return self::extractResultNode($node); + } + + private static function extractResultNode(mixed $node): array + { + if (! is_object($node)) { + return ['error' => '', 'result' => '', 'rawResult' => 'N/A']; + } + + $error = trim((string) ($node->ErrorMessage ?? '')); + $rawResult = trim((string) ($node->Result ?? '')); + $result = in_array($rawResult, ['N/A', 'OK'], true) ? '' : $rawResult; + + return ['error' => $error, 'result' => $result, 'rawResult' => $rawResult]; + } + + private static function appendM3Error(array &$messages, string $type, array $result): void + { + if ($result['error'] === '' && $result['result'] === '') { + return; + } + + $messages[] = $type . '-' . $result['result'] . '-' . $result['error']; } // Log All Success And Error to ICM699M diff --git a/Modules/M3/Http/Controllers/API/M3InventoryReport.php b/Modules/M3/Http/Controllers/API/M3InventoryReport.php deleted file mode 100644 index c0b81822..00000000 --- a/Modules/M3/Http/Controllers/API/M3InventoryReport.php +++ /dev/null @@ -1,484 +0,0 @@ -json()->all(); - $warehouse = trim((string) ($input['WHS'] ?? '')); - $payload = [ - 'WHS' => $warehouse, - 'PART' => trim((string) ($input['PART'] ?? '')), - 'LOT' => trim((string) ($input['LOT'] ?? '')), - ]; - - $tokenResponse = Helper::checkReceiveTokenV2(); - $tokenData = $tokenResponse->getData(); - - if ((string) ($tokenData->code ?? '') !== '200') { - self::logInventoryResponse( - self::ZERO_PAD, - (string) ($tokenData->message ?? ''), - $user, - $source, - $warehouse - ); - - return $tokenResponse; - } - - return self::sendInventoryRequest( - (string) ($tokenData->token ?? ''), - $payload, - $user, - $source, - $warehouse - ); - } catch (\Throwable $exception) { - return self::inventoryExceptionResponse($exception, $payload, $user, $source, $warehouse); - } - } - - private static function sendInventoryRequest( - string $token, - array $payload, - string $user, - string $source, - string $warehouse - ) { - if ($token === '') { - return response()->json([ - 'code' => '401', - 'status' => 'error', - 'message' => 'M3 authentication token was not returned.', - ], 401); - } - - $response = Http::withHeaders([ - 'accept' => self::JSON_CONTENT_TYPE, - 'Content-Type' => self::JSON_CONTENT_TYPE, - ]) - ->withToken($token) - ->timeout(Config::get('arrays.curlTimeOut')) - ->post(env('ENDPOINTURL') . 'APIFLOWS/Sentinel/M3InventoryReport?ionapiRespStyle=sync', $payload); - - $statusCode = $response->getStatusCode(); - $sessionId = $response->hasHeader('x-transaction-id') - ? '-sessionID:' . $response->header('x-transaction-id') - : '-sessionID'; - - if ($statusCode !== Response::HTTP_OK) { - self::logInventoryResponse( - self::ZERO_PAD, - $statusCode . self::NO_M3_RESPONSE . $sessionId, - $user, - $source, - $warehouse, - true - ); - - return response()->json([ - 'code' => '405', - 'status' => 'error', - 'message' => self::NO_M3_RESPONSE, - ], 502); - } - - $sanitized = Helper::sanityzeString($response->body()); - self::logInventoryResponse( - self::ZERO_PAD, - $statusCode . $sanitized . '-' . json_encode($payload) . $sessionId, - $user, - $source, - $warehouse - ); - - return response()->json([ - 'code' => $statusCode, - 'status' => 'Success', - 'message' => json_decode($sanitized), - ]); - } - - private static function inventoryExceptionResponse( - \Throwable $exception, - array $payload, - string $user, - string $source, - string $warehouse - ) { - $isCurlError = str_contains($exception->getMessage(), 'cURL error'); - $message = $isCurlError - ? 'Curl Error: Communication with M3 Timed Out.' - : 'M3 inventory request failed.'; - - self::logInventoryResponse( - self::ZERO_PAD, - $isCurlError ? $message : mb_substr($exception->getMessage(), 0, 300), - $user, - $source, - $warehouse, - true - ); - - Log::error('M3 inventory request failed.', [ - 'exception' => $exception, - 'payload' => $payload, - ]); - - return response()->json([ - 'code' => '405', - 'status' => 'error', - 'message' => $message, - ], 502); - } - - private static function logInventoryResponse( - string $deliveryNumber, - string $message, - string $user, - string $source, - string $warehouse, - bool $isError = false - ): void { - Helper::logToICM699M([ - 'SHMACPKR' => $deliveryNumber, - 'SHDT' => date('Y-m-d'), - 'SHTIME' => date('H:i:s'), - 'SHERROR' => $isError ? 'X' : '', - 'SHINTBOX' => '', - 'SHTYPE' => 'M3-Res-out', - 'SHDATA' => $message, - 'SHUSER' => $user, - 'SHSOURCE' => $source, - 'SHCO' => '', - 'SHWHS' => $warehouse, - ]); - } - - // End M3InventoryReport - - // M3InventoryReportList with URL Params - public function M3InventoryReportListURL(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')); - - // Set API Name - $SHSOURCE = 'M3InventoryReportListURL'; - // Get The Calling User - $SHUSER = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser'; - $M3Errors = ''; - - try { - // Get Json Contents - $urlParams = count($request->all()) > 0 ? $request->all() : ''; - - $REQPKWS = isset($urlParams['REQPKWS']) ? $urlParams['REQPKWS'] : ''; - $REQKPKUS = isset($urlParams['REQKPKUS']) ? $urlParams['REQKPKUS'] : 'WebUser'; - $CO = isset($urlParams['CO']) ? $urlParams['CO'] : ''; - $WHS = isset($urlParams['WHS']) ? $urlParams['WHS'] : ''; - $MACPKR = isset($urlParams['MACPKR']) ? $urlParams['MACPKR'] : self::ZERO_PAD; // Delivery Number - $PKLN = isset($urlParams['PKLN']) ? $urlParams['PKLN'] : ''; - - if ($urlParams != '') { - $IncomingParams = ['CO' => $CO, 'WHS' => $WHS, 'MACPKR' => $MACPKR, 'PKLN' => $PKLN, 'REQPKWS' => $REQPKWS, 'REQKPKUS' => $REQKPKUS]; - $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' => 'M3-Req-out', - 'SHDATA' => $IncomingParams, 'SHUSER' => $SHUSER, 'SHSOURCE' => $SHSOURCE, 'SHCO' => $CO, 'SHWHS' => $WHS]; - Helper::logToICM699M($data); - - // Get Parts And Lots from ICM688M2 - $ICM688M2SLTMP = DB::connection(config('m3.connection', 'pgsql'))->table('icm688m2') - ->select('A5WHS', 'A5PART', 'A5LOTNUM', 'A5SENTPKR') - ->distinct() - ->where('A5CO', $CO) - ->where('A5WHS', $WHS) - ->where('A5MACPKR', $MACPKR); - - // Get Pack Lines - if ($PKLN != '') { - $ICM688M2SLTMP->where('A5PKLN', $PKLN); - } - $ICM688M2SL = $ICM688M2SLTMP->get(); - // dd($ICM688M2SL); - - if (count($ICM688M2SL) > 0) { - // Update ICM688M1 : Add Timestamp - $ICM688M1TS = now(); - DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1') - ->where('A4MACPKR', $MACPKR) - ->update(['A4INRFSHTS' => $ICM688M1TS]); - - $PICKERARR = []; - foreach ($ICM688M2SL as $key => $ICM688M2) { - $PART = trim($ICM688M2->A5PART); - $LOT = trim($ICM688M2->A5LOTNUM); - $PARTS[] = ['PART' => $PART, 'LOT' => $LOT]; - $PICKERARR[$PART] = $ICM688M2->A5SENTPKR; - } - // Log::info($PARTS); - // dump($PARTS); - $JSON = json_encode(['WHS' => $WHS, 'PARTS' => $PARTS]); - // Log::info($JSON); - - // Get Token - $tokenResponse = Helper::checkReceiveTokenV2(); - $tokenData = $tokenResponse->getData(); - if ($tokenData->code == '200') { - $IonToken = isset($tokenData->token) ? $tokenData->token : ''; - } else { - $SHDATA = isset($tokenData->message) ? $tokenData->message : ''; - // Write to Logs : Error - $data = ['SHMACPKR' => $MACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out', - 'SHDATA' => $SHDATA, 'SHUSER' => $SHUSER, 'SHSOURCE' => $SHSOURCE, 'SHCO' => $CO, 'SHWHS' => $WHS]; - Helper::logToICM699M($data); - - return $tokenResponse; - } - - // Call ionapi with Params - $endPointURL = env('ENDPOINTURL') . 'APIFLOWS/Sentinel/M3InventoryReportList?ionapiRespStyle=sync'; - $token = $IonToken; - - $response = Http::withHeaders([ - 'accept' => self::JSON_CONTENT_TYPE, - 'Content-Type' => self::JSON_CONTENT_TYPE, - ]) - ->withToken($token) - ->withBody($JSON, self::JSON_CONTENT_TYPE) - ->timeout(Config::get('arrays.curlTimeOut')) - ->post($endPointURL); - - $M3SessionId = $response->hasHeader('x-transaction-id') ? '-sessionID:' . $response->header('x-transaction-id') : '-sessionID'; - - $responseStatusCode = $response->getStatusCode(); - if ($responseStatusCode == '200') { - // Check M3 Response for Errors - $M3Errors = ''; - $responseJSON = $response->getBody()->getContents(); - $JsonSanytized = Helper::sanityzeString($responseJSON); - // Array - $responseArray = json_decode($JsonSanytized, true); - $responseTMP = $responseArray['result']; - } else { - $M3Errors = self::NO_M3_RESPONSE; - } - - // Validate response From M3 - if ($M3Errors != '') { - // Write to Logs : Error - $data = ['SHMACPKR' => $MACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out', - 'SHDATA' => $responseStatusCode . '-' . $M3Errors . $M3SessionId, 'SHUSER' => $SHUSER, 'SHSOURCE' => $SHSOURCE, 'SHCO' => $CO, 'SHWHS' => $WHS]; - Helper::logToICM699M($data); - - // Send Response - return response()->json([ - 'code' => '405', - 'status' => 'error', - 'message' => self::NO_M3_RESPONSE, - ]); - } else { - // Write to Logs : JSON - $data = ['SHMACPKR' => $MACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => '', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out', - 'SHDATA' => $responseStatusCode . '-' . $JsonSanytized . $M3SessionId, 'SHUSER' => $SHUSER, 'SHSOURCE' => $SHSOURCE, 'SHCO' => $CO, 'SHWHS' => $WHS]; - Helper::logToICM699M($data); - } - - $dataToWrite2 = []; - foreach ($responseTMP as $key => $value) { - // dump($value); - $dataToWrite = []; - - foreach ($value as $key2 => $content) { - if ($key2 != 'Locations') { - $dataToWrite[$key2] = $content; - } else { - foreach ($content as $key3 => $content2) { - // dump($content2); - $dataToWrite2[] = array_merge($dataToWrite, $content2); - } - } - } - } - // LOG::info($dataToWrite2); - - // Delete Old Records from IC630M - foreach ($dataToWrite2 as $key => $value) { - $ICM630MDeleteSQL = DB::connection(config('m3.connection', 'pgsql'))->table('ic630m') - ->where('LLCO', $CO) - ->where('LLWHNO', $WHS) - ->where('LLPPN', trim($value['PartNumber'])) - ->delete(); - } - - foreach ($dataToWrite2 as $key => $value) { - // Check if Location exist in DE640M ("LVCO", "WSELOC", "VLOCID") - $DE640MSQLSEL = DB::connection(config('m3.connection', 'pgsql'))->table('de640m') - ->where('LVCO', $CO) - ->where('WSELOC', $value['Warehouse']) - ->where('VLOCID', $value['Location']) - ->first(); - // dd($DE640MSQLSEL); - - $LocationExist = isset($DE640MSQLSEL->VLOCID) ? trim($DE640MSQLSEL->VLOCID) : ''; - $WHExist = isset($DE640MSQLSEL->WSELOC) ? trim($DE640MSQLSEL->WSELOC) : ''; - $COExist = isset($DE640MSQLSEL->LVCO) ? trim($DE640MSQLSEL->LVCO) : ''; - // dd($LocationExist .': '.$WHExist.': '.$COExist); - - // if No records Founds insert New Record - if ($LocationExist == '' && $WHExist == '' && $COExist == '') { - $SQLInsertPrep640M['LVCO'] = $CO; // Company - $SQLInsertPrep640M['WSELOC'] = $value['Warehouse']; - // $SQLInsertPrep640M['LVDELNO'] = $MACPKR; # M3 Delivery note number DELETED - // $SQLInsertPrep640M['LVUSD3'] = $value['']; # Pallet Size : Not Found in M3 JSON - $SQLInsertPrep640M['VLOCID'] = $value['Location']; // Stock location - $SQLInsertPrep640M['LVSEQN'] = $value['LocationSeq']; - $SQLInsertPrep640M['LVUSD2'] = $value['LocationType']; - $SQLInsertPrep640M['LVZONE'] = $value['Zone']; // Warehouse zone - $SQLInsertPrep640M['LVMAINTTS'] = now(); // Last maintained time stamp - - DB::connection(config('m3.connection', 'pgsql'))->table('de640m')->insert($SQLInsertPrep640M); - } - // Update - else { - $SQLUpdatePrep640M['LVSEQN'] = $value['LocationSeq']; - $SQLUpdatePrep640M['LVUSD2'] = $value['LocationType']; - $SQLUpdatePrep640M['LVZONE'] = $value['Zone']; - $SQLUpdatePrep640M['LVMAINTTS'] = now(); - - DB::connection(config('m3.connection', 'pgsql'))->table('de640m') - ->where('LVCO', $CO) - ->where('WSELOC', $value['Warehouse']) - ->where('VLOCID', $value['Location']) - ->update($SQLUpdatePrep640M); - } - - // Insert New Records to IC630M - $SQLInsertPrep630M = []; - $SQLInsertPrep630M['LLCO'] = $CO; // Company - $SQLInsertPrep630M['LLWHNO'] = $value['Warehouse']; // Warehouse - // $SQLInsertPrep630M['LLDELNO'] = $MACPKR; # M3 Delivery note number DELETED - $SQLInsertPrep630M['LLPPN'] = $value['PartNumber']; // Part Number - $SQLInsertPrep630M['LLBALANCE'] = $value['Quantity']; // Quantity - $SQLInsertPrep630M['LLSTLC'] = $value['Location']; // Stock location - $SQLInsertPrep630M['LLLTNO'] = $value['LotNumber']; // Lot Number - $SQLInsertPrep630M['LLMAINTTS'] = now(); // Request time timestamp - $SQLInsertPrep630M['LLIZLCF'] = trim($value['LotNumber']) != '' ? '1' : ''; // Lot control flag - $SQLInsertPrep630M['LLCMDESC'] = $value['Description']; // Description - $SQLInsertPrep630M['LLSPR10A01'] = isset($value['Allocation']) ? $value['Allocation'] : ''; // Allocation - $SQLInsertPrep630M['LLSPR15A01'] = isset($value['AllocatedTo']) ? $value['AllocatedTo'] : ''; // AllocatedTo - - DB::connection(config('m3.connection', 'pgsql'))->table('ic630m')->insert($SQLInsertPrep630M); - } - // dump($SQLInsertPrep); - - // Send Response - return response()->json([ - 'code' => '200', - 'status' => 'Success', - ]); - } else { - // Write to Logs Out : Error - $MACPKR = isset($MACPKR) ? $MACPKR : self::ZERO_PAD; - $data = ['SHMACPKR' => $MACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out', - 'SHDATA' => 'No Records Found in ICM688M2', 'SHUSER' => $REQKPKUS, 'SHSOURCE' => $SHSOURCE, 'SHCO' => $CO, 'SHWHS' => $WHS]; - Helper::logToICM699M($data); - - // Return Error - return response()->json([ - 'code' => '405', - 'status' => 'Error', - 'message' => 'No Records Found in ICM688M2', - ]); - } - - } else { - // Write to Logs Out : Error - $MACPKR = isset($MACPKR) ? $MACPKR : self::ZERO_PAD; - $data = ['SHMACPKR' => $MACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out', - 'SHDATA' => 'No URL Parameters Sent', 'SHUSER' => $REQKPKUS, 'SHSOURCE' => $SHSOURCE, 'SHCO' => $CO, 'SHWHS' => $WHS]; - Helper::logToICM699M($data); - - // Return Error - return response()->json([ - 'code' => '405', - 'status' => 'Error', - 'message' => 'No URL Parameters Sent', - ]); - } - } catch (\Throwable $th) { - // Write to Logs Out : Error - $MACPKR = isset($MACPKR) ? $MACPKR : self::ZERO_PAD; - $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' => 'M3-Res-out', - 'SHDATA' => $th->getMessage() . ' M3-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 M3 Timed Out.', - ]); - } else { - // Set SHERROR To Z if Duplicate Key Detected $th - // Read The substring from SHDATA after M3-JSN-out - $SHERROR = 'X'; - $word = 'Duplicate key value specified'; - $position = strpos($th, $word); - - if ($position !== false) { - $SHERROR = 'Z'; - } - - $data = ['SHMACPKR' => $MACPKR, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => $SHERROR, 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out', - 'SHDATA' => 'An Error Occured-' . substr($th, 0, 300), 'SHUSER' => $REQKPKUS, 'SHSOURCE' => $SHSOURCE, 'SHCO' => $CO, 'SHWHS' => $WHS]; - Helper::logToICM699M($data); - - Log::error($th); - - return response()->json([ - 'code' => '405', - 'status' => 'error', - 'message' => 'Catched Error: ' . $th, - ]); - } - } - } - // End M3InventoryReportListURL -} diff --git a/Modules/M3/Http/Controllers/API/PdfStorage.php b/Modules/M3/Http/Controllers/API/PdfStorage.php index 39c1fef5..ab1cdfbb 100644 --- a/Modules/M3/Http/Controllers/API/PdfStorage.php +++ b/Modules/M3/Http/Controllers/API/PdfStorage.php @@ -5,157 +5,146 @@ namespace Modules\M3\Http\Controllers\API; use Cache; use Config; use File; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Log; use Modules\M3\Helpers\Helper; use Modules\M3\Http\Controllers\Controller; -use Smalot\PdfParser\Parser; use Str; class PdfStorage extends Controller { private const ZERO_PAD = '000000'; - // PdfStorageAPI : PDF file read and save to Cache - public function PdfStorageAPI(Request $request) + public function PdfStorageAPI(Request $request): JsonResponse { - 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')); - // dd($request->all()); - // Get the Calling User - $SHUSER = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser'; - $SHSOURCE = 'PdfStorageAPI'; - // PDF File PATH - $pdfFilePath = Config::get('arrays.PdfPath'); + $this->configureTimeouts(); + + $context = $this->requestContext($request); + if ($context === null) { + return $this->errorResponse('No URL Parameters Sent', self::ZERO_PAD, '', '', ''); + } + try { - // Get URL PARAMS - $urlParams = count($request->all()) > 0 ? $request->all() : ''; - // dd($urlParams); + $this->logRequest($context); - $COMPANY = isset($urlParams['COMPANY']) ? trim($urlParams['COMPANY']) : ''; // Company - $WHS = isset($urlParams['WHS']) ? trim($urlParams['WHS']) : ''; // WareHouse - $DOCTYPE = isset($urlParams['DOCTYPE']) ? trim($urlParams['DOCTYPE']) : ''; // document type (PackingList, CarrierLabel, ContentLabel) - $FILENAME = isset($urlParams['FILENAME']) ? trim($urlParams['FILENAME']) : ''; // PDF File name - $DOCNUM = isset($urlParams['DOCNUM']) ? trim($urlParams['DOCNUM']) : ''; // document number (ie the internal box number, the pick list number) - $A4MACPKR = isset($urlParams['A4MACPKR']) ? trim($urlParams['A4MACPKR']) : ''; // Delivery note number + $cacheKey = $this->buildCacheKey($context); + $pdfFileName = (string) Config::get('arrays.PdfPath') . $context['FILENAME'] . '.pdf'; + $error = $this->validateStorageRequest($pdfFileName, $cacheKey); - if ($urlParams != '') { - // Key Combination : COMPANY WHS A4MACPKR DOCTYPE DOCNUM - // Document type (PackingList, CarrierLabel, ContentLabel) - $DELIVERYNO = isset($A4MACPKR) ? $A4MACPKR : self::ZERO_PAD; - $DELIVERYNO = $DELIVERYNO != '' && $DELIVERYNO != null ? $DELIVERYNO : self::ZERO_PAD; - // Write to Logs : Params In - $IncomingParams = ['COMPANY' => $COMPANY, 'WHS' => $WHS, 'DOCTYPE' => $DOCTYPE, 'FILENAME' => $FILENAME, 'A4MACPKR' => $A4MACPKR, 'DOCNUM' => $DOCNUM]; - $data = ['SHMACPKR' => $DELIVERYNO, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => '', 'SHINTBOX' => $DOCNUM, 'SHTYPE' => 'M3-Req-out', - 'SHDATA' => json_encode($IncomingParams), 'SHUSER' => $SHUSER, 'SHSOURCE' => $SHSOURCE, 'SHCO' => $COMPANY, 'SHWHS' => $WHS]; - Helper::logToICM699M($data); - - // Make Key and Test Validity - if ($A4MACPKR != '' && $DOCTYPE != '' && $DOCNUM != '' && $COMPANY != '' && $WHS != '') { - $cacheKey = $A4MACPKR . $DOCTYPE . $DOCNUM . $COMPANY . $WHS; - } else { - $cacheKey = ''; - } - - // Read PDF File and Encode to Base64 - $pdfFileName = $pdfFilePath . $FILENAME . '.pdf'; - // dd($pdfFileName); - // dd(File::exists($pdfFileName)); - if (File::exists($pdfFileName) && $cacheKey != '') { - // dd(File::exists($pdfFileName)); - - // Parse PDF file and build necessary objects. - $parser = new Parser; - - // $pdf = $parser->parseFile('/www/sentm3test/htdocs/labels/E1232824-10826596.pdf'); - // dump($pdf); - // $text = $pdf->getText(); - // dump("Text Only"); - // dump($text); - - $pdfFile = File::get($pdfFileName); - - // Transform content to base64 - $base64PDF = base64_encode($pdfFile); - // Output base64 string - // dump($base64PDF); - - // Save Cache - Cache::forever($cacheKey, $base64PDF); - // Cache::put($cacheKey, $base64PDF, now()->addMinutes(60)); - - // Retrieve Cache - // $cachedPdfFile = Cache::get($cacheKey); - // dump("Retrieve Base64 Encoded PDF saved in Cache:"); - // dump($cachedPdfFile); - // $pdfData = substr($cachedPdfFile,0,50); - // remove File from Cache - // Cache::put(pdfFileName, 'value', -5); - // Cache::forget($pdfFileName); - - // Base64 Decode and Display - // $pdf = $parser->parseContent(base64_decode($base64PDF)); - // $text = $pdf->getText(); - // dump($text); - - // Write to Logs Out : Success - $data = ['SHMACPKR' => $DELIVERYNO, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => '', 'SHINTBOX' => $DOCNUM, 'SHTYPE' => 'M3-Res-out', - 'SHDATA' => 'PDF File converted to Base64 and Saved to Cache.Key: ' . $cacheKey, 'SHUSER' => $SHUSER, 'SHSOURCE' => $SHSOURCE, 'SHCO' => $COMPANY, 'SHWHS' => $WHS]; - // dd($data); - Helper::logToICM699M($data); - - return response()->json([ - 'code' => '200', - 'status' => 'Success', - 'cacheKey' => $cacheKey, - // 'PDFData' => $pdfData - ]); - } else { - $SHDATA = File::exists($pdfFileName) ? '' : 'PDF File Not Found-'; - $SHDATA = $cacheKey != '' ? $SHDATA . '' : $SHDATA . 'Cache Key is missing Params'; - - // Write to Logs Out : Error - $data = ['SHMACPKR' => $DELIVERYNO, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => $DOCNUM, 'SHTYPE' => 'M3-Res-out', - 'SHDATA' => 'An Error Occured -' . $SHDATA, 'SHUSER' => $SHUSER, 'SHSOURCE' => $SHSOURCE, 'SHCO' => $COMPANY, 'SHWHS' => $WHS]; - Helper::logToICM699M($data); - - // Return Error - return response()->json([ - 'code' => '405', - 'status' => 'Error', - 'message' => $SHDATA, - ]); - } - } else { - // Write to Logs Out : Error - $DELIVERYNO = isset($DELIVERYNO) ? $DELIVERYNO : self::ZERO_PAD; - $data = ['SHMACPKR' => $DELIVERYNO, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out', - 'SHDATA' => 'No URL Parameters Sent.', 'SHUSER' => $SHUSER, 'SHSOURCE' => $SHSOURCE, 'SHCO' => $COMPANY, 'SHWHS' => $WHS]; - Helper::logToICM699M($data); - - // Return Error - return response()->json([ - 'code' => '405', - 'status' => 'Error', - 'message' => 'No URL Parameters Sent', - ]); + if ($error !== null) { + return $this->errorResponse($error, $context['DELIVERYNO'], $context['DOCNUM'], $context['COMPANY'], $context['WHS']); } - } catch (\Throwable $th) { - // Log::error($th); - // Write to Logs Out : Error - $DELIVERYNO = isset($DELIVERYNO) ? $DELIVERYNO : self::ZERO_PAD; - $data = ['SHMACPKR' => $DELIVERYNO, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'), 'SHERROR' => 'X', 'SHINTBOX' => '', 'SHTYPE' => 'M3-Res-out', - 'SHDATA' => 'An Error Occured - ' . substr($th, 0, 300), 'SHUSER' => $SHUSER, 'SHSOURCE' => $SHSOURCE, 'SHCO' => $COMPANY, 'SHWHS' => $WHS]; - Helper::logToICM699M($data); + Cache::forever($cacheKey, base64_encode(File::get($pdfFileName))); + $this->logResult($context, 'PDF File converted to Base64 and Saved to Cache.Key: ' . $cacheKey); + + return response()->json(['code' => '200', 'status' => 'Success', 'cacheKey' => $cacheKey]); + } catch (\Throwable $exception) { + $message = 'An Error Occured - ' . substr($exception->getMessage(), 0, 300); + $this->safeLog($context, $message, true); return response()->json([ 'code' => '405', 'status' => 'error', - 'message' => 'Catched Error: ' . $th, + 'message' => 'Catched Error: ' . $exception->getMessage(), ]); } } - // End PdfStorageAPI + + private function configureTimeouts(): void + { + $timeout = (string) Config::get('arrays.PHPTimeOut'); + ini_set('max_execution_time', $timeout); + ini_set('max_input_time', $timeout); + ini_set('default_socket_timeout', $timeout); + } + + private function requestContext(Request $request): ?array + { + if ($request->all() === []) { + return null; + } + + $context = []; + foreach (['COMPANY', 'WHS', 'DOCTYPE', 'FILENAME', 'DOCNUM', 'A4MACPKR'] as $key) { + $context[$key] = trim((string) $request->input($key, '')); + } + + $context['DELIVERYNO'] = $context['A4MACPKR'] !== '' ? $context['A4MACPKR'] : self::ZERO_PAD; + $context['SHUSER'] = m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser'; + $context['SHSOURCE'] = 'PdfStorageAPI'; + + return $context; + } + + private function buildCacheKey(array $context): string + { + foreach (['A4MACPKR', 'DOCTYPE', 'DOCNUM', 'COMPANY', 'WHS'] as $required) { + if ($context[$required] === '') { + return ''; + } + } + + return $context['A4MACPKR'] . $context['DOCTYPE'] . $context['DOCNUM'] . $context['COMPANY'] . $context['WHS']; + } + + private function validateStorageRequest(string $pdfFileName, string $cacheKey): ?string + { + $messages = []; + if (! File::exists($pdfFileName)) { + $messages[] = 'PDF File Not Found-'; + } + if ($cacheKey === '') { + $messages[] = 'Cache Key is missing Params'; + } + + return $messages === [] ? null : implode('', $messages); + } + + private function logRequest(array $context): void + { + $params = array_intersect_key($context, array_flip(['COMPANY', 'WHS', 'DOCTYPE', 'FILENAME', 'A4MACPKR', 'DOCNUM'])); + $this->safeLog($context, json_encode($params, JSON_THROW_ON_ERROR), false, 'M3-Req-out'); + } + + private function logResult(array $context, string $message): void + { + $this->safeLog($context, $message); + } + + private function safeLog(array $context, string $message, bool $error = false, string $type = 'M3-Res-out'): void + { + try { + Helper::logToICM699M([ + 'SHMACPKR' => $context['DELIVERYNO'] ?? self::ZERO_PAD, + 'SHDT' => date('Y-m-d'), + 'SHTIME' => date('H:i:s'), + 'SHERROR' => $error ? 'X' : '', + 'SHINTBOX' => $context['DOCNUM'] ?? '', + 'SHTYPE' => $type, + 'SHDATA' => $message, + 'SHUSER' => $context['SHUSER'] ?? (m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser'), + 'SHSOURCE' => $context['SHSOURCE'] ?? 'PdfStorageAPI', + 'SHCO' => $context['COMPANY'] ?? '', + 'SHWHS' => $context['WHS'] ?? '', + ]); + } catch (\Throwable) { + // Logging must never turn an API response into a 500 error. + } + } + + private function errorResponse(string $message, string $deliveryNo, string $docNum, string $company, string $warehouse): JsonResponse + { + $context = [ + 'DELIVERYNO' => $deliveryNo, + 'DOCNUM' => $docNum, + 'COMPANY' => $company, + 'WHS' => $warehouse, + 'SHUSER' => m3_user() !== '' ? Str::upper(m3_user()) : 'WebUser', + 'SHSOURCE' => 'PdfStorageAPI', + ]; + $this->safeLog($context, 'An Error Occured -' . $message, true); + + return response()->json(['code' => '405', 'status' => 'Error', 'message' => $message]); + } } diff --git a/Modules/M3/Http/Controllers/M3CallCloseAPIController.php b/Modules/M3/Http/Controllers/M3CallCloseAPIController.php index ad75821d..829bf0f4 100644 --- a/Modules/M3/Http/Controllers/M3CallCloseAPIController.php +++ b/Modules/M3/Http/Controllers/M3CallCloseAPIController.php @@ -5,101 +5,108 @@ namespace Modules\M3\Http\Controllers; use Artisan; use Config; use DB; +use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Schema; use Log; class M3CallCloseAPIController extends Controller { - public static function index(Request $request) + public static function index(Request $request): JsonResponse { - 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(); + $params = self::validatedParams($request); + + if ($params === null) { + Log::channel('dailyCron')->error('Manifest CloseAPI: Manifest, company and warehouse must be defined.'); + + return self::errorResponse('Manifest CloseAPI: Manifest, company and warehouse must be defined.'); + } try { + $records = self::findOpenManifestRecords($params); + if ($records->isEmpty()) { + Log::channel('dailyCron')->info('Manifest CloseAPI: No records found in ICM705M1.'); - // Get the Calling User - // Get URL PARAMS - $urlParams = count($request->all()) > 0 ? $request->all() : ''; - // dd($urlParams); - $SHUSER = isset($urlParams['SHUSER']) ? $urlParams['SHUSER'] : ''; - $MANIFEST = isset($urlParams['MANIFEST']) ? $urlParams['MANIFEST'] : ''; - $APCO = isset($urlParams['APCO']) ? $urlParams['APCO'] : ''; - $APWHS = isset($urlParams['APWHS']) ? $urlParams['APWHS'] : ''; - - if ($MANIFEST != '' && $APCO != '' && $APWHS != '') { // $SHUSER!='' && - // Log::channel('dailyCron')->info("Cron...Start CallCloseAPI...For user:".$SHUSER." Manifest#: ".$MANIFEST); - - // Get Logged User CO & WH - // Get users from ICM428M - // $ICM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m') - // ->select('R428US','R428CO', 'R428WH') - // ->where('R428US','=',$SHUSER) - // ->first(); - - // $R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : ''; - // $USERCO = isset($ICM428M->R428CO) ? $ICM428M->R428CO : ''; - // $USERWH = isset($ICM428M->R428WH) ? $ICM428M->R428WH : ''; - // dump($R428US.':'.$USERCO.':'.$USERWH); - - // Get Macpickers from ICM705M1 - // SELECT APMANIFEST FROM ICM705M1 WHERE APSPARE2='' AND APCO='110' AND APWHS='321' - $ICM705M1 = DB::connection(config('m3.connection', 'pgsql'))->table('icm705m1') - ->select('APMANIFEST', 'APCO', 'APWHS') - ->where('APSPARE2', '') - ->where('APCO', $APCO) - ->where('APWHS', $APWHS) - ->where('APMANIFEST', '=', $MANIFEST) - ->whereRaw('EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - APLDCLOZ)) / 60 > 15') - ->orderBy('APMANIFEST') - ->get(); - // dump($ICM705M1); - - if (count($ICM705M1) > 0) { - // Loop the Results from $ICM705M1 - foreach ($ICM705M1 as $key => $ICM705M1R) { - // Send CO and WHS to CallCloseAPI - $USERCO = isset($ICM705M1R->APCO) ? $ICM705M1R->APCO : ''; - $USERWH = isset($ICM705M1R->APWHS) ? $ICM705M1R->APWHS : ''; - - $callCloseShipment = Artisan::call('command:CallCloseAPI', ['user' => $SHUSER, 'manifest' => $ICM705M1R->APMANIFEST, - 'co' => $USERCO, 'whs' => $USERWH]); - // dump($callCloseShipment); - - if ($callCloseShipment == 1) { - return response()->json([ - 'code' => '200', - 'status' => 'Success', - 'message' => 'Manifest Closing submitted', - ]); - } else { - return response()->json([ - 'code' => '206', - 'status' => 'Failure', - 'message' => 'Manifest Closing Failed', - ]); - } - } - } else { - Log::channel('dailyCron')->info('Manifest CloseAPI: No records found in ICM705M1...'); - - return response()->json([ - 'code' => '206', - 'status' => 'Error', - 'message' => 'Manifest CloseAPI: No records found in ICM705M1...', - ]); - } - } else { - Log::channel('dailyCron')->error('Manifest CloseAPI: A User and Manifest must be defined...'); - - return response()->json([ - 'code' => '206', - 'status' => 'Error', - 'message' => 'Manifest CloseAPI: A User and Manifest must be defined...', - ]); + return self::errorResponse('Manifest CloseAPI: No records found in ICM705M1.'); } - } catch (\Throwable $th) { - Log::error($th); + + return self::submitManifest($records->first(), $params['SHUSER']); + } catch (\Throwable $exception) { + Log::error($exception); + + return response()->json([ + 'code' => '500', + 'status' => 'Error', + 'message' => 'Manifest Closing failed unexpectedly.', + ], 500); } } + + private static function configureTimeouts(): void + { + $timeout = (string) Config::get('arrays.PHPTimeOut'); + ini_set('max_execution_time', $timeout); + ini_set('max_input_time', $timeout); + ini_set('default_socket_timeout', $timeout); + } + + private static function validatedParams(Request $request): ?array + { + $params = [ + 'SHUSER' => trim((string) $request->input('SHUSER', '')), + 'MANIFEST' => trim((string) $request->input('MANIFEST', '')), + 'APCO' => trim((string) $request->input('APCO', '')), + 'APWHS' => trim((string) $request->input('APWHS', '')), + ]; + + if ($params['MANIFEST'] === '' || $params['APCO'] === '' || $params['APWHS'] === '') { + return null; + } + + return $params; + } + + private static function findOpenManifestRecords(array $params) + { + $connection = config('m3.connection', 'pgsql'); + if (! Schema::connection($connection)->hasTable('icm705m1')) { + return collect(); + } + + return DB::connection($connection)->table('icm705m1') + ->select('APMANIFEST', 'APCO', 'APWHS') + ->where('APSPARE2', '') + ->where('APCO', $params['APCO']) + ->where('APWHS', $params['APWHS']) + ->where('APMANIFEST', $params['MANIFEST']) + ->whereRaw('EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - APLDCLOZ)) / 60 > 15') + ->orderBy('APMANIFEST') + ->get(); + } + + private static function submitManifest(object $record, string $user): JsonResponse + { + $exitCode = Artisan::call('command:CallCloseAPI', [ + 'user' => $user, + 'manifest' => $record->APMANIFEST ?? '', + 'co' => $record->APCO ?? '', + 'whs' => $record->APWHS ?? '', + ]); + + if ($exitCode === 1) { + return response()->json([ + 'code' => '200', + 'status' => 'Success', + 'message' => 'Manifest Closing submitted', + ]); + } + + return self::errorResponse('Manifest Closing Failed'); + } + + private static function errorResponse(string $message): JsonResponse + { + return response()->json(['code' => '206', 'status' => 'Error', 'message' => $message]); + } } diff --git a/Modules/M3/Http/Controllers/M3ErrorsDispController.php b/Modules/M3/Http/Controllers/M3ErrorsDispController.php index 654e2001..0412419e 100644 --- a/Modules/M3/Http/Controllers/M3ErrorsDispController.php +++ b/Modules/M3/Http/Controllers/M3ErrorsDispController.php @@ -42,83 +42,100 @@ class M3ErrorsDispController extends Controller public function datatables(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->configureDataTableRuntime(); + $filters = $this->dataTableFilters($request); + $connection = config('m3.connection', 'pgsql'); + + if (! Schema::connection($connection)->hasTable('icm699m')) { + return Datatables::of(collect())->addIndexColumn()->make(true); + } + + $profile = $this->findM3Profile($connection, $filters['SHUSER']); + $query = $this->buildM3LogQuery($connection); + $this->applyM3LogFilters($query, $filters, $profile); + + return Datatables::of($query)->addIndexColumn()->make(true); + } + + private function configureDataTableRuntime(): void + { + $timeout = (string) Config::get('arrays.PHPTimeOut'); + ini_set('max_execution_time', $timeout); + ini_set('max_input_time', $timeout); + ini_set('default_socket_timeout', $timeout); ini_set('memory_limit', '1024M'); + } - // dump($request->all()); - $SHMACPKR = $request->SHMACPKR != null ? trim($request->SHMACPKR) : ''; - $SHSOURCE = $request->SHSOURCE != null ? trim($request->SHSOURCE) : ''; - $STARTDATE = $request->STARTDATE != null ? trim($request->STARTDATE) : today()->subDays(1)->format('Y-m-d'); - $ENDDATE = $request->ENDDATE != null ? trim($request->ENDDATE) : today()->format('Y-m-d'); - $SHOWERRORS = $request->SHOWERRORS != null ? trim($request->SHOWERRORS) : 'N'; - $SHUSER = m3_user() !== '' ? Str::upper(trim(m3_user())) : ''; - $SKIPDATES = $request->SKIPDATES != null ? trim($request->SKIPDATES) : 'N'; + private function dataTableFilters(Request $request): array + { + return [ + 'SHMACPKR' => trim((string) $request->input('SHMACPKR', '')), + 'SHSOURCE' => trim((string) $request->input('SHSOURCE', '')), + 'STARTDATE' => trim((string) $request->input('STARTDATE', today()->subDays(1)->format('Y-m-d'))), + 'ENDDATE' => trim((string) $request->input('ENDDATE', today()->format('Y-m-d'))), + 'SHOWERRORS' => trim((string) $request->input('SHOWERRORS', 'N')), + 'SHUSER' => m3_user() !== '' ? Str::upper(trim(m3_user())) : '', + 'SKIPDATES' => trim((string) $request->input('SKIPDATES', 'N')), + ]; + } - // Get Logged User CO & WH - // Get users from ICM428M - $ICM428M = DB::connection(config('m3.connection', 'pgsql'))->table('icm428m') + private function findM3Profile(string $connection, string $user): ?object + { + if ($user === '' || ! Schema::connection($connection)->hasTable('icm428m')) { + return null; + } + + return DB::connection($connection)->table('icm428m') ->select('R428US', 'R428CO', 'R428WH') - ->where('R428US', '=', $SHUSER) + ->where('R428US', $user) ->first(); + } - $R428US = isset($ICM428M->R428US) ? $ICM428M->R428US : ''; - $USERCO = isset($ICM428M->R428CO) ? $ICM428M->R428CO : ''; - $USERWH = isset($ICM428M->R428WH) ? $ICM428M->R428WH : ''; + private function buildM3LogQuery(string $connection) + { + $query = DB::connection($connection)->table('icm699m') + ->select('ICM699M.*', DB::raw("ICM699M.SHDT || ' ' || ICM699M.SHTIME AS SHDATETIME")) + ->distinct(); - // Merge date and time fields - $mergedDateTime = DB::raw(" ICM699M.SHDT || ' ' || ICM699M.SHTIME AS SHDATETIME"); + if (Schema::connection($connection)->hasTable('icm428m')) { + $query->addSelect('ICM428M.R428FIRST', 'ICM428M.R428FAMILY') + ->leftJoin('ICM428M', 'ICM699M.SHUSER', '=', 'ICM428M.R428US'); + } + if (Schema::connection($connection)->hasTable('icm688m1')) { + $query->addSelect('ICM688M1.A4ONO', 'ICM688M1.A4SHSHNM') + ->leftJoin('ICM688M1', 'ICM699M.SHMACPKR', '=', 'ICM688M1.A4MACPKR'); + } + if (Schema::connection($connection)->hasTable('icm666m')) { + $query->addSelect('ICM666M.A1ONO', 'ICM666M.A1SHSHNM') + ->leftJoin('ICM666M', 'ICM699M.SHMACPKR', '=', 'ICM666M.A1DELNO'); + } - $ICM699M = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m') - ->select( - 'ICM699M.*', - 'ICM699M.SHACKDATE', - 'ICM699M.SHACKTIME', - 'ICM699M.SHACKUSER', - $mergedDateTime, - 'ICM428M.R428FIRST', - 'ICM428M.R428FAMILY', - 'ICM688M1.A4ONO', - 'ICM688M1.A4SHSHNM', - 'ICM666M.A1ONO', - 'ICM666M.A1SHSHNM', - ) - ->distinct() - ->leftJoin('ICM428M', 'ICM699M.SHUSER', '=', 'ICM428M.R428US') - ->leftJoin('ICM688M1', 'ICM699M.SHMACPKR', '=', 'ICM688M1.A4MACPKR') - ->leftJoin('ICM666M', 'ICM699M.SHMACPKR', '=', 'ICM666M.A1DELNO'); + return $query; + } - if ($R428US != '') { - $ICM699M->whereIn('SHCO', [$USERCO, '']); - $ICM699M->whereIn('SHWHS', [$USERWH, '']); + private function applyM3LogFilters($query, array $filters, ?object $profile): void + { + if ($profile !== null) { + $query->whereIn('SHCO', [trim((string) ($profile->R428CO ?? '')), '']) + ->whereIn('SHWHS', [trim((string) ($profile->R428WH ?? '')), '']); } else { - $ICM699M->where('SHCO', null); - $ICM699M->where('SHWHS', null); + $query->whereNull('SHCO')->whereNull('SHWHS'); } - if ($SHMACPKR != '') { - $ICM699M->where('SHMACPKR', $SHMACPKR); + foreach (['SHMACPKR', 'SHSOURCE'] as $field) { + if ($filters[$field] !== '') { + $query->where($field, $filters[$field]); + } } - if ($SHSOURCE != '') { - $ICM699M->where('SHSOURCE', $SHSOURCE); + $query->where('SHSOURCE', '!=', 'PdfStorageAPI'); + if ($filters['SHMACPKR'] === '' && $filters['SKIPDATES'] === 'N') { + $query->whereBetween('SHDT', [$filters['STARTDATE'], $filters['ENDDATE']]); } - // Disable PDFStorage Search - $ICM699M->where('SHSOURCE', '!=', 'PdfStorageAPI'); - - if ($SHMACPKR == '' && $SKIPDATES == 'N') { - $ICM699M->whereBetween('SHDT', [$STARTDATE, $ENDDATE]); + if ($filters['SHOWERRORS'] === 'Y') { + $query->where('SHERROR', 'X'); } - - if ($SHOWERRORS == 'Y') { - $ICM699M->where('SHERROR', 'X'); - } - - // Get only M3 Logs - $ICM699M->where('SHTYPE', 'like', '%M3%'); - - return Datatables::of($ICM699M)->addIndexColumn()->make(true); + $query->where('SHTYPE', 'like', '%M3%'); } public static function update699M(Request $request)