Skip to content

Commit 67fbf08

Browse files
authored
Merge pull request #7399 from magento-l3/ACP2E-120-2.4.4
ACP2E-120: Magento_Fedex Package description and handling fee issue
2 parents b225913 + 7b3359d commit 67fbf08

File tree

5 files changed

+381
-226
lines changed

5 files changed

+381
-226
lines changed

app/code/Magento/Fedex/Model/Carrier.php

Lines changed: 84 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Magento\Framework\Webapi\Soap\ClientFactory;
1515
use Magento\Framework\Xml\Security;
1616
use Magento\Quote\Model\Quote\Address\RateRequest;
17+
use Magento\Shipping\Model\Carrier\AbstractCarrier;
1718
use Magento\Shipping\Model\Carrier\AbstractCarrierOnline;
1819
use Magento\Shipping\Model\Rate\Result;
1920

@@ -31,21 +32,21 @@ class Carrier extends AbstractCarrierOnline implements \Magento\Shipping\Model\C
3132
*
3233
* @var string
3334
*/
34-
const CODE = 'fedex';
35+
public const CODE = 'fedex';
3536

3637
/**
3738
* Purpose of rate request
3839
*
3940
* @var string
4041
*/
41-
const RATE_REQUEST_GENERAL = 'general';
42+
public const RATE_REQUEST_GENERAL = 'general';
4243

4344
/**
4445
* Purpose of rate request
4546
*
4647
* @var string
4748
*/
48-
const RATE_REQUEST_SMARTPOST = 'SMART_POST';
49+
public const RATE_REQUEST_SMARTPOST = 'SMART_POST';
4950

5051
/**
5152
* Code of the carrier
@@ -123,7 +124,7 @@ class Carrier extends AbstractCarrierOnline implements \Magento\Shipping\Model\C
123124
protected $_productCollectionFactory;
124125

125126
/**
126-
* @inheritdoc
127+
* @var string[]
127128
*/
128129
protected $_debugReplacePrivateDataKeys = [
129130
'Key', 'Password', 'MeterNumber',
@@ -381,15 +382,16 @@ public function setRequest(RateRequest $request)
381382
$r->setDestCity($request->getDestCity());
382383
}
383384

384-
$weight = $this->getTotalNumOfBoxes($request->getPackageWeight());
385-
$r->setWeight($weight);
386385
if ($request->getFreeMethodWeight() != $request->getPackageWeight()) {
387386
$r->setFreeMethodWeight($request->getFreeMethodWeight());
388387
}
389388

389+
$r->setWeight($request->getPackageWeight());
390390
$r->setValue($request->getPackagePhysicalValue());
391391
$r->setValueWithDiscount($request->getPackageValueWithDiscount());
392392

393+
$r->setPackages($this->createPackages((float) $request->getPackageWeight(), (array) $request->getPackages()));
394+
393395
$r->setMeterNumber($this->getConfigData('meter_number'));
394396
$r->setKey($this->getConfigData('key'));
395397
$r->setPassword($this->getConfigData('password'));
@@ -445,7 +447,6 @@ protected function _formRateRequest($purpose)
445447
'DropoffType' => $r->getDropoffType(),
446448
'ShipTimestamp' => date('c'),
447449
'PackagingType' => $r->getPackaging(),
448-
'TotalInsuredValue' => ['Amount' => $r->getValue(), 'Currency' => $this->getCurrencyCode()],
449450
'Shipper' => [
450451
'Address' => ['PostalCode' => $r->getOrigPostal(), 'CountryCode' => $r->getOrigCountry()],
451452
],
@@ -464,37 +465,36 @@ protected function _formRateRequest($purpose)
464465
'CustomsValue' => ['Amount' => $r->getValue(), 'Currency' => $this->getCurrencyCode()],
465466
],
466467
'RateRequestTypes' => 'LIST',
467-
'PackageCount' => '1',
468468
'PackageDetail' => 'INDIVIDUAL_PACKAGES',
469-
'RequestedPackageLineItems' => [
470-
'0' => [
471-
'Weight' => [
472-
'Value' => (double)$r->getWeight(),
473-
'Units' => $this->getConfigData('unit_of_measure'),
474-
],
475-
'GroupPackageCount' => 1,
476-
],
477-
],
478469
],
479470
];
480471

472+
foreach ($r->getPackages() as $packageNum => $package) {
473+
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][$packageNum]['GroupPackageCount'] = 1;
474+
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][$packageNum]['Weight']['Value']
475+
= (double) $package['weight'];
476+
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][$packageNum]['Weight']['Units']
477+
= $this->getConfigData('unit_of_measure');
478+
if (isset($package['price'])) {
479+
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][$packageNum]['InsuredValue']['Amount']
480+
= (double) $package['price'];
481+
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][$packageNum]['InsuredValue']['Currency']
482+
= $this->getCurrencyCode();
483+
}
484+
}
485+
486+
$ratesRequest['RequestedShipment']['PackageCount'] = count($r->getPackages());
487+
481488
if ($r->getDestCity()) {
482489
$ratesRequest['RequestedShipment']['Recipient']['Address']['City'] = $r->getDestCity();
483490
}
484491

485-
if ($purpose == self::RATE_REQUEST_GENERAL) {
486-
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][0]['InsuredValue'] = [
487-
'Amount' => $r->getValue(),
488-
'Currency' => $this->getCurrencyCode(),
492+
if ($purpose == self::RATE_REQUEST_SMARTPOST) {
493+
$ratesRequest['RequestedShipment']['ServiceType'] = self::RATE_REQUEST_SMARTPOST;
494+
$ratesRequest['RequestedShipment']['SmartPostDetail'] = [
495+
'Indicia' => (double)$r->getWeight() >= 1 ? 'PARCEL_SELECT' : 'PRESORTED_STANDARD',
496+
'HubId' => $this->getConfigData('smartpost_hubid'),
489497
];
490-
} else {
491-
if ($purpose == self::RATE_REQUEST_SMARTPOST) {
492-
$ratesRequest['RequestedShipment']['ServiceType'] = self::RATE_REQUEST_SMARTPOST;
493-
$ratesRequest['RequestedShipment']['SmartPostDetail'] = [
494-
'Indicia' => (double)$r->getWeight() >= 1 ? 'PARCEL_SELECT' : 'PRESORTED_STANDARD',
495-
'HubId' => $this->getConfigData('smartpost_hubid'),
496-
];
497-
}
498498
}
499499

500500
return $ratesRequest;
@@ -632,6 +632,40 @@ protected function _prepareRateResponse($response)
632632
return $result;
633633
}
634634

635+
/**
636+
* Get final price for shipping method with handling fee per package
637+
*
638+
* @param float $cost
639+
* @param string $handlingType
640+
* @param float $handlingFee
641+
* @return float
642+
*/
643+
protected function _getPerpackagePrice($cost, $handlingType, $handlingFee)
644+
{
645+
if ($handlingType == AbstractCarrier::HANDLING_TYPE_PERCENT) {
646+
return $cost + $cost * $this->_numBoxes * $handlingFee / 100;
647+
}
648+
649+
return $cost + $this->_numBoxes * $handlingFee;
650+
}
651+
652+
/**
653+
* Get final price for shipping method with handling fee per order
654+
*
655+
* @param float $cost
656+
* @param string $handlingType
657+
* @param float $handlingFee
658+
* @return float
659+
*/
660+
protected function _getPerorderPrice($cost, $handlingType, $handlingFee)
661+
{
662+
if ($handlingType == self::HANDLING_TYPE_PERCENT) {
663+
return $cost + $cost * $handlingFee / 100;
664+
}
665+
666+
return $cost + $handlingFee;
667+
}
668+
635669
/**
636670
* Get origin based amount form response of rate estimation
637671
*
@@ -809,14 +843,6 @@ protected function _parseXmlResponse($response)
809843
if (strlen(trim($response)) > 0) {
810844
$xml = $this->parseXml($response, \Magento\Shipping\Model\Simplexml\Element::class);
811845
if (is_object($xml)) {
812-
if (is_object($xml->Error) && is_object($xml->Error->Message)) {
813-
$errorTitle = (string)$xml->Error->Message;
814-
} elseif (is_object($xml->SoftError) && is_object($xml->SoftError->Message)) {
815-
$errorTitle = (string)$xml->SoftError->Message;
816-
} else {
817-
$errorTitle = 'Sorry, something went wrong. Please try again or contact us and we\'ll try to help.';
818-
}
819-
820846
$allowedMethods = explode(",", $this->getConfigData('allowed_methods'));
821847

822848
foreach ($xml->Entry as $entry) {
@@ -833,11 +859,7 @@ protected function _parseXmlResponse($response)
833859
}
834860

835861
asort($priceArr);
836-
} else {
837-
$errorTitle = 'Response is in the wrong format.';
838862
}
839-
} else {
840-
$errorTitle = 'For some reason we can\'t retrieve tracking info right now.';
841863
}
842864

843865
$result = $this->_rateFactory->create();
@@ -1212,6 +1234,7 @@ public function getResponse()
12121234
}
12131235
}
12141236
}
1237+
// phpstan:ignore
12151238
if (empty($statuses)) {
12161239
$statuses = __('Empty response');
12171240
}
@@ -1821,4 +1844,24 @@ private function getPaymentType(DataObject $request): string
18211844
? 'RECIPIENT'
18221845
: 'SENDER';
18231846
}
1847+
1848+
/**
1849+
* Creates packages for rate request.
1850+
*
1851+
* @param float $totalWeight
1852+
* @param array $packages
1853+
* @return array
1854+
*/
1855+
private function createPackages(float $totalWeight, array $packages): array
1856+
{
1857+
if (empty($packages)) {
1858+
$dividedWeight = $this->getTotalNumOfBoxes($totalWeight);
1859+
for ($i=0; $i < $this->_numBoxes; $i++) {
1860+
$packages[$i]['weight'] = $dividedWeight;
1861+
}
1862+
}
1863+
$this->_numBoxes = count($packages);
1864+
1865+
return $packages;
1866+
}
18241867
}

app/code/Magento/Shipping/Model/Shipping.php

Lines changed: 49 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -321,14 +321,12 @@ public function collectCarrierRates($carrierCode, $request)
321321
//Multiple shipments
322322
/** @var PackageResult $result */
323323
$result = $this->packageResultFactory->create();
324-
foreach ($packages as $weight => $packageCount) {
325-
$request->setPackageWeight($weight);
326-
$packageResult = $carrier->collectRates($request);
327-
if (!$packageResult) {
328-
return $this;
329-
} else {
330-
$result->appendPackageResult($packageResult, $packageCount);
331-
}
324+
$request->setPackages($packages);
325+
$packageResult = $carrier->collectRates($request);
326+
if (!$packageResult) {
327+
return $this;
328+
} else {
329+
$result->appendPackageResult($packageResult, 1);
332330
}
333331
}
334332
}
@@ -375,6 +373,10 @@ public function composePackagesForCarrier($carrier, $request)
375373
continue;
376374
}
377375

376+
if ($item->getFreeShipping()) {
377+
continue;
378+
}
379+
378380
$qty = $item->getQty();
379381
$changeQty = true;
380382
$checkWeight = true;
@@ -389,7 +391,7 @@ public function composePackagesForCarrier($carrier, $request)
389391
: $item->getParentItem()->getQty() * $item->getQty();
390392
}
391393

392-
$itemWeight = $item->getWeight();
394+
$itemWeight = (float) $item->getWeight();
393395
if ($item->getIsQtyDecimal()
394396
&& $item->getProductType() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
395397
) {
@@ -429,64 +431,64 @@ public function composePackagesForCarrier($carrier, $request)
429431

430432
if (!empty($decimalItems)) {
431433
foreach ($decimalItems as $decimalItem) {
432-
$weightItems[] = array_fill(0, $decimalItem['qty'] * $qty, $decimalItem['weight']);
434+
$weightItems[] = array_fill(
435+
0,
436+
$decimalItem['qty'] * $qty,
437+
[
438+
'weight' => $decimalItem['weight'],
439+
'price' => $item->getBasePrice()
440+
]
441+
);
433442
}
434443
} else {
435-
$weightItems[] = array_fill(0, $qty, $itemWeight);
444+
$weightItems[] = array_fill(
445+
0,
446+
$qty,
447+
[
448+
'weight' => $itemWeight,
449+
'price' => $item->getBasePrice()
450+
]
451+
);
436452
}
437453
}
438454
$fullItems = array_merge($fullItems, ...$weightItems);
439-
sort($fullItems);
440455

441456
return $this->_makePieces($fullItems, $maxWeight);
442457
}
443458

444459
/**
445-
* Make pieces
446-
*
447-
* Compose packages list based on given items, so that each package is as heavy as possible
460+
* Compose order items into packages using first fit decreasing algorithm
448461
*
449-
* @param array $items
450-
* @param float $maxWeight
462+
* @param array $orderItems
463+
* @param float $maxPackageWeight
451464
* @return array
452465
*/
453-
protected function _makePieces($items, $maxWeight)
466+
protected function _makePieces(array $orderItems, float $maxPackageWeight): array
454467
{
455-
$pieces = [];
456-
if (!empty($items)) {
457-
$sumWeight = 0;
468+
$packages = [];
458469

459-
$reverseOrderItems = $items;
460-
arsort($reverseOrderItems);
470+
usort($orderItems, function ($a, $b) {
471+
return $b['weight'] <=> $a['weight'];
472+
});
461473

462-
foreach ($reverseOrderItems as $key => $weight) {
463-
if (!isset($items[$key])) {
464-
continue;
465-
}
466-
unset($items[$key]);
467-
$sumWeight = $weight;
468-
foreach ($items as $key => $weight) {
469-
if ($sumWeight + $weight < $maxWeight) {
470-
unset($items[$key]);
471-
$sumWeight += $weight;
472-
} elseif ($sumWeight + $weight > $maxWeight) {
473-
$pieces[] = (string)(double)$sumWeight;
474-
break;
475-
} else {
476-
unset($items[$key]);
477-
$pieces[] = (string)(double)($sumWeight + $weight);
478-
$sumWeight = 0;
479-
break;
480-
}
481-
}
474+
for ($i = 0;; $i++) {
475+
if (!count($orderItems)) {
476+
break;
482477
}
483-
if ($sumWeight > 0) {
484-
$pieces[] = (string)(double)$sumWeight;
478+
479+
$packages[$i]['weight'] = 0;
480+
$packages[$i]['price'] = 0;
481+
482+
foreach ($orderItems as $k => $orderItem) {
483+
if ($orderItem['weight'] <= $maxPackageWeight - $packages[$i]['weight']) {
484+
$packages[$i]['weight'] += $orderItem['weight'];
485+
$packages[$i]['price'] += $orderItem['price'];
486+
unset($orderItems[$k]);
487+
}
485488
}
486-
$pieces = array_count_values($pieces);
487489
}
488490

489-
return $pieces;
491+
return $packages;
490492
}
491493

492494
/**

0 commit comments

Comments
 (0)