|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* (c) shopware AG <[email protected]> |
|
4
|
|
|
* For the full copyright and license information, please view the LICENSE |
|
5
|
|
|
* file that was distributed with this source code. |
|
6
|
|
|
*/ |
|
7
|
|
|
|
|
8
|
|
|
namespace ShopwarePlugins\Connect\Components; |
|
9
|
|
|
|
|
10
|
|
|
use Shopware\Connect\Gateway\PDO; |
|
11
|
|
|
use Shopware\Connect\Struct\CheckResult; |
|
12
|
|
|
use Shopware\Connect\SDK; |
|
13
|
|
|
use Shopware\Connect\Struct\Message; |
|
14
|
|
|
use Shopware\Connect\Struct\Product; |
|
15
|
|
|
|
|
16
|
|
|
/** |
|
17
|
|
|
* Handles the basket manipulation. Most of it is done by modifying the template variables shown to the user. |
|
18
|
|
|
* Once we have new basket and order core classes, this should be refactored. |
|
19
|
|
|
* |
|
20
|
|
|
* Class BasketHelper |
|
21
|
|
|
* @package ShopwarePlugins\Connect\Components |
|
22
|
|
|
*/ |
|
23
|
|
|
class BasketHelper |
|
24
|
|
|
{ |
|
25
|
|
|
const REMOTE_SHIPPING = 'remote'; |
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* The basket array decorated by this class |
|
29
|
|
|
* @var array |
|
30
|
|
|
*/ |
|
31
|
|
|
protected $basket; |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* Array of connect product structs |
|
35
|
|
|
* @var array |
|
36
|
|
|
*/ |
|
37
|
|
|
protected $connectProducts = []; |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* connect content as formated by shopware |
|
41
|
|
|
* |
|
42
|
|
|
* @var array |
|
43
|
|
|
*/ |
|
44
|
|
|
protected $connectContent = []; |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* Array of connect shops affected by this basket |
|
48
|
|
|
* |
|
49
|
|
|
* @var array |
|
50
|
|
|
*/ |
|
51
|
|
|
protected $connectShops = []; |
|
52
|
|
|
|
|
53
|
|
|
/** |
|
54
|
|
|
* @var \Shopware\Connect\Struct\CheckResult |
|
55
|
|
|
*/ |
|
56
|
|
|
protected $checkResult; |
|
57
|
|
|
|
|
58
|
|
|
/** |
|
59
|
|
|
* The original shopware shipping costs |
|
60
|
|
|
* |
|
61
|
|
|
* @var float |
|
62
|
|
|
*/ |
|
63
|
|
|
protected $originalShippingCosts = 0; |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* Should there be a connect hint in the template |
|
67
|
|
|
* |
|
68
|
|
|
* @var bool |
|
69
|
|
|
*/ |
|
70
|
|
|
protected $showCheckoutShopInfo; |
|
71
|
|
|
|
|
72
|
|
|
/** |
|
73
|
|
|
* @var \Shopware\Connect\SDK |
|
74
|
|
|
*/ |
|
75
|
|
|
protected $sdk; |
|
76
|
|
|
|
|
77
|
|
|
/** |
|
78
|
|
|
* @var \Enlight_Components_Db_Adapter_Pdo_Mysql |
|
79
|
|
|
*/ |
|
80
|
|
|
protected $database; |
|
81
|
|
|
|
|
82
|
|
|
/** |
|
83
|
|
|
* @var Helper |
|
84
|
|
|
*/ |
|
85
|
|
|
protected $helper; |
|
86
|
|
|
|
|
87
|
|
|
/** |
|
88
|
|
|
* @var \Shopware\Connect\Gateway\PDO |
|
89
|
|
|
*/ |
|
90
|
|
|
protected $connectGateway; |
|
91
|
|
|
|
|
92
|
|
|
/** |
|
93
|
|
|
* Indicates if the basket has only connect products or not |
|
94
|
|
|
* |
|
95
|
|
|
* @var bool |
|
96
|
|
|
*/ |
|
97
|
|
|
protected $onlyConnectProducts = false; |
|
98
|
|
|
|
|
99
|
|
|
/** |
|
100
|
|
|
* @param \Enlight_Components_Db_Adapter_Pdo_Mysql $database |
|
101
|
|
|
* @param \Shopware\Connect\SDK $sdk |
|
102
|
|
|
* @param Helper $helper |
|
103
|
|
|
* @param $showCheckoutShopInfo |
|
104
|
|
|
*/ |
|
105
|
|
|
public function __construct( |
|
106
|
|
|
\Enlight_Components_Db_Adapter_Pdo_Mysql $database, |
|
107
|
|
|
SDK $sdk, |
|
108
|
|
|
Helper $helper, |
|
109
|
|
|
PDO $connectGateway, |
|
110
|
|
|
$showCheckoutShopInfo) |
|
111
|
|
|
{ |
|
112
|
|
|
$this->database = $database; |
|
113
|
|
|
$this->sdk = $sdk; |
|
114
|
|
|
$this->helper = $helper; |
|
115
|
|
|
$this->connectGateway = $connectGateway; |
|
116
|
|
|
$this->showCheckoutShopInfo = $showCheckoutShopInfo; |
|
117
|
|
|
} |
|
118
|
|
|
|
|
119
|
|
|
/** |
|
120
|
|
|
* Prepare the basket for connect |
|
121
|
|
|
* |
|
122
|
|
|
* @return void |
|
123
|
|
|
*/ |
|
124
|
|
|
public function prepareBasketForConnect() |
|
125
|
|
|
{ |
|
126
|
|
|
$this->buildProductsArray(); |
|
127
|
|
|
$this->buildShopsArray(); |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
/** |
|
131
|
|
|
* Build array of connect products. This will remove connect products from the 'content' array |
|
132
|
|
|
*/ |
|
133
|
|
|
protected function buildProductsArray() |
|
134
|
|
|
{ |
|
135
|
|
|
$this->connectProducts = []; |
|
136
|
|
|
$this->connectContent = []; |
|
137
|
|
|
|
|
138
|
|
|
$this->basket['contentOrg'] = $this->basket['content']; |
|
139
|
|
|
|
|
140
|
|
|
foreach ($this->basket['content'] as $key => &$row) { |
|
141
|
|
|
if (!empty($row['mode'])) { |
|
142
|
|
|
continue; |
|
143
|
|
|
} |
|
144
|
|
|
|
|
145
|
|
|
$articleDetailId = $row['additional_details']['articleDetailsID']; |
|
146
|
|
|
if ($this->helper->isRemoteArticleDetailDBAL($articleDetailId) === false) { |
|
147
|
|
|
continue; |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
$shopProductId = $this->helper->getShopProductId($articleDetailId); |
|
151
|
|
|
$products = $this->getHelper()->getRemoteProducts([$shopProductId->sourceId], $shopProductId->shopId); |
|
152
|
|
|
if (empty($products)) { |
|
153
|
|
|
continue; |
|
154
|
|
|
} |
|
155
|
|
|
$product = reset($products); |
|
156
|
|
|
if ($product === null || $product->shopId === null) { |
|
157
|
|
|
continue; |
|
158
|
|
|
} |
|
159
|
|
|
$row['connectShopId'] = $product->shopId; |
|
160
|
|
|
$this->connectProducts[$product->shopId][$product->sourceId] = $product; |
|
161
|
|
|
$this->connectContent[$product->shopId][$product->sourceId] = $row; |
|
162
|
|
|
|
|
163
|
|
|
//if($actionName == 'cart') { |
|
|
|
|
|
|
164
|
|
|
unset($this->basket['content'][$key]); |
|
165
|
|
|
//} |
|
166
|
|
|
} |
|
167
|
|
|
} |
|
168
|
|
|
|
|
169
|
|
|
/** |
|
170
|
|
|
* Build array of connect remote shops |
|
171
|
|
|
*/ |
|
172
|
|
|
protected function buildShopsArray() |
|
173
|
|
|
{ |
|
174
|
|
|
$this->connectShops = []; |
|
175
|
|
|
|
|
176
|
|
|
$this->basket['content'] = array_values($this->basket['content']); |
|
177
|
|
|
foreach ($this->connectContent as $shopId => $items) { |
|
178
|
|
|
$this->connectShops[$shopId] = $this->getSdk()->getShop($shopId); |
|
179
|
|
|
} |
|
180
|
|
|
} |
|
181
|
|
|
|
|
182
|
|
|
/** |
|
183
|
|
|
* Returns the quantity of a given product in the sw basket |
|
184
|
|
|
* |
|
185
|
|
|
* @param \Shopware\Connect\Struct\Product $product |
|
186
|
|
|
* @return mixed |
|
187
|
|
|
*/ |
|
188
|
|
|
public function getQuantityForProduct(Product $product) |
|
189
|
|
|
{ |
|
190
|
|
|
if (isset($this->connectContent[$product->shopId]) && |
|
191
|
|
|
isset($this->connectContent[$product->shopId][$product->sourceId]) |
|
192
|
|
|
) { |
|
193
|
|
|
return (int) $this->connectContent[$product->shopId][$product->sourceId]['quantity']; |
|
194
|
|
|
} elseif (isset($this->basket['content'][$product->sourceId])) { |
|
195
|
|
|
return (int) $this->basket['content'][$product->sourceId]['quantity']; |
|
196
|
|
|
} |
|
197
|
|
|
|
|
198
|
|
|
return 1; |
|
199
|
|
|
} |
|
200
|
|
|
|
|
201
|
|
|
/** |
|
202
|
|
|
* This method will check, if any *real* products from the local shop are in the basket. If this is not the |
|
203
|
|
|
* case, this method will: |
|
204
|
|
|
* |
|
205
|
|
|
* - set the first connect shop as content of the default basket ($basket['content']) |
|
206
|
|
|
* - remove any surcharges, vouchers and discount from the original basket(!) |
|
207
|
|
|
* |
|
208
|
|
|
* @return bool|mixed |
|
209
|
|
|
*/ |
|
210
|
|
|
public function fixBasket() |
|
211
|
|
|
{ |
|
212
|
|
|
// Filter out basket items which cannot be purchased on their own |
|
213
|
|
|
$content = array_filter($this->basket['content'], function ($item) { |
|
214
|
|
|
switch ((int) $item['modus']) { |
|
215
|
|
|
case 0: // Default products |
|
216
|
|
|
case 1: // Premium products |
|
217
|
|
|
return true; |
|
218
|
|
|
default: |
|
219
|
|
|
return false; |
|
220
|
|
|
} |
|
221
|
|
|
}); |
|
222
|
|
|
|
|
223
|
|
|
// If only connect products are in the basket, do the basket fix |
|
224
|
|
|
if (empty($content)) { |
|
225
|
|
|
$this->onlyConnectProducts = true; |
|
226
|
|
|
|
|
227
|
|
|
$this->removeNonProductsFromBasket(); |
|
228
|
|
|
|
|
229
|
|
|
$connectContent = $this->getConnectContent(); |
|
230
|
|
|
if ($this->customerHasToPayLocalShipping($connectContent) === false) { |
|
231
|
|
|
$this->basket = $this->removeDefaultShipping($this->basket); |
|
232
|
|
|
} |
|
233
|
|
|
|
|
234
|
|
|
// Make the first connect shop the default basket-content |
|
235
|
|
|
reset($connectContent); |
|
236
|
|
|
$shopId = current(array_keys($connectContent)); |
|
237
|
|
|
$this->basket['content'] = $connectContent[$shopId]; |
|
238
|
|
|
unset($this->connectContent[$shopId]); |
|
239
|
|
|
|
|
240
|
|
|
return $shopId; |
|
241
|
|
|
} |
|
242
|
|
|
|
|
243
|
|
|
return false; |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
/** |
|
247
|
|
|
* Verifies that some of suppliers as shippingCosts type |
|
248
|
|
|
* different from "remote". Then endcustomer must pay |
|
249
|
|
|
* merchant shipping costs |
|
250
|
|
|
* |
|
251
|
|
|
* @param array $connectContent |
|
252
|
|
|
* @return bool |
|
253
|
|
|
*/ |
|
254
|
|
|
private function customerHasToPayLocalShipping(array $connectContent) |
|
255
|
|
|
{ |
|
256
|
|
|
$useLocalShipping = false; |
|
257
|
|
|
|
|
258
|
|
|
foreach (array_keys($connectContent) as $shopId) { |
|
259
|
|
|
$shopConfiguration = $this->connectGateway->getShopConfiguration($shopId); |
|
260
|
|
|
if ($shopConfiguration === null) { |
|
261
|
|
|
continue; |
|
262
|
|
|
} |
|
263
|
|
|
|
|
264
|
|
|
if ($shopConfiguration->importedProductsShippingCostType != self::REMOTE_SHIPPING) { |
|
265
|
|
|
$useLocalShipping = true; |
|
266
|
|
|
break; |
|
267
|
|
|
} |
|
268
|
|
|
} |
|
269
|
|
|
|
|
270
|
|
|
return $useLocalShipping; |
|
271
|
|
|
} |
|
272
|
|
|
|
|
273
|
|
|
/** |
|
274
|
|
|
* Remove shipping costs from given basket |
|
275
|
|
|
* |
|
276
|
|
|
* @param array $basket |
|
277
|
|
|
* @return array |
|
278
|
|
|
*/ |
|
279
|
|
|
private function removeDefaultShipping(array $basket) |
|
280
|
|
|
{ |
|
281
|
|
|
$basket['AmountNumeric'] -= $basket['sShippingcosts']; |
|
282
|
|
|
$basket['AmountNetNumeric'] -= $basket['sShippingcostsNet']; |
|
283
|
|
|
|
|
284
|
|
|
$basketHasTax = $this->hasTax(); |
|
285
|
|
|
if (!empty($this->basket['sAmountWithTax'])) { |
|
286
|
|
|
if ($basketHasTax) { |
|
287
|
|
|
$this->basket['sAmountWithTax'] -= $basket['sShippingcosts']; |
|
288
|
|
|
} else { |
|
289
|
|
|
$this->basket['sAmountWithTax'] -= $basket['sShippingcostsNet']; |
|
290
|
|
|
} |
|
291
|
|
|
} |
|
292
|
|
|
|
|
293
|
|
|
if ($basketHasTax) { |
|
294
|
|
|
$basket['sAmount'] -= $basket['sShippingcosts']; |
|
295
|
|
|
} else { |
|
296
|
|
|
$basket['sAmount'] -= $basket['sShippingcostsNet']; |
|
297
|
|
|
} |
|
298
|
|
|
|
|
299
|
|
|
$basket['sShippingcosts'] = 0; |
|
300
|
|
|
$basket['sShippingcostsNet'] = 0; |
|
301
|
|
|
$basket['sShippingcostsWithTax'] = 0; |
|
302
|
|
|
|
|
303
|
|
|
return $basket; |
|
304
|
|
|
} |
|
305
|
|
|
|
|
306
|
|
|
/** |
|
307
|
|
|
* Removes non-connect products from the database and fixes the basket variables |
|
308
|
|
|
*/ |
|
309
|
|
|
protected function removeNonProductsFromBasket() |
|
310
|
|
|
{ |
|
311
|
|
|
$removeItems = [ |
|
312
|
|
|
'ids' => [], |
|
313
|
|
|
'price' => 0, |
|
314
|
|
|
'netprice' => 0, |
|
315
|
|
|
'sessionId' => null |
|
316
|
|
|
]; |
|
317
|
|
|
|
|
318
|
|
|
// Build array of ids and amount to fix the basket later |
|
319
|
|
|
foreach ($this->basket['content'] as $key => $product) { |
|
320
|
|
|
$removeItems['ids'][] = $product['id']; |
|
321
|
|
|
$removeItems['price'] += $product['price'] * $product['quantity']; |
|
322
|
|
|
$removeItems['amountWithTax'] += $product['amountWithTax'] * $product['quantity']; |
|
323
|
|
|
$removeItems['netprice'] += $product['netprice'] * $product['quantity']; |
|
324
|
|
|
$removeItems['tax'] += str_replace(',', '.', $product['tax']) * $product['quantity']; |
|
325
|
|
|
$removeItems['sessionId'] = $product['sessionID']; |
|
326
|
|
|
|
|
327
|
|
|
// Remove surcharge, cannot be deleted with SQL |
|
328
|
|
|
if ($product['modus'] == 4) { |
|
329
|
|
|
unset($this->basket['content'][$key]); |
|
330
|
|
|
} |
|
331
|
|
|
} |
|
332
|
|
|
|
|
333
|
|
|
if (empty($removeItems['ids'])) { |
|
334
|
|
|
return; |
|
335
|
|
|
} |
|
336
|
|
|
|
|
337
|
|
|
// Fix basket prices |
|
338
|
|
|
$this->basket['AmountNumeric'] -= $removeItems['price']; |
|
339
|
|
|
$this->basket['AmountNetNumeric'] -= $removeItems['netprice']; |
|
340
|
|
|
$this->basket['sAmount'] -= $removeItems['price']; |
|
341
|
|
|
$this->basket['Amount'] = str_replace(',', '.', $this->basket['Amount']) - $removeItems['price']; |
|
342
|
|
|
|
|
343
|
|
|
$this->basket['sAmountTax'] -= $removeItems['tax']; |
|
344
|
|
|
if (!empty($this->basket['sAmountWithTax'])) { |
|
345
|
|
|
if ($this->hasTax()) { |
|
346
|
|
|
$this->basket['sAmountWithTax'] -= $removeItems['price']; |
|
347
|
|
|
} else { |
|
348
|
|
|
$this->basket['sAmountWithTax'] -= $removeItems['amountWithTax']; |
|
349
|
|
|
$this->basket['AmountWithTaxNumeric'] -= $removeItems['amountWithTax']; |
|
350
|
|
|
$this->basket['AmountWithTax'] = $this->basket['AmountWithTaxNumeric']; |
|
351
|
|
|
$this->basket['amountnet'] = $this->basket['amount']; |
|
352
|
|
|
} |
|
353
|
|
|
} |
|
354
|
|
|
|
|
355
|
|
|
// Remove items from basket |
|
356
|
|
|
$this->getDatabase()->query( |
|
357
|
|
|
'DELETE FROM s_order_basket WHERE sessionID = ? and id IN (?)', |
|
358
|
|
|
[ |
|
359
|
|
|
$removeItems['sessionId'], |
|
360
|
|
|
implode(',', $removeItems['ids']) |
|
361
|
|
|
] |
|
362
|
|
|
); |
|
363
|
|
|
|
|
364
|
|
|
// Filter out basket items - surcharge |
|
365
|
|
|
$this->basket['contentOrg'] = array_filter($this->basket['contentOrg'], function ($item) { |
|
366
|
|
|
switch ((int) $item['modus']) { |
|
367
|
|
|
case 4: // Surcharge |
|
368
|
|
|
return false; |
|
369
|
|
|
default: |
|
370
|
|
|
return true; |
|
371
|
|
|
} |
|
372
|
|
|
}); |
|
373
|
|
|
} |
|
374
|
|
|
|
|
375
|
|
|
/** |
|
376
|
|
|
* @todo: This function is basically a copy of the same function in Controllers/Frontend/Checkout. |
|
377
|
|
|
* As that function cannot be called, I copied it for the time being - this should be refactored |
|
378
|
|
|
* |
|
379
|
|
|
* @param $basket array returned from this->getBasket |
|
380
|
|
|
* @return array |
|
381
|
|
|
*/ |
|
382
|
|
|
public function getTaxRates($basket) |
|
383
|
|
|
{ |
|
384
|
|
|
$result = []; |
|
385
|
|
|
|
|
386
|
|
|
// The original method also calculates the tax rates of the shipping costs - this |
|
387
|
|
|
// is done in a separate methode here |
|
388
|
|
|
|
|
389
|
|
|
if (empty($basket['content'])) { |
|
390
|
|
|
ksort($result, SORT_NUMERIC); |
|
391
|
|
|
|
|
392
|
|
|
return $result; |
|
393
|
|
|
} |
|
394
|
|
|
|
|
395
|
|
|
foreach ($basket['content'] as $item) { |
|
396
|
|
|
if (!empty($item['tax_rate'])) { |
|
|
|
|
|
|
397
|
|
|
} elseif (!empty($item['taxPercent'])) { |
|
398
|
|
|
$item['tax_rate'] = $item['taxPercent']; |
|
399
|
|
|
} elseif ($item['modus'] == 2) { |
|
400
|
|
|
// Ticket 4842 - dynamic tax-rates |
|
401
|
|
|
$resultVoucherTaxMode = Shopware()->Db()->fetchOne( |
|
402
|
|
|
'SELECT taxconfig FROM s_emarketing_vouchers WHERE ordercode=? |
|
403
|
|
|
', [$item['ordernumber']]); |
|
404
|
|
|
// Old behaviour |
|
405
|
|
|
if (empty($resultVoucherTaxMode) || $resultVoucherTaxMode == 'default') { |
|
406
|
|
|
$tax = Shopware()->Config()->get('sVOUCHERTAX'); |
|
407
|
|
|
} elseif ($resultVoucherTaxMode == 'auto') { |
|
408
|
|
|
// Automatically determinate tax |
|
409
|
|
|
$tax = Shopware()->Modules()->Basket()->getMaxTax(); |
|
410
|
|
|
} elseif ($resultVoucherTaxMode == 'none') { |
|
411
|
|
|
// No tax |
|
412
|
|
|
$tax = '0'; |
|
413
|
|
|
} elseif (intval($resultVoucherTaxMode)) { |
|
414
|
|
|
// Fix defined tax |
|
415
|
|
|
$tax = Shopware()->Db()->fetchOne(' |
|
416
|
|
|
SELECT tax FROM s_core_tax WHERE id = ? |
|
417
|
|
|
', [$resultVoucherTaxMode]); |
|
418
|
|
|
} |
|
419
|
|
|
$item['tax_rate'] = $tax; |
|
|
|
|
|
|
420
|
|
|
} else { |
|
421
|
|
|
// Ticket 4842 - dynamic tax-rates |
|
422
|
|
|
$taxAutoMode = Shopware()->Config()->get('sTAXAUTOMODE'); |
|
423
|
|
|
if (!empty($taxAutoMode)) { |
|
424
|
|
|
$tax = Shopware()->Modules()->Basket()->getMaxTax(); |
|
425
|
|
|
} else { |
|
426
|
|
|
$tax = Shopware()->Config()->get('sDISCOUNTTAX'); |
|
427
|
|
|
} |
|
428
|
|
|
$item['tax_rate'] = $tax; |
|
429
|
|
|
} |
|
430
|
|
|
|
|
431
|
|
|
// Ignore 0 % tax |
|
432
|
|
|
if (empty($item['tax_rate']) || empty($item['tax'])) { |
|
433
|
|
|
continue; |
|
434
|
|
|
} |
|
435
|
|
|
$taxKey = number_format(floatval($item['tax_rate']), 2); |
|
436
|
|
|
$result[$taxKey] += str_replace(',', '.', $item['tax']); |
|
437
|
|
|
} |
|
438
|
|
|
|
|
439
|
|
|
ksort($result, SORT_NUMERIC); |
|
440
|
|
|
|
|
441
|
|
|
return $result; |
|
442
|
|
|
} |
|
443
|
|
|
|
|
444
|
|
|
/** |
|
445
|
|
|
* Returns an array of tax positions in the same way, as shopware does in the sTaxRates. |
|
446
|
|
|
* This will only take connect products returned by getConnectContent() into account, |
|
447
|
|
|
* so that connect positions moved into basket['content'] earlier are not calculated twice. |
|
448
|
|
|
* |
|
449
|
|
|
* @return array |
|
450
|
|
|
*/ |
|
451
|
|
|
public function getConnectTaxRates() |
|
452
|
|
|
{ |
|
453
|
|
|
$taxes = []; |
|
454
|
|
|
|
|
455
|
|
|
foreach ($this->getConnectContent() as $shopId => $products) { |
|
456
|
|
|
foreach ($products as $product) { |
|
457
|
|
|
$vat = (string) number_format($product['tax_rate'], 2); |
|
458
|
|
|
if (!isset($taxes[$vat])) { |
|
459
|
|
|
$taxes[$vat] = 0; |
|
460
|
|
|
} |
|
461
|
|
|
|
|
462
|
|
|
if ($this->hasTax()) { |
|
463
|
|
|
$taxes[$vat] += $product['priceNumeric'] - $product['netprice']; |
|
464
|
|
|
} else { |
|
465
|
|
|
$taxes[$vat] += $product['amountWithTax'] - ($product['netprice'] * $product['quantity']); |
|
466
|
|
|
} |
|
467
|
|
|
} |
|
468
|
|
|
} |
|
469
|
|
|
|
|
470
|
|
|
return $taxes; |
|
471
|
|
|
} |
|
472
|
|
|
|
|
473
|
|
|
/** |
|
474
|
|
|
* Will merge/add various arrays of "sTaxRates" like arrays into one array |
|
475
|
|
|
* |
|
476
|
|
|
* @param array $taxRates |
|
477
|
|
|
* @return array |
|
478
|
|
|
*/ |
|
479
|
|
|
public function getMergedTaxRates(array $taxRates) |
|
480
|
|
|
{ |
|
481
|
|
|
$result = []; |
|
482
|
|
|
|
|
483
|
|
|
foreach ($taxRates as $taxRate) { |
|
484
|
|
|
foreach ($taxRate as $vat => $amount) { |
|
485
|
|
|
if (!isset($result[$vat])) { |
|
486
|
|
|
$result[$vat] = 0; |
|
487
|
|
|
} |
|
488
|
|
|
$result[$vat] += $amount; |
|
489
|
|
|
} |
|
490
|
|
|
} |
|
491
|
|
|
|
|
492
|
|
|
return $result; |
|
493
|
|
|
} |
|
494
|
|
|
|
|
495
|
|
|
/** |
|
496
|
|
|
* Increase the basket's shipping costs and amount by the total value of connect shipping costs |
|
497
|
|
|
* |
|
498
|
|
|
* @param \Shopware\Connect\Struct\CheckResult $checkResult |
|
499
|
|
|
*/ |
|
500
|
|
|
public function recalculate(CheckResult $checkResult) |
|
501
|
|
|
{ |
|
502
|
|
|
$this->checkResult = $checkResult; |
|
503
|
|
|
$this->basket['sAmount'] = number_format($this->basket['sAmount'], 2, '.', ''); |
|
504
|
|
|
|
|
505
|
|
|
$shippingCostsNet = 0; |
|
506
|
|
|
$shippingCostsWithTax = 0; |
|
507
|
|
|
|
|
508
|
|
|
/** @var \Shopware\Connect\Struct\Shipping $shipping */ |
|
509
|
|
|
foreach ($this->checkResult->shippingCosts as $shipping) { |
|
510
|
|
|
$shopConfiguration = $this->connectGateway->getShopConfiguration($shipping->shopId); |
|
511
|
|
|
if ($shopConfiguration->importedProductsShippingCostType == self::REMOTE_SHIPPING) { |
|
512
|
|
|
$shippingCostsNet += $shipping->shippingCosts; |
|
513
|
|
|
$shippingCostsWithTax += $shipping->grossShippingCosts; |
|
514
|
|
|
} |
|
515
|
|
|
} |
|
516
|
|
|
$shippingCostsNet = number_format($shippingCostsNet, 2, '.', ''); |
|
517
|
|
|
$shippingCostsWithTax = number_format($shippingCostsWithTax, 2, '.', ''); |
|
518
|
|
|
|
|
519
|
|
|
$basketHasTax = $this->hasTax(); |
|
520
|
|
|
|
|
521
|
|
|
// Set the shipping cost tax rate for shopware |
|
522
|
|
|
|
|
523
|
|
|
$this->setOriginalShippingCosts($this->basket['sShippingcosts']); |
|
524
|
|
|
|
|
525
|
|
|
// Update shipping costs |
|
526
|
|
|
if ($basketHasTax) { |
|
527
|
|
|
$this->basket['sShippingcosts'] += $shippingCostsWithTax; |
|
528
|
|
|
} else { |
|
529
|
|
|
$this->basket['sShippingcosts'] += $shippingCostsNet; |
|
530
|
|
|
} |
|
531
|
|
|
$this->basket['sShippingcostsNet'] += $shippingCostsNet; |
|
532
|
|
|
$this->basket['sShippingcostsWithTax'] += $shippingCostsWithTax; |
|
533
|
|
|
|
|
534
|
|
|
$this->basket['AmountNetNumeric'] += $shippingCostsNet; |
|
535
|
|
|
|
|
536
|
|
|
if (!empty($this->basket['sAmountWithTax'])) { |
|
537
|
|
|
if ($basketHasTax) { |
|
538
|
|
|
$this->basket['sAmountWithTax'] += $this->basket['sShippingcostsWithTax']; |
|
539
|
|
|
} else { |
|
540
|
|
|
$this->basket['sAmountWithTax'] += $shippingCostsWithTax; |
|
541
|
|
|
} |
|
542
|
|
|
} |
|
543
|
|
|
|
|
544
|
|
|
if ($basketHasTax) { |
|
545
|
|
|
$this->basket['sAmount'] += $shippingCostsWithTax; |
|
546
|
|
|
} else { |
|
547
|
|
|
$this->basket['sAmount'] += $shippingCostsNet; |
|
548
|
|
|
} |
|
549
|
|
|
|
|
550
|
|
|
$this->basket['sAmountTax'] += $this->basket['sShippingcostsWithTax'] - $shippingCostsNet; |
|
551
|
|
|
|
|
552
|
|
|
// Core workaround: Shopware tries to re-calculate the shipping tax rate from the net price |
|
553
|
|
|
// \Shopware_Models_Document_Order::processOrder |
|
554
|
|
|
// Therefore we need to round the net price |
|
555
|
|
|
$this->basket['sShippingcostsNet'] = round($this->basket['sShippingcostsNet'], 2); |
|
556
|
|
|
|
|
557
|
|
|
|
|
558
|
|
|
// Recalculate the tax rates |
|
559
|
|
|
$this->basket['sTaxRates'] = $this->getMergedTaxRates( |
|
560
|
|
|
[ |
|
561
|
|
|
$this->getTaxRates($this->basket), |
|
562
|
|
|
$this->getConnectTaxRates(), |
|
563
|
|
|
$this->getShippingCostsTaxRates() |
|
564
|
|
|
] |
|
565
|
|
|
); |
|
566
|
|
|
|
|
567
|
|
|
//@todo:stefan Check for better solution |
|
568
|
|
|
$this->basket['AmountWithTaxNumeric'] = $this->basket['sAmountWithTax']; |
|
569
|
|
|
$this->basket['AmountNumeric'] = $this->basket['sAmount']; |
|
570
|
|
|
} |
|
571
|
|
|
|
|
572
|
|
|
/** |
|
573
|
|
|
* Returns the tax rate of the shipping costs and also sets the the net shipping cost amount(!) |
|
574
|
|
|
*/ |
|
575
|
|
|
public function getShippingCostsTaxRates() |
|
576
|
|
|
{ |
|
577
|
|
|
$taxAmount = $this->basket['sShippingcostsWithTax'] - $this->basket['sShippingcostsNet']; |
|
578
|
|
|
|
|
579
|
|
|
$taxRate = number_format($this->getMaxTaxRate(), 2, '.', ''); |
|
580
|
|
|
$this->basket['sShippingcostsNet'] = $this->basket['sShippingcostsWithTax'] / (($taxRate/100)+1); |
|
581
|
|
|
|
|
582
|
|
|
return [ |
|
583
|
|
|
(string) $taxRate => $taxAmount |
|
584
|
|
|
]; |
|
585
|
|
|
} |
|
586
|
|
|
|
|
587
|
|
|
/** |
|
588
|
|
|
* Get the highest tax rate from basket - currently only this is supported by SW |
|
589
|
|
|
* |
|
590
|
|
|
* @return int |
|
591
|
|
|
*/ |
|
592
|
|
|
public function getMaxTaxRate() |
|
593
|
|
|
{ |
|
594
|
|
|
$taxRate = 0; |
|
595
|
|
|
foreach ($this->getConnectContent() as $shopId => $products) { |
|
596
|
|
|
foreach ($products as $product) { |
|
597
|
|
|
if ($product['tax_rate'] > $taxRate) { |
|
598
|
|
|
$taxRate = $product['tax_rate']; |
|
599
|
|
|
} |
|
600
|
|
|
} |
|
601
|
|
|
} |
|
602
|
|
|
|
|
603
|
|
|
foreach ($this->basket['content'] as $product) { |
|
604
|
|
|
if ($product['tax_rate'] > $taxRate) { |
|
605
|
|
|
$taxRate = $product['tax_rate']; |
|
606
|
|
|
} |
|
607
|
|
|
} |
|
608
|
|
|
|
|
609
|
|
|
return $taxRate; |
|
610
|
|
|
} |
|
611
|
|
|
|
|
612
|
|
|
/** |
|
613
|
|
|
* Return array of variables which need to be available in the default template |
|
614
|
|
|
* |
|
615
|
|
|
* @return array |
|
616
|
|
|
*/ |
|
617
|
|
|
public function getDefaultTemplateVariables() |
|
618
|
|
|
{ |
|
619
|
|
|
return [ |
|
620
|
|
|
'sBasket' => $this->basket, |
|
621
|
|
|
'sShippingcosts' => $this->basket['sShippingcosts'], |
|
622
|
|
|
'sAmount' => $this->basket['sAmount'], |
|
623
|
|
|
'sAmountWithTax' => $this->basket['sAmountWithTax'], |
|
624
|
|
|
'sAmountNet' => $this->basket['AmountNetNumeric'] |
|
625
|
|
|
]; |
|
626
|
|
|
} |
|
627
|
|
|
|
|
628
|
|
|
/** |
|
629
|
|
|
* Return array of connect specific template variables |
|
630
|
|
|
* |
|
631
|
|
|
* @param $connectMessages array Messages to show |
|
632
|
|
|
* @return array |
|
633
|
|
|
*/ |
|
634
|
|
|
public function getConnectTemplateVariables(array $connectMessages) |
|
635
|
|
|
{ |
|
636
|
|
|
$snippets = Shopware()->Snippets()->getNamespace('frontend/checkout/error_messages'); |
|
637
|
|
|
/** @var Message $message */ |
|
638
|
|
|
foreach ($connectMessages as $message) { |
|
639
|
|
|
if ($message->message == 'Availability of product %product changed to %availability.') { |
|
640
|
|
|
if ($message->values['availability'] == 0) { |
|
641
|
|
|
$message->message = $snippets->get( |
|
642
|
|
|
'connect_product_out_of_stock_message', |
|
643
|
|
|
'Produkte in Ihrer Bestellung sind aktuell nicht lieferbar, bitte entfernen Sie die Produkte um fortzufahren.' |
|
644
|
|
|
); |
|
645
|
|
|
} else { |
|
646
|
|
|
$message->message = $snippets->get( |
|
647
|
|
|
'connect_product_lower_stock_message', |
|
648
|
|
|
'Der Lagerbestand von Produkt "%product" hat sich auf %availability geändert' |
|
649
|
|
|
); |
|
650
|
|
|
} |
|
651
|
|
|
} |
|
652
|
|
|
} |
|
653
|
|
|
|
|
654
|
|
|
return [ |
|
655
|
|
|
'connectContent' => $this->getConnectContent(), |
|
656
|
|
|
'connectShops' => $this->getConnectShops(), |
|
657
|
|
|
'connectMessages' => $connectMessages, |
|
658
|
|
|
'connectShippingCosts' => $this->getConnectGrossShippingCosts(), |
|
659
|
|
|
'connectShippingCostsOrg' => $this->getOriginalShippingCosts(), |
|
660
|
|
|
'connectShopInfo' => $this->showCheckoutShopInfo, |
|
661
|
|
|
'addBaseShop' => $this->onlyConnectProducts ? 0 : 1, |
|
662
|
|
|
]; |
|
663
|
|
|
} |
|
664
|
|
|
|
|
665
|
|
|
/** |
|
666
|
|
|
* Modifies a given OrderVariables ArrayObject |
|
667
|
|
|
* |
|
668
|
|
|
* @param $variables \ArrayObject |
|
669
|
|
|
* @return \ArrayObject |
|
670
|
|
|
*/ |
|
671
|
|
|
public function getOrderVariablesForSession($variables) |
|
672
|
|
|
{ |
|
673
|
|
|
// Get a copy of the basket array in order to not mess up the state of the basket array |
|
674
|
|
|
$basket = $this->basket; |
|
675
|
|
|
$newVariables = $this->getDefaultTemplateVariables(); |
|
676
|
|
|
|
|
677
|
|
|
// We need the manipulated content as the order is created from the session |
|
678
|
|
|
$basket['content'] = $basket['contentOrg']; |
|
679
|
|
|
unset($basket['contentOrg']); |
|
680
|
|
|
|
|
681
|
|
|
|
|
682
|
|
|
// Replace the original session array with the new one |
|
683
|
|
|
$variables->exchangeArray(array_merge( |
|
684
|
|
|
$variables->getArrayCopy(), $newVariables, ['sBasket' => $basket] |
|
685
|
|
|
)); |
|
686
|
|
|
|
|
687
|
|
|
return $variables; |
|
688
|
|
|
} |
|
689
|
|
|
|
|
690
|
|
|
/** |
|
691
|
|
|
* Find all percentaged vouchers for a given individual code |
|
692
|
|
|
* |
|
693
|
|
|
* @param $voucherCode |
|
694
|
|
|
* @return mixed |
|
695
|
|
|
*/ |
|
696
|
|
View Code Duplication |
public function findPercentagedIndividualVouchers($voucherCode) |
|
|
|
|
|
|
697
|
|
|
{ |
|
698
|
|
|
$builder = Shopware()->Models()->createQueryBuilder(); |
|
699
|
|
|
|
|
700
|
|
|
$builder->select('voucher') |
|
701
|
|
|
->from('Shopware\Models\Voucher\Voucher', 'voucher') |
|
702
|
|
|
->innerJoin('voucher.codes', 'codes', 'WITH', 'codes.code LIKE :voucherCode') |
|
703
|
|
|
->where('voucher.percental = true') |
|
704
|
|
|
->setParameter('voucherCode', $voucherCode); |
|
705
|
|
|
|
|
706
|
|
|
return $builder->getQuery()->getResult(); |
|
707
|
|
|
} |
|
708
|
|
|
|
|
709
|
|
|
/** |
|
710
|
|
|
* Find all vouchers matching the code |
|
711
|
|
|
* |
|
712
|
|
|
* @param $voucherCode |
|
713
|
|
|
* @return mixed |
|
714
|
|
|
*/ |
|
715
|
|
View Code Duplication |
public function findPercentagedVouchers($voucherCode) |
|
|
|
|
|
|
716
|
|
|
{ |
|
717
|
|
|
$builder = Shopware()->Models()->createQueryBuilder(); |
|
718
|
|
|
|
|
719
|
|
|
$builder->select('voucher') |
|
720
|
|
|
->from('Shopware\Models\Voucher\Voucher', 'voucher') |
|
721
|
|
|
->where('voucher.voucherCode LIKE :voucherCode') |
|
722
|
|
|
->andWhere('voucher.percental = true') |
|
723
|
|
|
->setParameter('voucherCode', $voucherCode); |
|
724
|
|
|
|
|
725
|
|
|
return $builder->getQuery()->getResult(); |
|
726
|
|
|
} |
|
727
|
|
|
|
|
728
|
|
|
/** |
|
729
|
|
|
* @return \Shopware\Connect\SDk |
|
730
|
|
|
*/ |
|
731
|
|
|
public function getSdk() |
|
732
|
|
|
{ |
|
733
|
|
|
return $this->sdk; |
|
734
|
|
|
} |
|
735
|
|
|
|
|
736
|
|
|
/** |
|
737
|
|
|
* @return Helper |
|
738
|
|
|
*/ |
|
739
|
|
|
public function getHelper() |
|
740
|
|
|
{ |
|
741
|
|
|
return $this->helper; |
|
742
|
|
|
} |
|
743
|
|
|
|
|
744
|
|
|
/** |
|
745
|
|
|
* @param mixed $basket |
|
746
|
|
|
*/ |
|
747
|
|
|
public function setBasket($basket) |
|
748
|
|
|
{ |
|
749
|
|
|
$this->basket = $basket; |
|
|
|
|
|
|
750
|
|
|
$this->prepareBasketForConnect(); |
|
751
|
|
|
} |
|
752
|
|
|
|
|
753
|
|
|
/** |
|
754
|
|
|
* @return mixed |
|
755
|
|
|
*/ |
|
756
|
|
|
public function getBasket() |
|
757
|
|
|
{ |
|
758
|
|
|
return $this->basket; |
|
759
|
|
|
} |
|
760
|
|
|
|
|
761
|
|
|
/** |
|
762
|
|
|
* @param array $connectContent |
|
763
|
|
|
*/ |
|
764
|
|
|
public function setConnectContent($connectContent) |
|
765
|
|
|
{ |
|
766
|
|
|
$this->connectContent = $connectContent; |
|
767
|
|
|
} |
|
768
|
|
|
|
|
769
|
|
|
/** |
|
770
|
|
|
* @return array |
|
771
|
|
|
*/ |
|
772
|
|
|
public function getConnectContent() |
|
773
|
|
|
{ |
|
774
|
|
|
return $this->connectContent; |
|
775
|
|
|
} |
|
776
|
|
|
|
|
777
|
|
|
/** |
|
778
|
|
|
* @param array $connectProducts |
|
779
|
|
|
*/ |
|
780
|
|
|
public function setConnectProducts($connectProducts) |
|
781
|
|
|
{ |
|
782
|
|
|
$this->connectProducts = $connectProducts; |
|
783
|
|
|
} |
|
784
|
|
|
|
|
785
|
|
|
/** |
|
786
|
|
|
* @return array |
|
787
|
|
|
*/ |
|
788
|
|
|
public function getConnectProducts() |
|
789
|
|
|
{ |
|
790
|
|
|
return $this->connectProducts; |
|
791
|
|
|
} |
|
792
|
|
|
|
|
793
|
|
|
/** |
|
794
|
|
|
* @param array $connectShops |
|
795
|
|
|
*/ |
|
796
|
|
|
public function setConnectShops($connectShops) |
|
797
|
|
|
{ |
|
798
|
|
|
$this->connectShops = $connectShops; |
|
799
|
|
|
} |
|
800
|
|
|
|
|
801
|
|
|
/** |
|
802
|
|
|
* @return array |
|
803
|
|
|
*/ |
|
804
|
|
|
public function getConnectShops() |
|
805
|
|
|
{ |
|
806
|
|
|
return $this->connectShops; |
|
807
|
|
|
} |
|
808
|
|
|
|
|
809
|
|
|
/** |
|
810
|
|
|
* @return array |
|
811
|
|
|
*/ |
|
812
|
|
|
public function getConnectGrossShippingCosts() |
|
813
|
|
|
{ |
|
814
|
|
|
$result = []; |
|
815
|
|
|
if (!$this->checkResult instanceof CheckResult) { |
|
816
|
|
|
return $result; |
|
817
|
|
|
} |
|
818
|
|
|
|
|
819
|
|
|
foreach ($this->checkResult->shippingCosts as $shipping) { |
|
820
|
|
|
if ($this->hasTax()) { |
|
821
|
|
|
$result[$shipping->shopId] = $shipping->grossShippingCosts; |
|
822
|
|
|
} else { |
|
823
|
|
|
$result[$shipping->shopId] = $shipping->shippingCosts; |
|
824
|
|
|
} |
|
825
|
|
|
} |
|
826
|
|
|
|
|
827
|
|
|
return $result; |
|
828
|
|
|
} |
|
829
|
|
|
|
|
830
|
|
|
/** |
|
831
|
|
|
* @param mixed $originalShippingCosts |
|
832
|
|
|
*/ |
|
833
|
|
|
public function setOriginalShippingCosts($originalShippingCosts) |
|
834
|
|
|
{ |
|
835
|
|
|
$this->originalShippingCosts = $originalShippingCosts; |
|
836
|
|
|
} |
|
837
|
|
|
|
|
838
|
|
|
/** |
|
839
|
|
|
* @return mixed |
|
840
|
|
|
*/ |
|
841
|
|
|
public function getOriginalShippingCosts() |
|
842
|
|
|
{ |
|
843
|
|
|
return $this->originalShippingCosts; |
|
844
|
|
|
} |
|
845
|
|
|
|
|
846
|
|
|
/** |
|
847
|
|
|
* @param \Enlight_Components_Db_Adapter_Pdo_Mysql $database |
|
848
|
|
|
*/ |
|
849
|
|
|
public function setDatabase($database) |
|
850
|
|
|
{ |
|
851
|
|
|
$this->database = $database; |
|
852
|
|
|
} |
|
853
|
|
|
|
|
854
|
|
|
/** |
|
855
|
|
|
* @return \Enlight_Components_Db_Adapter_Pdo_Mysql |
|
856
|
|
|
*/ |
|
857
|
|
|
public function getDatabase() |
|
858
|
|
|
{ |
|
859
|
|
|
return $this->database; |
|
860
|
|
|
} |
|
861
|
|
|
|
|
862
|
|
|
/** |
|
863
|
|
|
* Returns "Gross price displayed in frontend" value |
|
864
|
|
|
* @return bool |
|
865
|
|
|
*/ |
|
866
|
|
|
protected function hasTax() |
|
867
|
|
|
{ |
|
868
|
|
|
$customerGroup = Shopware()->Session()->sUserGroup; |
|
869
|
|
|
if (!$customerGroup) { |
|
870
|
|
|
$customerGroup = 'EK'; |
|
871
|
|
|
} |
|
872
|
|
|
|
|
873
|
|
|
$repository = Shopware()->Models()->getRepository('Shopware\Models\Customer\Group'); |
|
874
|
|
|
$groupModel = $repository->findOneBy(['key' => $customerGroup]); |
|
875
|
|
|
|
|
876
|
|
|
return $groupModel->getTax(); |
|
877
|
|
|
} |
|
878
|
|
|
|
|
879
|
|
|
/** |
|
880
|
|
|
* @return \Shopware\Connect\Struct\CheckResult |
|
881
|
|
|
*/ |
|
882
|
|
|
public function getCheckResult() |
|
883
|
|
|
{ |
|
884
|
|
|
return $this->checkResult ?: new CheckResult(); |
|
885
|
|
|
} |
|
886
|
|
|
|
|
887
|
|
|
/** |
|
888
|
|
|
* @param \Shopware\Connect\Struct\CheckResult $checkResult |
|
889
|
|
|
*/ |
|
890
|
|
|
public function setCheckResult(CheckResult $checkResult) |
|
891
|
|
|
{ |
|
892
|
|
|
$this->checkResult = $checkResult; |
|
893
|
|
|
} |
|
894
|
|
|
} |
|
895
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.