Skip to content

Commit

Permalink
Merge pull request #7399 from magento-l3/ACP2E-120-2.4.4
Browse files Browse the repository at this point in the history
ACP2E-120: Magento_Fedex Package description and handling fee issue
  • Loading branch information
sidolov authored Jan 18, 2022
2 parents b225913 + 7b3359d commit 67fbf08
Show file tree
Hide file tree
Showing 5 changed files with 381 additions and 226 deletions.
125 changes: 84 additions & 41 deletions app/code/Magento/Fedex/Model/Carrier.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Magento\Framework\Webapi\Soap\ClientFactory;
use Magento\Framework\Xml\Security;
use Magento\Quote\Model\Quote\Address\RateRequest;
use Magento\Shipping\Model\Carrier\AbstractCarrier;
use Magento\Shipping\Model\Carrier\AbstractCarrierOnline;
use Magento\Shipping\Model\Rate\Result;

Expand All @@ -31,21 +32,21 @@ class Carrier extends AbstractCarrierOnline implements \Magento\Shipping\Model\C
*
* @var string
*/
const CODE = 'fedex';
public const CODE = 'fedex';

/**
* Purpose of rate request
*
* @var string
*/
const RATE_REQUEST_GENERAL = 'general';
public const RATE_REQUEST_GENERAL = 'general';

/**
* Purpose of rate request
*
* @var string
*/
const RATE_REQUEST_SMARTPOST = 'SMART_POST';
public const RATE_REQUEST_SMARTPOST = 'SMART_POST';

/**
* Code of the carrier
Expand Down Expand Up @@ -123,7 +124,7 @@ class Carrier extends AbstractCarrierOnline implements \Magento\Shipping\Model\C
protected $_productCollectionFactory;

/**
* @inheritdoc
* @var string[]
*/
protected $_debugReplacePrivateDataKeys = [
'Key', 'Password', 'MeterNumber',
Expand Down Expand Up @@ -381,15 +382,16 @@ public function setRequest(RateRequest $request)
$r->setDestCity($request->getDestCity());
}

$weight = $this->getTotalNumOfBoxes($request->getPackageWeight());
$r->setWeight($weight);
if ($request->getFreeMethodWeight() != $request->getPackageWeight()) {
$r->setFreeMethodWeight($request->getFreeMethodWeight());
}

$r->setWeight($request->getPackageWeight());
$r->setValue($request->getPackagePhysicalValue());
$r->setValueWithDiscount($request->getPackageValueWithDiscount());

$r->setPackages($this->createPackages((float) $request->getPackageWeight(), (array) $request->getPackages()));

$r->setMeterNumber($this->getConfigData('meter_number'));
$r->setKey($this->getConfigData('key'));
$r->setPassword($this->getConfigData('password'));
Expand Down Expand Up @@ -445,7 +447,6 @@ protected function _formRateRequest($purpose)
'DropoffType' => $r->getDropoffType(),
'ShipTimestamp' => date('c'),
'PackagingType' => $r->getPackaging(),
'TotalInsuredValue' => ['Amount' => $r->getValue(), 'Currency' => $this->getCurrencyCode()],
'Shipper' => [
'Address' => ['PostalCode' => $r->getOrigPostal(), 'CountryCode' => $r->getOrigCountry()],
],
Expand All @@ -464,37 +465,36 @@ protected function _formRateRequest($purpose)
'CustomsValue' => ['Amount' => $r->getValue(), 'Currency' => $this->getCurrencyCode()],
],
'RateRequestTypes' => 'LIST',
'PackageCount' => '1',
'PackageDetail' => 'INDIVIDUAL_PACKAGES',
'RequestedPackageLineItems' => [
'0' => [
'Weight' => [
'Value' => (double)$r->getWeight(),
'Units' => $this->getConfigData('unit_of_measure'),
],
'GroupPackageCount' => 1,
],
],
],
];

foreach ($r->getPackages() as $packageNum => $package) {
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][$packageNum]['GroupPackageCount'] = 1;
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][$packageNum]['Weight']['Value']
= (double) $package['weight'];
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][$packageNum]['Weight']['Units']
= $this->getConfigData('unit_of_measure');
if (isset($package['price'])) {
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][$packageNum]['InsuredValue']['Amount']
= (double) $package['price'];
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][$packageNum]['InsuredValue']['Currency']
= $this->getCurrencyCode();
}
}

$ratesRequest['RequestedShipment']['PackageCount'] = count($r->getPackages());

if ($r->getDestCity()) {
$ratesRequest['RequestedShipment']['Recipient']['Address']['City'] = $r->getDestCity();
}

if ($purpose == self::RATE_REQUEST_GENERAL) {
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][0]['InsuredValue'] = [
'Amount' => $r->getValue(),
'Currency' => $this->getCurrencyCode(),
if ($purpose == self::RATE_REQUEST_SMARTPOST) {
$ratesRequest['RequestedShipment']['ServiceType'] = self::RATE_REQUEST_SMARTPOST;
$ratesRequest['RequestedShipment']['SmartPostDetail'] = [
'Indicia' => (double)$r->getWeight() >= 1 ? 'PARCEL_SELECT' : 'PRESORTED_STANDARD',
'HubId' => $this->getConfigData('smartpost_hubid'),
];
} else {
if ($purpose == self::RATE_REQUEST_SMARTPOST) {
$ratesRequest['RequestedShipment']['ServiceType'] = self::RATE_REQUEST_SMARTPOST;
$ratesRequest['RequestedShipment']['SmartPostDetail'] = [
'Indicia' => (double)$r->getWeight() >= 1 ? 'PARCEL_SELECT' : 'PRESORTED_STANDARD',
'HubId' => $this->getConfigData('smartpost_hubid'),
];
}
}

return $ratesRequest;
Expand Down Expand Up @@ -632,6 +632,40 @@ protected function _prepareRateResponse($response)
return $result;
}

/**
* Get final price for shipping method with handling fee per package
*
* @param float $cost
* @param string $handlingType
* @param float $handlingFee
* @return float
*/
protected function _getPerpackagePrice($cost, $handlingType, $handlingFee)
{
if ($handlingType == AbstractCarrier::HANDLING_TYPE_PERCENT) {
return $cost + $cost * $this->_numBoxes * $handlingFee / 100;
}

return $cost + $this->_numBoxes * $handlingFee;
}

/**
* Get final price for shipping method with handling fee per order
*
* @param float $cost
* @param string $handlingType
* @param float $handlingFee
* @return float
*/
protected function _getPerorderPrice($cost, $handlingType, $handlingFee)
{
if ($handlingType == self::HANDLING_TYPE_PERCENT) {
return $cost + $cost * $handlingFee / 100;
}

return $cost + $handlingFee;
}

/**
* Get origin based amount form response of rate estimation
*
Expand Down Expand Up @@ -809,14 +843,6 @@ protected function _parseXmlResponse($response)
if (strlen(trim($response)) > 0) {
$xml = $this->parseXml($response, \Magento\Shipping\Model\Simplexml\Element::class);
if (is_object($xml)) {
if (is_object($xml->Error) && is_object($xml->Error->Message)) {
$errorTitle = (string)$xml->Error->Message;
} elseif (is_object($xml->SoftError) && is_object($xml->SoftError->Message)) {
$errorTitle = (string)$xml->SoftError->Message;
} else {
$errorTitle = 'Sorry, something went wrong. Please try again or contact us and we\'ll try to help.';
}

$allowedMethods = explode(",", $this->getConfigData('allowed_methods'));

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

asort($priceArr);
} else {
$errorTitle = 'Response is in the wrong format.';
}
} else {
$errorTitle = 'For some reason we can\'t retrieve tracking info right now.';
}

$result = $this->_rateFactory->create();
Expand Down Expand Up @@ -1212,6 +1234,7 @@ public function getResponse()
}
}
}
// phpstan:ignore
if (empty($statuses)) {
$statuses = __('Empty response');
}
Expand Down Expand Up @@ -1821,4 +1844,24 @@ private function getPaymentType(DataObject $request): string
? 'RECIPIENT'
: 'SENDER';
}

/**
* Creates packages for rate request.
*
* @param float $totalWeight
* @param array $packages
* @return array
*/
private function createPackages(float $totalWeight, array $packages): array
{
if (empty($packages)) {
$dividedWeight = $this->getTotalNumOfBoxes($totalWeight);
for ($i=0; $i < $this->_numBoxes; $i++) {
$packages[$i]['weight'] = $dividedWeight;
}
}
$this->_numBoxes = count($packages);

return $packages;
}
}
96 changes: 49 additions & 47 deletions app/code/Magento/Shipping/Model/Shipping.php
Original file line number Diff line number Diff line change
Expand Up @@ -321,14 +321,12 @@ public function collectCarrierRates($carrierCode, $request)
//Multiple shipments
/** @var PackageResult $result */
$result = $this->packageResultFactory->create();
foreach ($packages as $weight => $packageCount) {
$request->setPackageWeight($weight);
$packageResult = $carrier->collectRates($request);
if (!$packageResult) {
return $this;
} else {
$result->appendPackageResult($packageResult, $packageCount);
}
$request->setPackages($packages);
$packageResult = $carrier->collectRates($request);
if (!$packageResult) {
return $this;
} else {
$result->appendPackageResult($packageResult, 1);
}
}
}
Expand Down Expand Up @@ -375,6 +373,10 @@ public function composePackagesForCarrier($carrier, $request)
continue;
}

if ($item->getFreeShipping()) {
continue;
}

$qty = $item->getQty();
$changeQty = true;
$checkWeight = true;
Expand All @@ -389,7 +391,7 @@ public function composePackagesForCarrier($carrier, $request)
: $item->getParentItem()->getQty() * $item->getQty();
}

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

if (!empty($decimalItems)) {
foreach ($decimalItems as $decimalItem) {
$weightItems[] = array_fill(0, $decimalItem['qty'] * $qty, $decimalItem['weight']);
$weightItems[] = array_fill(
0,
$decimalItem['qty'] * $qty,
[
'weight' => $decimalItem['weight'],
'price' => $item->getBasePrice()
]
);
}
} else {
$weightItems[] = array_fill(0, $qty, $itemWeight);
$weightItems[] = array_fill(
0,
$qty,
[
'weight' => $itemWeight,
'price' => $item->getBasePrice()
]
);
}
}
$fullItems = array_merge($fullItems, ...$weightItems);
sort($fullItems);

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

/**
* Make pieces
*
* Compose packages list based on given items, so that each package is as heavy as possible
* Compose order items into packages using first fit decreasing algorithm
*
* @param array $items
* @param float $maxWeight
* @param array $orderItems
* @param float $maxPackageWeight
* @return array
*/
protected function _makePieces($items, $maxWeight)
protected function _makePieces(array $orderItems, float $maxPackageWeight): array
{
$pieces = [];
if (!empty($items)) {
$sumWeight = 0;
$packages = [];

$reverseOrderItems = $items;
arsort($reverseOrderItems);
usort($orderItems, function ($a, $b) {
return $b['weight'] <=> $a['weight'];
});

foreach ($reverseOrderItems as $key => $weight) {
if (!isset($items[$key])) {
continue;
}
unset($items[$key]);
$sumWeight = $weight;
foreach ($items as $key => $weight) {
if ($sumWeight + $weight < $maxWeight) {
unset($items[$key]);
$sumWeight += $weight;
} elseif ($sumWeight + $weight > $maxWeight) {
$pieces[] = (string)(double)$sumWeight;
break;
} else {
unset($items[$key]);
$pieces[] = (string)(double)($sumWeight + $weight);
$sumWeight = 0;
break;
}
}
for ($i = 0;; $i++) {
if (!count($orderItems)) {
break;
}
if ($sumWeight > 0) {
$pieces[] = (string)(double)$sumWeight;

$packages[$i]['weight'] = 0;
$packages[$i]['price'] = 0;

foreach ($orderItems as $k => $orderItem) {
if ($orderItem['weight'] <= $maxPackageWeight - $packages[$i]['weight']) {
$packages[$i]['weight'] += $orderItem['weight'];
$packages[$i]['price'] += $orderItem['price'];
unset($orderItems[$k]);
}
}
$pieces = array_count_values($pieces);
}

return $pieces;
return $packages;
}

/**
Expand Down
Loading

0 comments on commit 67fbf08

Please sign in to comment.