UpsCalculator   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 221
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 117
c 1
b 0
f 0
dl 0
loc 221
rs 10
wmc 18

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getType() 0 3 1
B getUpsRateValue() 0 90 2
A getServiceCode() 0 16 5
A getPackageValues() 0 22 2
B calculate() 0 59 8
1
<?php
2
3
namespace Odiseo\SyliusUpsPlugin\Calculator;
4
5
use Doctrine\Common\Collections\Collection;
6
use Sylius\Component\Core\Exception\MissingChannelConfigurationException;
7
use Sylius\Component\Core\Model\AddressInterface;
8
use Sylius\Component\Core\Model\ChannelInterface;
9
use Sylius\Component\Core\Model\OrderInterface;
10
use Sylius\Component\Core\Model\OrderItemInterface;
11
use Sylius\Component\Core\Model\ProductVariantInterface;
12
use Sylius\Component\Core\Model\ShippingMethodInterface;
13
use Sylius\Component\Resource\Exception\UnexpectedTypeException;
14
use Sylius\Component\Shipping\Calculator\CalculatorInterface;
15
use Sylius\Component\Shipping\Model\ShipmentInterface;
16
use Ups\Entity\Address;
17
use Ups\Entity\Dimensions;
18
use Ups\Entity\Package;
19
use Ups\Entity\RatedShipment;
20
use Ups\Entity\RateInformation;
21
use Ups\Entity\RateResponse;
22
use Ups\Entity\Service;
23
use Ups\Entity\ShipFrom;
24
use Ups\Entity\Shipment;
25
use Ups\Entity\UnitOfMeasurement;
26
use Ups\Rate;
27
28
final class UpsCalculator implements CalculatorInterface
29
{
30
    /**
31
     * {@inheritdoc}
32
     */
33
    public function calculate(ShipmentInterface $subject, array $configuration): int
34
    {
35
        if (!$subject instanceof \Sylius\Component\Core\Model\Shipment) {
36
            throw new UnexpectedTypeException($subject, 'ShipmentInterface');
37
        }
38
39
        /** @var ShippingMethodInterface $method */
40
        $method = $subject->getMethod();
41
42
        if (!$method instanceof ShippingMethodInterface) {
0 ignored issues
show
introduced by
$method is always a sub-type of Sylius\Component\Core\Mo...ShippingMethodInterface.
Loading history...
43
            throw new UnexpectedTypeException($method, 'ShippingMethodInterface');
44
        }
45
46
        /** @var OrderInterface $order */
47
        $order = $subject->getOrder();
48
49
        if (!$order instanceof OrderInterface) {
0 ignored issues
show
introduced by
$order is always a sub-type of Sylius\Component\Core\Model\OrderInterface.
Loading history...
50
            throw new UnexpectedTypeException($order, 'OrderInterface');
51
        }
52
53
        /** @var ChannelInterface $channel */
54
        $channel = $order->getChannel();
55
56
        if (!$channel instanceof ChannelInterface) {
0 ignored issues
show
introduced by
$channel is always a sub-type of Sylius\Component\Core\Model\ChannelInterface.
Loading history...
57
            throw new UnexpectedTypeException($order, 'ChannelInterface');
58
        }
59
60
        $channelCode = $channel->getCode();
61
        if (!isset($configuration[$channelCode])) {
62
            throw new MissingChannelConfigurationException(sprintf(
63
                'Channel %s has no configuration defined for shipping method %s',
64
                $channel->getName(),
65
                $method->getName()
66
            ));
67
        }
68
69
        /** @var AddressInterface $shippingAddress */
70
        $shippingAddress = $order->getShippingAddress();
71
72
        if (!$shippingAddress instanceof AddressInterface) {
0 ignored issues
show
introduced by
$shippingAddress is always a sub-type of Sylius\Component\Core\Model\AddressInterface.
Loading history...
73
            return 0;
74
        }
75
76
        $packageValues = array_merge($this->getPackageValues($order->getItems()), array(
77
            'destination_country_code' => $shippingAddress->getCountryCode(),
78
            'destination_postcode' =>  $shippingAddress->getPostcode(),
79
            'destination_city' => $shippingAddress->getCity(),
80
            'destination_address' => $shippingAddress->getCountryCode()
81
        ));
82
83
        $packageValues['type'] = '02';
84
85
        $rateValue = 0;
86
87
        if ($packageValues['weight'] > 0) {
88
            $rateValue = $this->getUpsRateValue($configuration[$channelCode], $packageValues);
89
        }
90
91
        return intval($rateValue*100);
92
    }
93
94
    /**
95
     * @param Collection $items
96
     * @return array('width', 'height', 'length', 'weight', 'pounds', 'ounces')
97
     */
98
    private function getPackageValues(Collection $items)
99
    {
100
        $width = 0;
101
        $height = 0;
102
        $length = 0;
103
        $weight = 0;
104
105
        /** @var OrderItemInterface $item */
106
        foreach ($items as $item) {
107
            /** @var ProductVariantInterface $variant */
108
            $variant = $item->getVariant();
109
110
            $width += $item->getQuantity() * $variant->getWidth();
111
            $height += $item->getQuantity() * $variant->getHeight();
112
            $length += $item->getQuantity() * $variant->getDepth();
113
            $weight += $item->getQuantity() * $variant->getWeight();
114
        }
115
116
        $pounds = $weight*2.20462;
117
        $ounces = $weight*35.274;
118
119
        return array('width' => $width, 'height' => $height, 'length' => $length, 'weight' => $weight, 'pounds' => $pounds, 'ounces' => $ounces);
120
    }
121
122
    /**
123
     * @param string $origin // origin country code
124
     * @param string $destination // destination country code
125
     * @return string
126
     */
127
    private function getServiceCode(string $origin, string $destination)
128
    {
129
        $serviceCode = '01';
130
        if ($origin =='PR') {
131
            if ($destination == 'PR') {
132
                $serviceCode = '01'; // Next Day Air
133
            } elseif ($destination == 'US') {
134
                $serviceCode = '02'; // Second Day Air
135
            } else {
136
                $serviceCode = '65'; // Ups Saver
137
            }
138
        } elseif ($origin =='DR') {
139
            $serviceCode = '65'; // Ups Saver
140
        }
141
142
        return $serviceCode;
143
    }
144
145
    /**
146
     * @param array $configuration
147
     * @param array $packageValues
148
     * @return float
149
     * @throws \Exception
150
     */
151
    private function getUpsRateValue(array $configuration, array $packageValues)
152
    {
153
        $rate = new Rate(
154
            $configuration['accesskey'],
155
            $configuration['username'],
156
            $configuration['password']
157
        );
158
159
        $packageValues['account'] = $configuration['account'];
160
        $packageValues['origination_postcode'] = $configuration['origination_postcode'];
161
        $packageValues['origination_country_code'] = $configuration['origination_country_code'];
162
        $packageValues['origination_address'] = $configuration['origination_address'];
163
        $packageValues['origination_city'] = $configuration['origination_city'];
164
        $packageValues['service_code'] = $this->getServiceCode($packageValues['origination_country_code'], $packageValues['destination_country_code']);
165
166
        $shipment = new Shipment();
167
168
        $shipper = $shipment->getShipper();
169
        $shipper->setShipperNumber($packageValues['account']);
170
171
        $shipperAddress = $shipment->getShipper()->getAddress();
172
        $shipperAddress->setPostalCode($packageValues['origination_postcode']);
173
        $shipperAddress->setAddressLine1($packageValues['origination_address']);
174
        $shipperAddress->setCity($packageValues['origination_city']);
175
        $shipperAddress->setCountryCode($packageValues['origination_country_code']);
176
177
        $address = new Address();
178
        $address->setPostalCode($packageValues['origination_postcode']);
179
        $address->setCountryCode($packageValues['origination_country_code']);
180
        $address->setCity($packageValues['origination_city']);
181
        $address->setStateProvinceCode($packageValues['origination_city']);
182
183
        $shipFrom = new ShipFrom();
184
        $shipFrom->setAddress($address);
185
186
        $shipment->setShipFrom($shipFrom);
187
188
        $shipTo = $shipment->getShipTo();
189
        $shipToAddress = $shipTo->getAddress();
190
        $shipToAddress->setCountryCode($packageValues['destination_country_code']);
191
        $shipToAddress->setCity($packageValues['destination_city']);
192
        $shipToAddress->setPostalCode($packageValues['destination_postcode']);
193
        $shipToAddress->setResidentialAddressIndicator($packageValues['destination_address']);
194
195
        $pounds = $packageValues['weight'] *2.20462;
196
197
        $package = new Package();
198
        $package->getPackagingType()->setCode($packageValues['type']);
199
200
201
        $package->getPackageWeight()->setWeight((string)$pounds);
202
        $weightUnit = new UnitOfMeasurement();
203
        $weightUnit->setCode(UnitOfMeasurement::UOM_LBS);
204
        $package->getPackageWeight()->setUnitOfMeasurement($weightUnit);
205
206
        $dimensions = new Dimensions();
207
        $dimensions->setHeight($packageValues['height']);
208
        $dimensions->setWidth($packageValues['width']);
209
        $dimensions->setLength($packageValues['length']);
210
211
        $unit = new UnitOfMeasurement();
212
        $unit->setCode(UnitOfMeasurement::UOM_IN);
213
214
        $dimensions->setUnitOfMeasurement($unit);
215
        $package->setDimensions($dimensions);
216
217
        $shipment->addPackage($package);
218
219
        $service = new Service();
220
        $service->setCode($packageValues['service_code']);
221
222
        $shipment->setService($service);
223
        $object = new \stdClass();
224
        $object->NegotiatedRatesIndicator = true;
225
226
        $shipment->setRateInformation(new RateInformation($object));
227
228
        /** @var RateResponse $rateResponse */
229
        $rateResponse = $rate->getRate($shipment);
230
231
        /** @var RatedShipment $ratedShipment */
232
        $ratedShipment = $rateResponse->RatedShipment[0];
233
234
        if ($ratedShipment->NegotiatedRates) {
235
            $monetaryValue = $ratedShipment->NegotiatedRates->NetSummaryCharges->GrandTotal->MonetaryValue;
236
        } else {
237
            $monetaryValue = $ratedShipment->TotalCharges->MonetaryValue;
238
        }
239
240
        return $monetaryValue;
241
    }
242
243
    /**
244
     * {@inheritdoc}
245
     */
246
    public function getType(): string
247
    {
248
        return 'ups';
249
    }
250
}
251