Completed
Push — master ( d998d0...abb41b )
by Kamil
38:34
created

OrderTaxesApplicator   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 147
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 15
c 1
b 1
f 0
lcom 1
cbo 9
dl 0
loc 147
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 1
A apply() 0 15 3
B processTaxes() 0 24 5
A addAdjustment() 0 10 1
A getTaxZone() 0 9 3
A clearTaxes() 0 7 2
1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sylius\Component\Core\Taxation;
13
14
use Sylius\Bundle\CoreBundle\Distributor\IntegerDistributorInterface;
15
use Sylius\Component\Addressing\Matcher\ZoneMatcherInterface;
16
use Sylius\Component\Addressing\Model\AddressInterface;
17
use Sylius\Component\Addressing\Model\ZoneInterface;
18
use Sylius\Component\Core\Model\AdjustmentInterface;
19
use Sylius\Component\Core\Model\OrderInterface;
20
use Sylius\Component\Core\Model\OrderItemUnitInterface;
21
use Sylius\Component\Core\Provider\DefaultTaxZoneProviderInterface;
22
use Sylius\Component\Resource\Factory\FactoryInterface;
23
use Sylius\Component\Taxation\Calculator\CalculatorInterface;
24
use Sylius\Component\Taxation\Resolver\TaxRateResolverInterface;
25
26
/**
27
 * @author Paweł Jędrzejewski <[email protected]>
28
 * @author Mateusz Zalewski <[email protected]>
29
 */
30
class OrderTaxesApplicator implements OrderTaxesApplicatorInterface
31
{
32
    /**
33
     * @var CalculatorInterface
34
     */
35
    protected $taxCalculator;
36
37
    /**
38
     * @var DefaultTaxZoneProviderInterface
39
     */
40
    protected $defaultTaxZoneProvider;
41
42
    /**
43
     * @var FactoryInterface
44
     */
45
    protected $adjustmentFactory;
46
47
    /**
48
     * @var IntegerDistributorInterface
49
     */
50
    protected $integerDistributor;
51
52
    /**
53
     * @var TaxRateResolverInterface
54
     */
55
    protected $taxRateResolver;
56
57
    /**
58
     * @var ZoneMatcherInterface
59
     */
60
    protected $zoneMatcher;
61
62
    /**
63
     * @param CalculatorInterface $taxCalculator
64
     * @param DefaultTaxZoneProviderInterface $defaultTaxZoneProvider
65
     * @param FactoryInterface $adjustmentFactory
66
     * @param IntegerDistributorInterface $integerDistributor
67
     * @param TaxRateResolverInterface $taxRateResolver
68
     * @param ZoneMatcherInterface $zoneMatcher
69
     */
70
    public function __construct(
71
        CalculatorInterface $taxCalculator,
72
        DefaultTaxZoneProviderInterface $defaultTaxZoneProvider,
73
        FactoryInterface $adjustmentFactory,
74
        IntegerDistributorInterface $integerDistributor,
75
        TaxRateResolverInterface $taxRateResolver,
76
        ZoneMatcherInterface $zoneMatcher
77
    ) {
78
        $this->taxCalculator = $taxCalculator;
79
        $this->defaultTaxZoneProvider = $defaultTaxZoneProvider;
80
        $this->adjustmentFactory = $adjustmentFactory;
81
        $this->integerDistributor = $integerDistributor;
82
        $this->taxRateResolver = $taxRateResolver;
83
        $this->zoneMatcher = $zoneMatcher;
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function apply(OrderInterface $order)
90
    {
91
        $this->clearTaxes($order);
92
        if ($order->isEmpty()) {
93
            return;
94
        }
95
96
        $zone = $this->getTaxZone($order->getShippingAddress());
97
98
        if (null === $zone) {
99
            return;
100
        }
101
102
        $this->processTaxes($order, $zone);
103
    }
104
105
    /**
106
     * @param OrderInterface $order
107
     * @param ZoneInterface $zone
108
     */
109
    protected function processTaxes(OrderInterface $order, ZoneInterface $zone)
110
    {
111
        foreach ($order->getItems() as $item) {
112
            $rate = $this->taxRateResolver->resolve($item->getProduct(), array('zone' => $zone));
113
114
            if (null === $rate) {
115
                continue;
116
            }
117
118
            $percentageAmount = $rate->getAmountAsPercentage();
119
            $totalTaxAmount = $this->taxCalculator->calculate($item->getTotal(), $rate);
120
            $label = sprintf('%s (%s%%)', $rate->getName(), (float) $percentageAmount);
121
122
            $splitTaxes = $this->integerDistributor->distribute($item->getQuantity(), $totalTaxAmount);
123
124
            foreach ($splitTaxes as $key => $tax) {
125
                if (0 === $tax) {
126
                    break;
127
                }
128
129
                $this->addAdjustment($item->getUnits()->get($key), $tax, $label, $rate->isIncludedInPrice());
130
            }
131
        }
132
    }
133
134
    /**
135
     * @param OrderItemUnitInterface $unit
136
     * @param int $amount
137
     * @param string $label
138
     * @param bool $included
139
     */
140
    protected function addAdjustment(OrderItemUnitInterface $unit, $amount, $label, $included)
141
    {
142
        $adjustment = $this->adjustmentFactory->createNew();
143
        $adjustment->setType(AdjustmentInterface::TAX_ADJUSTMENT);
144
        $adjustment->setAmount($amount);
145
        $adjustment->setDescription($label);
146
        $adjustment->setNeutral($included);
147
148
        $unit->addAdjustment($adjustment);
149
    }
150
151
    /**
152
     * @param AddressInterface|null $shippingAddress
153
     *
154
     * @return ZoneInterface|null
155
     */
156
    private function getTaxZone(AddressInterface $shippingAddress = null)
157
    {
158
        $zone = null;
159
        if (null !== $shippingAddress) {
160
            $zone = $this->zoneMatcher->match($shippingAddress);
161
        }
162
163
        return $zone ?: $this->defaultTaxZoneProvider->getZone();
164
    }
165
166
    /**
167
     * @param OrderInterface $order
168
     */
169
    private function clearTaxes(OrderInterface $order)
170
    {
171
        $order->removeAdjustments(AdjustmentInterface::TAX_ADJUSTMENT);
172
        foreach ($order->getItems() as $item) {
173
            $item->removeAdjustmentsRecursively(AdjustmentInterface::TAX_ADJUSTMENT);
174
        }
175
    }
176
}
177