Completed
Push — 5.5 ( db54c2...e0f04c )
by Christian
37:53 queued 11:13
created

getBasket()   C

Complexity

Conditions 14
Paths 32

Size

Total Lines 70
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 45
nc 32
nop 1
dl 0
loc 70
rs 5.6188
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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:

1
<?php
2
/**
3
 * Shopware 5
4
 * Copyright (c) shopware AG
5
 *
6
 * According to our dual licensing model, this program can be used either
7
 * under the terms of the GNU Affero General Public License, version 3,
8
 * or under a proprietary license.
9
 *
10
 * The texts of the GNU Affero General Public License with an additional
11
 * permission and of our proprietary license can be found at and
12
 * in the LICENSE file you have received along with this program.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * "Shopware" is a registered trademark of shopware AG.
20
 * The licensing of the program under the AGPLv3 does not imply a
21
 * trademark license. Therefore any rights, title and interest in
22
 * our trademarks remain entirely with us.
23
 */
24
use Enlight_Controller_Request_Request as Request;
25
use Shopware\Components\BasketSignature\Basket;
26
use Shopware\Components\BasketSignature\BasketPersister;
27
use Shopware\Components\BasketSignature\BasketSignatureGeneratorInterface;
28
use Shopware\Components\CSRFGetProtectionAware;
29
use Shopware\Models\Customer\Address;
30
31
/**
32
 * @category  Shopware
33
 *
34
 * @copyright Copyright (c) shopware AG (http://www.shopware.de)
35
 */
36
class Shopware_Controllers_Frontend_Checkout extends Enlight_Controller_Action implements CSRFGetProtectionAware
37
{
38
    /**
39
     * Reference to sAdmin object (core/class/sAdmin.php)
40
     *
41
     * @var sAdmin
42
     */
43
    protected $admin;
44
45
    /**
46
     * Reference to sBasket object (core/class/sBasket.php)
47
     *
48
     * @var sBasket
49
     */
50
    protected $basket;
51
52
    /**
53
     * Reference to Shopware session object (Shopware()->Session)
54
     *
55
     * @var Enlight_Components_Session_Namespace
56
     */
57
    protected $session;
58
59
    /**
60
     * Init method that get called automatically
61
     *
62
     * Set class properties
63
     */
64
    public function init()
65
    {
66
        $this->admin = Shopware()->Modules()->Admin();
67
        $this->basket = Shopware()->Modules()->Basket();
68
        $this->session = Shopware()->Session();
69
    }
70
71
    /**
72
     * @return array
73
     */
74
    public function getCSRFProtectedActions()
75
    {
76
        return [
77
            'ajaxAddArticle',
78
            'addArticle',
79
            'ajaxAddArticleCart',
80
            'ajaxDeleteArticle',
81
            'ajaxDeleteArticleCart',
82
            'deleteArticle',
83
            'addAccessories',
84
            'changeQuantity',
85
            'addPremium',
86
            'setAddress',
87
        ];
88
    }
89
90
    /**
91
     * Pre dispatch method
92
     */
93
    public function preDispatch()
94
    {
95
        $events = Shopware()->Container()->get('events');
96
        $events->addListener('Shopware_Modules_Admin_Payment_Fallback', [$this, 'flagPaymentBlocked']);
97
98
        $this->View()->setScope(Enlight_Template_Manager::SCOPE_PARENT);
99
100
        $this->View()->sUserLoggedIn = $this->admin->sCheckUser();
101
        $this->View()->sUserData = $this->getUserData();
102
    }
103
104
    /**
105
     * Called if the sAdmin resets the selected customer payment to the shop preset
106
     */
107
    public function flagPaymentBlocked()
108
    {
109
        $this->View()->assign('paymentBlocked', true);
110
    }
111
112
    /**
113
     * Save basket to session
114
     */
115
    public function postDispatch()
116
    {
117
        $this->session->sBasketCurrency = Shopware()->Shop()->getCurrency()->getId();
118
        $this->session->sBasketQuantity = $this->basket->sCountBasket();
119
        $amount = $this->basket->sGetAmount();
120
        $this->session->sBasketAmount = empty($amount) ? 0 : array_shift($amount);
121
    }
122
123
    /**
124
     * Forward to cart or confirm action depending on user state
125
     */
126
    public function indexAction()
127
    {
128
        if ($this->basket->sCountBasket() < 1 || empty($this->View()->sUserLoggedIn)) {
129
            $this->forward('cart');
130
        } else {
131
            $this->forward('confirm');
132
        }
133
    }
134
135
    /**
136
     * Read all data from objects / models that are required in cart view
137
     * (User-Data / Payment-Data / Basket-Data etc.)
138
     */
139
    public function cartAction()
140
    {
141
        $this->View()->sCountry = $this->getSelectedCountry();
142
        $this->View()->sPayment = $this->getSelectedPayment();
143
        $this->View()->sDispatch = $this->getSelectedDispatch();
144
        $this->View()->sCountryList = $this->getCountryList();
145
        $this->View()->sPayments = $this->getPayments();
146
        $this->View()->sDispatches = $this->getDispatches();
147
        $this->View()->sDispatchNoOrder = $this->getDispatchNoOrder();
148
        $this->View()->sState = $this->getSelectedState();
149
150
        $this->View()->sUserData = $this->getUserData();
151
        $this->View()->sBasket = $this->getBasket();
152
153
        $this->View()->sShippingcosts = $this->View()->sBasket['sShippingcosts'];
154
        $this->View()->sShippingcostsDifference = $this->View()->sBasket['sShippingcostsDifference'];
155
        $this->View()->sAmount = $this->View()->sBasket['sAmount'];
156
        $this->View()->sAmountWithTax = $this->View()->sBasket['sAmountWithTax'];
157
        $this->View()->sAmountTax = $this->View()->sBasket['sAmountTax'];
158
        $this->View()->sAmountNet = $this->View()->sBasket['AmountNetNumeric'];
159
160
        $this->View()->sMinimumSurcharge = $this->getMinimumCharge();
161
        $this->View()->sPremiums = $this->getPremiums();
162
163
        $this->View()->sInquiry = $this->getInquiry();
164
        $this->View()->sInquiryLink = $this->getInquiryLink();
165
166
        $this->View()->sTargetAction = 'cart';
167
    }
168
169
    /**
170
     * Mostly equivalent to cartAction
171
     * Get user, basket and payment data for view assignment
172
     * Create temporary entry in s_order table
173
     * Check some conditions (minimum charge)
174
     */
175
    public function confirmAction()
176
    {
177
        if (empty($this->View()->sUserLoggedIn)) {
178
            return $this->forward(
179
                'login',
180
                'account',
181
                null,
182
                ['sTarget' => 'checkout', 'sTargetAction' => 'confirm', 'showNoAccount' => true]
183
            );
184
        } elseif ($this->basket->sCountBasket() < 1) {
185
            return $this->forward('cart');
186
        }
187
188
        $this->View()->sCountry = $this->getSelectedCountry();
189
        $this->View()->sState = $this->getSelectedState();
190
191
        $payment = $this->getSelectedPayment();
192
        if (array_key_exists('validation', $payment) && !empty($payment['validation'])) {
193
            $this->onPaymentMethodValidationFail();
194
195
            return;
196
        }
197
198
        $this->View()->sPayment = $payment;
199
200
        $userData = $this->View()->sUserData;
201
        $userData['additional']['payment'] = $this->View()->sPayment;
202
        $this->View()->sUserData = $userData;
203
204
        $this->View()->sDispatch = $this->getSelectedDispatch();
205
        $this->View()->sPayments = $this->getPayments();
206
        $this->View()->sDispatches = $this->getDispatches();
207
208
        $this->View()->sBasket = $this->getBasket();
209
210
        $this->View()->sLaststock = $this->basket->sCheckBasketQuantities();
211
        $this->View()->sShippingcosts = $this->View()->sBasket['sShippingcosts'];
212
        $this->View()->sShippingcostsDifference = $this->View()->sBasket['sShippingcostsDifference'];
213
        $this->View()->sAmount = $this->View()->sBasket['sAmount'];
214
        $this->View()->sAmountWithTax = $this->View()->sBasket['sAmountWithTax'];
215
        $this->View()->sAmountTax = $this->View()->sBasket['sAmountTax'];
216
        $this->View()->sAmountNet = $this->View()->sBasket['AmountNetNumeric'];
217
218
        $this->View()->sPremiums = $this->getPremiums();
219
220
        $this->View()->sNewsletter = isset($this->session['sNewsletter']) ? $this->session['sNewsletter'] : null;
221
        $this->View()->sComment = isset($this->session['sComment']) ? $this->session['sComment'] : null;
222
223
        $this->View()->sShowEsdNote = $this->getEsdNote();
224
        $this->View()->sDispatchNoOrder = $this->getDispatchNoOrder();
225
        $this->View()->sRegisterFinished = !empty($this->session['sRegisterFinished']);
226
227
        $this->saveTemporaryOrder();
228
229
        if ($this->getMinimumCharge() || count($this->View()->sBasket['content']) <= 0) {
230
            return $this->forward('cart');
231
        }
232
233
        $sOrderVariables = $this->View()->getAssign();
234
        $sOrderVariables['sBasketView'] = $sOrderVariables['sBasket'];
235
        $sOrderVariables['sBasket'] = $this->getBasket(false);
236
237
        $this->session['sOrderVariables'] = new ArrayObject($sOrderVariables, ArrayObject::ARRAY_AS_PROPS);
238
239
        $agbChecked = $this->Request()->getParam('sAGB');
240
        if (!empty($agbChecked)) {
241
            $this->View()->assign('sAGBChecked', true);
242
        }
243
244
        $this->View()->sTargetAction = 'confirm';
245
246
        $this->View()->assign('hasMixedArticles', $this->basketHasMixedArticles($this->View()->sBasket));
247
        $this->View()->assign('hasServiceArticles', $this->basketHasServiceArticles($this->View()->sBasket));
248
249
        if (Shopware()->Config()->get('showEsdWarning')) {
250
            $this->View()->assign('hasEsdArticles', $this->basketHasEsdArticles($this->View()->sBasket));
251
        }
252
253
        $serviceChecked = $this->Request()->getParam('serviceAgreementChecked');
254
        if (!empty($serviceChecked)) {
255
            $this->View()->assign('serviceAgreementChecked', true);
256
        }
257
258
        $esdChecked = $this->Request()->getParam('esdAgreementChecked');
259
        if (!empty($esdChecked)) {
260
            $this->View()->assign('esdAgreementChecked', true);
261
        }
262
263
        $errors = $this->Request()->getParam('agreementErrors');
264
        if (!empty($errors)) {
265
            $this->View()->assign('agreementErrors', $errors);
266
        }
267
268
        $voucherErrors = $this->Request()->getParam('voucherErrors');
269
        if (!empty($voucherErrors)) {
270
            $this->View()->assign('sVoucherError', $voucherErrors);
271
        }
272
273
        if (empty($activeBillingAddressId = $this->session->offsetGet('checkoutBillingAddressId'))) {
274
            $activeBillingAddressId = $userData['additional']['user']['default_billing_address_id'];
275
        }
276
277
        if (empty($activeShippingAddressId = $this->session->offsetGet('checkoutShippingAddressId'))) {
278
            $activeShippingAddressId = $userData['additional']['user']['default_shipping_address_id'];
279
        }
280
281
        $this->View()->assign('activeBillingAddressId', $activeBillingAddressId);
282
        $this->View()->assign('activeShippingAddressId', $activeShippingAddressId);
283
284
        $this->View()->assign('invalidBillingAddress', !$this->isValidAddress($activeBillingAddressId));
285
        $this->View()->assign('invalidShippingAddress', !$this->isValidAddress($activeShippingAddressId));
286
    }
287
288
    /**
289
     * Called from confirmAction View
290
     * Customers requests to finish current order
291
     * Check if all conditions match and save order
292
     */
293
    public function finishAction()
294
    {
295
        if ($this->Request()->getParam('sUniqueID') && !empty($this->session['sOrderVariables'])) {
296
            $sql = '
297
                SELECT transactionID as sTransactionumber, ordernumber as sOrderNumber
298
                FROM s_order
299
                WHERE temporaryID=? AND userID=?
300
            ';
301
302
            $order = Shopware()->Db()->fetchRow($sql, [$this->Request()->getParam('sUniqueID'), Shopware()->Session()->sUserId]);
303
304
            if (empty($order)) {
305
                if ($this->Request()->isGet()) {
306
                    return $this->forward('confirm');
307
                }
308
            } else {
309
                $this->View()->assign($order);
310
                $orderVariables = $this->session['sOrderVariables']->getArrayCopy();
311
312
                if (!empty($orderVariables['sOrderNumber'])) {
313
                    $orderVariables['sAddresses']['billing'] = $this->getOrderAddress($orderVariables['sOrderNumber'], 'billing');
314
                    $orderVariables['sAddresses']['shipping'] = $this->getOrderAddress($orderVariables['sOrderNumber'], 'shipping');
315
                    $orderVariables['sAddresses']['equal'] = $this->areAddressesEqual($orderVariables['sAddresses']['billing'], $orderVariables['sAddresses']['shipping']);
316
                }
317
318
                $this->View()->assign($orderVariables);
319
320
                if ($this->View()->sBasketView) {
321
                    $this->View()->sBasket = $this->View()->sBasketView;
322
                    unset($this->View()->sBasketView);
323
                }
324
325
                return;
326
            }
327
        }
328
329
        if (empty($this->session['sOrderVariables']) || $this->getMinimumCharge() || $this->getEsdNote() || $this->getDispatchNoOrder()) {
330
            return $this->forward('confirm');
331
        }
332
333
        $checkQuantities = $this->basket->sCheckBasketQuantities();
334
        if (!empty($checkQuantities['hideBasket'])) {
335
            return $this->forward('confirm');
336
        }
337
338
        $orderVariables = $this->session['sOrderVariables']->getArrayCopy();
339
340
        if (!empty($orderVariables['sOrderNumber'])) {
341
            $orderVariables['sAddresses']['billing'] = $this->getOrderAddress($orderVariables['sOrderNumber'], 'billing');
342
            $orderVariables['sAddresses']['shipping'] = $this->getOrderAddress($orderVariables['sOrderNumber'], 'shipping');
343
            $orderVariables['sAddresses']['equal'] = $this->areAddressesEqual($orderVariables['sAddresses']['billing'], $orderVariables['sAddresses']['shipping']);
344
        }
345
346
        $this->View()->assign($orderVariables);
347
        if ($this->View()->sBasketView) {
348
            $this->View()->sBasket = $this->View()->sBasketView;
349
            unset($this->View()->sBasketView);
350
        }
351
352
        if ($this->basket->sCountBasket() <= 0) {
353
            return;
354
        }
355
356
        if (!empty($this->View()->sUserData['additional']['payment']['embediframe'])) {
357
            return;
358
        }
359
360
        if ($this->Request()->getParam('sNewsletter') !== null) {
361
            $this->session['sNewsletter'] = $this->Request()->getParam('sNewsletter') ? true : false;
362
        }
363
        if ($this->Request()->getParam('sComment') !== null) {
364
            $this->session['sComment'] = trim(strip_tags($this->Request()->getParam('sComment')));
365
        }
366
367
        $basket = $this->View()->sBasket;
368
        $agreements = $this->getInvalidAgreements($basket, $this->Request());
369
370
        if (!empty($agreements)) {
371
            $this->View()->sAGBError = array_key_exists('agbError', $agreements);
372
373
            return $this->forward(
374
                'confirm',
375
                null,
376
                null,
377
                ['agreementErrors' => $agreements]
378
            );
379
        }
380
381
        if (!$this->basket->validateVoucher($this->session['sessionId'], $this->session['sUserId'])) {
382
            $namespace = $this->container->get('snippets')->getNamespace('frontend/basket/internalMessages');
383
384
            return $this->forward(
385
                'confirm',
386
                null,
387
                null,
388
                ['voucherErrors' => [
389
                    $namespace->get('VoucherFailureAlreadyUsed', 'This voucher was used in an previous order'),
390
                ]]
391
            );
392
        }
393
394
        if (empty($activeBillingAddressId = $this->session->offsetGet('checkoutBillingAddressId'))) {
395
            $activeBillingAddressId = $this->View()->sUserData['additional']['user']['default_billing_address_id'];
396
        }
397
398
        if (empty($activeShippingAddressId = $this->session->offsetGet('checkoutShippingAddressId'))) {
399
            $activeShippingAddressId = $this->View()->sUserData['additional']['user']['default_shipping_address_id'];
400
        }
401
402
        if (!$this->isValidAddress($activeBillingAddressId) || !$this->isValidAddress($activeShippingAddressId)) {
403
            $this->forward('confirm');
404
405
            return;
406
        }
407
408
        if (!empty($this->session['sNewsletter'])) {
409
            $this->admin->sUpdateNewsletter(true, $this->admin->sGetUserMailById(), true);
410
        }
411
412
        if ($this->Request()->isGet()) {
413
            return $this->forward('confirm');
414
        }
415
416
        $this->updateCurrencyDependencies($basket['sCurrencyId']);
417
418
        $this->saveOrder();
419
        $this->saveDefaultAddresses();
420
        $this->resetTemporaryAddresses();
421
422
        $this->session->offsetUnset('sComment');
423
424
        $orderVariables = $this->session['sOrderVariables']->getArrayCopy();
425
426
        $orderVariables['sAddresses']['billing'] = $this->getOrderAddress($orderVariables['sOrderNumber'], 'billing');
427
        $orderVariables['sAddresses']['shipping'] = $this->getOrderAddress($orderVariables['sOrderNumber'], 'shipping');
428
        $orderVariables['sAddresses']['equal'] = $this->areAddressesEqual($orderVariables['sAddresses']['billing'], $orderVariables['sAddresses']['shipping']);
429
430
        $this->View()->assign($orderVariables);
431
432
        if ($this->View()->sBasketView) {
433
            $this->View()->sBasket = $this->View()->sBasketView;
434
            unset($this->View()->sBasketView);
435
        }
436
    }
437
438
    /**
439
     * Used during the checkout process
440
     * Returns the user to the shop homepage
441
     * If the user has a noAccount account, it is automatically logged out
442
     */
443
    public function returnAction()
444
    {
445
        if ($this->View()->sUserData['additional']['user']['accountmode'] == 1) {
446
            Shopware()->Session()->unsetAll();
447
            Shopware()->Modules()->Basket()->sRefreshBasket();
448
        }
449
450
        return $this->redirect(['controller' => 'index']);
451
    }
452
453
    /**
454
     * If any external payment mean chooses by customer
455
     * Forward to payment page after order submitting
456
     */
457
    public function paymentAction()
458
    {
459
        if (empty($this->session['sOrderVariables'])
460
                || $this->getMinimumCharge()
461
                || $this->getEsdNote()
462
                || $this->getDispatchNoOrder()) {
463
            return $this->forward('confirm');
464
        }
465
466
        if ($this->Request()->getParam('sNewsletter') !== null) {
467
            $this->session['sNewsletter'] = $this->Request()->getParam('sNewsletter') ? true : false;
468
        }
469
        if ($this->Request()->getParam('sComment') !== null) {
470
            $this->session['sComment'] = trim(strip_tags($this->Request()->getParam('sComment')));
471
        }
472
473
        if (!Shopware()->Config()->get('IgnoreAGB') && !$this->Request()->getParam('sAGB')) {
474
            $this->View()->sAGBError = true;
475
476
            return $this->forward('confirm');
477
        }
478
479
        $this->View()->assign($this->session['sOrderVariables']->getArrayCopy());
480
        $this->View()->sAGBError = false;
481
482
        if (empty($this->View()->sPayment['embediframe'])
483
                && empty($this->View()->sPayment['action'])) {
484
            return $this->forward('confirm');
485
        }
486
487
        if (!empty($this->session['sNewsletter'])) {
488
            $this->admin->sUpdateNewsletter(true, $this->admin->sGetUserMailById(), true);
489
        }
490
491
        if (!empty($this->View()->sPayment['embediframe'])) {
492
            $embedded = $this->View()->sPayment['embediframe'];
493
            $embedded = preg_replace('#^[./]+#', '', $embedded);
494
            $embedded .= '?sCoreId=' . Shopware()->Session()->get('sessionId');
495
            $embedded .= '&sAGB=1';
496
            $embedded .= '&__basket_signature=' . $this->persistBasket();
497
            $this->View()->sEmbedded = $embedded;
498
        } else {
499
            $action = explode('/', $this->View()->sPayment['action']);
500
            $this->redirect([
501
                'controller' => $action[0],
502
                'action' => empty($action[1]) ? 'index' : $action[1],
503
            ]);
504
        }
505
    }
506
507
    /**
508
     * Add an article to cart directly from cart / confirm view
509
     *
510
     * @param sAdd = ordernumber
511
     * @param sQuantity = quantity
512
     *
513
     * @throws LogicException
514
     */
515
    public function addArticleAction()
516
    {
517
        if (strtolower($this->Request()->getMethod()) !== 'post') {
518
            throw new \LogicException('This action only admits post requests');
519
        }
520
521
        $ordernumber = trim($this->Request()->getParam('sAdd'));
522
        $quantity = $this->Request()->getParam('sQuantity');
523
        $articleID = Shopware()->Modules()->Articles()->sGetArticleIdByOrderNumber($ordernumber);
524
525
        $this->View()->sBasketInfo = $this->getInstockInfo($ordernumber, $quantity);
526
527
        if (!empty($articleID)) {
528
            $insertID = $this->basket->sAddArticle($ordernumber, $quantity);
529
            $this->View()->sArticleName = Shopware()->Modules()->Articles()->sGetArticleNameByOrderNumber($ordernumber);
530
            if (!empty($insertID)) {
531
                $basket = $this->getBasket();
532
                foreach ($basket['content'] as $item) {
533
                    if ($item['id'] == $insertID) {
534
                        $this->View()->sArticle = $item;
535
                        break;
536
                    }
537
                }
538
            }
539
540
            if (Shopware()->Config()->get('similarViewedShow', true)) {
541
                $this->View()->sCrossSimilarShown = $this->getSimilarShown($articleID);
542
            }
543
544
            if (Shopware()->Config()->get('alsoBoughtShow', true)) {
545
                $this->View()->sCrossBoughtToo = $this->getBoughtToo($articleID);
546
            }
547
        }
548
549
        if ($this->Request()->getParam('isXHR') || !empty($this->Request()->callback)) {
550
            $this->Request()->setParam('sTargetAction', 'ajax_add_article');
551
        }
552
553
        if ($this->Request()->getParam('sAddAccessories')) {
554
            $this->forward('addAccessories');
555
        } else {
556
            $this->forward($this->Request()->getParam('sTargetAction', 'cart'));
557
        }
558
    }
559
560
    /**
561
     * Add more then one article directly from cart / confirm view
562
     *
563
     * @param sAddAccessories = List of article order numbers separated by ;
564
     * @param sAddAccessoriesQuantity = List of article quantities separated by ;
565
     */
566
    public function addAccessoriesAction()
567
    {
568
        $this->addAccessories(
569
            $this->Request()->getParam('sAddAccessories'),
570
            $this->Request()->getParam('sAddAccessoriesQuantity')
571
        );
572
573
        $this->forward($this->Request()->getParam('sTargetAction', 'cart'));
574
    }
575
576
    /**
577
     * Delete an article from cart -
578
     *
579
     * @param sDelete = id from s_basket identifying the product to delete
580
     * Forward to cart / confirmation page after success
581
     */
582
    public function deleteArticleAction()
583
    {
584
        if ($this->Request()->getParam('sDelete')) {
585
            $this->basket->sDeleteArticle($this->Request()->getParam('sDelete'));
586
        }
587
        $this->forward($this->Request()->getParam('sTargetAction', 'index'));
588
    }
589
590
    /**
591
     * Change quantity of a certain product
592
     *
593
     * @param sArticle = The article to update
594
     * @param sQuantity = new quantity
595
     * Forward to cart / confirm view after success
596
     */
597
    public function changeQuantityAction()
598
    {
599
        if ($this->Request()->getParam('sArticle') && $this->Request()->getParam('sQuantity')) {
600
            $this->View()->sBasketInfo = $this->basket->sUpdateArticle($this->Request()->getParam('sArticle'), $this->Request()->getParam('sQuantity'));
601
        }
602
        $this->redirect(['action' => $this->Request()->getParam('sTargetAction', 'index')]);
603
    }
604
605
    /**
606
     * Add voucher to cart
607
     *
608
     * At failure view variable sVoucherError will give further information
609
     * At success return to cart / confirm view
610
     */
611
    public function addVoucherAction()
612
    {
613
        if ($this->Request()->isPost()) {
614
            $voucher = $this->basket->sAddVoucher($this->Request()->getParam('sVoucher'));
615
            if (!empty($voucher['sErrorMessages'])) {
616
                $this->View()->sVoucherError = $voucher['sErrorMessages'];
617
            }
618
        }
619
        $this->forward($this->Request()->getParam('sTargetAction', 'index'));
620
    }
621
622
    /**
623
     * Add premium / bonus article to cart
624
     *
625
     * @param sAddPremium - ordernumber of bonus article (defined in s_articles_premiums)
626
     * Return to cart / confirm page on success
627
     */
628
    public function addPremiumAction()
629
    {
630
        if ($this->Request()->isPost()) {
631
            if (!$this->Request()->getParam('sAddPremium')) {
632
                $this->View()->sBasketInfo = Shopware()->Snippets()->getNamespace()->get(
633
                    'CheckoutSelectPremiumVariant',
634
                    'Please select an option to place the required premium to the cart',
635
                    true
636
                );
637
            } else {
638
                $this->basket->sSYSTEM->_GET['sAddPremium'] = $this->Request()->getParam('sAddPremium');
0 ignored issues
show
Documentation introduced by
The property _GET does not exist on object<sSystem>. Since you implemented __get, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
Deprecated Code introduced by
The property sBasket::$sSYSTEM has been deprecated.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
639
                $this->basket->sInsertPremium();
640
            }
641
        }
642
        $this->forward($this->Request()->getParam('sTargetAction', 'index'));
643
    }
644
645
    /**
646
     * On any change on country, payment or dispatch recalculate shipping costs
647
     * and forward to cart / confirm view
648
     */
649
    public function calculateShippingCostsAction()
650
    {
651
        if ($this->Request()->getPost('sCountry')) {
652
            $this->session['sCountry'] = (int) $this->Request()->getPost('sCountry');
653
            $this->session['sState'] = 0;
654
            $this->session['sArea'] = Shopware()->Db()->fetchOne('
655
            SELECT areaID FROM s_core_countries WHERE id = ?
656
            ', [$this->session['sCountry']]);
657
        }
658
659
        if ($this->Request()->getPost('sPayment')) {
660
            $this->session['sPaymentID'] = (int) $this->Request()->getPost('sPayment');
661
        }
662
663
        if ($this->Request()->getPost('sDispatch')) {
664
            $this->session['sDispatch'] = (int) $this->Request()->getPost('sDispatch');
665
        }
666
667
        if ($this->Request()->getPost('sState')) {
668
            $this->session['sState'] = (int) $this->Request()->getPost('sState');
669
        }
670
671
        // We might change the shop context here so we need to initialize it again
672
        $this->get('shopware_storefront.context_service')->initializeShopContext();
673
674
        // We need an indicator in the view to expand the shipping costs pre-calculation on page load
675
        $this->View()->assign('calculateShippingCosts', true);
676
677
        $this->forward($this->Request()->getParam('sTargetAction', 'index'));
678
    }
679
680
    /**
681
     * Action to handle selection of shipping and payment methods
682
     */
683
    public function shippingPaymentAction()
684
    {
685
        if (empty($this->View()->sUserLoggedIn)) {
686
            return $this->forward(
687
                'login',
688
                'account',
689
                null,
690
                ['sTarget' => 'checkout', 'sTargetAction' => 'shippingPayment', 'showNoAccount' => true]
691
            );
692
        }
693
694
        // Load payment options, select option and details
695
        $this->View()->sPayments = $this->getPayments();
696
        $this->View()->sFormData = ['payment' => $this->View()->sUserData['additional']['user']['paymentID']];
697
        $getPaymentDetails = $this->admin->sGetPaymentMeanById($this->View()->sFormData['payment']);
698
699
        $paymentClass = $this->admin->sInitiatePaymentClass($getPaymentDetails);
700
        if ($paymentClass instanceof \ShopwarePlugin\PaymentMethods\Components\BasePaymentMethod) {
701
            $data = $paymentClass->getCurrentPaymentDataAsArray(Shopware()->Session()->sUserId);
702
            if (!empty($data)) {
703
                $this->View()->sFormData += $data;
704
            }
705
        }
706
        if ($this->Request()->isPost()) {
707
            $values = $this->Request()->getPost();
708
            $values['payment'] = $this->Request()->getPost('payment');
709
            $values['isPost'] = true;
710
            $this->View()->sFormData = $values;
711
        }
712
713
        // Load current and all shipping methods
714
        $this->View()->sDispatch = $this->getSelectedDispatch();
715
        $this->View()->sDispatches = $this->getDispatches($this->View()->sFormData['payment']);
716
717
        // We might change the shop context here so we need to initialize it again
718
        $this->get('shopware_storefront.context_service')->initializeShopContext();
719
720
        $this->View()->sBasket = $this->getBasket();
721
722
        $this->View()->sLaststock = $this->basket->sCheckBasketQuantities();
723
        $this->View()->sShippingcosts = $this->View()->sBasket['sShippingcosts'];
724
        $this->View()->sShippingcostsDifference = $this->View()->sBasket['sShippingcostsDifference'];
725
        $this->View()->sAmount = $this->View()->sBasket['sAmount'];
726
        $this->View()->sAmountWithTax = $this->View()->sBasket['sAmountWithTax'];
727
        $this->View()->sAmountTax = $this->View()->sBasket['sAmountTax'];
728
        $this->View()->sAmountNet = $this->View()->sBasket['AmountNetNumeric'];
729
        $this->View()->sRegisterFinished = !empty($this->session['sRegisterFinished']);
730
        $this->View()->sTargetAction = 'shippingPayment';
731
732
        if ($this->Request()->getParam('isXHR')) {
733
            return $this->View()->loadTemplate('frontend/checkout/shipping_payment_core.tpl');
734
        }
735
    }
736
737
    /**
738
     * Action to simultaneously save shipping and payment details
739
     */
740
    public function saveShippingPaymentAction()
741
    {
742
        if (!$this->Request()->isPost()) {
743
            return $this->forward('shippingPayment');
744
        }
745
746
        // Load data from request
747
        $dispatch = $this->Request()->getPost('sDispatch');
748
        $payment = $this->Request()->getPost('payment');
749
750
        // If request is ajax, we skip the validation, because the user is still editing
751
        if ($this->Request()->getParam('isXHR')) {
752
            // Save payment and shipping method data.
753
            $this->admin->sUpdatePayment($payment);
754
            $this->setDispatch($dispatch, $payment);
755
756
            return $this->forward('shippingPayment');
757
        }
758
759
        $sErrorFlag = [];
760
        $sErrorMessages = [];
761
762
        if (is_null($dispatch) && Shopware()->Config()->get('premiumshippingnoorder') === true && !$this->getDispatches($payment)) {
763
            $sErrorFlag['sDispatch'] = true;
764
            $sErrorMessages[] = Shopware()->Snippets()->getNamespace('frontend/checkout/error_messages')
765
                ->get('ShippingPaymentSelectShipping', 'Please select a shipping method');
766
        }
767
        if (is_null($payment)) {
768
            $sErrorFlag['payment'] = true;
769
            $sErrorMessages[] = Shopware()->Snippets()->getNamespace('frontend/checkout/error_messages')
770
                ->get('ShippingPaymentSelectPayment', 'Please select a payment method');
771
        }
772
773
        // If any basic info is missing, return error messages
774
        if (!empty($sErrorFlag) || !empty($sErrorMessages)) {
775
            $this->View()->assign('sErrorFlag', $sErrorFlag);
776
            $this->View()->assign('sErrorMessages', $sErrorMessages);
777
778
            return $this->forward('shippingPayment');
779
        }
780
781
        // Validate the payment details
782
        Shopware()->Modules()->Admin()->sSYSTEM->_POST['sPayment'] = $payment;
0 ignored issues
show
Documentation introduced by
The property _POST does not exist on object<sSystem>. Since you implemented __get, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
Deprecated Code introduced by
The property sAdmin::$sSYSTEM has been deprecated.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
783
        $checkData = $this->admin->sValidateStep3();
784
785
        // Problem with the payment details, return error
786
        if (!empty($checkData['checkPayment']['sErrorMessages']) || empty($checkData['sProcessed'])) {
787
            $this->View()->assign('sErrorFlag', $checkData['checkPayment']['sErrorFlag']);
788
            $this->View()->assign('sErrorMessages', $checkData['checkPayment']['sErrorMessages']);
789
790
            return $this->forward('shippingPayment');
791
        }
792
793
        // Save payment method details db
794
        if ($checkData['sPaymentObject'] instanceof \ShopwarePlugin\PaymentMethods\Components\BasePaymentMethod) {
795
            $checkData['sPaymentObject']->savePaymentData(Shopware()->Session()->sUserId, $this->Request());
796
        }
797
798
        // Save the payment info
799
        $previousPayment = Shopware()->Modules()->Admin()->sGetUserData();
800
        $previousPayment = $previousPayment['additional']['user']['paymentID'];
801
802
        $previousPayment = $this->admin->sGetPaymentMeanById($previousPayment);
803
        if ($previousPayment['paymentTable']) {
804
            Shopware()->Db()->delete(
805
                $previousPayment['paymentTable'],
806
                ['userID = ?' => Shopware()->Session()->sUserId]
807
            );
808
        }
809
810
        // Save payment and shipping method data.
811
        $this->admin->sUpdatePayment($payment);
812
        $this->setDispatch($dispatch, $payment);
813
814
        $this->redirect([
815
            'controller' => $this->Request()->getParam('sTarget', 'checkout'),
816
            'action' => $this->Request()->getParam('sTargetAction', 'confirm'),
817
        ]);
818
    }
819
820
    /**
821
     * Get complete user-data as an array to use in view
822
     *
823
     * @return array
824
     */
825
    public function getUserData()
826
    {
827
        $system = Shopware()->System();
0 ignored issues
show
Deprecated Code introduced by
The method Shopware::System() has been deprecated with message: sSystem is deprecated

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
828
        $userData = $this->admin->sGetUserData();
829
        if (!empty($userData['additional']['countryShipping'])) {
830
            $system->sUSERGROUPDATA = Shopware()->Db()->fetchRow('
831
                SELECT * FROM s_core_customergroups
832
                WHERE groupkey = ?
833
            ', [$system->sUSERGROUP]);
834
835
            $taxFree = $this->isTaxFreeDelivery($userData);
836
            $this->session->offsetSet('taxFree', $taxFree);
837
838
            if ($taxFree) {
839
                $system->sUSERGROUPDATA['tax'] = 0;
840
                $system->sCONFIG['sARTICLESOUTPUTNETTO'] = 1; //Old template
0 ignored issues
show
Deprecated Code introduced by
The property sSystem::$sCONFIG has been deprecated with message: Use Shopware()->Config()

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
841
                Shopware()->Session()->sUserGroupData = $system->sUSERGROUPDATA;
842
                $userData['additional']['charge_vat'] = false;
843
                $userData['additional']['show_net'] = false;
844
                Shopware()->Session()->sOutputNet = true;
845
            } else {
846
                $userData['additional']['charge_vat'] = true;
847
                $userData['additional']['show_net'] = !empty($system->sUSERGROUPDATA['tax']);
848
                Shopware()->Session()->sOutputNet = empty($system->sUSERGROUPDATA['tax']);
849
            }
850
        }
851
852
        return $userData;
853
    }
854
855
    /**
856
     * Create temporary order in s_order_basket on confirm page
857
     * Used to track failed / aborted orders
858
     */
859
    public function saveTemporaryOrder()
860
    {
861
        $order = Shopware()->Modules()->Order();
862
863
        $orgBasketData = $this->View()->sBasket;
864
        $this->View()->sBasket = $this->getBasket(false);
865
866
        $order->sUserData = $this->View()->sUserData;
867
        $order->sComment = isset($this->session['sComment']) ? $this->session['sComment'] : '';
868
        $order->sBasketData = $this->View()->sBasket;
869
        $order->sAmount = $this->View()->sBasket['sAmount'];
870
        $order->sAmountWithTax = !empty($this->View()->sBasket['AmountWithTaxNumeric']) ? $this->View()->sBasket['AmountWithTaxNumeric'] : $this->View()->sBasket['AmountNumeric'];
871
        $order->sAmountNet = $this->View()->sBasket['AmountNetNumeric'];
872
        $order->sShippingcosts = $this->View()->sBasket['sShippingcosts'];
873
        $order->sShippingcostsNumeric = $this->View()->sBasket['sShippingcostsWithTax'];
874
        $order->sShippingcostsNumericNet = $this->View()->sBasket['sShippingcostsNet'];
875
        $order->dispatchId = $this->session['sDispatch'];
876
        $order->sNet = !$this->View()->sUserData['additional']['charge_vat'];
877
        $order->deviceType = $this->Request()->getDeviceType();
878
879
        $this->View()->sBasket = $orgBasketData;
880
881
        $order->sDeleteTemporaryOrder();    // Delete previous temporary orders
882
        $order->sCreateTemporaryOrder();    // Create new temporary order
883
    }
884
885
    /**
886
     * Finish order - set some object properties to do this
887
     */
888
    public function saveOrder()
889
    {
890
        $order = Shopware()->Modules()->Order();
891
892
        $orgBasketData = $this->View()->sBasket;
893
        $this->View()->sBasket = $this->getBasket(false);
894
895
        $order->sUserData = $this->View()->sUserData;
896
        $order->sComment = isset($this->session['sComment']) ? $this->session['sComment'] : '';
897
        $order->sBasketData = $this->View()->sBasket;
898
        $order->sAmount = $this->View()->sBasket['sAmount'];
899
        $order->sAmountWithTax = !empty($this->View()->sBasket['AmountWithTaxNumeric']) ? $this->View()->sBasket['AmountWithTaxNumeric'] : $this->View()->sBasket['AmountNumeric'];
900
        $order->sAmountNet = $this->View()->sBasket['AmountNetNumeric'];
901
        $order->sShippingcosts = $this->View()->sBasket['sShippingcosts'];
902
        $order->sShippingcostsNumeric = $this->View()->sBasket['sShippingcostsWithTax'];
903
        $order->sShippingcostsNumericNet = $this->View()->sBasket['sShippingcostsNet'];
904
        $order->dispatchId = $this->session['sDispatch'];
905
        $order->sNet = !$this->View()->sUserData['additional']['charge_vat'];
906
        $order->deviceType = $this->Request()->getDeviceType();
907
908
        $this->View()->sBasket = $orgBasketData;
909
910
        return $order->sSaveOrder();
911
    }
912
913
    /**
914
     * Used in ajax add cart action
915
     * Check availability of product and return info / error - messages
916
     *
917
     * @param string $orderNumber article order number
918
     * @param int    $quantity    quantity
919
     *
920
     * @return string|null
921
     */
922
    public function getInstockInfo($orderNumber, $quantity)
923
    {
924
        if (empty($orderNumber)) {
925
            return Shopware()->Snippets()->getNamespace('frontend')->get('CheckoutSelectVariant',
926
                'Please select an option to place the required product in the cart', true);
927
        }
928
929
        $quantity = max(1, (int) $quantity);
930
        $inStock = $this->getAvailableStock($orderNumber);
931
        $inStock['quantity'] += $quantity;
932
933
        if (empty($inStock['articleID'])) {
934
            return Shopware()->Snippets()->getNamespace('frontend')->get('CheckoutArticleNotFound',
935
                'Product could not be found.', true);
936
        }
937
        if (!empty($inStock['laststock']) || !empty(Shopware()->Config()->InstockInfo)) {
0 ignored issues
show
Documentation introduced by
The property InstockInfo does not exist on object<Shopware_Components_Config>. Since you implemented __get, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
938
            if ($inStock['instock'] <= 0 && !empty($inStock['laststock'])) {
939
                return Shopware()->Snippets()->getNamespace('frontend')->get('CheckoutArticleNoStock',
940
                    'Unfortunately we can not deliver the desired product in sufficient quantity', true);
941
            } elseif ($inStock['instock'] < $inStock['quantity']) {
942
                $result = 'Unfortunately we can not deliver the desired product in sufficient quantity. (#0 of #1 in stock).';
943
                $result = Shopware()->Snippets()->getNamespace('frontend')->get('CheckoutArticleLessStock', $result,
944
                    true);
945
946
                return str_replace(['#0', '#1'], [$inStock['instock'], $inStock['quantity']], $result);
947
            }
948
        }
949
950
        return null;
951
    }
952
953
    /**
954
     * Get current stock from a certain product defined by $ordernumber
955
     * Support for multidimensional variants
956
     *
957
     * @param unknown_type $ordernumber
958
     *
959
     * @return array with article id / current basket quantity / instock / laststock
960
     */
961
    public function getAvailableStock($ordernumber)
962
    {
963
        $sql = '
964
            SELECT
965
                a.id as articleID,
966
                ob.quantity,
967
                IF(ad.instock < 0, 0, ad.instock) as instock,
968
                a.laststock,
969
                ad.ordernumber as ordernumber
970
            FROM s_articles a
971
            LEFT JOIN s_articles_details ad
972
            ON ad.ordernumber=?
973
            LEFT JOIN s_order_basket ob
974
            ON ob.sessionID=?
975
            AND ob.ordernumber=ad.ordernumber
976
            AND ob.modus=0
977
            WHERE a.id=ad.articleID
978
        ';
979
        $row = Shopware()->Db()->fetchRow($sql, [
980
                $ordernumber,
981
                Shopware()->Session()->get('sessionId'),
982
            ]);
983
984
        return $row;
985
    }
986
987
    /**
988
     * Get shipping costs as an array (brutto / netto) depending on selected country / payment
989
     *
990
     * @return array
991
     */
992
    public function getShippingCosts()
993
    {
994
        $country = $this->getSelectedCountry();
995
        $payment = $this->getSelectedPayment();
996
        if (empty($country) || empty($payment)) {
997
            return ['brutto' => 0, 'netto' => 0];
998
        }
999
        $shippingcosts = $this->admin->sGetPremiumShippingcosts($country);
1000
1001
        return empty($shippingcosts) ? ['brutto' => 0, 'netto' => 0] : $shippingcosts;
1002
    }
1003
1004
    /**
1005
     * Return complete basket data to view
1006
     * Basket items / Shippingcosts / Amounts / Tax-Rates
1007
     *
1008
     * @param bool $mergeProportional
1009
     *
1010
     * @return array
1011
     */
1012
    public function getBasket($mergeProportional = true)
1013
    {
1014
        $shippingcosts = $this->getShippingCosts();
1015
1016
        $basket = $this->basket->sGetBasket();
1017
1018
        /** @var \Shopware\Models\Shop\Currency $currency */
1019
        $currency = $this->get('shop')->getCurrency();
1020
1021
        $positions = $this->container->get('shopware.cart.basket_helper')->getPositionPrices();
1022
        $taxCalculator = $this->container->get('shopware.cart.proportional_tax_calculator');
1023
        $hasDifferentTaxes = $taxCalculator->hasDifferentTaxes($positions);
1024
1025
        $basket['sCurrencyId'] = $currency->getId();
1026
        $basket['sCurrencyName'] = $currency->getCurrency();
1027
        $basket['sCurrencyFactor'] = $currency->getFactor();
1028
1029
        if ($hasDifferentTaxes && empty($shippingcosts['taxMode']) && $this->get('config')->get('proportionalTaxCalculation') && !$this->session->get('taxFree')) {
1030
            $taxProportional = $taxCalculator->calculate($shippingcosts['brutto'], $positions, !Shopware()->System()->sUSERGROUPDATA['tax'] && Shopware()->System()->sUSERGROUPDATA['id']);
0 ignored issues
show
Deprecated Code introduced by
The method Shopware::System() has been deprecated with message: sSystem is deprecated

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1031
1032
            $basket['sShippingcostsTaxProportional'] = $taxProportional;
1033
1034
            $shippingNet = 0;
1035
1036
            foreach ($taxProportional as $shippingProportional) {
1037
                $shippingNet += $shippingProportional->getNetPrice();
1038
            }
1039
1040
            $basket['sShippingcostsWithTax'] = $shippingcosts['brutto'];
1041
            $basket['sShippingcostsNet'] = $shippingNet;
1042
            $basket['sShippingcostsTax'] = $shippingcosts['tax'];
1043
1044
            $shippingcosts['netto'] = $shippingNet;
1045
        } else {
1046
            $basket['sShippingcostsWithTax'] = $shippingcosts['brutto'];
1047
            $basket['sShippingcostsNet'] = $shippingcosts['netto'];
1048
            $basket['sShippingcostsTax'] = $shippingcosts['tax'];
1049
        }
1050
1051
        if (!empty($shippingcosts['brutto'])) {
1052
            $basket['AmountNetNumeric'] += $shippingcosts['netto'];
1053
            $basket['AmountNumeric'] += $shippingcosts['brutto'];
1054
            $basket['sShippingcostsDifference'] = $shippingcosts['difference']['float'];
1055
        }
1056
        if (!empty($basket['AmountWithTaxNumeric'])) {
1057
            $basket['AmountWithTaxNumeric'] += $shippingcosts['brutto'];
1058
        }
1059
        if (!Shopware()->System()->sUSERGROUPDATA['tax'] && Shopware()->System()->sUSERGROUPDATA['id']) {
0 ignored issues
show
Deprecated Code introduced by
The method Shopware::System() has been deprecated with message: sSystem is deprecated

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1060
            $basket['sTaxRates'] = $this->getTaxRates($basket);
1061
1062
            $basket['sShippingcosts'] = $shippingcosts['netto'];
1063
            $basket['sAmount'] = round($basket['AmountNetNumeric'], 2);
1064
            $basket['sAmountTax'] = round($basket['AmountWithTaxNumeric'] - $basket['AmountNetNumeric'], 2);
1065
            $basket['sAmountWithTax'] = round($basket['AmountWithTaxNumeric'], 2);
1066
        } else {
1067
            $basket['sTaxRates'] = $this->getTaxRates($basket);
1068
1069
            $basket['sShippingcosts'] = $shippingcosts['brutto'];
1070
            $basket['sAmount'] = $basket['AmountNumeric'];
1071
1072
            $basket['sAmountTax'] = round($basket['AmountNumeric'] - $basket['AmountNetNumeric'], 2);
1073
        }
1074
1075
        $this->View()->sBasketProportional = $basket;
1076
        if ($mergeProportional && $hasDifferentTaxes && $this->get('config')->get('proportionalTaxCalculation')) {
1077
            $basket['content'] = $this->mergeProportionalItems($basket['content']);
1078
        }
1079
1080
        return $basket;
1081
    }
1082
1083
    /**
1084
     * Merges proportional cart items into one
1085
     * @param array $content
1086
     * @return array
1087
     */
1088
    private function mergeProportionalItems(array $content)
1089
    {
1090
        $newCart = [];
1091
1092
        foreach ($content as $cartItem) {
1093
            if (!isset($newCart[$cartItem['ordernumber']])) {
1094
                $newCart[$cartItem['ordernumber']] = $cartItem;
1095
                continue;
1096
            }
1097
1098
            // There are some plugins, which allows to have same product multiple times in cart
1099
            if (empty($newCart[$cartItem['ordernumber']]['modus'])) {
1100
                $newCart[] = $cartItem;
1101
                continue;
1102
            }
1103
1104
            if (!isset($newCart[$cartItem['ordernumber']]['fixedName'])) {
1105
                $newCart[$cartItem['ordernumber']]['articlename'] = substr($newCart[$cartItem['ordernumber']]['articlename'], 0, strrpos($newCart[$cartItem['ordernumber']]['articlename'], ' '));
1106
            }
1107
1108
            $newCart[$cartItem['ordernumber']]['price'] = $this->mergeAmount($newCart[$cartItem['ordernumber']], $cartItem, 'price');
1109
            $newCart[$cartItem['ordernumber']]['netprice'] = $this->mergeAmount($newCart[$cartItem['ordernumber']], $cartItem, 'netprice');
1110
            $newCart[$cartItem['ordernumber']]['amountWithTax'] = $this->mergeAmount($newCart[$cartItem['ordernumber']], $cartItem, 'amountWithTax');
1111
            $newCart[$cartItem['ordernumber']]['amount'] = $this->mergeAmount($newCart[$cartItem['ordernumber']], $cartItem, 'amount');
1112
            $newCart[$cartItem['ordernumber']]['amountnet'] = $this->mergeAmount($newCart[$cartItem['ordernumber']], $cartItem, 'amountnet');
1113
            $newCart[$cartItem['ordernumber']]['priceNumeric'] = $this->mergeAmount($newCart[$cartItem['ordernumber']], $cartItem, 'priceNumeric');
1114
            $newCart[$cartItem['ordernumber']]['tax'] = $this->mergeAmount($newCart[$cartItem['ordernumber']], $cartItem, 'tax');
1115
        }
1116
1117
        return array_values($newCart);
1118
    }
1119
1120
    /**
1121
     * @param array $item1
1122
     * @param array $item2
1123
     * @param string $property
1124
     * @return string
1125
     */
1126
    private function mergeAmount(array $item1, array $item2, $property)
1127
    {
1128
        $hasComma = strpos($item1[$property], ',') !== false;
1129
        $amount = str_replace(',', '.', $item1[$property]) + str_replace(',', '.', $item2[$property]);
1130
1131
        if ($hasComma) {
1132
            $amount = Shopware()->Modules()->Articles()->sFormatPrice($amount);
1133
        }
1134
1135
        return $amount;
1136
    }
1137
1138
    /**
1139
     * Returns tax rates for all basket positions
1140
     *
1141
     * @param array $basket array returned from this->getBasket
1142
     *
1143
     * @return array
1144
     */
1145
    public function getTaxRates($basket)
1146
    {
1147
        $result = [];
1148
1149
        if (!empty($basket['sShippingcostsTax'])) {
1150
            if (!empty($basket['sShippingcostsTaxProportional'])) {
1151
                /** @var \Shopware\Components\Cart\Struct\Price $shippingTax */
1152
                foreach ($basket['sShippingcostsTaxProportional'] as $shippingTax) {
1153
                    $result[number_format($shippingTax->getTaxRate(), 2)] += $shippingTax->getTax();
1154
                }
1155
            } else {
1156
                $basket['sShippingcostsTax'] = number_format((float) $basket['sShippingcostsTax'], 2);
1157
1158
                $result[$basket['sShippingcostsTax']] = $basket['sShippingcostsWithTax'] - $basket['sShippingcostsNet'];
1159
                if (empty($result[$basket['sShippingcostsTax']])) {
1160
                    unset($result[$basket['sShippingcostsTax']]);
1161
                }
1162
            }
1163
        }
1164
1165
        if (empty($basket['content'])) {
1166
            ksort($result, SORT_NUMERIC);
1167
1168
            return $result;
1169
        }
1170
1171
        foreach ($basket['content'] as $item) {
1172
            if (!empty($item['tax_rate'])) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

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

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

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

could be turned into

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

This is much more concise to read.

Loading history...
1173
            } elseif (!empty($item['taxPercent'])) {
1174
                $item['tax_rate'] = $item['taxPercent'];
1175
            } elseif ($item['modus'] == 2) {
1176
                // Ticket 4842 - dynamic tax-rates
1177
                $resultVoucherTaxMode = Shopware()->Db()->fetchOne(
1178
                    'SELECT taxconfig FROM s_emarketing_vouchers WHERE ordercode=?
1179
                ', [$item['ordernumber']]);
1180
                // Old behaviour
1181
                if (empty($resultVoucherTaxMode) || $resultVoucherTaxMode === 'default') {
1182
                    $tax = Shopware()->Config()->get('sVOUCHERTAX');
1183
                } elseif ($resultVoucherTaxMode === 'auto') {
1184
                    // Automatically determinate tax
1185
                    $tax = $this->basket->getMaxTax();
1186
                } elseif ($resultVoucherTaxMode === 'none') {
1187
                    // No tax
1188
                    $tax = '0';
1189
                } elseif ((int) $resultVoucherTaxMode) {
1190
                    // Fix defined tax
1191
                    $tax = Shopware()->Db()->fetchOne('
1192
                    SELECT tax FROM s_core_tax WHERE id = ?
1193
                    ', [$resultVoucherTaxMode]);
1194
                }
1195
                $item['tax_rate'] = $tax;
1196
            } else {
1197
                // Ticket 4842 - dynamic tax-rates
1198
                $taxAutoMode = Shopware()->Config()->get('sTAXAUTOMODE');
1199
                if (!empty($taxAutoMode)) {
1200
                    $tax = $this->basket->getMaxTax();
1201
                } else {
1202
                    $tax = Shopware()->Config()->get('sDISCOUNTTAX');
1203
                }
1204
                $item['tax_rate'] = $tax;
1205
            }
1206
1207
            if (empty($item['tax_rate']) || empty($item['tax'])) {
1208
                continue;
1209
            } // Ignore 0 % tax
1210
1211
            $taxKey = number_format((float) $item['tax_rate'], 2);
1212
1213
            $result[$taxKey] += str_replace(',', '.', $item['tax']);
1214
        }
1215
1216
        ksort($result, SORT_NUMERIC);
1217
1218
        return $result;
1219
    }
1220
1221
    /**
1222
     * Get similar shown products to display in ajax add dialog
1223
     *
1224
     * @param int $articleID
1225
     *
1226
     * @return array
1227
     */
1228
    public function getSimilarShown($articleID)
1229
    {
1230
        Shopware()->Modules()->Marketing()->sBlacklist = $this->basket->sGetBasketIds();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->basket->sGetBasketIds() can be null. However, the property $sBlacklist is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

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

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
1231
1232
        $similarId = Shopware()->Modules()->Marketing()->sGetSimilaryShownArticles($articleID);
1233
1234
        $similars = [];
1235
        if (!empty($similarId)) {
1236
            foreach ($similarId as $similarID) {
1237
                $temp = Shopware()->Modules()->Articles()->sGetPromotionById('fix', 0, (int) $similarID['id']);
1238
                if (!empty($temp)) {
1239
                    $similars[] = $temp;
1240
                }
1241
            }
1242
        }
1243
1244
        return $similars;
1245
    }
1246
1247
    /**
1248
     * Get articles that bought in combination with last added product to
1249
     * display on cart page
1250
     *
1251
     * @param int $articleID
1252
     *
1253
     * @return array
1254
     */
1255
    public function getBoughtToo($articleID)
1256
    {
1257
        Shopware()->Modules()->Marketing()->sBlacklist = $this->basket->sGetBasketIds();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->basket->sGetBasketIds() can be null. However, the property $sBlacklist is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

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

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
1258
1259
        $alsoBoughtId = Shopware()->Modules()->Marketing()->sGetAlsoBoughtArticles($articleID);
1260
        $alsoBoughts = [];
1261
        if (!empty($alsoBoughtId)) {
1262
            foreach ($alsoBoughtId as $alsoBoughtItem) {
1263
                $temp = Shopware()->Modules()->Articles()->sGetPromotionById('fix', 0, (int) $alsoBoughtItem['id']);
1264
                if (!empty($temp)) {
1265
                    $alsoBoughts[] = $temp;
1266
                }
1267
            }
1268
        }
1269
1270
        return $alsoBoughts;
1271
    }
1272
1273
    /**
1274
     * Get configured minimum charge to check in order processing
1275
     *
1276
     * @return bool
1277
     */
1278
    public function getMinimumCharge()
1279
    {
1280
        return $this->basket->sCheckMinimumCharge();
1281
    }
1282
1283
    /**
1284
     * Check if order is possible under current conditions (dispatch)
1285
     *
1286
     * @return bool
1287
     */
1288
    public function getDispatchNoOrder()
1289
    {
1290
        return !empty(Shopware()->Config()->PremiumShippingNoOrder) && (empty($this->session['sDispatch']) || empty($this->session['sCountry']));
0 ignored issues
show
Documentation introduced by
The property PremiumShippingNoOrder does not exist on object<Shopware_Components_Config>. Since you implemented __get, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
1291
    }
1292
1293
    /**
1294
     * Get all premium products that are configured and available for this order
1295
     *
1296
     * @return array
1297
     */
1298
    public function getPremiums()
1299
    {
1300
        $sql = 'SELECT `id` FROM `s_order_basket` WHERE `sessionID`=? AND `modus`=1';
1301
        $result = Shopware()->Db()->fetchOne($sql, [Shopware()->Session()->get('sessionId')]);
1302
        if (!empty($result)) {
1303
            return [];
1304
        }
1305
1306
        return Shopware()->Modules()->Marketing()->sGetPremiums();
1307
    }
1308
1309
    /**
1310
     * Check if any electronically distribution product is in basket
1311
     *
1312
     * @return bool
1313
     */
1314
    public function getEsdNote()
1315
    {
1316
        $payment = empty($this->View()->sUserData['additional']['payment']) ? $this->session['sOrderVariables']['sUserData']['additional']['payment'] : $this->View()->sUserData['additional']['payment'];
1317
1318
        return $this->basket->sCheckForESD() && !$payment['esdactive'];
1319
    }
1320
1321
    /**
1322
     * Check if a custom inquiry possibility should displayed on cart page
1323
     * Compare configured inquirevalue with current amount
1324
     *
1325
     * @return bool
1326
     */
1327
    public function getInquiry()
1328
    {
1329
        if (Shopware()->Config()->get('sINQUIRYVALUE')) {
1330
            $factor = Shopware()->System()->sCurrency['factor'] ? 1 : Shopware()->System()->sCurrency['factor'];
0 ignored issues
show
Deprecated Code introduced by
The method Shopware::System() has been deprecated with message: sSystem is deprecated

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
Deprecated Code introduced by
The property sSystem::$sCurrency has been deprecated with message: Use Shopware()->Shop()->getCurrency() or Shopware()->Shop()->getCurrency()->toArray()

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1331
            $value = Shopware()->Config()->get('sINQUIRYVALUE') * $factor;
1332
            if ((!Shopware()->System()->sUSERGROUPDATA['tax'] && Shopware()->System()->sUSERGROUPDATA['id'])) {
0 ignored issues
show
Deprecated Code introduced by
The method Shopware::System() has been deprecated with message: sSystem is deprecated

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1333
                $amount = $this->View()->sBasket['AmountWithTaxNumeric'];
1334
            } else {
1335
                $amount = $this->View()->sBasket['AmountNumeric'];
1336
            }
1337
            if (!empty($amount) && $amount >= $value) {
1338
                return true;
1339
            }
1340
        }
1341
1342
        return false;
1343
    }
1344
1345
    /**
1346
     * Get link to inquiry form if getInquiry returend true
1347
     *
1348
     * @return string
1349
     */
1350
    public function getInquiryLink()
1351
    {
1352
        return Shopware()->Config()->get('sBASEFILE') . '?sViewport=support&sFid=' . Shopware()->Config()->get('sINQUIRYID') . '&sInquiry=basket';
1353
    }
1354
1355
    /**
1356
     * Get all countries from database via sAdmin object
1357
     *
1358
     * @return array list of countries
1359
     */
1360
    public function getCountryList()
1361
    {
1362
        return $this->admin->sGetCountryList();
1363
    }
1364
1365
    /**
1366
     * Get all dispatches available in selected country from sAdmin object
1367
     *
1368
     * @param null $paymentId
1369
     *
1370
     * @return array list of dispatches
1371
     */
1372
    public function getDispatches($paymentId = null)
1373
    {
1374
        $country = $this->getSelectedCountry();
1375
        $state = $this->getSelectedState();
1376
        if (empty($country)) {
1377
            return false;
1378
        }
1379
        $stateId = !empty($state['id']) ? $state['id'] : null;
1380
1381
        return $this->admin->sGetPremiumDispatches($country['id'], $paymentId, $stateId);
1382
    }
1383
1384
    /**
1385
     * Returns all available payment methods from sAdmin object
1386
     *
1387
     * @return array list of payment methods
1388
     */
1389
    public function getPayments()
1390
    {
1391
        return $this->admin->sGetPaymentMeans();
1392
    }
1393
1394
    /**
1395
     * Get current selected country - if no country is selected, choose first one from list
1396
     * of available countries
1397
     *
1398
     * @return array with country information
1399
     */
1400
    public function getSelectedCountry()
1401
    {
1402
        if (!empty($this->View()->sUserData['additional']['countryShipping'])) {
1403
            $this->session['sCountry'] = (int) $this->View()->sUserData['additional']['countryShipping']['id'];
1404
            $this->session['sArea'] = (int) $this->View()->sUserData['additional']['countryShipping']['areaID'];
1405
1406
            return $this->View()->sUserData['additional']['countryShipping'];
1407
        }
1408
        $countries = $this->getCountryList();
1409
        if (empty($countries)) {
1410
            unset($this->session['sCountry']);
1411
1412
            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 Shopware_Controllers_Fro...out::getSelectedCountry of type array.

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...
1413
        }
1414
        $country = reset($countries);
1415
        $this->session['sCountry'] = (int) $country['id'];
1416
        $this->session['sArea'] = (int) $country['areaID'];
1417
        $this->View()->sUserData['additional']['countryShipping'] = $country;
1418
1419
        return $country;
1420
    }
1421
1422
    /**
1423
     * Get current selected country - if no country is selected, choose first one from list
1424
     * of available countries
1425
     *
1426
     * @return array with country information
1427
     */
1428
    public function getSelectedState()
1429
    {
1430
        if (!empty($this->View()->sUserData['additional']['stateShipping'])) {
1431
            $this->session['sState'] = (int) $this->View()->sUserData['additional']['stateShipping']['id'];
1432
1433
            return $this->View()->sUserData['additional']['stateShipping'];
1434
        }
1435
1436
        return ['id' => $this->session['sState']];
1437
    }
1438
1439
    /**
1440
     * Get selected payment or do payment mean selection automatically
1441
     *
1442
     * @return array
1443
     */
1444
    public function getSelectedPayment()
1445
    {
1446
        $paymentMethods = $this->getPayments();
1447
1448
        if (!empty($this->View()->sUserData['additional']['payment'])) {
1449
            $payment = $this->View()->sUserData['additional']['payment'];
1450
        } elseif (!empty($this->session['sPaymentID'])) {
1451
            $payment = $this->admin->sGetPaymentMeanById($this->session['sPaymentID'], $this->View()->sUserData);
1452
        }
1453
1454
        if ($payment && !$this->checkPaymentAvailability($payment, $paymentMethods)) {
1455
            $payment = null;
1456
        }
1457
1458
        $paymentClass = $this->admin->sInitiatePaymentClass($payment);
1459
        if ($payment && $paymentClass instanceof \ShopwarePlugin\PaymentMethods\Components\BasePaymentMethod) {
1460
            $data = $paymentClass->getCurrentPaymentDataAsArray(Shopware()->Session()->sUserId);
1461
            $payment['validation'] = $paymentClass->validate($data);
0 ignored issues
show
Bug introduced by
It seems like $data defined by $paymentClass->getCurren...()->Session()->sUserId) on line 1460 can also be of type null; however, ShopwarePlugin\PaymentMe...ymentMethod::validate() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1462
            if (!empty($data)) {
1463
                $payment['data'] = $data;
1464
            }
1465
        }
1466
1467
        if (!empty($payment)) {
1468
            return $payment;
1469
        }
1470
1471
        if (empty($paymentMethods)) {
1472
            unset($this->session['sPaymentID']);
1473
1474
            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 Shopware_Controllers_Fro...out::getSelectedPayment of type array.

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...
1475
        }
1476
1477
        $payment = $this->getDefaultPaymentMethod($paymentMethods);
1478
1479
        $this->session['sPaymentID'] = (int) $payment['id'];
1480
        $this->front->Request()->setPost('sPayment', (int) $payment['id']);
1481
        $this->admin->sUpdatePayment();
1482
1483
        //if customer logged in and payment switched to fallback, display cart notice. Otherwise anonymous customers will see the message too
1484
        if (Shopware()->Session()->sUserId) {
1485
            $this->flagPaymentBlocked();
1486
        }
1487
1488
        return $payment;
1489
    }
1490
1491
    /**
1492
     * Get selected dispatch or select a default dispatch
1493
     *
1494
     * @return bool|array
1495
     */
1496
    public function getSelectedDispatch()
1497
    {
1498
        if (empty($this->session['sCountry'])) {
1499
            return false;
1500
        }
1501
1502
        $dispatches = $this->admin->sGetPremiumDispatches($this->session['sCountry'], null, $this->session['sState']);
1503
        if (empty($dispatches)) {
1504
            unset($this->session['sDispatch']);
1505
1506
            return false;
1507
        }
1508
1509
        foreach ($dispatches as $dispatch) {
1510
            if ($dispatch['id'] == $this->session['sDispatch']) {
1511
                return $dispatch;
1512
            }
1513
        }
1514
        $dispatch = reset($dispatches);
1515
        $this->session['sDispatch'] = (int) $dispatch['id'];
1516
1517
        return $dispatch;
1518
    }
1519
1520
    /**
1521
     * Set the provided dispatch method
1522
     *
1523
     * @param $dispatchId $dispatchId ID of the dispatch method to set
0 ignored issues
show
Documentation introduced by
The doc-type $dispatchId could not be parsed: Unknown type name "$dispatchId" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1524
     * @param int|null $paymentId Payment id to validate
1525
     *
1526
     * @return int set dispatch method id
1527
     */
1528
    public function setDispatch($dispatchId, $paymentId = null)
1529
    {
1530
        $supportedDispatches = $this->getDispatches($paymentId);
1531
1532
        // Iterate over supported dispatches, look for the provided one
1533
        foreach ($supportedDispatches as $dispatch) {
0 ignored issues
show
Bug introduced by
The expression $supportedDispatches of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1534
            if ($dispatch['id'] == $dispatchId) {
1535
                $this->session['sDispatch'] = $dispatchId;
1536
1537
                return $dispatchId;
1538
            }
1539
        }
1540
1541
        // If it was not found, we fallback to the default (head of supported)
1542
        $defaultDispatch = array_shift($supportedDispatches);
1543
        $this->session['sDispatch'] = $defaultDispatch['id'];
1544
1545
        return $this->session['sDispatch'];
1546
    }
1547
1548
    /**
1549
     * Ajax add article action
1550
     *
1551
     * This action will get redirected from the default addArticleAction
1552
     * when the request was an AJAX request.
1553
     */
1554
    public function ajaxAddArticleAction()
1555
    {
1556
        // Empty but can't be removed for legacy reasons
1557
    }
1558
1559
    /**
1560
     * Ajax add article cart action
1561
     *
1562
     * This action is a lightweight way to add an article by the passed
1563
     * article order number and quantity.
1564
     *
1565
     * The order number is expected to get passed by the 'sAdd' parameter
1566
     * This quantity is expected to get passed by the 'sQuantity' parameter.
1567
     *
1568
     * After the article was added to the basket, the whole cart content will be returned.
1569
     *
1570
     * @throws \LogicException
1571
     */
1572
    public function ajaxAddArticleCartAction()
1573
    {
1574
        if (strtolower($this->Request()->getMethod()) !== 'post') {
1575
            throw new \LogicException('This action only admits post requests');
1576
        }
1577
1578
        $orderNumber = $this->Request()->getParam('sAdd');
1579
        $quantity = $this->Request()->getParam('sQuantity');
1580
1581
        $this->View()->assign(
1582
            'basketInfoMessage',
1583
            $this->getInstockInfo($orderNumber, $quantity)
1584
        );
1585
1586
        if ($this->Request()->get('sAddAccessories')) {
1587
            $this->addAccessories(
1588
                $this->Request()->getParam('sAddAccessories'),
1589
                $this->Request()->getParam('sAddAccessoriesQuantity')
1590
            );
1591
        }
1592
1593
        $this->basket->sAddArticle($orderNumber, $quantity);
1594
1595
        $this->forward('ajaxCart');
1596
    }
1597
1598
    /**
1599
     * Ajax delete article action
1600
     *
1601
     * This action is a lightweight way to delete an article by the passed
1602
     * basket item id.
1603
     *
1604
     * This id is expected to get passed by the 'sDelete' parameter.
1605
     *
1606
     * After the article was removed from the basket, the whole cart content will be returned.
1607
     */
1608
    public function ajaxDeleteArticleCartAction()
1609
    {
1610
        if (strtolower($this->Request()->getMethod()) !== 'post') {
1611
            throw new \LogicException('This action only admits post requests');
1612
        }
1613
1614
        $itemId = $this->Request()->getParam('sDelete');
1615
1616
        if ($itemId) {
1617
            $this->basket->sDeleteArticle($itemId);
1618
        }
1619
1620
        $this->forward('ajaxCart');
1621
    }
1622
1623
    /**
1624
     * Ajax cart action
1625
     *
1626
     * This action loads the cart content and returns it.
1627
     * Its purpose is to return all necessary information in a minimal template
1628
     * for a good performance so e.g. ajax requests are finished more quickly.
1629
     */
1630
    public function ajaxCartAction()
1631
    {
1632
        $view = $this->View();
1633
        $basket = $this->getBasket();
1634
1635
        $view->sBasket = $basket;
1636
1637
        $view->sShippingcosts = $basket['sShippingcosts'];
1638
        $view->sShippingcostsDifference = $basket['sShippingcostsDifference'];
1639
        $view->sAmount = $basket['sAmount'];
1640
        $view->sAmountWithTax = $basket['sAmountWithTax'];
1641
        $view->sAmountTax = $basket['sAmountTax'];
1642
        $view->sAmountNet = $basket['AmountNetNumeric'];
1643
        $view->sDispatches = $this->getDispatches();
1644
        $view->sDispatchNoOrder = $this->getDispatchNoOrder();
1645
    }
1646
1647
    /**
1648
     * Get current amount from cart via ajax to display in realtime
1649
     */
1650
    public function ajaxAmountAction()
1651
    {
1652
        $this->Response()->setHeader('Content-Type', 'application/json');
1653
1654
        $amount = $this->basket->sGetAmount();
1655
        $quantity = $this->basket->sCountBasket();
1656
1657
        $this->View()->sBasketQuantity = $quantity;
1658
        $this->View()->sBasketAmount = empty($amount) ? 0 : array_shift($amount);
1659
1660
        $this->Front()->Plugins()->ViewRenderer()->setNoRender();
1661
1662
        $this->Response()->setBody(
1663
            json_encode([
1664
                    'amount' => Shopware()->Template()->fetch('frontend/checkout/ajax_amount.tpl'),
1665
                    'quantity' => $quantity,
1666
            ])
1667
        );
1668
    }
1669
1670
    /**
1671
     * Sets a temporary session variable which holds an address for the current order
1672
     */
1673
    public function setAddressAction()
1674
    {
1675
        $this->Front()->Plugins()->ViewRenderer()->setNoRender(true);
1676
        $target = $this->Request()->getParam('target', 'shipping');
1677
        $sessionKey = $target === 'shipping' ? 'checkoutShippingAddressId' : 'checkoutBillingAddressId';
1678
1679
        $this->session->offsetSet($sessionKey, $this->Request()->getParam('addressId'));
1680
1681
        if ($target === 'both') {
1682
            $this->session->offsetSet('checkoutShippingAddressId', $this->Request()->getParam('addressId'));
1683
            $this->session->offsetSet('checkoutBillingAddressId', $this->Request()->getParam('addressId'));
1684
        }
1685
    }
1686
1687
    /**
1688
     * Validates if the provided customer should get a tax free delivery
1689
     *
1690
     * @param array $userData
1691
     *
1692
     * @return bool
1693
     */
1694
    protected function isTaxFreeDelivery($userData)
1695
    {
1696
        if (!empty($userData['additional']['countryShipping']['taxfree'])) {
1697
            return true;
1698
        }
1699
1700
        if (empty($userData['additional']['countryShipping']['taxfree_ustid'])) {
1701
            return false;
1702
        }
1703
1704
        if (empty($userData['shippingaddress']['ustid']) &&
1705
            !empty($userData['billingaddress']['ustid']) &&
1706
            !empty($userData['additional']['country']['taxfree_ustid'])) {
1707
            return true;
1708
        }
1709
1710
        return !empty($userData['shippingaddress']['ustid']);
1711
    }
1712
1713
    /**
1714
     * @param $basket
1715
     * @param Request $request
1716
     *
1717
     * @throws Exception
1718
     *
1719
     * @return array
1720
     */
1721
    private function getInvalidAgreements($basket, Request $request)
1722
    {
1723
        $errors = [];
1724
1725
        if (!$this->container->get('config')->get('IgnoreAGB') && !$this->Request()->getParam('sAGB')) {
1726
            $errors['agbError'] = true;
1727
        }
1728
1729
        $esdAgreement = $request->getParam('esdAgreementChecked');
1730
        if ($this->container->get('config')->get('showEsdWarning')
1731
            && $this->basketHasEsdArticles($basket)
1732
            && empty($esdAgreement)
1733
        ) {
1734
            $errors['esdError'] = true;
1735
        }
1736
1737
        $serviceChecked = $request->getParam('serviceAgreementChecked');
1738
        if ($this->basketHasServiceArticles($basket) && empty($serviceChecked)) {
1739
            $errors['serviceError'] = true;
1740
        }
1741
1742
        return $errors;
1743
    }
1744
1745
    /**
1746
     * checks if the current user selected an available payment method
1747
     *
1748
     * @param array $currentPayment
1749
     * @param array $payments
1750
     *
1751
     * @return bool
1752
     */
1753
    private function checkPaymentAvailability($currentPayment, $payments)
1754
    {
1755
        foreach ($payments as $availablePayment) {
1756
            if ($availablePayment['id'] === $currentPayment['id']) {
1757
                return true;
1758
            }
1759
        }
1760
1761
        return false;
1762
    }
1763
1764
    /**
1765
     * @param string|array $accessories
1766
     * @param array        $quantities
1767
     */
1768
    private function addAccessories($accessories, $quantities)
1769
    {
1770
        if (is_string($accessories)) {
1771
            $accessories = explode(';', $accessories);
1772
        }
1773
1774
        if (empty($accessories) || !is_array($accessories)) {
1775
            return;
1776
        }
1777
1778
        foreach ($accessories as $key => $accessory) {
1779
            try {
1780
                $quantity = 1;
1781
                if (!empty($quantities[$key])) {
1782
                    $quantity = (int) $quantities[$key];
1783
                }
1784
1785
                $this->basket->sAddArticle($accessory, $quantity);
1786
            } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1787
            }
1788
        }
1789
    }
1790
1791
    /**
1792
     * Helper function that checks whether or not the given basket has an esd article in it.
1793
     *
1794
     * @param array $basket
1795
     *
1796
     * @return bool
1797
     */
1798
    private function basketHasEsdArticles($basket)
1799
    {
1800
        if (!isset($basket['content'])) {
1801
            return false;
1802
        }
1803
1804
        foreach ($basket['content'] as $article) {
1805
            if ($article['esd']) {
1806
                return true;
1807
            }
1808
        }
1809
1810
        return false;
1811
    }
1812
1813
    /**
1814
     * Helper function that iterates through the basket articles.
1815
     * It checks if an article is a service article by comparing its attributes
1816
     * with the plugin config serviceAttrField value.
1817
     *
1818
     * @param array $basket
1819
     *
1820
     * @return bool
1821
     */
1822
    private function basketHasServiceArticles($basket)
1823
    {
1824
        $config = Shopware()->Config();
1825
1826
        if (!$config->offsetExists('serviceAttrField')) {
1827
            return false;
1828
        }
1829
1830
        $attrName = $config->serviceAttrField;
0 ignored issues
show
Documentation introduced by
The property serviceAttrField does not exist on object<Shopware_Components_Config>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
1831
        if (empty($attrName) || !isset($basket['content'])) {
1832
            return false;
1833
        }
1834
1835
        foreach ($basket['content'] as $article) {
1836
            $serviceAttr = $article['additional_details'][$attrName];
1837
1838
            if ($serviceAttr && $serviceAttr != 'false') {
1839
                return true;
1840
            }
1841
        }
1842
1843
        return false;
1844
    }
1845
1846
    /**
1847
     * Helper function that iterates through the basket articles.
1848
     * If checks if the basket has a normal article e.g. not an esd article
1849
     * and not a article with the service attribute is set to true.
1850
     *
1851
     * @param array $basket
1852
     *
1853
     * @return bool
1854
     */
1855
    private function basketHasMixedArticles($basket)
1856
    {
1857
        $config = Shopware()->Config();
1858
        $attrName = $config->serviceAttrField;
0 ignored issues
show
Documentation introduced by
The property serviceAttrField does not exist on object<Shopware_Components_Config>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
1859
1860
        if (!isset($basket['content'])) {
1861
            return false;
1862
        }
1863
1864
        foreach ($basket['content'] as $article) {
1865
            if ($article['modus'] == 4 || $article['esd']) {
1866
                continue;
1867
            }
1868
1869
            $serviceAttr = $article['additional_details'][$attrName];
1870
            if (empty($attrName) || ($serviceAttr && $serviceAttr != 'false')) {
1871
                continue;
1872
            }
1873
1874
            return true;
1875
        }
1876
1877
        return false;
1878
    }
1879
1880
    /**
1881
     * Handles payment method validation fail on checkout
1882
     * Redirects the user to the payment edit page
1883
     */
1884
    private function onPaymentMethodValidationFail()
1885
    {
1886
        $target = ['controller' => 'checkout', 'action' => 'shippingPayment'];
1887
        $this->redirect($target);
1888
    }
1889
1890
    /**
1891
     * Selects the default payment method defined in the backend. If no payment method is defined,
1892
     * the first payment method of the provided list will be returned.
1893
     *
1894
     * @param array $paymentMethods
1895
     *
1896
     * @return array
1897
     */
1898
    private function getDefaultPaymentMethod(array $paymentMethods)
1899
    {
1900
        $payment = null;
1901
1902
        foreach ($paymentMethods as $paymentMethod) {
1903
            if ($paymentMethod['id'] == Shopware()->Config()->offsetGet('defaultpayment')) {
1904
                $payment = $paymentMethod;
1905
                break;
1906
            }
1907
        }
1908
1909
        if (!$payment) {
1910
            $payment = reset($paymentMethods);
1911
        }
1912
1913
        return $payment;
1914
    }
1915
1916
    /**
1917
     * Resets the temporary session address ids back to default
1918
     */
1919
    private function resetTemporaryAddresses()
1920
    {
1921
        $this->session->offsetUnset('checkoutBillingAddressId');
1922
        $this->session->offsetUnset('checkoutShippingAddressId');
1923
    }
1924
1925
    /**
1926
     * Sets the default addresses for the user if he decided to use the temporary addresses as new default
1927
     */
1928
    private function saveDefaultAddresses()
1929
    {
1930
        $billingId = $this->session->offsetGet('checkoutBillingAddressId', false);
1931
        $shippingId = $this->session->offsetGet('checkoutShippingAddressId', false);
1932
        $setBoth = $this->Request()->getPost('setAsDefaultAddress', false);
1933
1934
        if (!$this->Request()->getPost('setAsDefaultBillingAddress') && !$setBoth) {
1935
            $billingId = false;
1936
        }
1937
1938
        if (!$this->Request()->getPost('setAsDefaultShippingAddress') && !$setBoth) {
1939
            $shippingId = false;
1940
        }
1941
1942
        if ($billingId && $billingId != $this->View()->sUserData['additional']['user']['default_billing_address_id']) {
1943
            $address = $this->get('models')
1944
                ->getRepository(Address::class)
1945
                ->getOneByUser(
1946
                    $billingId,
1947
                    $this->View()->sUserData['additional']['user']['id']
1948
                );
1949
1950
            $this->get('shopware_account.address_service')->setDefaultBillingAddress($address);
1951
        }
1952
1953
        if ($shippingId && $shippingId != $this->View()->sUserData['additional']['user']['default_shipping_address_id']) {
1954
            $address = $this->get('models')
1955
                ->getRepository(Address::class)
1956
                ->getOneByUser(
1957
                    $shippingId,
1958
                    $this->View()->sUserData['additional']['user']['id']
1959
                );
1960
1961
            $this->get('shopware_account.address_service')->setDefaultShippingAddress($address);
1962
        }
1963
    }
1964
1965
    /**
1966
     * Validates the given address id with current shop configuration
1967
     *
1968
     * @param int $addressId
1969
     *
1970
     * @return bool
1971
     */
1972
    private function isValidAddress($addressId)
1973
    {
1974
        $address = $this->get('models')->find(Address::class, $addressId);
1975
1976
        return $this->get('shopware_account.address_validator')->isValid($address);
1977
    }
1978
1979
    /**
1980
     * @param int    $orderNumber
1981
     * @param string $source
1982
     *
1983
     * @return array
1984
     */
1985
    private function getOrderAddress($orderNumber, $source)
1986
    {
1987
        /** @var \Doctrine\DBAL\Query\QueryBuilder $builder */
1988
        $builder = $this->get('dbal_connection')->createQueryBuilder();
1989
        $context = $this->get('shopware_storefront.context_service')->getShopContext();
1990
1991
        $sourceTable = $source === 'billing' ? 's_order_billingaddress' : 's_order_shippingaddress';
1992
1993
        $address = $builder->select(['address.*'])
1994
            ->from($sourceTable, 'address')
1995
            ->join('address', 's_order', '', 'address.orderID = s_order.id AND s_order.ordernumber = :orderNumber')
1996
            ->setParameter('orderNumber', $orderNumber)
1997
            ->execute()
1998
            ->fetch();
1999
2000
        $countryStruct = $this->get('shopware_storefront.country_gateway')->getCountry($address['countryID'], $context);
2001
        $stateStruct = $this->get('shopware_storefront.country_gateway')->getState($address['stateID'], $context);
2002
2003
        $address['country'] = json_decode(json_encode($countryStruct), true);
2004
        $address['state'] = json_decode(json_encode($stateStruct), true);
2005
        $address['attribute'] = $this->get('shopware_attribute.data_loader')->load($sourceTable . '_attributes', $address['id']);
2006
2007
        return $address;
2008
    }
2009
2010
    /**
2011
     * @param array $addressA
2012
     * @param array $addressB
2013
     *
2014
     * @return bool
2015
     */
2016
    private function areAddressesEqual(array $addressA, array $addressB)
2017
    {
2018
        $unset = ['id', 'customernumber', 'phone', 'ustid'];
2019
        foreach ($unset as $key) {
2020
            unset($addressA[$key], $addressB[$key]);
2021
        }
2022
2023
        return count(array_diff($addressA, $addressB)) === 0;
2024
    }
2025
2026
    /**
2027
     * @return string
2028
     */
2029
    private function persistBasket()
2030
    {
2031
        /** @var BasketSignatureGeneratorInterface $generator */
2032
        $generator = $this->get('basket_signature_generator');
2033
        $basket = $this->session->offsetGet('sOrderVariables')->getArrayCopy();
2034
        $signature = $generator->generateSignature($basket['sBasket'], $this->session->get('sUserId'));
2035
2036
        /** @var BasketPersister $persister */
2037
        $persister = $this->get('basket_persister');
2038
        $persister->persist($signature, $basket);
2039
2040
        return $signature;
2041
    }
2042
2043
    /**
2044
     * Updates all currency dependies (e.g. in the shop model or in the shop context).
2045
     *
2046
     * @param int $currencyId
2047
     */
2048
    private function updateCurrencyDependencies($currencyId)
2049
    {
2050
        /** @var \Shopware\Models\Shop\Currency $currencyModel */
2051
        $currencyModel = $this->get('models')->find(\Shopware\Models\Shop\Currency::class, $currencyId);
2052
2053
        /** @var Shopware\Models\Shop\Shop $shopModel */
2054
        $shopModel = $this->get('shop');
2055
        $shopModel->setCurrency($currencyModel);
2056
2057
        /** @var $currency \Zend_Currency */
2058
        $currency = $this->get('Currency');
2059
        $currency->setFormat($currencyModel->toArray());
2060
2061
        $this->get('shopware_storefront.context_service')->initializeShopContext();
2062
    }
2063
}
2064