This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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
|
|||
14 | { |
||
15 | |||
16 | /** |
||
17 | * |
||
18 | * @var Boolean |
||
19 | */ |
||
20 | private static $show_in_cart_table = true; |
||
0 ignored issues
–
show
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
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 <?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. ![]() |
|||
87 | |||
88 | $fields->removeByName("DefaultRate"); |
||
89 | $fields->addFieldToTab("Root.Debug", new ReadonlyField("DefaultRateShown", "Default rate", $this->DefaultRate)); |
||
0 ignored issues
–
show
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 <?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. ![]() |
|||
90 | |||
91 | $fields->removeByName("CurrentRate"); |
||
92 | $fields->addFieldToTab("Root.Debug", new ReadonlyField("CurrentRateShown", "Rate for current order", $this->CurrentRate)); |
||
0 ignored issues
–
show
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 <?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. ![]() |
|||
93 | |||
94 | $fields->removeByName("RawTableValue"); |
||
95 | $fields->addFieldToTab("Root.Debug", new ReadonlyField("RawTableValueShown", "Raw table value", $this->RawTableValue)); |
||
0 ignored issues
–
show
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 <?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. ![]() |
|||
96 | |||
97 | $fields->removeByName("DebugString"); |
||
98 | $fields->addFieldToTab("Root.Debug", new ReadonlyField("DebugStringShown", "Debug String", $this->DebugString)); |
||
0 ignored issues
–
show
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 <?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. ![]() |
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
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.. ![]() |
|||
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
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 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;
}
![]() |
|||
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
|
|||
293 | */ |
||
294 | protected function currentTaxObjects() |
||
295 | { |
||
296 | if (self::$current_tax_objects === null) { |
||
297 | $this->GSTTaxModifierOptions()->removeAll(); |
||
0 ignored issues
–
show
The method
GSTTaxModifierOptions does not exist on object<GSTTaxModifier> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when 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 { }
![]() |
|||
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
The method
GSTTaxModifierOptions does not exist on object<GSTTaxModifier> ? Since you implemented __call , maybe consider adding a @method annotation.
If you implement This is often the case, when 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 { }
![]() |
|||
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
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.. ![]() |
|||
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
|
|||
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
|
|||
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
$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 function fx() {
try {
doSomething();
return true;
}
catch (\Exception $e) {
return false;
}
return false;
}
In the above example, the last ![]() |
|||
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
|
|||
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
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. ![]() |
|||
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
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. ![]() |
|||
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
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 dependenciesPHP Analyzer uses your Are you sure this class is defined by one of your dependencies, or did you maybe
not list a dependency in either the 2. Missing use statementPHP does not complain about undefined classes in if ($x instanceof DoesNotExist) {
// Do something.
}
If you have not tested against this specific condition, such errors might go unnoticed. ![]() |
|||
468 | if (!$buyable->hasExtension("GSTTaxDecorator")) { |
||
469 | if ($parent = $buyable->Parent()) { |
||
470 | if ($parent->hasExtension("GSTTaxDecorator")) { |
||
471 | $buyable = $parent; |
||
0 ignored issues
–
show
$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 ![]() |
|||
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
|
|||
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
This
if statement is empty and can be removed.
This check looks for the bodies of These 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. ![]() |
|||
492 | //do nothing |
||
493 | //we just double-check this... |
||
494 | } else { |
||
495 | if ($modifier instanceof GSTTaxModifier) { |
||
0 ignored issues
–
show
This
if statement is empty and can be removed.
This check looks for the bodies of These 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. ![]() |
|||
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
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. ![]() |
|||
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
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. ![]() |
|||
532 | if ($modifier->hasMethod($functionName)) { |
||
533 | $totalForModifier -= $item->$functionName(); |
||
0 ignored issues
–
show
|
|||
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
|
|||
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
|
|||
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
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 <?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. ![]() |
|||
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
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 <?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. ![]() |
|||
646 | } |
||
647 | return self::$temp_raw_table_value[$this->OrderID]; |
||
0 ignored issues
–
show
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 <?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. ![]() |
|||
648 | } |
||
649 | |||
650 | /** |
||
651 | * Used to save DebugString to database |
||
652 | * @return float |
||
0 ignored issues
–
show
|
|||
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
|
|||
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
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 <?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. ![]() |
|||
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
|
|||
769 | |||
770 | public function getTableSubTitle() |
||
0 ignored issues
–
show
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 ![]() |
|||
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 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.