PaymentModuleCore::validateOrder()   F
last analyzed

Complexity

Conditions 132
Paths 0

Size

Total Lines 667
Code Lines 430

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 132
eloc 430
nc 0
nop 10
dl 0
loc 667
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/*
3
* 2007-2016 PrestaShop
4
*
5
* NOTICE OF LICENSE
6
*
7
* This source file is subject to the Open Software License (OSL 3.0)
8
* that is bundled with this package in the file LICENSE.txt.
9
* It is also available through the world-wide-web at this URL:
10
* http://opensource.org/licenses/osl-3.0.php
11
* If you did not receive a copy of the license and are unable to
12
* obtain it through the world-wide-web, please send an email
13
* to [email protected] so we can send you a copy immediately.
14
*
15
* DISCLAIMER
16
*
17
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
18
* versions in the future. If you wish to customize PrestaShop for your
19
* needs please refer to http://www.prestashop.com for more information.
20
*
21
*  @author PrestaShop SA <[email protected]>
22
*  @copyright  2007-2016 PrestaShop SA
23
*  @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
24
*  International Registered Trademark & Property of PrestaShop SA
25
*/
26
27
abstract class PaymentModuleCore extends Module
0 ignored issues
show
Coding Style introduced by
The property $currencies_mode is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
28
{
29
    /** @var int Current order's id */
30
    public $currentOrder;
31
    public $currencies = true;
32
    public $currencies_mode = 'checkbox';
33
34
    const DEBUG_MODE = false;
35
36
    public function install()
37
    {
38
        if (!parent::install()) {
39
            return false;
40
        }
41
42
        // Insert currencies availability
43
        if ($this->currencies_mode == 'checkbox') {
44
            if (!$this->addCheckboxCurrencyRestrictionsForModule()) {
45
                return false;
46
            }
47
        } elseif ($this->currencies_mode == 'radio') {
48
            if (!$this->addRadioCurrencyRestrictionsForModule()) {
49
                return false;
50
            }
51
        } else {
52
            Tools::displayError('No currency mode for payment module');
53
        }
54
55
        // Insert countries availability
56
        $return = $this->addCheckboxCountryRestrictionsForModule();
57
58
        if (!Configuration::get('CONF_'.strtoupper($this->name).'_FIXED')) {
59
            Configuration::updateValue('CONF_'.strtoupper($this->name).'_FIXED', '0.2');
60
        }
61
        if (!Configuration::get('CONF_'.strtoupper($this->name).'_VAR')) {
62
            Configuration::updateValue('CONF_'.strtoupper($this->name).'_VAR', '2');
63
        }
64
        if (!Configuration::get('CONF_'.strtoupper($this->name).'_FIXED_FOREIGN')) {
65
            Configuration::updateValue('CONF_'.strtoupper($this->name).'_FIXED_FOREIGN', '0.2');
66
        }
67
        if (!Configuration::get('CONF_'.strtoupper($this->name).'_VAR_FOREIGN')) {
68
            Configuration::updateValue('CONF_'.strtoupper($this->name).'_VAR_FOREIGN', '2');
69
        }
70
71
        return $return;
72
    }
73
74
    public function uninstall()
75
    {
76
        if (!Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'module_country` WHERE id_module = '.(int)$this->id)
77
            || !Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'module_currency` WHERE id_module = '.(int)$this->id)
78
            || !Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'module_group` WHERE id_module = '.(int)$this->id)) {
79
            return false;
80
        }
81
        return parent::uninstall();
82
    }
83
84
85
    /**
86
     * Add checkbox currency restrictions for a new module
87
     * @param array $shops
88
     *
89
     * @return bool
90
     */
91
    public function addCheckboxCurrencyRestrictionsForModule(array $shops = array())
92
    {
93
        if (!$shops) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $shops of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
94
            $shops = Shop::getShops(true, null, true);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $shops. This often makes code more readable.
Loading history...
95
        }
96
97
        foreach ($shops as $s) {
98
            if (!Db::getInstance()->execute('
99
					INSERT INTO `'._DB_PREFIX_.'module_currency` (`id_module`, `id_shop`, `id_currency`)
100
					SELECT '.(int)$this->id.', "'.(int)$s.'", `id_currency` FROM `'._DB_PREFIX_.'currency` WHERE deleted = 0')) {
101
                return false;
102
            }
103
        }
104
        return true;
105
    }
106
107
    /**
108
     * Add radio currency restrictions for a new module
109
     * @param array $shops
110
     *
111
     * @return bool
112
     */
113
    public function addRadioCurrencyRestrictionsForModule(array $shops = array())
114
    {
115
        if (!$shops) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $shops of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
116
            $shops = Shop::getShops(true, null, true);
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $shops. This often makes code more readable.
Loading history...
117
        }
118
119
        foreach ($shops as $s) {
120
            if (!Db::getInstance()->execute('INSERT INTO `'._DB_PREFIX_.'module_currency` (`id_module`, `id_shop`, `id_currency`)
121
				VALUES ('.(int)$this->id.', "'.(int)$s.'", -2)')) {
122
                return false;
123
            }
124
        }
125
        return true;
126
    }
127
128
    /**
129
     * Add checkbox country restrictions for a new module
130
     * @param array $shops
131
     *
132
     * @return bool
133
     */
134
    public function addCheckboxCountryRestrictionsForModule(array $shops = array())
135
    {
136
        $countries = Country::getCountries((int)Context::getContext()->language->id, true); //get only active country
137
        $country_ids = array();
138
        foreach ($countries as $country) {
139
            $country_ids[] = $country['id_country'];
140
        }
141
        return Country::addModuleRestrictions($shops, $countries, array(array('id_module' => (int)$this->id)));
142
    }
143
144
    /**
145
     * Validate an order in database
146
     * Function called from a payment module
147
     *
148
     * @param int $id_cart
149
     * @param int $id_order_state
150
     * @param float   $amount_paid    Amount really paid by customer (in the default currency)
151
     * @param string  $payment_method Payment method (eg. 'Credit card')
152
     * @param null    $message        Message to attach to order
153
     * @param array   $extra_vars
154
     * @param null    $currency_special
155
     * @param bool    $dont_touch_amount
156
     * @param bool    $secure_key
157
     * @param Shop    $shop
158
     *
159
     * @return bool
160
     * @throws PrestaShopException
161
     */
162
    public function validateOrder($id_cart, $id_order_state, $amount_paid, $payment_method = 'Unknown',
163
        $message = null, $extra_vars = array(), $currency_special = null, $dont_touch_amount = false,
164
        $secure_key = false, Shop $shop = null)
165
    {
166
        if (self::DEBUG_MODE) {
167
            PrestaShopLogger::addLog('PaymentModule::validateOrder - Function called', 1, null, 'Cart', (int)$id_cart, true);
168
        }
169
170
        if (!isset($this->context)) {
171
            $this->context = Context::getContext();
172
        }
173
        $this->context->cart = new Cart((int)$id_cart);
174
        $this->context->customer = new Customer((int)$this->context->cart->id_customer);
175
        // The tax cart is loaded before the customer so re-cache the tax calculation method
176
        $this->context->cart->setTaxCalculationMethod();
177
178
        $this->context->language = new Language((int)$this->context->cart->id_lang);
179
        $this->context->shop = ($shop ? $shop : new Shop((int)$this->context->cart->id_shop));
180
        ShopUrl::resetMainDomainCache();
181
        $id_currency = $currency_special ? (int)$currency_special : (int)$this->context->cart->id_currency;
182
        $this->context->currency = new Currency((int)$id_currency, null, (int)$this->context->shop->id);
183
        if (Configuration::get('PS_TAX_ADDRESS_TYPE') == 'id_address_delivery') {
184
            $context_country = $this->context->country;
185
        }
186
187
        $order_status = new OrderState((int)$id_order_state, (int)$this->context->language->id);
188
        if (!Validate::isLoadedObject($order_status)) {
189
            PrestaShopLogger::addLog('PaymentModule::validateOrder - Order Status cannot be loaded', 3, null, 'Cart', (int)$id_cart, true);
190
            throw new PrestaShopException('Can\'t load Order status');
191
        }
192
193
        if (!$this->active) {
194
            PrestaShopLogger::addLog('PaymentModule::validateOrder - Module is not active', 3, null, 'Cart', (int)$id_cart, true);
195
            die(Tools::displayError());
196
        }
197
198
        // Does order already exists ?
199
        if (Validate::isLoadedObject($this->context->cart) && $this->context->cart->OrderExists() == false) {
200
            if ($secure_key !== false && $secure_key != $this->context->cart->secure_key) {
201
                PrestaShopLogger::addLog('PaymentModule::validateOrder - Secure key does not match', 3, null, 'Cart', (int)$id_cart, true);
202
                die(Tools::displayError());
203
            }
204
205
            // For each package, generate an order
206
            $delivery_option_list = $this->context->cart->getDeliveryOptionList();
207
            $package_list = $this->context->cart->getPackageList();
208
            $cart_delivery_option = $this->context->cart->getDeliveryOption();
209
210
            // If some delivery options are not defined, or not valid, use the first valid option
211
            foreach ($delivery_option_list as $id_address => $package) {
212
                if (!isset($cart_delivery_option[$id_address]) || !array_key_exists($cart_delivery_option[$id_address], $package)) {
213
                    foreach ($package as $key => $val) {
214
                        $cart_delivery_option[$id_address] = $key;
215
                        break;
216
                    }
217
                }
218
            }
219
220
            $order_list = array();
221
            $order_detail_list = array();
222
223
            do {
224
                $reference = Order::generateReference();
225
            } while (Order::getByReference($reference)->count());
226
227
            $this->currentOrderReference = $reference;
228
229
            $order_creation_failed = false;
230
            $cart_total_paid = (float)Tools::ps_round((float)$this->context->cart->getOrderTotal(true, Cart::BOTH), 2);
231
232
            foreach ($cart_delivery_option as $id_address => $key_carriers) {
233
                foreach ($delivery_option_list[$id_address][$key_carriers]['carrier_list'] as $id_carrier => $data) {
234
                    foreach ($data['package_list'] as $id_package) {
235
                        // Rewrite the id_warehouse
236
                        $package_list[$id_address][$id_package]['id_warehouse'] = (int)$this->context->cart->getPackageIdWarehouse($package_list[$id_address][$id_package], (int)$id_carrier);
237
                        $package_list[$id_address][$id_package]['id_carrier'] = $id_carrier;
238
                    }
239
                }
240
            }
241
            // Make sure CartRule caches are empty
242
            CartRule::cleanCache();
243
            $cart_rules = $this->context->cart->getCartRules();
244
            foreach ($cart_rules as $cart_rule) {
245
                if (($rule = new CartRule((int)$cart_rule['obj']->id)) && Validate::isLoadedObject($rule)) {
246
                    if ($error = $rule->checkValidity($this->context, true, true)) {
0 ignored issues
show
Unused Code introduced by
$error 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...
247
                        $this->context->cart->removeCartRule((int)$rule->id);
248
                        if (isset($this->context->cookie) && isset($this->context->cookie->id_customer) && $this->context->cookie->id_customer && !empty($rule->code)) {
249
                            if (Configuration::get('PS_ORDER_PROCESS_TYPE') == 1) {
250
                                Tools::redirect('index.php?controller=order-opc&submitAddDiscount=1&discount_name='.urlencode($rule->code));
251
                            }
252
                            Tools::redirect('index.php?controller=order&submitAddDiscount=1&discount_name='.urlencode($rule->code));
253
                        } else {
254
                            $rule_name = isset($rule->name[(int)$this->context->cart->id_lang]) ? $rule->name[(int)$this->context->cart->id_lang] : $rule->code;
255
                            $error = sprintf(Tools::displayError('CartRule ID %1s (%2s) used in this cart is not valid and has been withdrawn from cart'), (int)$rule->id, $rule_name);
256
                            PrestaShopLogger::addLog($error, 3, '0000002', 'Cart', (int)$this->context->cart->id);
257
                        }
258
                    }
259
                }
260
            }
261
262
            foreach ($package_list as $id_address => $packageByAddress) {
263
                foreach ($packageByAddress as $id_package => $package) {
264
                    /** @var Order $order */
265
                    $order = new Order();
266
                    $order->product_list = $package['product_list'];
267
268
                    if (Configuration::get('PS_TAX_ADDRESS_TYPE') == 'id_address_delivery') {
269
                        $address = new Address((int)$id_address);
270
                        $this->context->country = new Country((int)$address->id_country, (int)$this->context->cart->id_lang);
271
                        if (!$this->context->country->active) {
272
                            throw new PrestaShopException('The delivery address country is not active.');
273
                        }
274
                    }
275
276
                    $carrier = null;
277
                    if (!$this->context->cart->isVirtualCart() && isset($package['id_carrier'])) {
278
                        $carrier = new Carrier((int)$package['id_carrier'], (int)$this->context->cart->id_lang);
279
                        $order->id_carrier = (int)$carrier->id;
280
                        $id_carrier = (int)$carrier->id;
281
                    } else {
282
                        $order->id_carrier = 0;
283
                        $id_carrier = 0;
284
                    }
285
286
                    $order->id_customer = (int)$this->context->cart->id_customer;
287
                    $order->id_address_invoice = (int)$this->context->cart->id_address_invoice;
288
                    $order->id_address_delivery = (int)$id_address;
289
                    $order->id_currency = $this->context->currency->id;
290
                    $order->id_lang = (int)$this->context->cart->id_lang;
291
                    $order->id_cart = (int)$this->context->cart->id;
292
                    $order->reference = $reference;
293
                    $order->id_shop = (int)$this->context->shop->id;
294
                    $order->id_shop_group = (int)$this->context->shop->id_shop_group;
295
296
                    $order->secure_key = ($secure_key ? pSQL($secure_key) : pSQL($this->context->customer->secure_key));
297
                    $order->payment = $payment_method;
298
                    if (isset($this->name)) {
299
                        $order->module = $this->name;
300
                    }
301
                    $order->recyclable = $this->context->cart->recyclable;
302
                    $order->gift = (int)$this->context->cart->gift;
303
                    $order->gift_message = $this->context->cart->gift_message;
304
                    $order->mobile_theme = $this->context->cart->mobile_theme;
305
                    $order->conversion_rate = $this->context->currency->conversion_rate;
306
                    $amount_paid = !$dont_touch_amount ? Tools::ps_round((float)$amount_paid, 2) : $amount_paid;
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $amount_paid. This often makes code more readable.
Loading history...
307
                    $order->total_paid_real = 0;
308
309
                    $order->total_products = (float)$this->context->cart->getOrderTotal(false, Cart::ONLY_PRODUCTS, $order->product_list, $id_carrier);
310
                    $order->total_products_wt = (float)$this->context->cart->getOrderTotal(true, Cart::ONLY_PRODUCTS, $order->product_list, $id_carrier);
311
                    $order->total_discounts_tax_excl = (float)abs($this->context->cart->getOrderTotal(false, Cart::ONLY_DISCOUNTS, $order->product_list, $id_carrier));
312
                    $order->total_discounts_tax_incl = (float)abs($this->context->cart->getOrderTotal(true, Cart::ONLY_DISCOUNTS, $order->product_list, $id_carrier));
313
                    $order->total_discounts = $order->total_discounts_tax_incl;
314
315
                    $order->total_shipping_tax_excl = (float)$this->context->cart->getPackageShippingCost((int)$id_carrier, false, null, $order->product_list);
316
                    $order->total_shipping_tax_incl = (float)$this->context->cart->getPackageShippingCost((int)$id_carrier, true, null, $order->product_list);
317
                    $order->total_shipping = $order->total_shipping_tax_incl;
318
319
                    if (!is_null($carrier) && Validate::isLoadedObject($carrier)) {
320
                        $order->carrier_tax_rate = $carrier->getTaxesRate(new Address((int)$this->context->cart->{Configuration::get('PS_TAX_ADDRESS_TYPE')}));
321
                    }
322
323
                    $order->total_wrapping_tax_excl = (float)abs($this->context->cart->getOrderTotal(false, Cart::ONLY_WRAPPING, $order->product_list, $id_carrier));
324
                    $order->total_wrapping_tax_incl = (float)abs($this->context->cart->getOrderTotal(true, Cart::ONLY_WRAPPING, $order->product_list, $id_carrier));
325
                    $order->total_wrapping = $order->total_wrapping_tax_incl;
326
327
                    $order->total_paid_tax_excl = (float)Tools::ps_round((float)$this->context->cart->getOrderTotal(false, Cart::BOTH, $order->product_list, $id_carrier), _PS_PRICE_COMPUTE_PRECISION_);
328
                    $order->total_paid_tax_incl = (float)Tools::ps_round((float)$this->context->cart->getOrderTotal(true, Cart::BOTH, $order->product_list, $id_carrier), _PS_PRICE_COMPUTE_PRECISION_);
329
                    $order->total_paid = $order->total_paid_tax_incl;
330
                    $order->round_mode = Configuration::get('PS_PRICE_ROUND_MODE');
331
                    $order->round_type = Configuration::get('PS_ROUND_TYPE');
332
333
                    $order->invoice_date = '0000-00-00 00:00:00';
334
                    $order->delivery_date = '0000-00-00 00:00:00';
335
336
                    if (self::DEBUG_MODE) {
337
                        PrestaShopLogger::addLog('PaymentModule::validateOrder - Order is about to be added', 1, null, 'Cart', (int)$id_cart, true);
338
                    }
339
340
                    // Creating order
341
                    $result = $order->add();
342
343
                    if (!$result) {
344
                        PrestaShopLogger::addLog('PaymentModule::validateOrder - Order cannot be created', 3, null, 'Cart', (int)$id_cart, true);
345
                        throw new PrestaShopException('Can\'t save Order');
346
                    }
347
348
                    // Amount paid by customer is not the right one -> Status = payment error
349
                    // We don't use the following condition to avoid the float precision issues : http://www.php.net/manual/en/language.types.float.php
350
                    // if ($order->total_paid != $order->total_paid_real)
351
                    // We use number_format in order to compare two string
352
                    if ($order_status->logable && number_format($cart_total_paid, _PS_PRICE_COMPUTE_PRECISION_) != number_format($amount_paid, _PS_PRICE_COMPUTE_PRECISION_)) {
353
                        $id_order_state = Configuration::get('PS_OS_ERROR');
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $id_order_state. This often makes code more readable.
Loading history...
354
                    }
355
356
                    $order_list[] = $order;
357
358
                    if (self::DEBUG_MODE) {
359
                        PrestaShopLogger::addLog('PaymentModule::validateOrder - OrderDetail is about to be added', 1, null, 'Cart', (int)$id_cart, true);
360
                    }
361
362
                    // Insert new Order detail list using cart for the current order
363
                    $order_detail = new OrderDetail(null, null, $this->context);
364
                    $order_detail->createList($order, $this->context->cart, $id_order_state, $order->product_list, 0, true, $package_list[$id_address][$id_package]['id_warehouse']);
365
                    $order_detail_list[] = $order_detail;
366
367
                    if (self::DEBUG_MODE) {
368
                        PrestaShopLogger::addLog('PaymentModule::validateOrder - OrderCarrier is about to be added', 1, null, 'Cart', (int)$id_cart, true);
369
                    }
370
371
                    // Adding an entry in order_carrier table
372
                    if (!is_null($carrier)) {
373
                        $order_carrier = new OrderCarrier();
374
                        $order_carrier->id_order = (int)$order->id;
375
                        $order_carrier->id_carrier = (int)$id_carrier;
376
                        $order_carrier->weight = (float)$order->getTotalWeight();
377
                        $order_carrier->shipping_cost_tax_excl = (float)$order->total_shipping_tax_excl;
378
                        $order_carrier->shipping_cost_tax_incl = (float)$order->total_shipping_tax_incl;
379
                        $order_carrier->add();
380
                    }
381
                }
382
            }
383
384
            // The country can only change if the address used for the calculation is the delivery address, and if multi-shipping is activated
385
            if (Configuration::get('PS_TAX_ADDRESS_TYPE') == 'id_address_delivery') {
386
                $this->context->country = $context_country;
0 ignored issues
show
Bug introduced by
The variable $context_country does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
387
            }
388
389
            if (!$this->context->country->active) {
390
                PrestaShopLogger::addLog('PaymentModule::validateOrder - Country is not active', 3, null, 'Cart', (int)$id_cart, true);
391
                throw new PrestaShopException('The order address country is not active.');
392
            }
393
394
            if (self::DEBUG_MODE) {
395
                PrestaShopLogger::addLog('PaymentModule::validateOrder - Payment is about to be added', 1, null, 'Cart', (int)$id_cart, true);
396
            }
397
398
            // Register Payment only if the order status validate the order
399
            if ($order_status->logable) {
400
                // $order is the last order loop in the foreach
401
                // The method addOrderPayment of the class Order make a create a paymentOrder
402
                // linked to the order reference and not to the order id
403
                if (isset($extra_vars['transaction_id'])) {
404
                    $transaction_id = $extra_vars['transaction_id'];
405
                } else {
406
                    $transaction_id = null;
407
                }
408
409
                if (!isset($order) || !Validate::isLoadedObject($order) || !$order->addOrderPayment($amount_paid, null, $transaction_id)) {
410
                    PrestaShopLogger::addLog('PaymentModule::validateOrder - Cannot save Order Payment', 3, null, 'Cart', (int)$id_cart, true);
411
                    throw new PrestaShopException('Can\'t save Order Payment');
412
                }
413
            }
414
415
            // Next !
416
            $only_one_gift = false;
0 ignored issues
show
Unused Code introduced by
$only_one_gift 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...
417
            $cart_rule_used = array();
418
            $products = $this->context->cart->getProducts();
0 ignored issues
show
Unused Code introduced by
$products 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...
419
420
            // Make sure CartRule caches are empty
421
            CartRule::cleanCache();
422
            foreach ($order_detail_list as $key => $order_detail) {
423
                /** @var OrderDetail $order_detail */
424
425
                $order = $order_list[$key];
426
                if (!$order_creation_failed && isset($order->id)) {
427
                    if (!$secure_key) {
428
                        $message .= '<br />'.Tools::displayError('Warning: the secure key is empty, check your payment account before validation');
429
                    }
430
                    // Optional message to attach to this order
431
                    if (isset($message) & !empty($message)) {
432
                        $msg = new Message();
433
                        $message = strip_tags($message, '<br>');
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $message. This often makes code more readable.
Loading history...
434
                        if (Validate::isCleanHtml($message)) {
435
                            if (self::DEBUG_MODE) {
436
                                PrestaShopLogger::addLog('PaymentModule::validateOrder - Message is about to be added', 1, null, 'Cart', (int)$id_cart, true);
437
                            }
438
                            $msg->message = $message;
439
                            $msg->id_cart = (int)$id_cart;
440
                            $msg->id_customer = (int)($order->id_customer);
441
                            $msg->id_order = (int)$order->id;
442
                            $msg->private = 1;
443
                            $msg->add();
444
                        }
445
                    }
446
447
                    // Insert new Order detail list using cart for the current order
448
                    //$orderDetail = new OrderDetail(null, null, $this->context);
449
                    //$orderDetail->createList($order, $this->context->cart, $id_order_state);
450
451
                    // Construct order detail table for the email
452
                    $products_list = '';
0 ignored issues
show
Unused Code introduced by
$products_list 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...
453
                    $virtual_product = true;
454
455
                    $product_var_tpl_list = array();
456
                    foreach ($order->product_list as $product) {
457
                        $price = Product::getPriceStatic((int)$product['id_product'], false, ($product['id_product_attribute'] ? (int)$product['id_product_attribute'] : null), 6, null, false, true, $product['cart_quantity'], false, (int)$order->id_customer, (int)$order->id_cart, (int)$order->{Configuration::get('PS_TAX_ADDRESS_TYPE')});
458
                        $price_wt = Product::getPriceStatic((int)$product['id_product'], true, ($product['id_product_attribute'] ? (int)$product['id_product_attribute'] : null), 2, null, false, true, $product['cart_quantity'], false, (int)$order->id_customer, (int)$order->id_cart, (int)$order->{Configuration::get('PS_TAX_ADDRESS_TYPE')});
459
460
                        $product_price = Product::getTaxCalculationMethod() == PS_TAX_EXC ? Tools::ps_round($price, 2) : $price_wt;
461
462
                        $product_var_tpl = array(
463
                            'reference' => $product['reference'],
464
                            'name' => $product['name'].(isset($product['attributes']) ? ' - '.$product['attributes'] : ''),
465
                            'unit_price' => Tools::displayPrice($product_price, $this->context->currency, false),
466
                            'price' => Tools::displayPrice($product_price * $product['quantity'], $this->context->currency, false),
467
                            'quantity' => $product['quantity'],
468
                            'customization' => array()
469
                        );
470
471
                        $customized_datas = Product::getAllCustomizedDatas((int)$order->id_cart);
472
                        if (isset($customized_datas[$product['id_product']][$product['id_product_attribute']])) {
473
                            $product_var_tpl['customization'] = array();
474
                            foreach ($customized_datas[$product['id_product']][$product['id_product_attribute']][$order->id_address_delivery] as $customization) {
475
                                $customization_text = '';
476
                                if (isset($customization['datas'][Product::CUSTOMIZE_TEXTFIELD])) {
477
                                    foreach ($customization['datas'][Product::CUSTOMIZE_TEXTFIELD] as $text) {
478
                                        $customization_text .= $text['name'].': '.$text['value'].'<br />';
479
                                    }
480
                                }
481
482
                                if (isset($customization['datas'][Product::CUSTOMIZE_FILE])) {
483
                                    $customization_text .= sprintf(Tools::displayError('%d image(s)'), count($customization['datas'][Product::CUSTOMIZE_FILE])).'<br />';
484
                                }
485
486
                                $customization_quantity = (int)$product['customization_quantity'];
487
488
                                $product_var_tpl['customization'][] = array(
489
                                    'customization_text' => $customization_text,
490
                                    'customization_quantity' => $customization_quantity,
491
                                    'quantity' => Tools::displayPrice($customization_quantity * $product_price, $this->context->currency, false)
492
                                );
493
                            }
494
                        }
495
496
                        $product_var_tpl_list[] = $product_var_tpl;
497
                        // Check if is not a virutal product for the displaying of shipping
498
                        if (!$product['is_virtual']) {
499
                            $virtual_product &= false;
500
                        }
501
                    } // end foreach ($products)
502
503
                    $product_list_txt = '';
504
                    $product_list_html = '';
505
                    if (count($product_var_tpl_list) > 0) {
506
                        $product_list_txt = $this->getEmailTemplateContent('order_conf_product_list.txt', Mail::TYPE_TEXT, $product_var_tpl_list);
507
                        $product_list_html = $this->getEmailTemplateContent('order_conf_product_list.tpl', Mail::TYPE_HTML, $product_var_tpl_list);
508
                    }
509
510
                    $cart_rules_list = array();
511
                    $total_reduction_value_ti = 0;
512
                    $total_reduction_value_tex = 0;
513
                    foreach ($cart_rules as $cart_rule) {
514
                        $package = array('id_carrier' => $order->id_carrier, 'id_address' => $order->id_address_delivery, 'products' => $order->product_list);
515
                        $values = array(
516
                            'tax_incl' => $cart_rule['obj']->getContextualValue(true, $this->context, CartRule::FILTER_ACTION_ALL_NOCAP, $package),
517
                            'tax_excl' => $cart_rule['obj']->getContextualValue(false, $this->context, CartRule::FILTER_ACTION_ALL_NOCAP, $package)
518
                        );
519
520
                        // If the reduction is not applicable to this order, then continue with the next one
521
                        if (!$values['tax_excl']) {
522
                            continue;
523
                        }
524
525
                        // IF
526
                        //	This is not multi-shipping
527
                        //	The value of the voucher is greater than the total of the order
528
                        //	Partial use is allowed
529
                        //	This is an "amount" reduction, not a reduction in % or a gift
530
                        // THEN
531
                        //	The voucher is cloned with a new value corresponding to the remainder
532
                        if (count($order_list) == 1 && $values['tax_incl'] > ($order->total_products_wt - $total_reduction_value_ti) && $cart_rule['obj']->partial_use == 1 && $cart_rule['obj']->reduction_amount > 0) {
533
                            // Create a new voucher from the original
534
                            $voucher = new CartRule((int)$cart_rule['obj']->id); // We need to instantiate the CartRule without lang parameter to allow saving it
535
                            unset($voucher->id);
536
537
                            // Set a new voucher code
538
                            $voucher->code = empty($voucher->code) ? substr(md5($order->id.'-'.$order->id_customer.'-'.$cart_rule['obj']->id), 0, 16) : $voucher->code.'-2';
539
                            if (preg_match('/\-([0-9]{1,2})\-([0-9]{1,2})$/', $voucher->code, $matches) && $matches[1] == $matches[2]) {
540
                                $voucher->code = preg_replace('/'.$matches[0].'$/', '-'.(intval($matches[1]) + 1), $voucher->code);
541
                            }
542
543
                            // Set the new voucher value
544
                            if ($voucher->reduction_tax) {
545
                                $voucher->reduction_amount = ($total_reduction_value_ti + $values['tax_incl']) - $order->total_products_wt;
546
547
                                // Add total shipping amout only if reduction amount > total shipping
548
                                if ($voucher->free_shipping == 1 && $voucher->reduction_amount >= $order->total_shipping_tax_incl) {
549
                                    $voucher->reduction_amount -= $order->total_shipping_tax_incl;
550
                                }
551
                            } else {
552
                                $voucher->reduction_amount = ($total_reduction_value_tex + $values['tax_excl']) - $order->total_products;
553
554
                                // Add total shipping amout only if reduction amount > total shipping
555
                                if ($voucher->free_shipping == 1 && $voucher->reduction_amount >= $order->total_shipping_tax_excl) {
556
                                    $voucher->reduction_amount -= $order->total_shipping_tax_excl;
557
                                }
558
                            }
559
                            if ($voucher->reduction_amount <= 0) {
560
                                continue;
561
                            }
562
563
                            if ($this->context->customer->isGuest()) {
564
                                $voucher->id_customer = 0;
565
                            } else {
566
                                $voucher->id_customer = $order->id_customer;
567
                            }
568
569
                            $voucher->quantity = 1;
570
                            $voucher->reduction_currency = $order->id_currency;
571
                            $voucher->quantity_per_user = 1;
572
                            $voucher->free_shipping = 0;
573
                            if ($voucher->add()) {
574
                                // If the voucher has conditions, they are now copied to the new voucher
575
                                CartRule::copyConditions($cart_rule['obj']->id, $voucher->id);
576
577
                                $params = array(
578
                                    '{voucher_amount}' => Tools::displayPrice($voucher->reduction_amount, $this->context->currency, false),
579
                                    '{voucher_num}' => $voucher->code,
580
                                    '{firstname}' => $this->context->customer->firstname,
581
                                    '{lastname}' => $this->context->customer->lastname,
582
                                    '{id_order}' => $order->reference,
583
                                    '{order_name}' => $order->getUniqReference()
584
                                );
585
                                Mail::Send(
586
                                    (int)$order->id_lang,
587
                                    'voucher',
588
                                    sprintf(Mail::l('New voucher for your order %s', (int)$order->id_lang), $order->reference),
589
                                    $params,
590
                                    $this->context->customer->email,
591
                                    $this->context->customer->firstname.' '.$this->context->customer->lastname,
592
                                    null, null, null, null, _PS_MAIL_DIR_, false, (int)$order->id_shop
593
                                );
594
                            }
595
596
                            $values['tax_incl'] = $order->total_products_wt - $total_reduction_value_ti;
597
                            $values['tax_excl'] = $order->total_products - $total_reduction_value_tex;
598
                        }
599
                        $total_reduction_value_ti += $values['tax_incl'];
600
                        $total_reduction_value_tex += $values['tax_excl'];
601
602
                        $order->addCartRule($cart_rule['obj']->id, $cart_rule['obj']->name, $values, 0, $cart_rule['obj']->free_shipping);
603
604
                        if ($id_order_state != Configuration::get('PS_OS_ERROR') && $id_order_state != Configuration::get('PS_OS_CANCELED') && !in_array($cart_rule['obj']->id, $cart_rule_used)) {
605
                            $cart_rule_used[] = $cart_rule['obj']->id;
606
607
                            // Create a new instance of Cart Rule without id_lang, in order to update its quantity
608
                            $cart_rule_to_update = new CartRule((int)$cart_rule['obj']->id);
609
                            $cart_rule_to_update->quantity = max(0, $cart_rule_to_update->quantity - 1);
610
                            $cart_rule_to_update->update();
611
                        }
612
613
                        $cart_rules_list[] = array(
614
                            'voucher_name' => $cart_rule['obj']->name,
615
                            'voucher_reduction' => ($values['tax_incl'] != 0.00 ? '-' : '').Tools::displayPrice($values['tax_incl'], $this->context->currency, false)
616
                        );
617
                    }
618
619
                    $cart_rules_list_txt = '';
620
                    $cart_rules_list_html = '';
621
                    if (count($cart_rules_list) > 0) {
622
                        $cart_rules_list_txt = $this->getEmailTemplateContent('order_conf_cart_rules.txt', Mail::TYPE_TEXT, $cart_rules_list);
623
                        $cart_rules_list_html = $this->getEmailTemplateContent('order_conf_cart_rules.tpl', Mail::TYPE_HTML, $cart_rules_list);
624
                    }
625
626
                    // Specify order id for message
627
                    $old_message = Message::getMessageByCartId((int)$this->context->cart->id);
628
                    if ($old_message && !$old_message['private']) {
629
                        $update_message = new Message((int)$old_message['id_message']);
630
                        $update_message->id_order = (int)$order->id;
631
                        $update_message->update();
632
633
                        // Add this message in the customer thread
634
                        $customer_thread = new CustomerThread();
635
                        $customer_thread->id_contact = 0;
636
                        $customer_thread->id_customer = (int)$order->id_customer;
637
                        $customer_thread->id_shop = (int)$this->context->shop->id;
638
                        $customer_thread->id_order = (int)$order->id;
639
                        $customer_thread->id_lang = (int)$this->context->language->id;
640
                        $customer_thread->email = $this->context->customer->email;
641
                        $customer_thread->status = 'open';
642
                        $customer_thread->token = Tools::passwdGen(12);
643
                        $customer_thread->add();
644
645
                        $customer_message = new CustomerMessage();
646
                        $customer_message->id_customer_thread = $customer_thread->id;
647
                        $customer_message->id_employee = 0;
648
                        $customer_message->message = $update_message->message;
649
                        $customer_message->private = 0;
650
651
                        if (!$customer_message->add()) {
652
                            $this->errors[] = Tools::displayError('An error occurred while saving message');
653
                        }
654
                    }
655
656
                    if (self::DEBUG_MODE) {
657
                        PrestaShopLogger::addLog('PaymentModule::validateOrder - Hook validateOrder is about to be called', 1, null, 'Cart', (int)$id_cart, true);
658
                    }
659
660
                    // Hook validate order
661
                    Hook::exec('actionValidateOrder', array(
662
                        'cart' => $this->context->cart,
663
                        'order' => $order,
664
                        'customer' => $this->context->customer,
665
                        'currency' => $this->context->currency,
666
                        'orderStatus' => $order_status
667
                    ));
668
669
                    foreach ($this->context->cart->getProducts() as $product) {
670
                        if ($order_status->logable) {
671
                            ProductSale::addProductSale((int)$product['id_product'], (int)$product['cart_quantity']);
672
                        }
673
                    }
674
675
                    if (self::DEBUG_MODE) {
676
                        PrestaShopLogger::addLog('PaymentModule::validateOrder - Order Status is about to be added', 1, null, 'Cart', (int)$id_cart, true);
677
                    }
678
679
                    // Set the order status
680
                    $new_history = new OrderHistory();
681
                    $new_history->id_order = (int)$order->id;
682
                    $new_history->changeIdOrderState((int)$id_order_state, $order, true);
683
                    $new_history->addWithemail(true, $extra_vars);
684
685
                    // Switch to back order if needed
686
                    if (Configuration::get('PS_STOCK_MANAGEMENT') && ($order_detail->getStockState() || $order_detail->product_quantity_in_stock <= 0)) {
687
                        $history = new OrderHistory();
688
                        $history->id_order = (int)$order->id;
689
                        $history->changeIdOrderState(Configuration::get($order->valid ? 'PS_OS_OUTOFSTOCK_PAID' : 'PS_OS_OUTOFSTOCK_UNPAID'), $order, true);
690
                        $history->addWithemail();
691
                    }
692
693
                    unset($order_detail);
694
695
                    // Order is reloaded because the status just changed
696
                    $order = new Order((int)$order->id);
697
698
                    // Send an e-mail to customer (one order = one email)
699
                    if ($id_order_state != Configuration::get('PS_OS_ERROR') && $id_order_state != Configuration::get('PS_OS_CANCELED') && $this->context->customer->id) {
700
                        $invoice = new Address((int)$order->id_address_invoice);
701
                        $delivery = new Address((int)$order->id_address_delivery);
702
                        $delivery_state = $delivery->id_state ? new State((int)$delivery->id_state) : false;
703
                        $invoice_state = $invoice->id_state ? new State((int)$invoice->id_state) : false;
704
705
                        $data = array(
706
                        '{firstname}' => $this->context->customer->firstname,
707
                        '{lastname}' => $this->context->customer->lastname,
708
                        '{email}' => $this->context->customer->email,
709
                        '{delivery_block_txt}' => $this->_getFormatedAddress($delivery, "\n"),
710
                        '{invoice_block_txt}' => $this->_getFormatedAddress($invoice, "\n"),
711
                        '{delivery_block_html}' => $this->_getFormatedAddress($delivery, '<br />', array(
712
                            'firstname'    => '<span style="font-weight:bold;">%s</span>',
713
                            'lastname'    => '<span style="font-weight:bold;">%s</span>'
714
                        )),
715
                        '{invoice_block_html}' => $this->_getFormatedAddress($invoice, '<br />', array(
716
                                'firstname'    => '<span style="font-weight:bold;">%s</span>',
717
                                'lastname'    => '<span style="font-weight:bold;">%s</span>'
718
                        )),
719
                        '{delivery_company}' => $delivery->company,
720
                        '{delivery_firstname}' => $delivery->firstname,
721
                        '{delivery_lastname}' => $delivery->lastname,
722
                        '{delivery_address1}' => $delivery->address1,
723
                        '{delivery_address2}' => $delivery->address2,
724
                        '{delivery_city}' => $delivery->city,
725
                        '{delivery_postal_code}' => $delivery->postcode,
726
                        '{delivery_country}' => $delivery->country,
727
                        '{delivery_state}' => $delivery->id_state ? $delivery_state->name : '',
728
                        '{delivery_phone}' => ($delivery->phone) ? $delivery->phone : $delivery->phone_mobile,
729
                        '{delivery_other}' => $delivery->other,
730
                        '{invoice_company}' => $invoice->company,
731
                        '{invoice_vat_number}' => $invoice->vat_number,
732
                        '{invoice_firstname}' => $invoice->firstname,
733
                        '{invoice_lastname}' => $invoice->lastname,
734
                        '{invoice_address2}' => $invoice->address2,
735
                        '{invoice_address1}' => $invoice->address1,
736
                        '{invoice_city}' => $invoice->city,
737
                        '{invoice_postal_code}' => $invoice->postcode,
738
                        '{invoice_country}' => $invoice->country,
739
                        '{invoice_state}' => $invoice->id_state ? $invoice_state->name : '',
740
                        '{invoice_phone}' => ($invoice->phone) ? $invoice->phone : $invoice->phone_mobile,
741
                        '{invoice_other}' => $invoice->other,
742
                        '{order_name}' => $order->getUniqReference(),
743
                        '{date}' => Tools::displayDate(date('Y-m-d H:i:s'), null, 1),
744
                        '{carrier}' => ($virtual_product || !isset($carrier->name)) ? Tools::displayError('No carrier') : $carrier->name,
0 ignored issues
show
Bug introduced by
The variable $carrier does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
745
                        '{payment}' => Tools::substr($order->payment, 0, 32),
746
                        '{products}' => $product_list_html,
747
                        '{products_txt}' => $product_list_txt,
748
                        '{discounts}' => $cart_rules_list_html,
749
                        '{discounts_txt}' => $cart_rules_list_txt,
750
                        '{total_paid}' => Tools::displayPrice($order->total_paid, $this->context->currency, false),
751
                        '{total_products}' => Tools::displayPrice(Product::getTaxCalculationMethod() == PS_TAX_EXC ? $order->total_products : $order->total_products_wt, $this->context->currency, false),
752
                        '{total_discounts}' => Tools::displayPrice($order->total_discounts, $this->context->currency, false),
753
                        '{total_shipping}' => Tools::displayPrice($order->total_shipping, $this->context->currency, false),
754
                        '{total_wrapping}' => Tools::displayPrice($order->total_wrapping, $this->context->currency, false),
755
                        '{total_tax_paid}' => Tools::displayPrice(($order->total_products_wt - $order->total_products) + ($order->total_shipping_tax_incl - $order->total_shipping_tax_excl), $this->context->currency, false));
756
757
                        if (is_array($extra_vars)) {
758
                            $data = array_merge($data, $extra_vars);
759
                        }
760
761
                        // Join PDF invoice
762
                        if ((int)Configuration::get('PS_INVOICE') && $order_status->invoice && $order->invoice_number) {
763
                            $order_invoice_list = $order->getInvoicesCollection();
764
                            Hook::exec('actionPDFInvoiceRender', array('order_invoice_list' => $order_invoice_list));
765
                            $pdf = new PDF($order_invoice_list, PDF::TEMPLATE_INVOICE, $this->context->smarty);
766
                            $file_attachement['content'] = $pdf->render(false);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$file_attachement was never initialized. Although not strictly required by PHP, it is generally a good practice to add $file_attachement = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
767
                            $file_attachement['name'] = Configuration::get('PS_INVOICE_PREFIX', (int)$order->id_lang, null, $order->id_shop).sprintf('%06d', $order->invoice_number).'.pdf';
0 ignored issues
show
Bug introduced by
The variable $file_attachement does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
768
                            $file_attachement['mime'] = 'application/pdf';
769
                        } else {
770
                            $file_attachement = null;
771
                        }
772
773
                        if (self::DEBUG_MODE) {
774
                            PrestaShopLogger::addLog('PaymentModule::validateOrder - Mail is about to be sent', 1, null, 'Cart', (int)$id_cart, true);
775
                        }
776
777
                        if (Validate::isEmail($this->context->customer->email)) {
778
                            Mail::Send(
779
                                (int)$order->id_lang,
780
                                'order_conf',
781
                                Mail::l('Order confirmation', (int)$order->id_lang),
782
                                $data,
783
                                $this->context->customer->email,
784
                                $this->context->customer->firstname.' '.$this->context->customer->lastname,
785
                                null,
786
                                null,
787
                                $file_attachement,
788
                                null, _PS_MAIL_DIR_, false, (int)$order->id_shop
789
                            );
790
                        }
791
                    }
792
793
                    // updates stock in shops
794
                    if (Configuration::get('PS_ADVANCED_STOCK_MANAGEMENT')) {
795
                        $product_list = $order->getProducts();
796
                        foreach ($product_list as $product) {
797
                            // if the available quantities depends on the physical stock
798
                            if (StockAvailable::dependsOnStock($product['product_id'])) {
799
                                // synchronizes
800
                                StockAvailable::synchronize($product['product_id'], $order->id_shop);
801
                            }
802
                        }
803
                    }
804
805
                    $order->updateOrderDetailTax();
806
                } else {
807
                    $error = Tools::displayError('Order creation failed');
808
                    PrestaShopLogger::addLog($error, 4, '0000002', 'Cart', intval($order->id_cart));
809
                    die($error);
810
                }
811
            } // End foreach $order_detail_list
812
813
            // Use the last order as currentOrder
814
            if (isset($order) && $order->id) {
815
                $this->currentOrder = (int)$order->id;
816
            }
817
818
            if (self::DEBUG_MODE) {
819
                PrestaShopLogger::addLog('PaymentModule::validateOrder - End of validateOrder', 1, null, 'Cart', (int)$id_cart, true);
820
            }
821
822
            return true;
823
        } else {
824
            $error = Tools::displayError('Cart cannot be loaded or an order has already been placed using this cart');
825
            PrestaShopLogger::addLog($error, 4, '0000001', 'Cart', intval($this->context->cart->id));
826
            die($error);
827
        }
828
    }
829
830
    /**
831
     * @deprecated 1.6.0.7
832
     * @param mixed $content
833
     * @return mixed
834
     */
835
    public function formatProductAndVoucherForEmail($content)
836
    {
837
        Tools::displayAsDeprecated();
838
        return $content;
839
    }
840
841
    /**
842
     * @param Object Address $the_address that needs to be txt formated
843
     * @return String the txt formated address block
844
     */
845
    protected function _getTxtFormatedAddress($the_address)
846
    {
847
        $adr_fields = AddressFormat::getOrderedAddressFields($the_address->id_country, false, true);
848
        $r_values = array();
849
        foreach ($adr_fields as $fields_line) {
850
            $tmp_values = array();
851
            foreach (explode(' ', $fields_line) as $field_item) {
852
                $field_item = trim($field_item);
853
                $tmp_values[] = $the_address->{$field_item};
854
            }
855
            $r_values[] = implode(' ', $tmp_values);
856
        }
857
858
        $out = implode("\n", $r_values);
859
        return $out;
860
    }
861
862
    /**
863
     * @param Object Address $the_address that needs to be txt formated
864
     * @return String the txt formated address block
865
     */
866
867
    protected function _getFormatedAddress(Address $the_address, $line_sep, $fields_style = array())
868
    {
869
        return AddressFormat::generateAddress($the_address, array('avoid' => array()), $line_sep, ' ', $fields_style);
870
    }
871
872
    /**
873
     * @param int $id_currency : this parameter is optionnal but on 1.5 version of Prestashop, it will be REQUIRED
0 ignored issues
show
Documentation introduced by
There is no parameter named $id_currency. Did you maybe mean $current_id_currency?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
874
     * @return Currency
875
     */
876
    public function getCurrency($current_id_currency = null)
877
    {
878
        if (!(int)$current_id_currency) {
879
            $current_id_currency = Context::getContext()->currency->id;
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $current_id_currency. This often makes code more readable.
Loading history...
880
        }
881
882
        if (!$this->currencies) {
883
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by PaymentModuleCore::getCurrency of type Currency.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
884
        }
885
        if ($this->currencies_mode == 'checkbox') {
886
            $currencies = Currency::getPaymentCurrencies($this->id);
887
            return $currencies;
888
        } elseif ($this->currencies_mode == 'radio') {
889
            $currencies = Currency::getPaymentCurrenciesSpecial($this->id);
890
            $currency = $currencies['id_currency'];
891
            if ($currency == -1) {
892
                $id_currency = (int)$current_id_currency;
893
            } elseif ($currency == -2) {
894
                $id_currency = (int)Configuration::get('PS_CURRENCY_DEFAULT');
895
            } else {
896
                $id_currency = $currency;
897
            }
898
        }
899
        if (!isset($id_currency) || empty($id_currency)) {
900
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by PaymentModuleCore::getCurrency of type Currency.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
901
        }
902
        $currency = new Currency((int)$id_currency);
903
        return $currency;
904
    }
905
906
    /**
907
     * Allows specified payment modules to be used by a specific currency
908
     *
909
     * @since 1.4.5
910
     * @param int $id_currency
911
     * @param array $id_module_list
912
     * @return bool
913
     */
914
    public static function addCurrencyPermissions($id_currency, array $id_module_list = array())
915
    {
916
        $values = '';
917
        if (count($id_module_list) == 0) {
918
            // fetch all installed module ids
919
            $modules = PaymentModuleCore::getInstalledPaymentModules();
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
920
            foreach ($modules as $module) {
921
                $id_module_list[] = $module['id_module'];
922
            }
923
        }
924
925
        foreach ($id_module_list as $id_module) {
926
            $values .= '('.(int)$id_module.','.(int)$id_currency.'),';
927
        }
928
929
        if (!empty($values)) {
930
            return Db::getInstance()->execute('
931
			INSERT INTO `'._DB_PREFIX_.'module_currency` (`id_module`, `id_currency`)
932
			VALUES '.rtrim($values, ','));
933
        }
934
935
        return true;
936
    }
937
938
    /**
939
     * List all installed and active payment modules
940
     * @see Module::getPaymentModules() if you need a list of module related to the user context
941
     *
942
     * @since 1.4.5
943
     * @return array module informations
944
     */
945
    public static function getInstalledPaymentModules()
946
    {
947
        $hook_payment = 'Payment';
948
        if (Db::getInstance()->getValue('SELECT `id_hook` FROM `'._DB_PREFIX_.'hook` WHERE `name` = \'displayPayment\'')) {
949
            $hook_payment = 'displayPayment';
950
        }
951
952
        return Db::getInstance()->executeS('
953
		SELECT DISTINCT m.`id_module`, h.`id_hook`, m.`name`, hm.`position`
954
		FROM `'._DB_PREFIX_.'module` m
955
		LEFT JOIN `'._DB_PREFIX_.'hook_module` hm ON hm.`id_module` = m.`id_module`'
956
        .Shop::addSqlRestriction(false, 'hm').'
957
		LEFT JOIN `'._DB_PREFIX_.'hook` h ON hm.`id_hook` = h.`id_hook`
958
		INNER JOIN `'._DB_PREFIX_.'module_shop` ms ON (m.`id_module` = ms.`id_module` AND ms.id_shop='.(int)Context::getContext()->shop->id.')
959
		WHERE h.`name` = \''.pSQL($hook_payment).'\'');
960
    }
961
962
    public static function preCall($module_name)
963
    {
964
        if (!parent::preCall($module_name)) {
965
            return false;
966
        }
967
968
        if (($module_instance = Module::getInstanceByName($module_name))) {
969
            /** @var PaymentModule $module_instance */
970
            if (!$module_instance->currencies || ($module_instance->currencies && count(Currency::checkPaymentCurrencies($module_instance->id)))) {
971
                return true;
972
            }
973
        }
974
975
        return false;
976
    }
977
978
    /**
979
     * Fetch the content of $template_name inside the folder current_theme/mails/current_iso_lang/ if found, otherwise in mails/current_iso_lang
980
     *
981
     * @param string  $template_name template name with extension
982
     * @param int $mail_type     Mail::TYPE_HTML or Mail::TYPE_TXT
983
     * @param array   $var           list send to smarty
984
     *
985
     * @return string
986
     */
987
    protected function getEmailTemplateContent($template_name, $mail_type, $var)
988
    {
989
        $email_configuration = Configuration::get('PS_MAIL_TYPE');
990
        if ($email_configuration != $mail_type && $email_configuration != Mail::TYPE_BOTH) {
991
            return '';
992
        }
993
994
        $theme_template_path = _PS_THEME_DIR_.'mails'.DIRECTORY_SEPARATOR.$this->context->language->iso_code.DIRECTORY_SEPARATOR.$template_name;
995
        $default_mail_template_path = _PS_MAIL_DIR_.$this->context->language->iso_code.DIRECTORY_SEPARATOR.$template_name;
996
997
        if (Tools::file_exists_cache($theme_template_path)) {
998
            $default_mail_template_path = $theme_template_path;
999
        }
1000
1001
        if (Tools::file_exists_cache($default_mail_template_path)) {
1002
            $this->context->smarty->assign('list', $var);
1003
            return $this->context->smarty->fetch($default_mail_template_path);
1004
        }
1005
        return '';
1006
    }
1007
}
1008