This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * BoxBilling |
||
4 | * |
||
5 | * @copyright BoxBilling, Inc (http://www.boxbilling.com) |
||
6 | * @license Apache-2.0 |
||
7 | * |
||
8 | * Copyright BoxBilling, Inc |
||
9 | * This source file is subject to the Apache-2.0 License that is bundled |
||
10 | * with this source code in the file LICENSE |
||
11 | */ |
||
12 | |||
13 | namespace Box\Mod\Cart; |
||
14 | |||
15 | use Box\InjectionAwareInterface; |
||
16 | |||
17 | class Service implements InjectionAwareInterface |
||
18 | { |
||
19 | |||
20 | protected $di; |
||
21 | |||
22 | 43 | public function setDi($di) |
|
23 | { |
||
24 | 43 | $this->di = $di; |
|
25 | 43 | } |
|
26 | |||
27 | 1 | public function getDi() |
|
28 | { |
||
29 | 1 | return $this->di; |
|
30 | } |
||
31 | |||
32 | 1 | public function getSearchQuery($data) |
|
33 | { |
||
34 | $sql = " |
||
35 | SELECT cart.id FROM cart |
||
36 | LEFT JOIN currency ON cart.currency_id = currency.id |
||
37 | 1 | LEFT JOIN promo ON cart.promo_id = promo.id"; |
|
38 | |||
39 | 1 | return array($sql, array()); |
|
40 | } |
||
41 | |||
42 | /** |
||
43 | * @return \Model_Cart |
||
44 | */ |
||
45 | 8 | public function getSessionCart() |
|
46 | { |
||
47 | 3 | $sqlBindings = array(':session_id' => $this->di['session']->getId()); |
|
48 | 3 | $cart = $this->di['db']->findOne('Cart', 'session_id = :session_id', $sqlBindings); |
|
49 | |||
50 | 3 | if ($cart instanceof \Model_Cart) { |
|
51 | 1 | return $cart; |
|
52 | } |
||
53 | |||
54 | 2 | $cc = $this->di['mod_service']('currency'); |
|
55 | |||
56 | 2 | if ($this->di['session']->get('client_id')) { |
|
57 | 1 | $client_id = $this->di['session']->get('client_id'); |
|
58 | 1 | $currency = $cc->getCurrencyByClientId($client_id); |
|
59 | 1 | } else { |
|
60 | 1 | $currency = $cc->getDefault(); |
|
61 | } |
||
62 | |||
63 | 8 | $cart = $this->di['db']->dispense('Cart'); |
|
64 | 2 | $cart->session_id = $this->di['session']->getId(); |
|
65 | 2 | $cart->currency_id = $currency->id; |
|
66 | 2 | $cart->created_at = date('Y-m-d H:i:s'); |
|
67 | 2 | $cart->updated_at = date('Y-m-d H:i:s'); |
|
68 | 2 | $this->di['db']->store($cart); |
|
69 | |||
70 | 2 | return $cart; |
|
71 | } |
||
72 | |||
73 | 6 | public function addItem(\Model_Cart $cart, \Model_Product $product, array $data) |
|
74 | { |
||
75 | 6 | $event_params = array_merge($data, array('cart_id' => $cart->id, 'product_id' => $product->id)); |
|
76 | 6 | $this->di['events_manager']->fire(array('event' => 'onBeforeProductAddedToCart', 'params' => $event_params)); |
|
77 | |||
78 | 6 | $productService = $product->getService(); |
|
79 | |||
80 | 6 | if ($this->isRecurrentPricing($product)) { |
|
81 | $required = array( |
||
82 | 2 | 'period' => 'Period parameter not passed', |
|
83 | 2 | ); |
|
84 | 2 | $this->di['validator']->checkRequiredParamsForArray($required, $data); |
|
85 | |||
86 | 1 | if (!$this->isPeriodEnabledForProduct($product, $data['period'])) { |
|
87 | 1 | throw new \Box_Exception('Selected billing period is not valid'); |
|
88 | } |
||
89 | } |
||
90 | |||
91 | 4 | $qty = $this->di['array_get']($data, 'quantity', 1); |
|
92 | // check stock |
||
93 | 4 | if (!$this->isStockAvailable($product, $qty)) { |
|
94 | 1 | throw new \Box_Exception("I'm afraid we are out of stock."); |
|
95 | } |
||
96 | |||
97 | 3 | $addons = $this->di['array_get']($data, 'addons', array()); |
|
98 | 3 | unset($data['id']); |
|
99 | 3 | unset($data['addons']); |
|
100 | |||
101 | 3 | $list = array(); |
|
102 | 3 | $list[] = array( |
|
103 | 3 | 'product' => $product, |
|
104 | 3 | 'config' => $data, |
|
105 | ); |
||
106 | |||
107 | //check for required domain product |
||
108 | 3 | if (method_exists($productService, 'getDomainProductFromConfig')) { |
|
109 | 1 | $dc = $productService->getDomainProductFromConfig($product, $data); |
|
110 | 1 | if (isset($dc['config']) && $dc['product'] && $dc['product'] instanceof \Model_Product) { |
|
111 | 1 | $list[] = $dc; |
|
112 | 1 | } |
|
113 | 1 | } |
|
114 | |||
115 | 3 | $productService = $this->di['mod_service']('Product'); |
|
116 | 3 | foreach ($addons as $id => $ac) { |
|
117 | if (isset($ac['selected']) && (bool)$ac['selected']) { |
||
118 | $addon = $productService->getAddonById($id); |
||
119 | if ($addon instanceof \Model_Product) { |
||
120 | if ($this->isRecurrentPricing($addon)) { |
||
121 | |||
122 | $required = array( |
||
123 | 'period' => 'Addon period parameter not passed', |
||
124 | ); |
||
125 | $this->di['validator']->checkRequiredParamsForArray($required, $ac); |
||
126 | |||
127 | if (!$this->isPeriodEnabledForProduct($addon, $ac['period'])) { |
||
128 | throw new \Box_Exception('Selected billing period is not valid for addon'); |
||
129 | } |
||
130 | } |
||
131 | $ac['parent_id'] = $product->id; |
||
132 | |||
133 | $list[] = array( |
||
134 | 'product' => $addon, |
||
135 | 'config' => $ac, |
||
136 | ); |
||
137 | } else { |
||
138 | error_log('Addon not found by id ' . $id); |
||
139 | } |
||
140 | } |
||
141 | 3 | } |
|
142 | |||
143 | 3 | foreach ($list as $c) { |
|
144 | 3 | $productFromList = $c['product']; |
|
145 | 3 | $productFromListConfig = $c['config']; |
|
146 | |||
147 | 3 | $productServiceFromList = $productFromList->getService(); |
|
148 | |||
149 | //@deprecated logic |
||
150 | 3 | if (method_exists($productServiceFromList, 'prependOrderConfig')) { |
|
151 | 1 | $productFromListConfig = $productServiceFromList->prependOrderConfig($productFromList, $productFromListConfig); |
|
152 | 1 | } |
|
153 | |||
154 | 3 | if (method_exists($productServiceFromList, 'attachOrderConfig')) { |
|
155 | 1 | $model = $this->di['db']->load('Product', $productFromList->id); |
|
156 | 1 | $productFromListConfig = $productServiceFromList->attachOrderConfig($model, $productFromListConfig); |
|
157 | 1 | } |
|
158 | 3 | if (method_exists($productServiceFromList, 'validateOrderData')) { |
|
159 | 2 | $productServiceFromList->validateOrderData($productFromListConfig); |
|
160 | 2 | } |
|
161 | 3 | if (method_exists($productServiceFromList, 'validateCustomForm')) { |
|
162 | 1 | $productServiceFromList->validateCustomForm($productFromListConfig, $this->di['db']->toArray($productFromList)); |
|
163 | 1 | } |
|
164 | 3 | $this->addProduct($cart, $productFromList, $productFromListConfig); |
|
165 | 3 | } |
|
166 | |||
167 | 3 | $this->di['logger']->info('Added "%s" to shopping cart', $product->title); |
|
168 | |||
169 | 3 | $this->di['events_manager']->fire(array('event' => 'onAfterProductAddedToCart', 'params' => $event_params)); |
|
170 | |||
171 | 3 | return true; |
|
172 | } |
||
173 | |||
174 | 2 | public function isStockAvailable(\Model_Product $product, $qty) |
|
175 | { |
||
176 | 2 | if ($product->stock_control) { |
|
177 | 1 | return ($product->quantity_in_stock >= $qty); |
|
178 | } |
||
179 | |||
180 | 1 | return TRUE; |
|
181 | } |
||
182 | |||
183 | 1 | public function isRecurrentPricing(\Model_Product $model) |
|
184 | { |
||
185 | 1 | $productTable = $model->getTable(); |
|
186 | 1 | $pricing = $productTable->getPricingArray($model); |
|
187 | 1 | return (isset($pricing['type']) && $pricing['type'] == \Model_ProductPayment::RECURRENT); |
|
188 | } |
||
189 | |||
190 | 2 | public function isPeriodEnabledForProduct(\Model_Product $model, $period) |
|
191 | { |
||
192 | 2 | $productTable = $model->getTable(); |
|
193 | 2 | $pricing = $productTable->getPricingArray($model); |
|
194 | 2 | if ($pricing['type'] == \Model_ProductPayment::RECURRENT) { |
|
195 | 1 | return (bool)$pricing['recurrent'][$period]['enabled']; |
|
196 | } |
||
197 | |||
198 | 1 | return true; |
|
199 | } |
||
200 | |||
201 | 1 | protected function addProduct(\Model_Cart $cart, \Model_Product $product, array $data) |
|
202 | { |
||
203 | 1 | $item = $this->di['db']->dispense('CartProduct'); |
|
204 | 1 | $item->cart_id = $cart->id; |
|
205 | 1 | $item->product_id = $product->id; |
|
206 | 1 | $item->config = json_encode($data); |
|
207 | 1 | $this->di['db']->store($item); |
|
208 | |||
209 | 1 | return true; |
|
210 | } |
||
211 | |||
212 | 2 | public function removeProduct(\Model_Cart $cart, $id, $removeAddons = true) |
|
213 | { |
||
214 | $bindings = array( |
||
215 | 2 | ':cart_id' => $cart->id, |
|
216 | ':id' => $id |
||
217 | 2 | ); |
|
218 | |||
219 | 2 | $cartProduct = $this->di['db']->findOne('CartProduct', 'id = :id AND cart_id = :cart_id', $bindings); |
|
220 | 2 | if (!$cartProduct instanceof \Model_CartProduct) { |
|
221 | 1 | throw new \Box_Exception('Product not found'); |
|
222 | } |
||
223 | |||
224 | 1 | if ($removeAddons) { |
|
225 | 1 | $allCartProducts = $this->di['db']->find('CartProduct', 'cart_id = :cart_id', array(':cart_id' => $cart->id)); |
|
226 | 1 | foreach ((array)$allCartProducts as $cProduct) { |
|
227 | 1 | $config = json_decode($cProduct->config, true); |
|
228 | 1 | if (isset($config['parent_id']) && $config['parent_id'] == $cartProduct->product_id) { |
|
229 | $this->di['db']->trash($cProduct); |
||
230 | $this->di['logger']->info('Removed product addon from shopping cart'); |
||
231 | } |
||
232 | 1 | } |
|
233 | 1 | } |
|
234 | |||
235 | 1 | $this->di['db']->trash($cartProduct); |
|
236 | |||
237 | 1 | $this->di['logger']->info('Removed product from shopping cart'); |
|
238 | |||
239 | 1 | return true; |
|
240 | } |
||
241 | |||
242 | 1 | public function changeCartCurrency(\Model_Cart $cart, \Model_Currency $currency) |
|
243 | { |
||
244 | 1 | $cart->currency_id = $currency->id; |
|
245 | 1 | $this->di['db']->store($cart); |
|
246 | |||
247 | 1 | $this->di['logger']->info('Changed shopping cart #%s currency to %s', $cart->id, $currency->title); |
|
248 | |||
249 | 1 | return true; |
|
250 | } |
||
251 | |||
252 | 1 | public function resetCart(\Model_Cart $cart) |
|
253 | { |
||
254 | 1 | $cartProducts = $this->di['db']->find('CartProduct', 'cart_id = :cart_id', array(':cart_id' => $cart->id)); |
|
255 | 1 | foreach ($cartProducts as $cartProduct) { |
|
256 | 1 | $this->di['db']->trash($cartProduct); |
|
257 | 1 | } |
|
258 | 1 | $cart->promo_id = NULL; |
|
259 | 1 | $cart->updated_at = date('Y-m-d H:i:s'); |
|
260 | 1 | $this->di['db']->store($cart); |
|
261 | |||
262 | 1 | return true; |
|
263 | } |
||
264 | |||
265 | 1 | public function removePromo(\Model_Cart $cart) |
|
266 | { |
||
267 | 1 | $cart->promo_id = NULL; |
|
268 | 1 | $cart->updated_at = date('Y-m-d H:i:s'); |
|
269 | 1 | $this->di['db']->store($cart); |
|
270 | |||
271 | 1 | $this->di['logger']->info('Removed promo code from shopping cart #%s', $cart->id); |
|
272 | |||
273 | 1 | return true; |
|
274 | } |
||
275 | |||
276 | 3 | public function applyPromo(\Model_Cart $cart, \Model_Promo $promo) |
|
277 | { |
||
278 | 3 | if ($cart->promo_id == $promo->id) { |
|
279 | 1 | return true; |
|
280 | } |
||
281 | |||
282 | 2 | if ($this->isEmptyCart($cart)) { |
|
283 | 1 | throw new \Box_Exception('Add products to cart before applying promo code'); |
|
284 | } |
||
285 | |||
286 | 1 | $cart->promo_id = $promo->id; |
|
287 | 1 | $this->di['db']->store($cart); |
|
288 | |||
289 | 1 | $this->di['logger']->info('Applied promo code %s to shopping cart', $promo->code); |
|
290 | |||
291 | 1 | return true; |
|
292 | } |
||
293 | |||
294 | 1 | protected function isEmptyCart(\Model_Cart $cart) |
|
295 | { |
||
296 | 1 | $cartProducts = $this->di['db']->find('CartProduct', 'cart_id = :cart_id', array(':cart_id' => $cart->id)); |
|
297 | |||
298 | 1 | return (count($cartProducts) == 0); |
|
299 | } |
||
300 | |||
301 | 1 | public function rm(\Model_Cart $cart) |
|
302 | { |
||
303 | 1 | $cartProducts = $this->di['db']->find('CartProduct', 'cart_id = :cart_id', array(':cart_id' => $cart->id)); |
|
304 | |||
305 | 1 | foreach ($cartProducts as $cartProduct) { |
|
306 | 1 | $this->di['db']->trash($cartProduct); |
|
307 | 1 | } |
|
308 | |||
309 | 1 | $this->di['db']->trash($cart); |
|
310 | |||
311 | 1 | return true; |
|
312 | } |
||
313 | |||
314 | 1 | public function toApiArray(\Model_Cart $model, $deep = false, $identity = null) |
|
315 | { |
||
316 | 1 | $products = $this->getCartProducts($model); |
|
317 | |||
318 | 1 | $currency = $this->di['db']->getExistingModelById('Currency', $model->currency_id); |
|
319 | |||
320 | 1 | $items = array(); |
|
321 | 1 | $total = 0; |
|
322 | 1 | $cart_discount = 0; |
|
323 | 1 | $items_discount = 0; |
|
324 | 1 | foreach ($products as $product) { |
|
325 | 1 | $p = $this->cartProductToApiArray($product); |
|
326 | 1 | $total += $p['total'] + $p['setup_price']; |
|
327 | 1 | $items_discount += $p['discount']; |
|
328 | 1 | $items[] = $p; |
|
329 | 1 | } |
|
330 | |||
331 | 1 | if ($model->promo_id) { |
|
332 | $promo = $this->di['db']->getExistingModelById('Promo', $model->promo_id, 'Promo not found'); |
||
333 | $promocode = $promo->code; |
||
334 | } else { |
||
335 | 1 | $promocode = NULL; |
|
336 | } |
||
337 | |||
338 | 1 | $currencyService = $this->di['mod_service']('currency'); |
|
339 | $result = array( |
||
340 | 1 | 'promocode' => $promocode, |
|
341 | 1 | 'discount' => $items_discount, |
|
342 | 1 | 'subtotal' => $total, |
|
343 | 1 | 'total' => $total - $items_discount, |
|
344 | 1 | 'items' => $items, |
|
345 | 1 | 'currency' => $currencyService->toApiArray($currency), |
|
346 | ); |
||
347 | 1 | ||
348 | return $result; |
||
349 | } |
||
350 | 4 | ||
351 | public function isClientAbleToUsePromo(\Model_Client $client, \Model_Promo $promo) |
||
352 | 4 | { |
|
353 | 1 | if (!$this->promoCanBeApplied($promo)) { |
|
354 | return false; |
||
355 | } |
||
356 | 3 | ||
357 | 1 | if (!$promo->once_per_client) { |
|
358 | return true; |
||
359 | } |
||
360 | 2 | ||
361 | return !$this->clientHadUsedPromo($client, $promo); |
||
362 | } |
||
363 | 5 | ||
364 | public function promoCanBeApplied(\Model_Promo $promo) |
||
365 | 5 | { |
|
366 | 1 | if (!$promo->active) { |
|
367 | return false; |
||
368 | } |
||
369 | 4 | ||
370 | 1 | if ($promo->maxuses && $promo->maxuses <= $promo->used) { |
|
371 | return false; |
||
372 | } |
||
373 | 3 | ||
374 | 1 | if ($promo->start_at && (strtotime($promo->start_at) - time() > 0)) { |
|
375 | return false; |
||
376 | } |
||
377 | 2 | ||
378 | 1 | if ($promo->end_at && (strtotime($promo->end_at) - time() < 0)) { |
|
379 | return false; |
||
380 | } |
||
381 | 1 | ||
382 | return true; |
||
383 | } |
||
384 | 6 | ||
385 | public function isPromoAvailableForClientGroup(\Model_Promo $promo) |
||
386 | 6 | { |
|
387 | $clientGroups = $this->di['tools']->decodeJ($promo->client_groups); |
||
388 | 6 | ||
389 | 2 | if (empty($clientGroups)) { |
|
390 | return true; |
||
391 | } |
||
392 | |||
393 | 4 | try { |
|
394 | 4 | $client = $this->di['loggedin_client']; |
|
395 | } catch (\Exception $e) { |
||
396 | $client = null; |
||
397 | } |
||
398 | 4 | ||
399 | 1 | if (is_null($client)){ |
|
400 | return false; |
||
401 | } |
||
402 | 3 | ||
403 | 1 | if (!$client->client_group_id) { |
|
404 | return false; |
||
405 | } |
||
406 | 2 | ||
407 | return in_array($client->client_group_id, $clientGroups); |
||
408 | } |
||
409 | 1 | ||
410 | protected function clientHadUsedPromo(\Model_Client $client, \Model_Promo $promo) |
||
411 | 1 | { |
|
412 | 1 | $sql = "SELECT id FROM client_order WHERE promo_id = :promo AND client_id = :cid LIMIT 1"; |
|
413 | $promoId = $this->di['db']->getCell($sql, array(':promo' => $promo->id, ':cid' => $client->id)); |
||
414 | 1 | ||
415 | return ($promoId !== null); |
||
416 | } |
||
417 | 1 | ||
418 | public function getCartProducts(\Model_Cart $model) |
||
419 | 1 | { |
|
420 | return $this->di['db']->find('CartProduct', 'cart_id = :cart_id ORDER BY id ASC', array(':cart_id' => $model->id)); |
||
421 | } |
||
422 | 2 | ||
423 | public function checkoutCart(\Model_Cart $cart, \Model_Client $client, $gateway_id = null) |
||
424 | 2 | { |
|
425 | 2 | if ($cart->promo_id) { |
|
426 | 2 | $promo = $this->di['db']->getExistingModelById('Promo', $cart->promo_id, 'Promo not found'); |
|
427 | 1 | if (!$this->isClientAbleToUsePromo($client, $promo)) { |
|
428 | throw new \Box_Exception('You have already used this promo code. Please remove promo code and checkout again.', null, 9874); |
||
429 | 1 | } |
|
430 | } |
||
431 | 1 | ||
432 | $this->di['events_manager']->fire( |
||
433 | 1 | array( |
|
434 | 'event' => 'onBeforeClientCheckout', |
||
435 | 1 | 'params' => array( |
|
436 | 1 | 'ip' => $this->di['request']->getClientAddress(), |
|
437 | 1 | 'client_id' => $client->id, |
|
438 | 1 | 'cart_id' => $cart->id) |
|
439 | 1 | ) |
|
440 | ); |
||
441 | 1 | ||
442 | list($order, $invoice, $orders) = $this->createFromCart($client, $gateway_id); |
||
443 | 1 | ||
444 | $this->rm($cart); |
||
445 | 1 | ||
446 | $this->di['logger']->info('Checked out shopping cart'); |
||
447 | 1 | ||
448 | $this->di['events_manager']->fire( |
||
449 | 1 | array( |
|
450 | 'event' => 'onAfterClientOrderCreate', |
||
451 | 1 | 'params' => array( |
|
452 | 1 | 'ip' => $this->di['request']->getClientAddress(), |
|
453 | 1 | 'client_id' => $client->id, |
|
454 | 1 | 'id' => $order->id |
|
455 | 1 | ) |
|
456 | 1 | ) |
|
457 | ); |
||
458 | |||
459 | 1 | $result = array( |
|
460 | 1 | 'gateway_id' => $gateway_id, |
|
461 | 1 | 'invoice_hash' => null, |
|
462 | 1 | 'order_id' => $order->id, |
|
463 | 1 | 'orders' => $orders, |
|
464 | ); |
||
465 | |||
466 | 1 | // invoice may not be created if total is 0 |
|
467 | if ($invoice instanceof \Model_Invoice && $invoice->status == \Model_Invoice::STATUS_UNPAID) { |
||
468 | $result['invoice_hash'] = $invoice->hash; |
||
469 | } |
||
470 | 1 | ||
471 | return $result; |
||
472 | } |
||
473 | |||
474 | public function createFromCart(\Model_Client $client, $gateway_id = null) |
||
475 | { |
||
476 | $cart = $this->getSessionCart(); |
||
477 | $ca = $this->toApiArray($cart); |
||
478 | if (count($ca['items']) == 0) { |
||
479 | throw new \Box_Exception('Can not checkout empty cart.'); |
||
480 | } |
||
481 | |||
482 | |||
483 | $currency = $this->di['db']->getExistingModelById('Currency', $cart->currency_id, 'Currency not found.'); |
||
484 | |||
485 | //set default client currency |
||
486 | if (!$client->currency) { |
||
487 | $client->currency = $currency->code; |
||
488 | $this->di['db']->store($client); |
||
489 | } |
||
490 | |||
491 | if ($client->currency != $currency->code) { |
||
492 | throw new \Box_Exception('Selected currency :selected does not match your profile currency :code. Please change cart currency to continue.', |
||
493 | array(':selected' => $currency->code, ':code' => $client->currency)); |
||
494 | } |
||
495 | |||
496 | $clientService = $this->di['mod_service']('client'); |
||
497 | $taxed = $clientService->isClientTaxable($client); |
||
498 | |||
499 | $orders = array(); |
||
500 | $invoice_items = array(); |
||
501 | $master_order = null; |
||
502 | $i = 0; |
||
503 | |||
504 | foreach ($this->getCartProducts($cart) as $p) { |
||
505 | $item = $this->cartProductToApiArray($p); |
||
506 | |||
507 | $order = $this->di['db']->dispense('ClientOrder'); |
||
508 | $order->client_id = $client->id; |
||
509 | $order->promo_id = $cart->promo_id; |
||
510 | $order->product_id = $item['product_id']; |
||
511 | $order->form_id = $item['form_id']; |
||
512 | |||
513 | $order->group_id = $cart->id; |
||
514 | $order->group_master = ($i == 0); |
||
515 | $order->invoice_option = 'issue-invoice'; |
||
516 | $order->title = $item['title']; |
||
517 | $order->currency = $currency->code; |
||
518 | $order->service_type = $item['type']; |
||
519 | $order->unit = $this->di['array_get']($item, 'unit', NULL); |
||
520 | $order->period = $this->di['array_get']($item, 'period', NULL); |
||
521 | $order->quantity = $this->di['array_get']($item, 'quantity', NULL); |
||
522 | $order->price = $item['price'] * $currency->conversion_rate; |
||
523 | $order->discount = $item['discount_price'] * $currency->conversion_rate; |
||
524 | $order->status = \Model_ClientOrder::STATUS_PENDING_SETUP; |
||
525 | $order->notes = $this->di['array_get']($item, 'notes', NULL); |
||
526 | $order->config = json_encode($item); |
||
527 | $order->created_at = date('Y-m-d H:i:s'); |
||
528 | $order->updated_at = date('Y-m-d H:i:s'); |
||
529 | $this->di['db']->store($order); |
||
530 | |||
531 | $orders[] = $order; |
||
532 | |||
533 | // mark promo as used |
||
534 | if ($cart->promo_id) { |
||
535 | $promo = $this->di['db']->getExistingModelById('Promo', $cart->promo_id, 'Promo not found.'); |
||
536 | $this->usePromo($promo); |
||
537 | |||
538 | //set promo info for later use |
||
539 | $order->promo_recurring = $promo->recurring; |
||
540 | $order->promo_used = 1; |
||
541 | $this->di['db']->store($order); |
||
542 | } |
||
543 | |||
544 | $orderService = $this->di['mod_service']('order'); |
||
545 | $orderService->saveStatusChange($order, 'Order created'); |
||
546 | |||
547 | $invoice_items[] = array( |
||
548 | 'title' => $order->title, |
||
549 | 'price' => $order->price, |
||
550 | 'quantity' => $order->quantity, |
||
551 | 'unit' => $order->unit, |
||
552 | 'period' => $order->period, |
||
553 | 'taxed' => $taxed, |
||
554 | 'type' => \Model_InvoiceItem::TYPE_ORDER, |
||
555 | 'rel_id' => $order->id, |
||
556 | 'task' => \Model_InvoiceItem::TASK_ACTIVATE, |
||
557 | ); |
||
558 | |||
559 | if($order->discount > 0){ |
||
560 | $invoice_items[] = array( |
||
561 | 'title' => __('Discount: :product', array(':product' => $order->title)), |
||
562 | 'price' => $order->discount * -1, |
||
563 | 'quantity' => 1, |
||
564 | 'unit' => 'discount', |
||
565 | 'rel_id' => $order->id, |
||
566 | 'taxed' => $taxed, |
||
567 | ); |
||
568 | } |
||
569 | |||
570 | if ($item['setup_price'] > 0) { |
||
571 | $setup_price = ($item['setup_price'] * $currency->conversion_rate) - ($item['discount_setup'] * $currency->conversion_rate); |
||
572 | $invoice_items[] = array( |
||
573 | 'title' => __(':product setup', array(':product' => $order->title)), |
||
574 | 'price' => $setup_price, |
||
575 | 'quantity' => 1, |
||
576 | 'unit' => 'service', |
||
577 | 'taxed' => $taxed, |
||
578 | ); |
||
579 | } |
||
580 | |||
581 | //define master order to be returned |
||
582 | if (null === $master_order) { |
||
583 | $master_order = $order; |
||
584 | } |
||
585 | |||
586 | $i++; |
||
587 | } |
||
588 | |||
589 | if ($ca['total'] > 0) { //crete invoice if order total > 0 |
||
590 | |||
591 | $invoiceService = $this->di['mod_service']('Invoice'); |
||
592 | $invoiceModel = $invoiceService->prepareInvoice($client, array('client_id' => $client->id, 'items' => $invoice_items, 'gateway_id' => $gateway_id)); |
||
593 | |||
594 | $clientBalanceService = $this->di['mod_service']('Client', 'Balance'); |
||
595 | $balanceAmount = $clientBalanceService->getClientBalance($client); |
||
596 | $useCredits = $balanceAmount >= $ca['total']; |
||
597 | |||
598 | $invoiceService->approveInvoice($invoiceModel, array('id' => $invoiceModel->id, 'use_credits' => $useCredits)); |
||
599 | |||
600 | if ($invoiceModel->status == \Model_Invoice::STATUS_UNPAID) { |
||
601 | foreach ($orders as $order) { |
||
602 | $order->unpaid_invoice_id = $invoiceModel->id; |
||
603 | $this->di['db']->store($order); |
||
604 | } |
||
605 | } |
||
606 | } |
||
607 | |||
608 | //activate orders if product is setup to be activated after order place or order total is $0 |
||
609 | $orderService = $this->di['mod_service']('Order'); |
||
610 | $ids = array(); |
||
611 | foreach ($orders as $order) { |
||
612 | $ids[] = $order->id; |
||
613 | $oa = $orderService->toApiArray($order, false, $client); |
||
614 | $product = $this->di['db']->getExistingModelById('Product', $oa['product_id']); |
||
615 | try { |
||
616 | if ($product->setup == \Model_ProductTable::SETUP_AFTER_ORDER){ |
||
617 | $orderService->activateOrder($order); |
||
618 | } |
||
619 | |||
620 | if ($ca['total'] <= 0 && $product->setup == \Model_ProductTable::SETUP_AFTER_PAYMENT && $oa['total'] - $oa['discount'] <= 0){ |
||
621 | $orderService->activateOrder($order); |
||
622 | } |
||
623 | |||
624 | if ($ca['total'] > 0 && $product->setup == \Model_ProductTable::SETUP_AFTER_PAYMENT && $invoiceModel->status == \Model_Invoice::STATUS_PAID ){ |
||
625 | $orderService->activateOrder($order); |
||
626 | } |
||
627 | } |
||
628 | catch (\Exception $e) { |
||
629 | error_log($e->getMessage()); |
||
630 | $status = 'error'; |
||
631 | 1 | $notes = 'Order could not be activated after checkout due to error: ' . $e->getMessage(); |
|
632 | $orderService->orderStatusAdd($order, $status, $notes); |
||
633 | 1 | } |
|
634 | 1 | } |
|
635 | 1 | ||
636 | 1 | return array( |
|
637 | $master_order, |
||
638 | 1 | isset($invoiceModel) ? $invoiceModel : null, |
|
639 | $ids, |
||
640 | 1 | ); |
|
641 | } |
||
642 | |||
643 | public function usePromo(\Model_Promo $promo) |
||
644 | { |
||
645 | $promo->used++; |
||
646 | $promo->updated_at = date('Y-m-d H:i:s'); |
||
647 | $this->di['db']->store($promo); |
||
648 | } |
||
649 | |||
650 | public function findActivePromoByCode($code) |
||
651 | { |
||
652 | return $this->di['db']->findOne('Promo', 'code = :code AND active = 1 ORDER BY id ASC', array(':code' => $code)); |
||
653 | } |
||
654 | |||
655 | private function getItemPrice(\Model_CartProduct $model) |
||
0 ignored issues
–
show
Unused Code
introduced
by
![]() |
|||
656 | { |
||
657 | $product = $this->di['db']->load('Product', $model->product_id); |
||
658 | $config = $this->getItemConfig($model); |
||
659 | $repo = $product->getTable(); |
||
660 | return $repo->getProductPrice($product, $config); |
||
661 | } |
||
662 | |||
663 | private function getItemSetupPrice(\Model_CartProduct $model) |
||
0 ignored issues
–
show
|
|||
664 | { |
||
665 | $product = $this->di['db']->load('Product', $model->product_id); |
||
666 | $config = $this->getItemConfig($model); |
||
667 | $repo = $product->getTable(); |
||
668 | return $repo->getProductSetupPrice($product, $config); |
||
669 | } |
||
670 | |||
671 | /** |
||
672 | * Function checks if product is related to other products in cart |
||
673 | * If relation exists then count discount for this |
||
674 | * |
||
675 | * @param \Model_Cart $cart |
||
676 | * @param \Model_CartProduct $model |
||
677 | * @return number |
||
678 | */ |
||
679 | protected function getRelatedItemsDiscount(\Model_Cart $cart, \Model_CartProduct $model) |
||
680 | { |
||
681 | $product = $this->di['db']->load('Product', $model->product_id); |
||
682 | $repo = $product->getTable(); |
||
683 | $config = $this->getItemConfig($model); |
||
684 | |||
685 | $discount = 0; |
||
686 | if(method_exists($repo, 'getRelatedDiscount')) { |
||
687 | $list = array(); |
||
688 | $products = $this->getCartProducts($cart); |
||
689 | foreach($products as $p) { |
||
690 | $item = $this->di['db']->toArray($p); |
||
691 | $item['config'] = $this->getItemConfig($p); |
||
692 | $list[] = $item; |
||
693 | } |
||
694 | $discount = $repo->getRelatedDiscount($list, $product, $config); |
||
695 | } |
||
696 | return $discount; |
||
697 | } |
||
698 | |||
699 | private function getItemTitle(\Model_CartProduct $model) |
||
700 | { |
||
701 | $product = $this->di['db']->load('Product', $model->product_id); |
||
702 | $config = $this->getItemConfig($model); |
||
703 | $service = $product->getService(); |
||
704 | if(method_exists($service, 'getCartProductTitle')) { |
||
705 | return $service->getCartProductTitle($product, $config); |
||
706 | } else { |
||
707 | return __(':product_title', array(':product_title'=>$product->title)); |
||
708 | } |
||
709 | } |
||
710 | |||
711 | protected function getItemPromoDiscount(\Model_CartProduct $model, \Model_Promo $promo) |
||
712 | { |
||
713 | $product = $this->di['db']->load('Product', $model->product_id); |
||
714 | $repo = $this->di['mod_service']('product'); |
||
715 | $config = $this->getItemConfig($model); |
||
716 | return $repo->getProductDiscount($product, $promo, $config); |
||
717 | } |
||
718 | |||
719 | public function getItemConfig(\Model_CartProduct $model) |
||
720 | { |
||
721 | return $this->di['tools']->decodeJ($model->config); |
||
722 | } |
||
723 | |||
724 | public function cartProductToApiArray(\Model_CartProduct $model) |
||
725 | { |
||
726 | $product = $this->di['db']->load('Product', $model->product_id); |
||
727 | $repo = $product->getTable(); |
||
728 | $config = $this->getItemConfig($model); |
||
729 | $setup = $repo->getProductSetupPrice($product, $config); |
||
730 | $price = $repo->getProductPrice($product, $config); |
||
731 | $qty = $this->di['array_get']($config, 'quantity', 1) ; |
||
732 | |||
733 | list ($discount_price, $discount_setup) = $this->getProductDiscount($model, $setup); |
||
734 | |||
735 | $discount_total = $discount_price + $discount_setup; |
||
736 | |||
737 | $subtotal = ($price * $qty); |
||
738 | if(abs($discount_total) > ($subtotal + $setup) ) { |
||
739 | $discount_total = $subtotal; |
||
740 | $discount_price = $subtotal; |
||
741 | } |
||
742 | |||
743 | $data = array_merge($config, array( |
||
744 | 'id' => $model->id, |
||
745 | 'product_id' => $product->id, |
||
746 | 'form_id' => $product->form_id, |
||
747 | 'title' => $this->getItemTitle($model), |
||
748 | 'type' => $product->type, |
||
749 | 3 | 'quantity' => $qty, |
|
750 | 'unit' => $repo->getUnit($product), |
||
751 | 3 | 'price' => $price, |
|
752 | 3 | 'setup_price' => $setup, |
|
753 | 3 | 'discount' => $discount_total, |
|
754 | 3 | 'discount_price'=> $discount_price, |
|
755 | 2 | 'discount_setup'=> $discount_setup, |
|
756 | 2 | 'total' => $subtotal, |
|
757 | )); |
||
758 | 2 | return $data; |
|
759 | 1 | } |
|
760 | 1 | ||
761 | 2 | public function getProductDiscount(\Model_CartProduct $cartProduct, $setup) |
|
762 | 3 | { |
|
763 | $cart = $this->di['db']->load('Cart', $cartProduct->cart_id); |
||
764 | $discount_price = $this->getRelatedItemsDiscount($cart, $cartProduct); |
||
765 | $discount_setup = 0; // discount for setup price |
||
766 | if($cart->promo_id) { |
||
767 | $promo = $this->di['db']->getExistingModelById('Promo', $cart->promo_id, 'Promo not found'); |
||
768 | //Promo discount should override related item discount |
||
769 | $discount_price = $this->getItemPromoDiscount($cartProduct, $promo); |
||
770 | |||
771 | if($promo->freesetup) { |
||
772 | $discount_setup = $setup; |
||
773 | } |
||
774 | } |
||
775 | return array($discount_price, $discount_setup); |
||
776 | } |
||
777 | } |