352 lines
15 KiB
PHP
Executable File
352 lines
15 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Modules\M3\Http\Controllers\API;
|
|
|
|
use Config;
|
|
use DateTime;
|
|
use DB;
|
|
use Http;
|
|
use Illuminate\Http\Request;
|
|
use Log;
|
|
use Modules\M3\Helpers\Helper;
|
|
use Modules\M3\Http\Controllers\Controller;
|
|
use Str;
|
|
|
|
class TransferAPI extends Controller
|
|
{
|
|
private const ZERO_PAD = '000000';
|
|
private const JSON_CONTENT_TYPE = 'application/json';
|
|
private const ORDER_CATEGORY_LABEL = ' Order Category: ';
|
|
private const NO_URL_PARAMETERS = 'No URL Parameters Sent';
|
|
|
|
public function TransferAPIURL(Request $request)
|
|
{
|
|
$this->configureTimeouts();
|
|
$context = $this->transferContext($request);
|
|
|
|
try {
|
|
$response = $this->processTransfer($context);
|
|
} catch (\Throwable $throwable) {
|
|
$response = $this->exceptionResponse($context, $throwable, $context['json_in'] ?? '');
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
public function UpdateShipmentNumber(Request $request)
|
|
{
|
|
$this->configureTimeouts();
|
|
$context = $this->shipmentContext($request);
|
|
|
|
try {
|
|
$response = $this->processShipmentUpdate($context);
|
|
} catch (\Throwable $throwable) {
|
|
$response = $this->exceptionResponse($context, $throwable, $context['json'] ?? '');
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
private function configureTimeouts(): void
|
|
{
|
|
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'));
|
|
}
|
|
|
|
private function transferContext(Request $request): array
|
|
{
|
|
$params = $request->all();
|
|
|
|
return [
|
|
'params' => $params,
|
|
'user' => m3User() !== '' ? Str::upper(m3User()) : 'WebUser',
|
|
'source' => 'TransferAPIURL',
|
|
'company' => $params['COMPANY'] ?? '',
|
|
'warehouse' => $params['WHS'] ?? '',
|
|
'delivery' => $params['DELIVERYNO'] ?? self::ZERO_PAD,
|
|
];
|
|
}
|
|
|
|
private function shipmentContext(Request $request): array
|
|
{
|
|
$params = $request->all();
|
|
|
|
return [
|
|
'params' => $params,
|
|
'user' => m3User() !== '' ? Str::upper(m3User()) : 'WebUser',
|
|
'source' => 'UpdateShipmentNumber',
|
|
'company' => $params['COMPANY'] ?? '',
|
|
'warehouse' => $params['WHS'] ?? '',
|
|
'plant' => $params['PLANT'] ?? '',
|
|
'delivery' => $params['DELIVERYNO'] ?? '',
|
|
];
|
|
}
|
|
|
|
private function processTransfer(array &$context)
|
|
{
|
|
if ($context['params'] === []) {
|
|
$this->log($context, self::NO_URL_PARAMETERS, 'X', 'M3-Res-out');
|
|
return $this->errorResponse('405', self::NO_URL_PARAMETERS);
|
|
}
|
|
|
|
$context = array_merge($context, $this->transferFields($context['params']));
|
|
$this->restoreRetryPayload($context);
|
|
|
|
if ($context['part'] === '') {
|
|
return $this->errorResponse('405', 'Json Payload Not Valid, possible cause: Retry butto was clicked by another user.');
|
|
}
|
|
|
|
return $this->executeTransfer($context);
|
|
}
|
|
|
|
private function transferFields(array $params): array
|
|
{
|
|
return [
|
|
'part' => $params['PART'] ?? '', 'from' => $params['LOCATIONFROM'] ?? '',
|
|
'to' => $params['LOCATIONTO'] ?? '', 'quantity' => $params['QUANTITY'] ?? '',
|
|
'order_category' => $params['ORDERCATEGORY'] ?? '', 'lot' => $params['LOTNUMBER'] ?? '',
|
|
'co_number' => $params['CONUMBER'] ?? '', 'co_line' => $params['COLINENO'] ?? '',
|
|
'delivery' => $params['DELIVERYNO'] ?? self::ZERO_PAD, 'pick_suffix' => $params['PICKLISTSUFFIX'] ?? '',
|
|
'end_flag' => $params['ENDFLAG'] ?? '', 'type' => $params['TYPE'] ?? '',
|
|
'co_line_suffix' => $params['COLINESUFFIX'] ?? '', 'trans_type' => $params['TRANSTYPE'] ?? '',
|
|
'shdt' => $params['SHDT'] ?? '', 'shtime' => $params['SHTIME'] ?? '', 'shtype' => $params['SHTYPE'] ?? '',
|
|
];
|
|
}
|
|
|
|
private function restoreRetryPayload(array &$context): void
|
|
{
|
|
if ($context['trans_type'] !== 'R') {
|
|
return;
|
|
}
|
|
|
|
$record = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
|
|
->select('SHDATA')->where('SHMACPKR', $context['delivery'])->where('SHDT', $context['shdt'])
|
|
->where('SHTIME', $context['shtime'])->where('SHCO', $context['company'])->where('SHWHS', $context['warehouse'])
|
|
->where('SHSOURCE', $context['source'])->where('SHERROR', 'X')->where('SHTYPE', $context['shtype'])->first();
|
|
|
|
$payload = $this->retryPayload($record->SHDATA ?? '');
|
|
if ($payload !== []) {
|
|
$context = array_merge($context, $this->transferFields($payload));
|
|
}
|
|
|
|
DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
|
|
->where('SHMACPKR', $context['delivery'])->where('SHDT', $context['shdt'])->where('SHTIME', $context['shtime'])
|
|
->where('SHCO', $context['company'])->where('SHWHS', $context['warehouse'])->where('SHSOURCE', $context['source'])
|
|
->where('SHERROR', 'X')->where('SHTYPE', $context['shtype'])
|
|
->update(['SHERROR' => 'Z', 'SHACKUSER' => $context['user'], 'SHACKDATE' => date('Y-m-d'), 'SHACKTIME' => date('H:i:s')]);
|
|
}
|
|
|
|
private function retryPayload(string $data): array
|
|
{
|
|
$json = '';
|
|
$marker = 'M3-JSN-out';
|
|
$urlMarker = 'url:http://127.0.0.1:10005/api/TransferAPIURL?';
|
|
$position = strpos($data, $marker);
|
|
$urlPosition = strpos($data, $urlMarker);
|
|
|
|
if ($position !== false) {
|
|
$json = substr($data, $position + strlen($marker));
|
|
} elseif ($urlPosition !== false) {
|
|
parse_str(substr($data, $urlPosition + strlen($urlMarker)), $payload);
|
|
return is_array($payload) ? $payload : [];
|
|
}
|
|
|
|
$start = strpos($json, '{');
|
|
$end = strrpos($json, '}');
|
|
if ($start === false || $end === false || $end < $start) {
|
|
return [];
|
|
}
|
|
|
|
$decoded = json_decode(substr($json, $start, $end - $start + 1), true);
|
|
return is_array($decoded) ? $decoded : [];
|
|
}
|
|
|
|
private function executeTransfer(array &$context)
|
|
{
|
|
$line = $this->transferLine($context);
|
|
$context['brad_line'] = $line->A5BRADLN ?? '';
|
|
$context['co_line_suffix'] = $line->A5PKSFX ?? '';
|
|
$context['plant'] = $line->A5PLANT ?? '';
|
|
$context['sent_picker'] = $line->A5SENTPKR ?? '';
|
|
$context['json_in'] = $this->transferJson($context, false);
|
|
$json = $this->transferJson($context, true);
|
|
$this->log($context, $json, '', 'M3-Req-out');
|
|
|
|
$token = $this->token($context);
|
|
if (! is_string($token)) {
|
|
return $token;
|
|
}
|
|
|
|
$result = $this->callM3($token, $json);
|
|
return $this->transferResult($context, $result);
|
|
}
|
|
|
|
private function transferLine(array $context): ?object
|
|
{
|
|
$query = DB::connection(config('m3.connection', 'pgsql'))->table('icm688m2')
|
|
->select('A5BRADLN', 'A5PKSFX', 'A5PLANT', 'A5SENTPKR')->distinct()
|
|
->where('A5CO', $context['company'])->where('A5WHS', $context['warehouse'])->where('A5MACPKR', $context['delivery']);
|
|
if ($context['co_line'] !== '') {
|
|
$query->where('A5PKLN', $context['co_line']);
|
|
}
|
|
return $query->first();
|
|
}
|
|
|
|
private function transferJson(array $c, bool $useBradLine): string
|
|
{
|
|
return (string) json_encode([
|
|
'DIVI' => $c['company'], 'WHS' => $c['warehouse'], 'PART' => $c['part'], 'LOCATIONFROM' => $c['from'],
|
|
'LOCATIONTO' => $c['to'], 'QUANTITY' => $c['quantity'], 'ORDERCATEGORY' => $c['order_category'], 'LOTNUMBER' => $c['lot'],
|
|
'CONUMBER' => $c['co_number'], 'COLINENO' => $useBradLine ? $c['brad_line'] : $c['co_line'], 'DELIVERYNO' => $c['delivery'],
|
|
'PICKLISTSUFFIX' => $c['pick_suffix'], 'ENDFLAG' => $c['end_flag'], 'TYPE' => $c['type'], 'COLINESUFFIX' => $c['co_line_suffix'],
|
|
], JSON_UNESCAPED_SLASHES);
|
|
}
|
|
|
|
private function processShipmentUpdate(array &$context)
|
|
{
|
|
$shipment = $context['params']['A4SPR15A01'] ?? '';
|
|
$pickers = $this->shipmentPickers($context, $shipment);
|
|
$invalid = $this->shipmentValidationResponse($context, $shipment, $pickers);
|
|
if ($invalid !== null) {
|
|
return $invalid;
|
|
}
|
|
|
|
$context['type'] = 'UpdateShipmentNumber';
|
|
$context['json'] = (string) json_encode([
|
|
'DIVI' => $context['company'], 'WHS' => $context['warehouse'],
|
|
'ShipmentNumber' => ['Number' => $shipment, 'DeliveryNumbers' => $pickers],
|
|
'TYPE' => $context['type'], 'ENDFLAG' => '',
|
|
], JSON_UNESCAPED_SLASHES);
|
|
$this->log($context, $context['json'], '', 'M3-Req-out');
|
|
|
|
$token = $this->token($context);
|
|
if (! is_string($token)) {
|
|
return $token;
|
|
}
|
|
|
|
return $this->shipmentResult($context, $this->callM3($token, $context['json']));
|
|
}
|
|
|
|
private function shipmentValidationResponse(array $context, string $shipment, array $pickers)
|
|
{
|
|
if ($context['params'] === []) {
|
|
$this->log($context, self::NO_URL_PARAMETERS, 'X', 'M3-Res-out');
|
|
return $this->errorResponse('405', self::NO_URL_PARAMETERS);
|
|
}
|
|
if ($shipment === '' || $pickers === []) {
|
|
return $this->errorResponse('405', 'Json Payload Not Valid, missing Parameter or no Macpicker # found.');
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private function shipmentPickers(array $context, string $shipment): array
|
|
{
|
|
if ($shipment === '') {
|
|
return [];
|
|
}
|
|
$query = DB::connection(config('m3.connection', 'pgsql'))->table('icm688m1')->select('A4MACPKR')
|
|
->where('A4CO', $context['company'])->where('A4WHS', $context['warehouse'])->where('A4PLANT', $context['plant'])
|
|
->where('A4SPR15A01', $shipment);
|
|
if ($context['delivery'] !== '') {
|
|
$query->where('A4MACPKR', $context['delivery']);
|
|
}
|
|
return $query->get()->pluck('A4MACPKR')->all();
|
|
}
|
|
|
|
private function token(array $context)
|
|
{
|
|
$response = Helper::checkReceiveTokenV2();
|
|
$data = $response->getData();
|
|
if ((string) ($data->code ?? '') === '200') {
|
|
return (string) ($data->token ?? '');
|
|
}
|
|
$this->log($context, (string) ($data->message ?? ''), '', 'M3-Res-out');
|
|
return $response;
|
|
}
|
|
|
|
private function callM3(string $token, string $json): array
|
|
{
|
|
$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(env('ENDPOINTURL') . 'APIFLOWS/Sentinel/TransferAPI?ionapiRespStyle=sync');
|
|
$status = $response->getStatusCode();
|
|
$jsonResponse = '';
|
|
$errors = 'Error: No Response from M3';
|
|
if ((string) $status === '200') {
|
|
$contents = json_decode($response->getBody()->getContents());
|
|
$jsonResponse = json_encode($contents);
|
|
$errors = Helper::checkM3Errors($contents->result ?? null);
|
|
}
|
|
return ['status' => $status, 'json' => $jsonResponse, 'errors' => $errors, 'metrics' => $this->responseMetrics($response)];
|
|
}
|
|
|
|
private function responseMetrics($response): string
|
|
{
|
|
$session = $response->hasHeader('x-transaction-id') ? '-sessionID:' . $response->header('x-transaction-id') : '-sessionID';
|
|
$raw = $response->hasHeader('x-response-time') ? ltrim($response->header('x-response-time'), '-') : '0';
|
|
$dateTime = new DateTime;
|
|
$dateTime->setTimestamp(floatval($raw) / 1000);
|
|
$responseTime = $raw !== '' ? '-M3ResponseTime:' . $dateTime->format('d/m/Y H:i:s.v') : '-M3ResponseTime';
|
|
$transfer = $response->transferStats->getTransferTime();
|
|
return $session . $responseTime . ($transfer !== null ? '-M3transferTime:' . $transfer : '-M3transferTime');
|
|
}
|
|
|
|
private function transferResult(array $c, array $result)
|
|
{
|
|
$prefix = $result['status'] . 'TYPE :' . $c['type'] . self::ORDER_CATEGORY_LABEL . $c['order_category'] . '-';
|
|
if ($result['errors'] !== '') {
|
|
$this->log($c, $result['status'] . '-' . $result['errors'] . '-TYPE:' . $c['type'] . self::ORDER_CATEGORY_LABEL . $c['order_category'] . '-' . $result['json'] . 'M3-JSN-out' . $c['json_in'] . $result['metrics'], 'X', 'M3-Res-out');
|
|
return $this->errorResponse('206', $result['status'] . '-' . $result['errors'] . '- TYPE :' . $c['type'] . self::ORDER_CATEGORY_LABEL . $c['order_category'] . '-' . $result['json']);
|
|
}
|
|
$this->log($c, $prefix . $result['json'] . $result['metrics'], '', 'M3-Res-out');
|
|
$this->insertTransferAudit($c);
|
|
return response()->json(['code' => $result['status'], 'status' => 'Success']);
|
|
}
|
|
|
|
private function shipmentResult(array $c, array $result)
|
|
{
|
|
if ($result['errors'] !== '') {
|
|
$this->log($c, $result['status'] . '-' . $result['errors'] . '-TYPE:' . $c['type'] . '-' . $result['json'] . 'M3-JSN-out' . $c['json'] . $result['metrics'], 'X', 'M3-Res-out');
|
|
return $this->errorResponse('206', $result['status'] . '-' . $result['errors'] . '- TYPE :' . $c['type'] . '-' . $result['json']);
|
|
}
|
|
$this->log($c, $result['status'] . 'TYPE :' . $c['type'] . '-' . $result['json'] . $result['metrics'], '', 'M3-Res-out');
|
|
return response()->json(['code' => $result['status'], 'status' => 'Success']);
|
|
}
|
|
|
|
private function insertTransferAudit(array $c): void
|
|
{
|
|
DB::connection(config('m3.connection', 'pgsql'))->table('icm799m')->insert([
|
|
'IMDIV' => $c['company'], 'IMPLANT' => $c['plant'], 'IMWHS' => $c['warehouse'], 'IMSENTPKR' => $c['sent_picker'],
|
|
'IMDELNO' => $c['delivery'], 'IMORDER' => $c['co_number'], 'IMLINE' => $c['co_line'], 'IMBRADLN' => $c['brad_line'],
|
|
'IMSUFFIX' => $c['pick_suffix'], 'IMPART' => $c['part'], 'IMLOT' => $c['lot'], 'IMQTY' => $c['quantity'],
|
|
'IMFRLOC' => $c['from'], 'IMTOLOC' => $c['to'], 'IMDATE' => date('Ymd'), 'IMTIME' => date('His'),
|
|
]);
|
|
}
|
|
|
|
private function log(array $c, string $message, string $error, string $type): void
|
|
{
|
|
Helper::logToICM699M([
|
|
'SHMACPKR' => $c['delivery'] ?: self::ZERO_PAD, 'SHDT' => date('Y-m-d'), 'SHTIME' => date('H:i:s'),
|
|
'SHERROR' => $error, 'SHINTBOX' => '', 'SHTYPE' => $type, 'SHDATA' => $message,
|
|
'SHUSER' => $c['user'], 'SHSOURCE' => $c['source'], 'SHCO' => $c['company'], 'SHWHS' => $c['warehouse'],
|
|
]);
|
|
}
|
|
|
|
private function exceptionResponse(array $context, \Throwable $throwable, string $json)
|
|
{
|
|
if (str_contains($throwable->getMessage(), 'cURL error')) {
|
|
$this->log($context, 'Curl Error: Communication with M3 Timed Out M3-JSN-out' . $json, 'X', 'M3-Res-out');
|
|
return response()->json(['code' => '405', 'status' => 'error', 'message' => 'Curl Error: Communication with M3 Timed Out.']);
|
|
}
|
|
$this->log($context, 'An Error Occured - ' . substr((string) $throwable, 0, 300), 'X', 'M3-Err-out');
|
|
Log::error($throwable);
|
|
return response()->json(['code' => '405', 'status' => 'error', 'message' => 'Uncaught Error']);
|
|
}
|
|
|
|
private function errorResponse(string $code, string $message)
|
|
{
|
|
return response()->json(['code' => $code, 'status' => 'Error', 'message' => $message]);
|
|
}
|
|
}
|