Files
WMS/Modules/M3/Http/Controllers/API/CloseShipmentPackageBuilder.php
T

226 lines
11 KiB
PHP

<?php
namespace Modules\M3\Http\Controllers\API;
use Config;
use DB;
use File;
use Illuminate\Http\Request;
use Modules\M3\Http\Controllers\GetContentsController;
final class CloseShipmentPackageBuilder
{
public function __construct(private readonly array $context, private readonly mixed $freightTerms)
{
}
public function build(array $shipment, array $trailer): array
{
$state = ['packages' => [], 'errors' => '', 'coCharge' => [], 'details' => []];
foreach ($this->packageRows($shipment) as $row) {
$details = $this->packageDetails($shipment, $trailer, $row);
$state['errors'] .= $details['errors'];
$state['packages'][] = $details['package'];
$state['details'] = $details;
$state['coCharge'] = $this->coCharge($details);
}
return $state;
}
public function closeShipmentJson(array $shipment, array $trailer, array $state, int $endFlag): string
{
$details = $state['details'];
$payload = [
'DIVI' => $shipment['company'], 'WHS' => $shipment['whs'], 'DELIVERYNO' => $shipment['delivery'],
'PICKLISTSUFFIX' => $shipment['release'], 'TYPE' => 'CloseShipment', 'SCAC' => $details['scac'],
'CARRIER' => $details['carrier'], 'TRANSPORTMODE' => $details['transportMode'],
'SERVICETYPE' => $details['serviceType'], 'TRACKINGNUMBER' => $details['tracking'],
'PRONUMBER' => $trailer['pro'], 'BILLOFLADING' => $details['bol'], 'TRAILERNUMBER' => $trailer['trailer'],
'PACKAGES' => $state['packages'], 'DELIVERYNOTEDOC' => $this->documentUrl('PackingList', $shipment['delivery'], $shipment['delivery']),
'COMMODITIES' => $details['commodities']['value'], 'ENDFLAG' => $endFlag,
];
$serviceFields = $state['coCharge'] !== []
? ['shipType' => $details['shipType'], 'carrierServiceDescription' => $details['serviceName'], 'modeOfTransport' => $details['shipVia1']]
: ['ServiceType' => $details['shipType'], 'ServiceLevelCode' => $details['serviceName'], 'TransportationMode' => $details['shipVia1']];
return json_encode(array_merge($state['coCharge'], $payload, $serviceFields));
}
private function packageRows(array $shipment)
{
return DB::connection(config('m3.connection', 'pgsql'))->table('icm695m1')
->selectRaw('AYMACPKR,AYINTBOX,AYHEIGHT,AYWIDTH,AYDEPTH,AYTYPE,AYLNWGHT,AYID,AYSPR1A01,TRIM(AYSPR15A01) AS SCAC,TRIM(AYSPR30A01) AS TRACKING,AYTRUCK AS AGGREGATION,A4RATETCP AS FREIGHT,A4OFSTRM AS FREIGHTTERMS,A4RATEM3,A4CLOSEM3,A4SHIPTYPE,A4REFORD,A4ONO,A4SHIPVIA,A42SSVCNAM,A4SHIPVIA1')
->leftJoin('ICM688M1', function ($join) {
$join->on('AYCO', '=', 'A4CO')->on('AYWHS', '=', 'A4WHS')->on('AYMACPKR', '=', 'A4MACPKR');
})
->where('AYMACPKR', $shipment['delivery'])->where('AYCO', $shipment['company'])
->where('AYWHS', $shipment['whs'])->orderBy('AYINTBOX')->get();
}
private function packageDetails(array $shipment, array $trailer, object $row): array
{
$details = $this->rowDetails($row);
$details['carrier'] = $this->carrierName($details['scac']);
$details['bol'] = $details['scac'] === 'RETR' ? $trailer['pro'] : $details['bol'];
$details['errors'] = $this->shippingErrors($details, $trailer['pro']);
$details['errors'] .= $this->previousTransmissionError($shipment);
$details['bundle'] = $this->bundleType($details['bundle'], $row);
$details['commodities'] = $this->commodities($shipment['delivery'], $details['errors']);
$details['errors'] = $details['commodities']['errors'];
if ($details['billable'] === 'Y' && (float) $details['freight'] == 0 && $details['rateM3'] === '') {
$details['errors'] .= ' Please manually add a freight charge for this Prepaid and Add order; ';
}
$labels = $this->labels($shipment, $details['box'], $details['gs1Required']);
$details['errors'] .= $labels['error'];
$details['package'] = $this->packageArray($details, $labels);
return $details;
}
private function rowDetails(object $row): array
{
$freightTerms = trim($row->FREIGHTTERMS);
$currentFreightTerms = $this->freightTerms->where('FREIGHTTERMS', $freightTerms)->first();
return [
'shipType' => trim($row->A4SHIPTYPE), 'serviceName' => trim($row->A42SSVCNAM),
'shipVia1' => trim($row->A4SHIPVIA1), 'freightTerms' => $freightTerms,
'rateM3' => trim($row->A4RATEM3), 'freight' => trim($row->FREIGHT),
'refOrder' => trim($row->A4REFORD), 'orderNo' => trim($row->A4ONO),
'delivery' => trim($row->AYMACPKR), 'box' => trim($row->AYINTBOX),
'tracking' => trim($row->TRACKING), 'scac' => trim($row->SCAC),
'bundle' => trim($row->AYID) !== '' ? trim($row->AYID) : '',
'weight' => trim($row->AYLNWGHT), 'height' => trim($row->AYHEIGHT),
'width' => trim($row->AYWIDTH), 'depth' => trim($row->AYDEPTH),
'bol' => isset($row->AYSPR1A01) ? trim($row->AYSPR1A01) : '',
'transportMode' => trim(str_replace('&', '_', $row->A4SHIPVIA ?? '')),
'serviceType' => trim(str_replace('&', '_', $row->A4SHIPVIA ?? '')),
'billable' => isset($currentFreightTerms->BILLABLE) ? trim($currentFreightTerms->BILLABLE) : 'N',
'gs1Required' => $this->gs1Required($row), 'errors' => '',
];
}
private function shippingErrors(array $details, string $proNumber): string
{
$missingTracking = $details['scac'] === '' || $details['tracking'] === '';
$missingLtl = $details['shipType'] === 'LTL' && ($missingTracking || $details['bol'] === '');
$errors = ($missingTracking || $missingLtl)
? " Missing one or more of tracking number, BOL, or carrier. Please enter this data manually in 'invoicing' screen and retry this API call;"
: '';
if ($proNumber === '' && $details['scac'] === 'RETR') {
$errors .= ' Missing PARS info for this RETRans order. Please contact MPBA to ask them to enter the value for you, and then you can retry; ';
}
return $errors;
}
private function carrierName(string $scac): string
{
if ($scac === '') {
return '';
}
$row = DB::connection(config('m3.connection', 'pgsql'))->table('icm160m')
->select('CANAM')->where('CANUM', substr($scac, 0, 4))->where('CACO', $this->context['company'])->first();
return isset($row->CANAM) ? trim(str_replace('&', '_', $row->CANAM)) : '';
}
private function previousTransmissionError(array $shipment): string
{
$row = DB::connection(config('m3.connection', 'pgsql'))->table('icm699m')
->select('SHMACPKR')->where('SHMACPKR', $shipment['delivery'])
->where('SHCO', $shipment['company'])->where('SHWHS', $shipment['whs'])
->whereIn('SHSOURCE', ['TransferAPIURL', 'CloseShipmentCoChargeAPI', 'PackSyst4tray'])
->where('SHERROR', 'X')->whereIn('SHTYPE', ['M3-Res-out', 'M3-QtyChg'])->first();
return isset($row->SHMACPKR)
? ' There are previous M3 transmissions in error for this delivery note that have not been addressed; '
: '';
}
private function bundleType(string $bundle, object $row): string
{
if ($bundle !== '') {
return $bundle;
}
$record = DB::connection(config('m3.connection', 'pgsql'))->table('icm693m')
->select('AEID')->where('AESPR1A01', 'D')->where('AETYPE', trim($row->AYTYPE))->first();
return isset($record->AEID) ? trim($record->AEID) : '';
}
private function commodities(string $delivery, string $errors): array
{
$request = new Request;
$request->setMethod('POST');
$request->request->add(['MACPKR' => $delivery]);
$responseData = (new GetContentsController)->processShipment($request)->getData();
$commodities = empty($responseData->error) ? ($responseData->commodities ?? '') : '';
return ['value' => $commodities, 'errors' => $commodities !== '' ? $errors : $errors . ' Could not find Commodities; '];
}
private function labels(array $shipment, string $box, bool $gs1Required): array
{
$carrier = $this->documentUrl('CarrierLabel', $shipment['delivery'], $box);
$content = $this->documentUrl('ContentLabel', $shipment['delivery'], $box);
$gs1 = '';
if ($gs1Required) {
$row = DB::connection(config('m3.connection', 'pgsql'))->table('icm695m3')
->select('A2GS1LABEL')->where('A2CO', $shipment['company'])->where('A2WHS', $shipment['whs'])
->where('A2INTBOX', $box)->first();
$gs1 = isset($row->A2GS1LABEL) ? trim($row->A2GS1LABEL) : '';
}
return [
'carrier' => $carrier, 'content' => $content, 'gs1' => $gs1,
'error' => $gs1Required && $gs1 === ''
? ' Missing G1-128 Label. Please contact MPBA to ask them to enter the value for you, and then you can retry; '
: '',
];
}
private function documentUrl(string $type, string $delivery, string $documentNumber): string
{
$key = $type . '/' . $type . '_' . $delivery . '_' . $documentNumber . '_' . $this->context['company'] . '_' . $this->context['whs'];
if (! File::exists(Config::get('arrays.PdfPath') . $key . '.pdf')) {
return '';
}
return route('m3.M3GetPDFFromCache.index', ['MACPKR' => $delivery, 'DOCTYPE' => $type]);
}
private function gs1Required(object $row): bool
{
$record = DB::connection(config('m3.connection', 'pgsql'))->table('icm666m')
->select('A1SPR1A01')->where('A1CO', $this->context['company'])->where('A1WHS', $this->context['whs'])
->where('A1DELNO', trim($row->AYMACPKR))->first();
return (int) ($record->A1SPR1A01 ?? 0) === 1;
}
private function packageArray(array $details, array $labels): array
{
return [
'TRACKINGID' => $details['tracking'], 'BUNDLES' => $details['bundle'],
'WEIGHT' => $details['weight'], 'HEIGHT' => $details['height'], 'WIDTH' => $details['width'],
'LENGTH' => $details['depth'], 'SSCC' => $labels['gs1'],
'SHIPLABELDOC' => $labels['carrier'], 'CONTENTLABELDOC' => $labels['content'],
];
}
private function coCharge(array $details): array
{
if ($details['billable'] !== 'Y' || (float) $details['freight'] <= 0 || $details['rateM3'] !== '') {
return [];
}
$discountKey = $details['shipType'] === 'LTL'
? 'arrays.misc.discountCarrierRateForCustomerPriceLTL'
: 'arrays.misc.discountCarrierRateForCustomerPriceParcel';
$shippingToBill = (float) $details['freight'] * (100.00 - Config::get($discountKey)) / 100;
return ['QUANTITY' => round($shippingToBill, 2), 'ORDERCATEGORY' => $details['refOrder'], 'CONUMBER' => $details['orderNo']];
}
}