Skip to content

Commit 933f478

Browse files
author
gwharton
committed
Backport of PR-magento#11707 to 2.3-develop
1 parent 0f61d2d commit 933f478

14 files changed

+2128
-6
lines changed

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

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,9 @@ protected function _getXmlQuotes()
716716
if ($this->getConfigFlag('negotiated_active')) {
717717
$xmlParams .= "<RateInformation><NegotiatedRatesIndicator/></RateInformation>";
718718
}
719+
if ($this->getConfigFlag('include_taxes')) {
720+
$xmlParams .= "<TaxInformationIndicator/>";
721+
}
719722

720723
$xmlParams .= <<<XMLRequest
721724
</Shipment>
@@ -791,6 +794,8 @@ private function mapCurrencyCode($code)
791794
* @param mixed $xmlResponse
792795
* @return Result
793796
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
797+
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
798+
* @SuppressWarnings(PHPMD.ElseExpression)
794799
*/
795800
protected function _parseXmlResponse($xmlResponse)
796801
{
@@ -815,17 +820,45 @@ protected function _parseXmlResponse($xmlResponse)
815820
foreach ($arr as $shipElement) {
816821
$code = (string)$shipElement->Service->Code;
817822
if (in_array($code, $allowedMethods)) {
823+
//The location of tax information is in a different place depending on whether we are using negotiated rates or not
818824
if ($negotiatedActive) {
819-
$cost = $shipElement->NegotiatedRates->NetSummaryCharges->GrandTotal->MonetaryValue;
825+
$includeTaxesArr = $xml->getXpath("//RatingServiceSelectionResponse/RatedShipment/NegotiatedRates/NetSummaryCharges/TotalChargesWithTaxes");
826+
$includeTaxesActive = $this->getConfigFlag(
827+
'include_taxes'
828+
) && !empty($includeTaxesArr);
829+
if ($includeTaxesActive) {
830+
$cost = $shipElement->NegotiatedRates->NetSummaryCharges->TotalChargesWithTaxes->MonetaryValue;
831+
$responseCurrencyCode = $this->mapCurrencyCode(
832+
(string)$shipElement->NegotiatedRates->NetSummaryCharges->TotalChargesWithTaxes->CurrencyCode
833+
);
834+
}
835+
else {
836+
$cost = $shipElement->NegotiatedRates->NetSummaryCharges->GrandTotal->MonetaryValue;
837+
$responseCurrencyCode = $this->mapCurrencyCode(
838+
(string)$shipElement->NegotiatedRates->NetSummaryCharges->GrandTotal->CurrencyCode
839+
);
840+
}
820841
} else {
821-
$cost = $shipElement->TotalCharges->MonetaryValue;
842+
$includeTaxesArr = $xml->getXpath("//RatingServiceSelectionResponse/RatedShipment/TotalChargesWithTaxes");
843+
$includeTaxesActive = $this->getConfigFlag(
844+
'include_taxes'
845+
) && !empty($includeTaxesArr);
846+
if ($includeTaxesActive) {
847+
$cost = $shipElement->TotalChargesWithTaxes->MonetaryValue;
848+
$responseCurrencyCode = $this->mapCurrencyCode(
849+
(string)$shipElement->TotalChargesWithTaxes->CurrencyCode
850+
);
851+
}
852+
else {
853+
$cost = $shipElement->TotalCharges->MonetaryValue;
854+
$responseCurrencyCode = $this->mapCurrencyCode(
855+
(string)$shipElement->TotalCharges->CurrencyCode
856+
);
857+
}
822858
}
823859

824860
//convert price with Origin country currency code to base currency code
825861
$successConversion = true;
826-
$responseCurrencyCode = $this->mapCurrencyCode(
827-
(string)$shipElement->TotalCharges->CurrencyCode
828-
);
829862
if ($responseCurrencyCode) {
830863
if (in_array($responseCurrencyCode, $allowedCurrencies)) {
831864
$cost = (double)$cost * $this->_getBaseCurrencyRate($responseCurrencyCode);
Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Ups\Test\Unit\Model;
7+
8+
use PHPUnit_Framework_MockObject_MockObject as MockObject;
9+
use Magento\Ups\Model\Carrier;
10+
use Magento\Framework\App\Config\ScopeConfigInterface;
11+
use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory as RateResultErrorFactory;
12+
use Psr\Log\LoggerInterface;
13+
use Magento\Framework\Xml\Security;
14+
use Magento\Shipping\Model\Simplexml\ElementFactory;
15+
use Magento\Shipping\Model\Rate\ResultFactory as RateResultFactory;
16+
use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory;
17+
use Magento\Quote\Model\Quote\Address\RateResult\Method;
18+
use Magento\Shipping\Model\Rate\Result as RateResult;
19+
use Magento\Framework\Pricing\PriceCurrencyInterface;
20+
use Magento\Shipping\Model\Tracking\ResultFactory as TrackResultFactory;
21+
use Magento\Shipping\Model\Tracking\Result\ErrorFactory as TrackingResultErrorFactory;
22+
use Magento\Shipping\Model\Tracking\Result\StatusFactory;
23+
use Magento\Directory\Model\RegionFactory;
24+
use Magento\Directory\Model\Country;
25+
use Magento\Directory\Model\CountryFactory;
26+
use Magento\Directory\Model\CurrencyFactory;
27+
use Magento\Directory\Model\Currency;
28+
use Magento\Directory\Helper\Data;
29+
use Magento\CatalogInventory\Model\StockRegistry;
30+
use Magento\Framework\Locale\FormatInterface;
31+
use Magento\Ups\Helper\Config;
32+
use Magento\Quote\Model\Quote\Address\RateRequest;
33+
34+
/**
35+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
36+
*/
37+
class CarrierCollectRatesOptionsTest extends \PHPUnit\Framework\TestCase
38+
{
39+
40+
/**
41+
* @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
42+
*/
43+
private $objectManager;
44+
45+
/**
46+
* @var \Magento\Quote\Model\Quote\Address\RateRequest;
47+
*/
48+
private $rateRequest;
49+
50+
/**
51+
* @var string;
52+
*/
53+
private $allowed_methods;
54+
55+
/**
56+
* @var int;
57+
*/
58+
private $negotiatedactive;
59+
60+
/**
61+
* @var int;
62+
*/
63+
private $include_taxes;
64+
65+
/**
66+
* set up test environment
67+
*
68+
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
69+
*/
70+
protected function setUp()
71+
{
72+
73+
$this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
74+
75+
$scopeMock = $this->getMockBuilder(ScopeConfigInterface::class)
76+
->disableOriginalConstructor()
77+
->getMock();
78+
79+
$scopeMock->expects($this->any())
80+
->method('getValue')
81+
->willReturnCallback([$this, 'scopeConfigGetValue']);
82+
$scopeMock->expects($this->any())
83+
->method('isSetFlag')
84+
->willReturnCallback([$this, 'scopeConfigisSetFlag']);
85+
86+
$errorFactoryMock = $this->getMockBuilder(RateResultErrorFactory::class)
87+
->disableOriginalConstructor()
88+
->getMock();
89+
90+
$loggerInterfaceMock = $this->getMockBuilder(LoggerInterface::class)
91+
->disableOriginalConstructor()
92+
->getMock();
93+
94+
$securityMock = $this->getMockBuilder(Security::class)
95+
->disableOriginalConstructor()
96+
->getMock();
97+
98+
$elementFactoryMock = $this->getMockBuilder(ElementFactory::class)
99+
->disableOriginalConstructor()
100+
->getMock();
101+
102+
$rateResultMock = $this->getMockBuilder(RateResult::class)
103+
->disableOriginalConstructor()
104+
->setMethods(['getError'])
105+
->getMock();
106+
107+
$rateFactoryMock = $this->getMockBuilder(RateResultFactory::class)
108+
->disableOriginalConstructor()
109+
->setMethods(['create'])
110+
->getMock();
111+
112+
$rateFactoryMock->expects($this->any())
113+
->method('create')
114+
->willReturn($rateResultMock);
115+
116+
$priceInterfaceMock = $this->getMockBuilder(PriceCurrencyInterface::class)
117+
->disableOriginalConstructor()
118+
->getMock();
119+
120+
$rateMethodMock = $this->getMockBuilder(Method::class)
121+
->setConstructorArgs(['priceCurrency' => $priceInterfaceMock])
122+
->setMethods(null)
123+
->getMock();
124+
125+
$methodFactoryMock = $this->getMockBuilder(MethodFactory::class)
126+
->disableOriginalConstructor()
127+
->setMethods(['create'])
128+
->getMock();
129+
130+
$methodFactoryMock->expects($this->any())
131+
->method('create')
132+
->willReturn($rateMethodMock);
133+
134+
$resultFactoryMock = $this->getMockBuilder(TrackResultFactory::class)
135+
->disableOriginalConstructor()
136+
->getMock();
137+
138+
$trErrorFactoryMock = $this->getMockBuilder(TrackingResultErrorFactory::class)
139+
->disableOriginalConstructor()
140+
->getMock();
141+
142+
$statusFactoryMock = $this->getMockBuilder(StatusFactory::class)
143+
->disableOriginalConstructor()
144+
->getMock();
145+
146+
$regionFactoryMock = $this->getMockBuilder(RegionFactory::class)
147+
->disableOriginalConstructor()
148+
->getMock();
149+
150+
$countryMock = $this->getMockBuilder(Country::class)
151+
->disableOriginalConstructor()
152+
->setMethods(['load', 'getData'])
153+
->getMock();
154+
155+
$countryMock->expects($this->any())
156+
->method('load')
157+
->willReturnSelf();
158+
159+
$countryFactoryMock = $this->getMockBuilder(CountryFactory::class)
160+
->disableOriginalConstructor()
161+
->setMethods(['create'])
162+
->getMock();
163+
164+
$countryFactoryMock->expects($this->any())
165+
->method('create')
166+
->willReturn($countryMock);
167+
168+
$allowCurrencies = ['GBP'];
169+
$baseCurrencies = ['GBP'];
170+
$currencyRates = ['GBP' => ['GBP' => 1]];
171+
$currencyFactoryMock = $this->getMockBuilder(CurrencyFactory::class)
172+
->disableOriginalConstructor()
173+
->setMethods(['create'])
174+
->getMock();
175+
$currencyMock = $this->getMockBuilder(Currency::class)
176+
->disableOriginalConstructor()
177+
->setMethods(['getConfigAllowCurrencies', 'getConfigBaseCurrencies', 'getCurrencyRates'])
178+
->getMock();
179+
$currencyFactoryMock->expects($this->once())
180+
->method('create')
181+
->willReturn($currencyMock);
182+
$currencyMock->expects($this->any())
183+
->method('getConfigAllowCurrencies')
184+
->willReturn($allowCurrencies);
185+
$currencyMock->expects($this->any())
186+
->method('getConfigBaseCurrencies')
187+
->willReturn($baseCurrencies);
188+
$currencyMock->expects($this->any())
189+
->method('getCurrencyRates')
190+
->with($baseCurrencies, $allowCurrencies)
191+
->willReturn($currencyRates);
192+
193+
$dataMock = $this->getMockBuilder(Data::class)
194+
->disableOriginalConstructor()
195+
->getMock();
196+
197+
$stockRegistryMock = $this->getMockBuilder(StockRegistry::class)
198+
->disableOriginalConstructor()
199+
->getMock();
200+
201+
$formatInterfaceMock = $this->getMockBuilder(FormatInterface::class)
202+
->disableOriginalConstructor()
203+
->getMock();
204+
205+
$configHelperMock = $this->getMockBuilder(Config::class)
206+
->disableOriginalConstructor()
207+
->getMock();
208+
209+
$this->model = $this->getMockBuilder(Carrier::class)
210+
->setMethods(['_getCachedQuotes', 'canCollectRates', '_updateFreeMethodQuote', '_getBaseCurrencyRate'])
211+
->setConstructorArgs(
212+
[
213+
'scopeConfig' => $scopeMock,
214+
'rateErrorFactory' => $errorFactoryMock,
215+
'logger' => $loggerInterfaceMock,
216+
'xmlSecurity' => $securityMock,
217+
'xmlElFactory' => $elementFactoryMock,
218+
'rateFactory' => $rateFactoryMock,
219+
'rateMethodFactory' => $methodFactoryMock,
220+
'trackFactory' => $resultFactoryMock,
221+
'trackErrorFactory' => $trErrorFactoryMock,
222+
'trackStatusFactory' => $statusFactoryMock,
223+
'regionFactory' => $regionFactoryMock,
224+
'countryFactory' => $countryFactoryMock,
225+
'currencyFactory' => $currencyFactoryMock,
226+
'directoryData' => $dataMock,
227+
'stockRegistry' => $stockRegistryMock,
228+
'localeFormat' => $formatInterfaceMock,
229+
'configHelper' => $configHelperMock,
230+
'data' => [],
231+
]
232+
)
233+
->getMock();
234+
235+
$this->model->expects($this->any())
236+
->method('canCollectRates')
237+
->willReturn(true);
238+
239+
$this->model->expects($this->any())
240+
->method('_getBaseCurrencyRate')
241+
->willReturn(1.00);
242+
243+
$this->rateRequest = $this->objectManager->getObject(RateRequest::class);
244+
}
245+
246+
/**
247+
* Callback function, emulates getValue function
248+
* @param $path
249+
* @return null|string
250+
*/
251+
public function scopeConfigGetValue($path)
252+
{
253+
$pathMap = [
254+
'carriers/ups/type' => 'UPS_XML',
255+
'carriers/ups/shipper_number' => '12345',
256+
'carriers/ups/allowed_methods' => $this->allowed_methods,
257+
];
258+
259+
return isset($pathMap[$path]) ? $pathMap[$path] : null;
260+
}
261+
262+
/**
263+
* Callback function, emulates isSetFlag function
264+
* @param $path
265+
* @return bool
266+
*/
267+
public function scopeConfigisSetFlag($path)
268+
{
269+
$pathMap = [
270+
'carriers/ups/negotiated_active' => $this->negotiatedactive,
271+
'carriers/ups/include_taxes' => $this->include_taxes,
272+
];
273+
274+
if (isset($pathMap[$path])) {
275+
if ($pathMap[$path]) {
276+
return(true);
277+
}
278+
}
279+
return(false);
280+
}
281+
282+
/**
283+
* @param int $neg
284+
* @param int $tax
285+
* @param string $file
286+
* @param string $method
287+
* @param float $expectedprice
288+
* @dataProvider collectRatesDataProvider
289+
*/
290+
public function testCollectRates($neg, $tax, $file, $method, $expectedprice)
291+
{
292+
$this->negotiatedactive = $neg;
293+
$this->include_taxes = $tax;
294+
$this->allowed_methods = $method;
295+
296+
$response = file_get_contents(__DIR__ . $file);
297+
$this->model->expects($this->any())
298+
->method('_getCachedQuotes')
299+
->willReturn($response);
300+
301+
$rates = $this->model->collectRates($this->rateRequest)->getAllRates();
302+
$this->assertEquals($expectedprice, $rates[0]->getData('cost'));
303+
$this->assertEquals($method, $rates[0]->getData('method'));
304+
}
305+
306+
/**
307+
* Get list of rates variations
308+
* @return array
309+
*/
310+
public function collectRatesDataProvider()
311+
{
312+
return [
313+
[0, 0, '/_files/ups_rates_response_option1.xml', '11', 6.45 ],
314+
[0, 0, '/_files/ups_rates_response_option2.xml', '65', 29.59 ],
315+
[0, 1, '/_files/ups_rates_response_option3.xml', '11', 7.74 ],
316+
[0, 1, '/_files/ups_rates_response_option4.xml', '65', 29.59 ],
317+
[1, 0, '/_files/ups_rates_response_option5.xml', '11', 9.35 ],
318+
[1, 0, '/_files/ups_rates_response_option6.xml', '65', 41.61 ],
319+
[1, 1, '/_files/ups_rates_response_option7.xml', '11', 11.22 ],
320+
[1, 1, '/_files/ups_rates_response_option8.xml', '65', 41.61 ],
321+
];
322+
}
323+
}

0 commit comments

Comments
 (0)