GSTTaxModifier   F
last analyzed

Complexity

Total Complexity 112

Size/Duplication

Total Lines 779
Duplicated Lines 4.11 %

Coupling/Cohesion

Components 2
Dependencies 13

Importance

Changes 0
Metric Value
wmc 112
lcom 2
cbo 13
dl 32
loc 779
rs 2.6666
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
A i18n_singular_name() 0 4 1
A i18n_plural_name() 0 4 1
A getCMSFields() 0 22 1
A get_default_country_code_combined() 0 8 2
A runUpdate() 0 12 1
A CanBeRemoved() 0 4 1
A ShowInTable() 0 4 1
B defaultTaxObjects() 0 28 5
B currentTaxObjects() 0 27 5
A workOutSumRate() 0 14 4
A isExclusive() 0 4 2
C isInclusive() 0 26 8
A turnRateIntoCalculationRate() 0 4 2
C workoutOrderItemsTax() 16 55 16
B dealWithProductVariationException() 0 12 5
C workoutModifiersTax() 16 66 18
A hasExceptionTaxes() 0 7 2
A LiveDefaultCountry() 0 4 1
A LiveCountry() 0 4 1
A LiveDefaultRate() 0 5 1
A LiveCurrentRate() 0 5 1
A LiveTaxType() 0 7 2
A LiveRawTableValue() 0 11 2
A LiveDebugString() 0 4 1
A LiveTableValue() 0 4 1
C LiveName() 0 37 15
C LiveCalculatedTotal() 0 45 7
B getTableSubTitle() 0 13 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like GSTTaxModifier often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GSTTaxModifier, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @author Nicolaas [at] sunnysideup.co.nz
5
 * @package: ecommerce
6
 * @sub-package: ecommerce_tax
7
 * @description: allows adding  GST / VAT / any aother tax to an order
8
 *
9
 *
10
 *
11
 */
12
13
class GSTTaxModifier extends OrderModifier
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
14
{
15
16
    /**
17
     *
18
     * @var Boolean
19
     */
20
    private static $show_in_cart_table = true;
0 ignored issues
show
Unused Code introduced by
The property $show_in_cart_table is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
21
22
    /**
23
     * message explaining how GST is based on a sale
24
     * to a particular country ...
25
     * @var string
26
     */
27
    private static $based_on_country_note = "";
0 ignored issues
show
Unused Code introduced by
The property $based_on_country_note is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
28
    // ######################################## *** model defining static variables (e.g. $db, $has_one)
29
30
    /**
31
     * standard SS variable
32
     *
33
     * @var Array
34
     */
35
    private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
36
        'DefaultCountry' => 'Varchar(3)',
37
        'Country' => 'Varchar(3)',
38
        'DefaultRate' => 'Double',
39
        'CurrentRate' => 'Double',
40
        'TaxType' => "Enum('Exclusive, Inclusive','Inclusive')",
41
        'DebugString' => 'HTMLText',
42
        'RawTableValue' => 'Currency'
43
    );
44
45
    private static $many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $many_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
46
        "GSTTaxModifierOptions" => "GSTTaxModifierOptions"
47
    );
48
49
    /**
50
     * standard SS variable
51
     * @var String
52
     */
53
    private static $singular_name = "Tax Charge";
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $singular_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
54
    public function i18n_singular_name()
55
    {
56
        return _t("GSTTaxModifier.TAXCHARGE", "Tax Charge");
57
    }
58
59
60
    /**
61
     * standard SS variable
62
     * @var String
63
     */
64
    private static $plural_name = "Tax Charges";
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $plural_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
65
    public function i18n_plural_name()
66
    {
67
        return _t("GSTTaxModifier.TAXCHARGES", "Tax Charges");
68
    }
69
70
71
    // ######################################## *** cms variables + functions (e.g. getCMSFields, $searchableFields)
72
73
74
75
    /**
76
     * standard SS method
77
     * @return FieldList for CMS
78
     */
79
    public function getCMSFields()
80
    {
81
        $fields = parent::getCMSFields();
82
        $fields->replaceField("Country", new DropDownField("Country", "based on a sale to ", EcommerceCountry::get_country_dropdown()));
83
        $fields->replaceField("Root.Main", new DropdownField("TaxType", "Tax Type", singleton($this->ClassName)->dbObject('TaxType')->enumValues()));
84
85
        $fields->removeByName("DefaultCountry");
86
        $fields->addFieldToTab("Root.Debug", new ReadonlyField("DefaultCountryShown", "Prices are based on sale to", $this->DefaultCountry));
0 ignored issues
show
Documentation introduced by
The property DefaultCountry does not exist on object<GSTTaxModifier>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
87
88
        $fields->removeByName("DefaultRate");
89
        $fields->addFieldToTab("Root.Debug", new ReadonlyField("DefaultRateShown", "Default rate", $this->DefaultRate));
0 ignored issues
show
Documentation introduced by
The property DefaultRate does not exist on object<GSTTaxModifier>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
90
91
        $fields->removeByName("CurrentRate");
92
        $fields->addFieldToTab("Root.Debug", new ReadonlyField("CurrentRateShown", "Rate for current order", $this->CurrentRate));
0 ignored issues
show
Documentation introduced by
The property CurrentRate does not exist on object<GSTTaxModifier>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
93
94
        $fields->removeByName("RawTableValue");
95
        $fields->addFieldToTab("Root.Debug", new ReadonlyField("RawTableValueShown", "Raw table value", $this->RawTableValue));
0 ignored issues
show
Documentation introduced by
The property RawTableValue does not exist on object<GSTTaxModifier>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
96
97
        $fields->removeByName("DebugString");
98
        $fields->addFieldToTab("Root.Debug", new ReadonlyField("DebugStringShown", "Debug String", $this->DebugString));
0 ignored issues
show
Documentation introduced by
The property DebugString does not exist on object<GSTTaxModifier>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
99
        return $fields;
100
    }
101
102
    // ######################################## *** other (non) static variables (e.g. private static $special_name_for_something, protected $order)
103
    /**
104
     * default country for tax calculations
105
     * IMPORTANT: we need this variable - because in case of INCLUSIVE prices,
106
     * we need to know on what country the prices are based as to be able
107
     * to remove the tax for other countries.
108
     * @var String
109
     */
110
    private static $default_country_code = "";
0 ignored issues
show
Unused Code introduced by
The property $default_country_code is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
111
    protected static function get_default_country_code_combined()
112
    {
113
        $country = Config::inst()->get("GSTTaxModifier", "default_country_code");
114
        if (!$country) {
115
            $country = EcommerceConfig::get('EcommerceCountry', 'default_country_code');
116
        }
117
        return $country;
118
    }
119
120
    /**
121
     * wording in cart for prices that are tax exclusive (tax added on top of prices)
122
     * @var String
123
     */
124
    private static $exclusive_explanation = "";
0 ignored issues
show
Unused Code introduced by
The property $exclusive_explanation is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
125
126
    /**
127
     * wording in cart for prices that are tax inclusive (tax is part of the prices)
128
     * @var String
129
     */
130
    private static $inclusive_explanation = "";
0 ignored issues
show
Unused Code introduced by
The property $inclusive_explanation is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
131
132
    /**
133
     * wording in cart for prices that are include a tax refund.
134
     * A refund situation applies when the prices are tax inclusive
135
     * but NO tax applies to the country to which the goods are sold.
136
     * E.g. for a UK shop no VAT is charged to buyers outside the EU.
137
     * @var String
138
     */
139
    private static $refund_title = "Tax Exemption";
0 ignored issues
show
Unused Code introduced by
The property $refund_title is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
140
141
    /**
142
     * wording in cart for prices that are tax exempt (no tax applies)
143
     * @var String
144
     */
145
    private static $no_tax_description = "Tax-exempt";
0 ignored issues
show
Unused Code introduced by
The property $no_tax_description is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
146
147
    /**
148
     * name of the method in the buyable OrderItem that works out the
149
     * portion without tax. You can use this method by creating your own
150
     * OrderItem class and adding a method there.  This is by far the most
151
     * flexible way to work out the tax on products with complex tax rules.
152
     * @var String
153
     */
154
    private static $order_item_function_for_tax_exclusive_portion = "portionWithoutTax";//PortionWithoutTax
0 ignored issues
show
Unused Code introduced by
The property $order_item_function_for_tax_exclusive_portion is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
155
156
    /**
157
     * Use this variable IF:
158
     *
159
     * a. you have localised prices for countries
160
     * other than the default country
161
     *
162
     * b. prices on the website are TAX INCLUSIVE
163
     *
164
     * If not, the tax for an international for a
165
     * site with tax inclusive prices will firstly
166
     * deduct the default tax and then add the tax
167
     * of the country at hand.
168
     *
169
     * @var Boolean
170
     */
171
    private static $alternative_country_prices_already_include_their_own_tax = false;//PortionWithoutTax
0 ignored issues
show
Unused Code introduced by
The property $alternative_country_pri...y_include_their_own_tax is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
172
173
    /**
174
     * contains all the applicable DEFAULT tax objects
175
     * @var Object
176
     */
177
    private static $default_tax_objects = null;
178
179
180
    /**
181
     * tells us the default tax objects tax rate
182
     * @var Float | Null
183
     */
184
    private static $default_tax_objects_rate = null;
185
186
187
    /**
188
     * contains all the applicable tax objects for the current order
189
     * @var Object
190
     */
191
    private static $current_tax_objects = null;
192
193
    /**
194
     * tells us the current tax objects tax rate
195
     * @var NULL | Float
196
     */
197
    private static $current_tax_objects_rate = null;
198
199
    /**
200
     * any calculation messages are added to the Debug Message
201
     * @var String
202
     */
203
    protected $debugMessage = '';
204
205
206
    // ######################################## *** CRUD functions (e.g. canEdit)
207
    // ######################################## *** init and update functions
208
    /**
209
     * updates database fields
210
     * @param Bool $force - run it, even if it has run already
211
     * @return void
212
     */
213
    public function runUpdate($force = true)
214
    {
215
        //order is important!
216
        $this->checkField("DefaultCountry");
217
        $this->checkField("Country");
218
        $this->checkField("DefaultRate");
219
        $this->checkField("CurrentRate");
220
        $this->checkField("TaxType");
221
        $this->checkField("RawTableValue");
222
        $this->checkField("DebugString");
223
        parent::runUpdate($force);
224
    }
225
226
227
    // ######################################## *** form functions (e. g. Showform and getform)
228
    // ######################################## *** template functions (e.g. ShowInTable, TableTitle, etc...) ... USES DB VALUES
229
230
    /**
231
     * Can the user remove this modifier?
232
     * standard OrderModifier Method
233
     * @return Bool
234
     */
235
    public function CanBeRemoved()
236
    {
237
        return false;
238
    }
239
240
    /**
241
     * Show the GSTTaxModifier in the Cart?
242
     * standard OrderModifier Method
243
     * @return Bool
244
     */
245
    public function ShowInTable()
246
    {
247
        return $this->Config()->get("show_in_cart_table");
248
    }
249
250
251
    // ######################################## ***  inner calculations.... USES CALCULATED VALUES
252
253
    /**
254
     * works out what taxes apply in the default setup.
255
     * we need this, because prices may include tax
256
     * based on the default tax rate.
257
     *
258
     * @return ArrayList | FALSE of applicable taxes in the default country.
259
     */
260
    protected function defaultTaxObjects()
261
    {
262
        if (self::$default_tax_objects === null) {
263
            $defaultCountryCode = self::get_default_country_code_combined();
264
            if ($defaultCountryCode) {
265
                $this->debugMessage .= "<hr />There is a current live DEFAULT country code: ".$defaultCountryCode;
266
                self::$default_tax_objects = GSTTaxModifierOptions::get()
267
                    ->filter(
268
                        array(
269
                            "CountryCode" => $defaultCountryCode,
270
                            "DoesNotApplyToAllProducts" => 0
271
                        )
272
                    );
273
                if (self::$default_tax_objects->count()) {
274
                    $this->debugMessage .= "<hr />there are DEFAULT tax objects available for ".$defaultCountryCode;
275
                } else {
276
                    self::$default_tax_objects = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type object of property $default_tax_objects.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
277
                    $this->debugMessage .= "<hr />there are no DEFAULT tax object available for ".$defaultCountryCode;
278
                }
279
            } else {
280
                $this->debugMessage .= "<hr />There is no current live DEFAULT country";
281
            }
282
        }
283
        if (self::$default_tax_objects_rate === null) {
284
            self::$default_tax_objects_rate = $this->workOutSumRate(self::$default_tax_objects);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->workOutSumRate(self::$default_tax_objects) can also be of type integer. However, the property $default_tax_objects_rate is declared as type double. 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...
285
        }
286
        return self::$default_tax_objects;
287
    }
288
289
290
    /**
291
     * returns an ArrayList of all applicable tax options
292
     * @return ArrayList | Null
0 ignored issues
show
Documentation introduced by
Should the return type not be ArrayList|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
293
     */
294
    protected function currentTaxObjects()
295
    {
296
        if (self::$current_tax_objects === null) {
297
            $this->GSTTaxModifierOptions()->removeAll();
0 ignored issues
show
Documentation Bug introduced by
The method GSTTaxModifierOptions does not exist on object<GSTTaxModifier>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
298
            if ($countryCode = $this->LiveCountry()) {
299
                $this->debugMessage .= "<hr />There is a current live country: ".$countryCode;
300
                self::$current_tax_objects = GSTTaxModifierOptions::get()->where("(\"CountryCode\" = '".$countryCode."' OR \"AppliesToAllCountries\" = 1) AND \"DoesNotApplyToAllProducts\" = 0");
301
                GSTTaxModifierOptions::get()
302
                    ->where(
303
                        "(\"CountryCode\" = '".$countryCode."' OR \"AppliesToAllCountries\" = 1) AND \"DoesNotApplyToAllProducts\" = 0"
304
                    );
305
                if (self::$current_tax_objects->count()) {
306
                    $this->GSTTaxModifierOptions()->addMany(self::$current_tax_objects->map("ID", "ID")->toArray());
0 ignored issues
show
Documentation Bug introduced by
The method GSTTaxModifierOptions does not exist on object<GSTTaxModifier>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
307
                    $this->debugMessage .= "<hr />There are tax objects available for ".$countryCode;
308
                } else {
309
                    self::$current_tax_objects = null;
310
                    $this->debugMessage .= "<hr />there are no tax objects available for ".$countryCode;
311
                }
312
            } else {
313
                $this->debugMessage .= "<hr />there is no current live country code";
314
            }
315
        }
316
        if (self::$current_tax_objects_rate === null) {
317
            self::$current_tax_objects_rate = $this->workOutSumRate(self::$current_tax_objects);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->workOutSumRate(self::$current_tax_objects) of type double or integer is incompatible with the declared type null of property $current_tax_objects_rate.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
318
        }
319
        return self::$current_tax_objects;
320
    }
321
322
    /**
323
     * returns the sum of rates for the given taxObjects
324
     * @param Object - ArrayList of tax options
325
     * @return Float
0 ignored issues
show
Documentation introduced by
Should the return type not be double|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
326
     */
327
    protected function workOutSumRate($taxObjects)
328
    {
329
        $sumRate = 0;
330
        if ($taxObjects && $taxObjects->count()) {
331
            foreach ($taxObjects as $obj) {
332
                $this->debugMessage .= "<hr />found ".$obj->Title();
333
                $sumRate += floatval($obj->Rate);
334
            }
335
        } else {
336
            $this->debugMessage .= "<hr />could not find a rate";
337
        }
338
        $this->debugMessage .= "<hr />sum rate for tax objects: ".$sumRate;
339
        return $sumRate;
340
    }
341
342
    /**
343
     * tells us if the tax for the current order is exclusive
344
     * default: false
345
     * @return Bool
346
     */
347
    protected function isExclusive()
348
    {
349
        return $this->isInclusive() ? false : true;
350
    }
351
352
    /**
353
     * tells us if the tax for the current order is inclusive
354
     * default: true
355
     * @return Bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
356
     */
357
    protected function isInclusive()
358
    {
359
        return $this->EcomConfig()->ShopPricesAreTaxExclusive ? false : true;
360
        //this code is here to support e-commerce versions that
361
        //do not have the DB field EcomConfig()->ShopPricesAreTaxExclusive
362
        $array = array();
0 ignored issues
show
Unused Code introduced by
$array = array(); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
363
        //here we have to take the default tax objects
364
        //because we want to know for the default country
365
        //that is the actual country may not have any prices
366
        //associated with it!
367
        if ($objects = $this->defaultTaxObjects()) {
368
            foreach ($objects as $obj) {
369
                $array[$obj->InclusiveOrExclusive] = $obj->InclusiveOrExclusive;
370
            }
371
        }
372
        if (count($array) < 1) {
373
            return true;
374
        } elseif (count($array) > 1) {
375
            user_error("you can not have a collection of tax objects that is both inclusive and exclusive", E_USER_WARNING);
376
            return true;
377
        } else {
378
            foreach ($array as $item) {
379
                return $item == "Inclusive" ? true : false;
380
            }
381
        }
382
    }
383
384
    /**
385
     * turns a standard rate into a calculation rate.
386
     * That is, 0.125 for exclusive is 1/9 for inclusive rates
387
     * default: true
388
     * @param float $rate - input rate (e.g. 0.125 equals a 12.5% tax rate)
389
     * @return float
390
     */
391
    protected function turnRateIntoCalculationRate($rate)
392
    {
393
        return $this->isExclusive() ? $rate : (1 - (1 / (1 + $rate)));
394
    }
395
396
    /**
397
     * works out the tax to pay for the order items,
398
     * based on a rate and a country
399
     * @param float $rate
400
     * @param string $country
401
     * @return float - amount of tax to pay
0 ignored issues
show
Documentation introduced by
Should the return type not be double|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
402
     */
403
    protected function workoutOrderItemsTax($rate, $country)
404
    {
405
        $order = $this->Order();
406
        $itemsTotal = 0;
407
        if ($order) {
408
            $items = $this->Order()->Items();
409
            if ($items) {
410
                foreach ($items as $itemIndex => $item) {
411
                    //resetting actual rate...
412
                    $actualRate = $rate;
413
                    $buyable = $item->Buyable();
414
                    if ($buyable) {
415
                        $this->dealWithProductVariationException($buyable);
416
                        if ($buyable->hasExtension("GSTTaxDecorator")) {
417
                            $excludedTaxes = $buyable->BuyableCalculatedExcludedFrom();
418
                            $additionalTaxes = $buyable->BuyableCalculatedAdditionalTax();
419
                            if ($excludedTaxes) {
420
                                foreach ($excludedTaxes as $tax) {
421
                                    if (!$tax->DoesNotApplyToAllProducts) {
422
                                        $this->debugMessage .= "<hr />found tax to exclude for ".$buyable->Title.": ".$tax->Title();
423
                                        $actualRate -= $tax->Rate;
424
                                    }
425
                                }
426
                            }
427 View Code Duplication
                            if ($additionalTaxes) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
428
                                foreach ($additionalTaxes as $tax) {
429
                                    if ($tax->DoesNotApplyToAllProducts) {
430
                                        if ($tax->AppliesToAllCountries || $tax->CountryCode == $country) {
431
                                            $this->debugMessage .= "<hr />found tax to add for ".$buyable->Title.": ".$tax->Title();
432
                                            $actualRate += $tax->Rate;
433
                                        }
434
                                    }
435
                                }
436
                            }
437
                        }
438
                    }
439
                    $totalForItem = $item->Total();
440
                    $functionName = $this->config()->get("order_item_function_for_tax_exclusive_portion");
441 View Code Duplication
                    if ($functionName) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
442
                        if ($item->hasMethod($functionName)) {
443
                            $this->debugMessage .= "<hr />running $functionName on ".$item->ClassName.".".$item->ID;
444
                            $totalForItem -= $item->$functionName();
445
                        }
446
                    }
447
                    //turnRateIntoCalculationRate is really important -
448
                    //a 10% rate is different for inclusive than for an exclusive tax
449
                    $actualCalculationRate = $this->turnRateIntoCalculationRate($actualRate);
450
                    $this->debugMessage .= "<hr /><b>$actualRate</b> turned into ".round($actualCalculationRate, 2)." for a total of <b>$totalForItem</b> on ".$item->ClassName.".".$item->ID;
451
                    $itemsTotal += floatval($totalForItem) * $actualCalculationRate;
452
                }
453
            }
454
        }
455
        $this->debugMessage .= "<hr />Total order items tax: \$ ".round($itemsTotal, 4);
456
        return $itemsTotal;
457
    }
458
459
    /**
460
     * this method is a bit of a hack.
461
     * if a product variation does not have any specific tax rules
462
     * but the product does, then it uses the rules from the product.
463
     * @param DataObject $buyable
464
     */
465
    public function dealWithProductVariationException($buyable)
466
    {
467
        if ($buyable instanceof ProductVariation) {
0 ignored issues
show
Bug introduced by
The class ProductVariation does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
468
            if (!$buyable->hasExtension("GSTTaxDecorator")) {
469
                if ($parent = $buyable->Parent()) {
470
                    if ($parent->hasExtension("GSTTaxDecorator")) {
471
                        $buyable = $parent;
0 ignored issues
show
Unused Code introduced by
$buyable is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
472
                    }
473
                }
474
            }
475
        }
476
    }
477
478
    /**
479
     * works out the tax to pay for the order modifiers,
480
     * based on a rate
481
     * @param float $rate
482
     * @return float - amount of tax to pay
0 ignored issues
show
Documentation introduced by
Should the return type not be double|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
483
     */
484
    protected function workoutModifiersTax($rate, $country)
485
    {
486
        $modifiersTotal = 0;
487
        $order = $this->Order();
488
        if ($order) {
489
            if ($modifiers = $order->Modifiers()) {
490
                foreach ($modifiers as $modifier) {
491
                    if ($modifier->IsRemoved()) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
492
                        //do nothing
493
                        //we just double-check this...
494
                    } else {
495
                        if ($modifier instanceof GSTTaxModifier) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
496
                            //do nothing
497
                        } else {
498
                            $actualRate = $rate;
499
                            $modifierDescriptor = DataObject::get_one(
500
                                'OrderModifier_Descriptor',
501
                                array("ModifierClassName" => $modifier->ClassName)
502
                            );
503
                            if ($modifierDescriptor) {
504
                                if ($modifierDescriptor->hasExtension("GSTTaxDecorator")) {
505
                                    $excludedTaxes = $modifierDescriptor->ExcludedFrom();
506
                                    $additionalTaxes = $modifierDescriptor->AdditionalTax();
507
                                    if ($excludedTaxes) {
508
                                        foreach ($excludedTaxes as $tax) {
509
                                            if (!$tax->DoesNotApplyToAllProducts) {
510
                                                $this->debugMessage .= "<hr />found tax to exclude for ".$modifier->Title.": ".$tax->Title();
511
                                                $actualRate -= $tax->Rate;
512
                                            }
513
                                        }
514
                                    }
515 View Code Duplication
                                    if ($additionalTaxes) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
516
                                        foreach ($additionalTaxes as $tax) {
517
                                            if ($tax->DoesNotApplyToAllProducts) {
518
                                                if ($tax->AppliesToAllCountries || $tax->CountryCode == $country) {
519
                                                    $this->debugMessage .= "<hr />found adtax to add for ".$modifier->Title.": ".$tax->Title();
520
                                                    $actualRate += $tax->Rate;
521
                                                }
522
                                            }
523
                                        }
524
                                    }
525
                                } else {
526
                                    $this->debugMessage .= "<hr />".$modifierDescriptor->ClassName." does not have the GSTTaxDecorator extension";
527
                                }
528
                            }
529
                            $totalForModifier = $modifier->CalculationTotal();
530
                            $functionName = $this->config()->get("order_item_function_for_tax_exclusive_portion");
531 View Code Duplication
                            if ($functionName) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
532
                                if ($modifier->hasMethod($functionName)) {
533
                                    $totalForModifier -= $item->$functionName();
0 ignored issues
show
Bug introduced by
The variable $item does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
534
                                    $this->debugMessage .= "<hr />running $functionName on ".$modifier->ClassName.".".$modifier->ID;
535
                                }
536
                            }
537
                            //turnRateIntoCalculationRate is really important -
538
                            //a 10% rate is different for inclusive than for an exclusive tax
539
                            $actualRateCalculationRate = $this->turnRateIntoCalculationRate($actualRate);
540
                            $this->debugMessage .= "<hr />rate of $actualRate' turned into calculation rate of ".round($actualRateCalculationRate, 2)." for the total of $totalForModifier' on ".$modifier->ClassName.".".$modifier->ID;
541
                            $modifiersTotal += floatval($totalForModifier) * $actualRateCalculationRate;
542
                        }
543
                    }
544
                }
545
            }
546
        }
547
        $this->debugMessage .= "<hr />Total order modifiers tax: \$ ".round($modifiersTotal, 4);
548
        return $modifiersTotal;
549
    }
550
551
    /**
552
     * Are there Any taxes that do not apply to all products
553
     * @return Boolean
554
     */
555
    protected function hasExceptionTaxes()
556
    {
557
        return DataObject::get_one(
558
            'GSTTaxModifierOptions',
559
            array("DoesNotApplyToAllProducts" => 1)
560
        ) ? false : true;
561
    }
562
563
    // ######################################## *** calculate database fields: protected function Live[field name]  ... USES CALCULATED VALUES
564
565
566
    /**
567
     * Used to save DefaultCountry to database
568
     *
569
     * determines value for DB field: Country
570
     * @return String
0 ignored issues
show
Documentation introduced by
Should the return type not be array|integer|double|string|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
571
     */
572
    protected function LiveDefaultCountry()
573
    {
574
        return self::get_default_country_code_combined();
575
    }
576
577
    /**
578
     * Used to save Country to database
579
     *
580
     * determines value for DB field: Country
581
     * @return String
582
     */
583
    protected function LiveCountry()
584
    {
585
        return EcommerceCountry::get_country();
586
    }
587
588
    /**
589
     * determines value for the default rate
590
     * @return Float
591
     */
592
    protected function LiveDefaultRate()
593
    {
594
        $this->defaultTaxObjects();
595
        return self::$default_tax_objects_rate;
596
    }
597
598
    /**
599
     * Used to save CurrentRate to database
600
     *
601
     * determines value for DB field: Country
602
     * @return Float
0 ignored issues
show
Documentation introduced by
Should the return type not be double|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
603
     */
604
    protected function LiveCurrentRate()
605
    {
606
        $this->currentTaxObjects();
607
        return self::$current_tax_objects_rate;
608
    }
609
610
    /**
611
     * Used to save TaxType to database
612
     *
613
     * determines value for DB field: TaxType
614
     * @return String (Exclusive|Inclusive)
615
     */
616
    protected function LiveTaxType()
617
    {
618
        if ($this->isExclusive()) {
619
            return "Exclusive";
620
        }
621
        return "Inclusive";
622
    }
623
624
    /**
625
     * temporary store of data for additional speed.
626
     * @var Array
627
     */
628
629
    private static $temp_raw_table_value = array();
630
631
    /**
632
     * Used to save RawTableValue to database
633
     *
634
     * In case of a an exclusive rate, show what is actually added.
635
     * In case of inclusive rate, show what is actually included.
636
     * @return float
637
     */
638
    protected function LiveRawTableValue()
639
    {
640
        if (!isset(self::$temp_raw_table_value[$this->OrderID])) {
0 ignored issues
show
Documentation introduced by
The property OrderID does not exist on object<GSTTaxModifier>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
641
            $currentRate = $this->LiveCurrentRate();
642
            $currentCountry = $this->LiveCountry();
643
            $itemsTax = $this->workoutOrderItemsTax($currentRate, $currentCountry);
644
            $modifiersTax = $this->workoutModifiersTax($currentRate, $currentCountry);
645
            self::$temp_raw_table_value[$this->OrderID] = $itemsTax + $modifiersTax;
0 ignored issues
show
Documentation introduced by
The property OrderID does not exist on object<GSTTaxModifier>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
646
        }
647
        return self::$temp_raw_table_value[$this->OrderID];
0 ignored issues
show
Documentation introduced by
The property OrderID does not exist on object<GSTTaxModifier>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
648
    }
649
650
    /**
651
     * Used to save DebugString to database
652
     * @return float
0 ignored issues
show
Documentation introduced by
Should the return type not be string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
653
     */
654
    protected function LiveDebugString()
655
    {
656
        return $this->debugMessage;
657
    }
658
659
    /**
660
     * Used to save TableValue to database
661
     *
662
     * @return float
663
     */
664
    protected function LiveTableValue()
665
    {
666
        return $this->LiveRawTableValue();
667
    }
668
669
    /**
670
     * Used to save Name to database
671
     * @return String
672
     */
673
    protected function LiveName()
674
    {
675
        $finalString = _t("OrderModifier.TAXCOULDNOTBEDETERMINED", "tax could not be determined");
676
        $countryCode = $this->LiveCountry();
677
        $startString = '';
678
        $name = '';
679
        $endString = '';
680
        $taxObjects = $this->currentTaxObjects();
681
        if ($taxObjects) {
682
            $objectArray = array();
683
            foreach ($taxObjects as $object) {
684
                $objectArray[] = $object->Name;
685
            }
686
            if (count($objectArray)) {
687
                $name = implode(", ", $objectArray);
688
            }
689
            if ($this->config()->get("exclusive_explanation") && $this->isExclusive()) {
690
                $endString = $this->config()->get("exclusive_explanation");
691
            } elseif ($this->Config()->get("inclusive_explanation") && $this->isInclusive()) {
692
                $endString = $this->Config()->get("inclusive_explanation");
693
            }
694
            if ($name) {
695
                $finalString = $startString.$name.$endString;
696
            }
697
        } else {
698
            if ($this->hasExceptionTaxes()) {
699
                $finalString = $this->Config()->get("no_tax_description");
700
            }
701
        }
702
        if ($countryCode && $finalString) {
703
            $countryName = EcommerceCountry::find_title($countryCode);
704
            if ($this->Config()->get("based_on_country_note") && $countryName  && $countryCode != self::get_default_country_code_combined()) {
705
                $finalString .= $this->Config()->get("based_on_country_note").' '.$countryName;
706
            }
707
        }
708
        return $finalString;
709
    }
710
711
    /**
712
     * Used to save CalculatedTotal to database
713
714
     * works out the actual amount that needs to be deducted / added.
715
     * The exclusive case is easy: just add the applicable tax
716
     *
717
     * The inclusive case: work out what was included and then work out what is applicable
718
     * (current), then work out the difference.
719
     *
720
     * @return Float
0 ignored issues
show
Documentation introduced by
Should the return type not be double|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
721
     */
722
    protected function LiveCalculatedTotal()
723
    {
724
        if ($this->isExclusive()) {
725
            return $this->LiveRawTableValue();
726
        } else {
727
            if (Config::inst()->get('GSTTaxModifier', 'alternative_country_prices_already_include_their_own_tax')) {
728
                return 0;
729
            } else {
730
                $currentCountry = $this->LiveCountry();
731
                $defaultCountry = $this->LiveDefaultCountry();
732
                if ($currentCountry != $defaultCountry) {
733
                    //what should have actually been shown in prices:
734
                    $actualNeedToPay = $this->LiveRawTableValue();
735
736
                    //if there are country specific objects but no value
737
                    //then we assume: alternative_country_prices_already_include_their_own_tax
738
                    if ($objects = $this->currentTaxObjects) {
0 ignored issues
show
Documentation introduced by
The property currentTaxObjects does not exist on object<GSTTaxModifier>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
739
                        $objects = $objects->Filter(
740
                            array(
741
                                "CountryCode" => $currentCountry
742
                            )
743
                        );
744
                        if ($objects->count() && $actualNeedToPay == 0) {
745
                            return 0;
746
                        }
747
                    }
748
749
                    //already calculated into prices:
750
                    $defaultRate = $this->LiveDefaultRate();
751
                    $defaultItemsTax = $this->workoutOrderItemsTax($defaultRate, $defaultCountry);
752
                    $defaultModifiersTax = $this->workoutModifiersTax($defaultRate, $defaultCountry);
753
                    $taxIncludedByDefault = $defaultItemsTax + $defaultModifiersTax;
754
755
                    //use what actually needs to be paid in tax minus what is already showing in prices
756
                    //for example, if the shop is tax inclusive
757
                    //and it is based in NZ (tax = 0.15) and a sale is made to AU (tax = 0.1)
758
                    //and the shop also charges tax in AU then the Calculated TOTAL
759
                    //is: AUTAX - NZTAX
760
                    return $actualNeedToPay - $taxIncludedByDefault;
761
                } else {
762
                    return 0;
763
                }
764
            }
765
        }
766
    }
767
768
    private static $field_or_method_to_use_for_sub_title = "";
0 ignored issues
show
Unused Code introduced by
The property $field_or_method_to_use_for_sub_title is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
769
770
    public function getTableSubTitle()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
771
    {
772
        $title = $this->stat('field_or_method_to_use_for_sub_title');
773
        if ($title) {
774
            $taxObjects = $this->currentTaxObjects();
775
            if ($taxObjects) {
776
                $taxObject = $taxObjects->First();
777
                if ($taxObject) {
778
                    return $taxObject->hasMethod($title) ? $taxObject->$title() : $taxObject->$title;
779
                }
780
            }
781
        }
782
    }
783
784
    // ######################################## *** Type Functions (IsChargeable, IsDeductable, IsNoChange, IsRemoved)
785
786
// ######################################## *** standard database related functions (e.g. onBeforeWrite, onAfterWrite, etc...)
787
788
// ######################################## *** AJAX related functions
789
790
// ######################################## *** debug functions
791
}
792