Completed
Push — master ( 00856b...729400 )
by Gilles
36s queued 16s
created

Calculator::loadTaxRuleWithoutCountry()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 17
rs 9.9666
c 0
b 0
f 0
cc 3
nc 3
nop 2
1
<?php
2
/*************************************************************************************/
3
/*      This file is part of the Thelia package.                                     */
4
/*                                                                                   */
5
/*      Copyright (c) OpenStudio                                                     */
6
/*      email : [email protected]                                                       */
7
/*      web : http://www.thelia.net                                                  */
8
/*                                                                                   */
9
/*      For the full copyright and license information, please view the LICENSE.txt  */
10
/*      file that was distributed with this source code.                             */
11
/*************************************************************************************/
12
13
namespace Thelia\TaxEngine;
14
15
use Thelia\Exception\TaxEngineException;
16
use Thelia\Model\Country;
17
use Thelia\Model\OrderProductTax;
18
use Thelia\Model\Product;
19
use Thelia\Model\State;
20
use Thelia\Model\TaxRule;
21
use Thelia\Model\TaxRuleQuery;
22
use Thelia\Tools\I18n;
23
24
/**
25
 * Class Calculator
26
 * @package Thelia\TaxEngine
27
 * @author Etienne Roudeix <[email protected]>
28
 */
29
class Calculator
30
{
31
    /**
32
     * @var TaxRuleQuery
33
     */
34
    protected $taxRuleQuery = null;
35
36
    /**
37
     * @var null|\Propel\Runtime\Collection\ObjectCollection
38
     */
39
    protected $taxRulesCollection = null;
40
41
    protected $product = null;
42
    protected $country = null;
43
    protected $state = null;
44
45
46
    public function __construct()
47
    {
48
        $this->taxRuleQuery = new TaxRuleQuery();
49
    }
50
51
    public function load(Product $product, Country $country, State $state = null)
52
    {
53
        $this->product = null;
54
        $this->country = null;
55
        $this->state = null;
56
57
        $this->taxRulesCollection = null;
58
59
        if ($product->getId() === null) {
60
            throw new TaxEngineException('Product id is empty in Calculator::load', TaxEngineException::UNDEFINED_PRODUCT);
61
        }
62
        if ($country->getId() === null) {
63
            throw new TaxEngineException('Country id is empty in Calculator::load', TaxEngineException::UNDEFINED_COUNTRY);
64
        }
65
66
        $this->product = $product;
67
        $this->country = $country;
68
        $this->state = $state;
69
70
        $this->taxRulesCollection = $this->taxRuleQuery->getTaxCalculatorCollection($product->getTaxRule(), $country, $state);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->taxRuleQuery->get...le(), $country, $state) can also be of type array. However, the property $taxRulesCollection is declared as type Propel\Runtime\Collection\ObjectCollection|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
71
72
        return $this;
73
    }
74
75
    public function loadTaxRule(TaxRule $taxRule, Country $country, Product $product, State $state = null)
76
    {
77
        $this->product = null;
78
        $this->country = null;
79
        $this->taxRulesCollection = null;
80
81
        if ($taxRule->getId() === null) {
82
            throw new TaxEngineException('TaxRule id is empty in Calculator::loadTaxRule', TaxEngineException::UNDEFINED_TAX_RULE);
83
        }
84
        if ($country->getId() === null) {
85
            throw new TaxEngineException('Country id is empty in Calculator::loadTaxRule', TaxEngineException::UNDEFINED_COUNTRY);
86
        }
87
        if ($product->getId() === null) {
88
            throw new TaxEngineException('Product id is empty in Calculator::load', TaxEngineException::UNDEFINED_PRODUCT);
89
        }
90
91
        $this->country = $country;
92
        $this->product = $product;
93
        $this->state = $state;
94
95
        $this->taxRulesCollection = $this->taxRuleQuery->getTaxCalculatorCollection($taxRule, $country, $state);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->taxRuleQuery->get...Rule, $country, $state) can also be of type array. However, the property $taxRulesCollection is declared as type Propel\Runtime\Collection\ObjectCollection|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
96
97
        return $this;
98
    }
99
100
    public function loadTaxRuleWithoutCountry(TaxRule $taxRule, Product $product)
101
    {
102
        $this->product = null;
103
        $this->taxRulesCollection = null;
104
105
        if ($taxRule->getId() === null) {
106
            throw new TaxEngineException('TaxRule id is empty in Calculator::loadTaxRule', TaxEngineException::UNDEFINED_TAX_RULE);
107
        }
108
        if ($product->getId() === null) {
109
            throw new TaxEngineException('Product id is empty in Calculator::load', TaxEngineException::UNDEFINED_PRODUCT);
110
        }
111
112
        $this->product = $product;
113
114
        $this->taxRulesCollection = $this->taxRuleQuery->getTaxCalculatorCollection($taxRule);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->taxRuleQuery->get...torCollection($taxRule) can also be of type array. However, the property $taxRulesCollection is declared as type Propel\Runtime\Collection\ObjectCollection|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
115
116
        return $this;
117
    }
118
119
    /** @since 2.4 */
120
    public function loadTaxRuleWithoutProduct(TaxRule $taxRule, Country $country, State $state = null)
121
    {
122
        $this->product = null;
123
        $this->country = null;
124
        $this->taxRulesCollection = null;
125
126
        if ($taxRule->getId() === null) {
127
            throw new TaxEngineException('TaxRule id is empty in Calculator::loadTaxRule', TaxEngineException::UNDEFINED_TAX_RULE);
128
        }
129
        if ($country->getId() === null) {
130
            throw new TaxEngineException('Country id is empty in Calculator::loadTaxRule', TaxEngineException::UNDEFINED_COUNTRY);
131
        }
132
133
        $this->country = $country;
134
        $this->product = new Product();
135
        $this->state = $state;
136
137
        $this->taxRulesCollection = $this->taxRuleQuery->getTaxCalculatorCollection($taxRule, $country, $state);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->taxRuleQuery->get...Rule, $country, $state) can also be of type array. However, the property $taxRulesCollection is declared as type Propel\Runtime\Collection\ObjectCollection|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
138
139
        return $this;
140
    }
141
142
    public function getTaxAmountFromUntaxedPrice($untaxedPrice, &$taxCollection = null)
143
    {
144
        return $this->getTaxedPrice($untaxedPrice, $taxCollection) - $untaxedPrice;
145
    }
146
147
    public function getTaxAmountFromTaxedPrice($taxedPrice)
148
    {
149
        return $taxedPrice - $this->getUntaxedPrice($taxedPrice);
150
    }
151
152
    /**
153
     * @param      $untaxedPrice
154
     * @param null $taxCollection returns OrderProductTaxCollection
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $taxCollection is correct as it would always require null to be passed?
Loading history...
155
     * @param null $askedLocale
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $askedLocale is correct as it would always require null to be passed?
Loading history...
156
     *
157
     * @return int
158
     * @throws \Thelia\Exception\TaxEngineException
159
     */
160
    public function getTaxedPrice($untaxedPrice, &$taxCollection = null, $askedLocale = null)
161
    {
162
        if (null === $this->taxRulesCollection) {
163
            throw new TaxEngineException('Tax rules collection is empty in Calculator::getTaxedPrice', TaxEngineException::UNDEFINED_TAX_RULES_COLLECTION);
164
        }
165
166
        if (null === $this->product) {
167
            throw new TaxEngineException('Product is empty in Calculator::getTaxedPrice', TaxEngineException::UNDEFINED_PRODUCT);
168
        }
169
170
        if (false === filter_var($untaxedPrice, FILTER_VALIDATE_FLOAT)) {
171
            throw new TaxEngineException('BAD AMOUNT FORMAT', TaxEngineException::BAD_AMOUNT_FORMAT);
172
        }
173
174
        $taxedPrice = $untaxedPrice;
175
        $currentPosition = 1;
176
        $currentTax = 0;
177
178
        if (null !== $taxCollection) {
0 ignored issues
show
introduced by
The condition null !== $taxCollection is always false.
Loading history...
179
            $taxCollection = new OrderProductTaxCollection();
180
        }
181
        foreach ($this->taxRulesCollection as $taxRule) {
182
            $position = (int) $taxRule->getTaxRuleCountryPosition();
183
184
            $taxType = $taxRule->getTypeInstance();
185
186
            if ($currentPosition !== $position) {
187
                $taxedPrice += $currentTax;
188
                $currentTax = 0;
189
                $currentPosition = $position;
190
            }
191
192
            $taxAmount = $taxType->calculate($this->product, $taxedPrice);
193
            $currentTax += $taxAmount;
194
195
            if (null !== $taxCollection) {
196
                $taxI18n = I18n::forceI18nRetrieving($askedLocale, 'Tax', $taxRule->getId());
197
                $orderProductTax = new OrderProductTax();
198
                $orderProductTax->setTitle($taxI18n->getTitle());
199
                $orderProductTax->setDescription($taxI18n->getDescription());
200
                $orderProductTax->setAmount($taxAmount);
201
                $taxCollection->addTax($orderProductTax);
202
            }
203
        }
204
205
        $taxedPrice += $currentTax;
206
207
        return $taxedPrice;
208
    }
209
210
    public function getUntaxedPrice($taxedPrice)
211
    {
212
        if (null === $this->taxRulesCollection) {
213
            throw new TaxEngineException('Tax rules collection is empty in Calculator::getTaxAmount', TaxEngineException::UNDEFINED_TAX_RULES_COLLECTION);
214
        }
215
216
        if (null === $this->product) {
217
            throw new TaxEngineException('Product is empty in Calculator::getTaxedPrice', TaxEngineException::UNDEFINED_PRODUCT);
218
        }
219
220
        if (false === filter_var($taxedPrice, FILTER_VALIDATE_FLOAT)) {
221
            throw new TaxEngineException('BAD AMOUNT FORMAT', TaxEngineException::BAD_AMOUNT_FORMAT);
222
        }
223
224
        $taxRule = $this->taxRulesCollection->getLast();
225
226
        if (null === $taxRule) {
227
            throw new TaxEngineException('Tax rules collection got no tax ', TaxEngineException::NO_TAX_IN_TAX_RULES_COLLECTION);
228
        }
229
230
        $untaxedPrice = $taxedPrice;
231
        $currentPosition = (int) $taxRule->getTaxRuleCountryPosition();
232
        $currentFixTax = 0;
233
        $currentTaxFactor = 0;
234
235
        do {
236
            $position = (int) $taxRule->getTaxRuleCountryPosition();
237
238
            $taxType = $taxRule->getTypeInstance();
239
240
            if ($currentPosition !== $position) {
241
                $untaxedPrice -= $currentFixTax;
242
                $untaxedPrice = $untaxedPrice / (1+$currentTaxFactor);
243
                $currentFixTax = 0;
244
                $currentTaxFactor = 0;
245
                $currentPosition = $position;
246
            }
247
248
            $currentFixTax += $taxType->fixAmountRetriever($this->product);
249
            $currentTaxFactor += $taxType->pricePercentRetriever();
250
        } while ($taxRule = $this->taxRulesCollection->getPrevious());
251
252
        $untaxedPrice -= $currentFixTax;
253
        $untaxedPrice = $untaxedPrice / (1+$currentTaxFactor);
254
255
        return $untaxedPrice;
256
    }
257
}
258