null, 'msg' => null, ]; public function get_vars() { $vars = parent::get_vars(); $this->loadUser(); $this->id_order = getVal('id_order'); if (empty($this->id_order) && (isset($this->order) || is_null($this->order)) && !is_a($this->order, 'Order')) { $this->cart = null; $this->createCart(); $vars['cart'] = $this->cart; } else { $this->getCompleteOrder(); $vars['cart'] = $this->order; } $vars['display'] = getVal('display'); $vars['user'] = $this->user; $vars['discount'] = (!empty($ctrl['dealerPricelevel']['discount'])) ? $ctrl['dealerPricelevel']['discount'] : 0; $vars['coupon'] = (!empty($_SESSION['coupon'])) ? $_SESSION['coupon'] : ''; $vars['vats'] = PosCart::getVats(); $vars['result_info'] = $this->getResultInfo(); global $adminName; if (!empty($adminName)) { $vars['admin_name'] = $adminName; } if (findModule(Modules::WAREHOUSE) && !empty($vars['cart']->products)) { $StoreItemWorker = ServiceContainer::getService(StoreItemWorker::class); foreach ($vars['cart']->products as &$product) { $positions = $StoreItemWorker->getProductPositions( Operator::equalsNullable(['p.id' => $product->id, 'pv.id' => $product->id_variation]) ); if (is_array($product)) { $product['positions'] = $positions; } else { $product->positions = $positions; } } } return $vars; } public function handle() { $this->loadUser(); try { parent::handle(); } catch (POSInternalException $e) { $this->posHandleException($e); $msg = $e->getMessage(); $this->addErrorInfo(self::RESULT_TYPE_ERROR, $msg); header('pos-error: '.urlencode($msg)); } catch (Exception $e) { // Doctrine\DBAL\SQLParserUtilsException $this->posHandleException($e); $this->result_info = 'Chyba! Vývojáři byli informováni. Zkuste to za okamžik znovu.'; } } protected function posHandleException(Exception $e) { $sentry = getRaven(); $sentry->captureMessage('POS error', [], ['extra' => [ 'request' => $_REQUEST, 'error_message' => $e->getMessage(), 'error_except' => $e, ]]); } public function handleCheckCart() { } public function handleAddProduct() { $id_product = getVal('id_product'); $id_variation = (getVal('id_variation') != '') ? getVal('id_variation') : null; $code = getVal('ean'); $ean = intval($code); $find_by = ''; if (!$id_product && $ean) { $query = sqlQueryBuilder() ->select('p.title as label, p.id as id_product, pv.id as id_variation') ->from('products', 'p') ->leftJoin('p', 'products_variations', 'pv', 'pv.id_product=p.id'); if (findModule(Modules::SUPPLIERS)) { $query->addSelect('COALESCE(pos.ean, pv.ean, p.ean) as ean') ->leftJoin('p', 'products_of_suppliers', 'pos', 'pos.id_product=p.id AND (pos.id_variation = pv.id OR pv.id IS NULL)') ->andWhere("p.code LIKE '%{$code}%' OR p.ean LIKE '{$ean}' OR pv.ean LIKE '{$ean}' OR pos.ean LIKE '{$ean}'"); } else { $query->addSelect('COALESCE(pv.ean, p.ean) as ean') ->andWhere("p.code LIKE '%{$code}%' OR p.ean LIKE '{$ean}' OR pv.ean LIKE '{$ean}'"); } if (!empty($cfg['Modules']['products_variations']['variationCode'])) { $query->orWhere("pv.code LIKE '%{$code}%'") ->addSelect('COALESCE(pv.code, p.code) as code'); } else { $query->addSelect('p.code'); } $SQL = $query->groupBy('p.id') ->execute() ->fetchAll(); if (sizeof($SQL) == 0) { $this->addErrorInfo(self::RESULT_TYPE_ERROR, "Tento EAN/KOD ({$ean}) není přiřazen k žádnému produktu"); return; } elseif (sizeof($SQL) > 1) { $this->addErrorInfo(self::RESULT_TYPE_ERROR, "Existuje více produktů s tímto EAN/KOD ({$ean}), vložte ručně"); return; } else { $row = $SQL[0]; if ($ean == $row['code']) { $find_by = ' podle kódu '.$row['code']; } elseif ($ean == $row['ean']) { $find_by = ' podle eanu '.$row['code']; } $id_product = $row['id_product']; if (!empty($row['id_variation'])) { $id_variation = $row['id_variation']; } } } $this->id_order = getVal('id_order'); if ($id_product) { if (!$this->id_order) { $this->createCart(); $item = ['id_product' => $id_product, 'id_variation' => $id_variation, 'pieces' => 1]; $res = $this->cart->addItem($item); } else { $this->order = new Order($this->id_order); $this->order->createFromDB($this->id_order); $this->order->fetchItems(); $existing_item = array_filter($this->order->items, function ($i) use ($id_product, $id_variation) { return ($i['id_product'] == $id_product) && ($i['id_variation'] == $id_variation); }); if (!empty($existing_item)) { $existing_item = reset($existing_item); $res = $this->order->updateItem($existing_item['id'], $existing_item['pieces'] + 1); } else { $res = $this->order->insertItem($id_product, $id_variation, 1); $product = new Product($id_product); $dispatcher = ServiceContainer::getService('event_dispatcher'); $event = $dispatcher->dispatch( new OrderItemEvent($product, $id_variation, null, 1, null, $this->order), OrderItemEvent::ITEM_UPDATED ); } $this->order->recalculate(); $this->editOrderEvent(); } $this->addErrorInfo($res, $res == 1 ? 'Přidáno'.$find_by : $res); } } public function handleAddNonProduct() { $userKey = Cart::getCartID(); $price = getVal('price'); $params = [ 'id' => getVal('item_id'), 'pieces' => getVal('pieces'), 'discount' => getVal('product_discount'), 'title' => getVal('title'), 'piece_price' => $this->preparePrice($price), ]; $this->createCart(); $this->id_order = getVal('id_order'); if (empty($this->id_order)) { $price = getVal('piece_price', $params, ''); $price = $this->preparePrice($price); $price_with_vat = roundPrice($price); $vat = PosCart::getVatByID(getVal('vat', null, 0)); $price = $price_with_vat->removeVat($vat)->asFloat(); $res = $this->cart->addNonItem([ 'date' => date('Y-m-d H:i:s'), 'id_product' => 0, 'id_variation' => null, 'pieces' => 1, 'user_key' => $userKey, 'note' => json_encode([ 'title' => getVal('title'), 'piece_price' => $price, 'vat' => $vat, 'discount' => 0, ]), ] ); $this->addErrorInfo($res, 'Přidáno.'); } else { $this->order = new Order($this->id_order); $this->order->createFromDB($this->id_order); $vat = PosCart::getVatByID(getVal('vat', null, 0)); $price = toDecimal($params['piece_price'])->removeVat($vat); $this->order->insertNonItem($this->id_order, $price, $price, 1, $vat, $params['title']); $this->order->recalculate(); $this->editOrderEvent(); $this->addErrorInfo(self::RESULT_TYPE_OK, 'Přidáno.'); } } public function handleChangeItem() { $price = getVal('price'); $params = [ 'id' => getVal('item_id'), 'pieces' => getVal('pieces'), 'discount' => getVal('product_discount'), 'title' => getVal('title'), 'piece_price' => $this->preparePrice($price), ]; $this->createCart(); $this->id_order = getVal('id_order'); if (empty($this->id_order)) { $item = sqlFetchAssoc($this->selectSQL('cart', ['id' => $params['id']], ['id', 'pieces', 'note'])); $item = json_decode($item['note'], true); $price_with_vat = roundPrice($item['piece_price']); $row['piece_price'] = $price_with_vat->removeVat($item['vat'])->asFloat(); $params['note'] = json_encode([ 'piece_price' => $item['piece_price'], 'vat' => $item['vat'], 'title' => $item['title'], 'discount' => $params['discount'], ]); $res = $this->cart->updateNonItem($params); $this->addErrorInfo($res, $res ? 'Změněno.' : $res); } else { $this->order = new Order($this->id_order); $this->order->createFromDB($this->id_order); $this->order->updateItem($params['id'], $params['pieces']); $order_item = sqlQueryBuilder()->select('*')->from('order_items')->where('id=:item_id')->setParameter('item_id', $params['id'])->execute()->fetch(); $old_discount = getVal('discount', $order_item, 0); $price = toDecimal($order_item['piece_price'])->removeDiscount($old_discount); $price_with_vat = calcPrice($price, $order_item['tax'], $params['discount']); $price_with_vat_rounded = roundPrice($price_with_vat); $price_without_vat = $price_with_vat_rounded->removeVat($order_item['tax']); $res = $this->updateSQL('order_items', [ 'piece_price' => $price_without_vat, 'total_price' => $price_without_vat->mul(toDecimal($order_item['pieces'])), 'discount' => $params['discount'], ], ['id' => $params['id']]); $this->order->recalculate(); $this->editOrderEvent(); $this->addErrorInfo($res, 'Změněno.'); } } public function handleDeleteItem() { $item_id = getVal('item_id'); $this->id_order = getVal('id_order'); if (empty($this->id_order)) { $this->createCart(); $res = $this->cart->deleteItem($item_id); } else { $this->order = new Order($this->id_order); $this->order->createFromDB($this->id_order); $res = $this->order->deleteItem($item_id); $this->editOrderEvent(); } $this->addErrorInfo($res, 'Položka odebrána.'); } public function handleCreateOrder($submitCart = false) { $this->createCart(); global $dbcfg; $this->cachedbcfg(); $dbcfg->order_send_received_mail = 'N'; $dbcfg->order_send_status_mail = 'N'; $dbcfg->order_availability = Settings::ORDER_AVAILABILITY_ALL; $this->order = $this->cart->submitOrder(); if (is_a($this->order, 'Order')) { $this->order->addFlag('POS'); if (!empty($this->user->id)) { $this->order->assignUser($this->user->id); $this->order->id_user = $this->user->id; $eventDispatcher = ServiceContainer::getService('event_dispatcher'); $event = new OrderEvent($this->order); $eventDispatcher->dispatch($event, OrderEvent::ORDER_COMPLETE); } $this->cachedbcfg(true); $this->addErrorInfo(self::RESULT_TYPE_OK, 'Objednávka vytvořena.'); return true; } else { $this->addErrorInfo(self::RESULT_TYPE_ERROR, $this->getErrorMsg($this->order)); } $this->addErrorInfo(self::RESULT_TYPE_ERROR, 'Nedefinovaná chyba vytváření objednávky'); } protected function cachedbcfg($return = false) { global $dbcfg; static $dbcfg_backup = null; if ($dbcfg_backup) { $dbcfg = $dbcfg_backup; } else { $dbcfg_backup = $dbcfg; } } /** * @throws Throwable */ public function handlePay() { global $adminID; $this->id_order = getVal('id_order'); if (!$this->id_order) { sqlGetConnection()->setNestTransactionsWithSavepoints(true); sqlGetConnection()->transactional(function () { $create_order = $this->handleCreateOrder(true); if ($create_order !== true) { $this->addErrorInfo(self::RESULT_TYPE_ERROR, 'Vytvoření nákupu selhalo. Zkuste to za okamžik znovu.'); return; } $this->id_order = $this->order->id; }); } $result = $this->checkBuyingProcess(); if (!$result) { return; } if (!empty($this->id_order)) { $this->order = new Order($this->id_order); $this->order->createFromDB($this->id_order); $payment_method = getVal('method'); $descr = 'Nákup č. '.$this->order->order_no; $value = $this->order->getRemainingPayment(); if ($payment_method == Payment::METHOD_INVOICE || empty($value)) { $payed = true; $this->addErrorInfo(self::RESULT_TYPE_OK, 'Vše proběhlo v pořádku.'); } else { $payed = $this->order->insertPayment($value, $descr, date('Y-m-d H:i'), true, $payment_method); sqlQuery('UPDATE IGNORE order_payments SET admin=:admin WHERE id_order=:id_order ORDER BY id DESC LIMIT 1', ['id_order' => $this->order->id, 'admin' => $adminID]); $this->addErrorInfo(self::RESULT_TYPE_OK, 'Zaplaceno.'); } if ($payed) { $this->changeOrderStatus(); try { $this->updateOrderDeliveryType($payment_method, $this->order); } catch (Exception $e) { $this->posHandleException($e); $this->addErrorInfo(self::RESULT_TYPE_ERROR, 'Chyba převodu objednávky do správného způsobu doručení podle platby.'); } } else { $this->addErrorInfo(self::RESULT_TYPE_ERROR, 'Nákup byl již zaplacen.'); } } else { $this->addErrorInfo(self::RESULT_TYPE_ERROR, 'Chyba! Platba nemá vazbu na objednávku.'); } } public function updateOrderDeliveryType($payment_method, Order $order) { $delivery_type = PosCart::findDeliveryTypeByPayMethod($payment_method); $this->updateSQL('orders', [ 'delivery_type' => $delivery_type['name'], 'id_delivery' => $delivery_type['id'], ], ['id' => $order->id]); } public function handleOrderLoad() { $this->id_order = getVal('id_order'); if ($this->id_order) { $this->order = new Order($this->id_order); $this->getCompleteOrder(); $this->addErrorInfo(self::RESULT_TYPE_OK, 'Načteno.'); return; } else { $this->clearSessions(); } $this->addErrorInfo(self::RESULT_TYPE_OK, 'Odebráno.'); } public function handleUserLoad() { $this->id_order = getVal('id_order'); $user_id = getVal('user_id'); if ($this->id_order) { $this->order = new Order($this->id_order); $this->order->assignUser($user_id > 0 ? $user_id : null); if ($user_id == -1) { $this->updateSQL('orders', ['invoice_name' => 'Kamenný obchod'], ['id' => $this->id_order]); } $this->getCompleteOrder(); $this->addErrorInfo(self::RESULT_TYPE_OK, 'Načteno.'); return; } else { $this->clearSessions(); } $this->addErrorInfo(self::RESULT_TYPE_OK, 'Odebráno.'); } public function handleAddCoupon() { $this->id_order = getVal('id_order'); if (empty($this->id_order)) { $coupon = getVal('coupon_number'); // Pro odsrtaňování kuponu mu stačí dát ID -1 if (!empty($coupon)) { if ($coupon == '-1') { unset($_SESSION['coupon']); } else { $_SESSION['coupon'] = $coupon; } } // kontrola existujícího kupónu je zajištěna autocompletem // po přidání do session se při vytváření objektu košíku sama třída košíku postará o přidání kupónu $this->createCart(); } // Kupón nelze přidávat k již vytvořené objednávce, lze pouze do košíku } protected function clearSessions() { unset($_SESSION['priceLevel']); unset($_SESSION['coupon']); unset($_SESSION['pos_user_id']); } public function handleClearOrder() { $this->clearSessions(); $cart = new PosCart(); $cart->setPurchaseUtil(ServiceContainer::getService(\KupShop\OrderingBundle\Util\Purchase\PurchaseUtil::class)); $cart->clear(); } public function handleExtendSession() { echo 'ok'; } public function handleStorno() { $this->id_order = getVal('id_order'); if (!empty($this->id_order)) { $this->order = new Order($this->id_order); $this->getCompleteOrder(); } } public function handleMoveMoney() { $value = getVal('value'); $descr = getVal('descr'); global $adminID; if (!empty($value)) { if ($value > 0) { $method = 4; } else { $method = 5; } $fields = ['price' => $value, 'method' => $method, 'note' => $descr, 'date' => date('Y-m-d H:i:s'), 'admin' => !empty($adminID) ? $adminID : null, ]; $ret = $this->insertSQL('order_payments', $fields); if ($ret) { $this->addErrorInfo(self::RESULT_TYPE_OK, ($value > 0) ? 'Peníze vloženy' : 'Peníze vybrány'); } else { $this->addErrorInfo(self::RESULT_TYPE_ERROR, 'Chyba operace. Zkuste to za okamžik znovu.'); } } } protected function createCart() { global $cfg; if (empty($this->cart)) { $this->cart = new PosCart(); } $this->cart->setPurchaseUtil(ServiceContainer::getService(\KupShop\OrderingBundle\Util\Purchase\PurchaseUtil::class)); $this->cart->createFromDB(); if (!empty($_SESSION['coupon'])) { $this->cart->addCoupon($_SESSION['coupon']); } if (empty($cfg['Modules']['pos']['id_transport'])) { throw new RuntimeException('Missing pos id_transport in config'); } else { $this->cart->setTransport($cfg['Modules']['pos']['id_transport']); } if (!empty($ctrl['dealerPricelevel']['discount'])) { $this->cart->discounts = $ctrl['dealerPricelevel']['discount']; } } protected function getCompleteOrder() { if (!$this->order) { $this->order = new Order($this->id_order); $this->order->createFromDB($this->id_order); } $this->order->fetchItems(); $this->order->createFromDB($this->order->id); if ($this->order->id_user) { $this->user = User::createFromId($this->order->id_user); $this->user->activateUser(); $userContext = \KupShop\KupShopBundle\Util\Contexts::get(\KupShop\KupShopBundle\Context\UserContext::class); $userContext->activate($this->user); } $this->order['totalPricePayNoVat'] = toDecimal(0); $this->order['totalPriceWithVat'] = toDecimal($this->order->total_price); $this->order['totalPricePay'] = toDecimal($this->order->getRemainingPayment()); $this->order->products = []; // Z důvodu sjednocení - košík používá "products", kdežto objednávka "items" , to samé s price, title, .. foreach ($this->order->items as &$item) { $item['title'] = $item['descr']; $this->order['totalPricePayNoVat'] = $this->order['totalPricePayNoVat']->add($item['value_without_vat_no_rounding']->mul(toDecimal($item['pieces']))); // mozna neni potreba - pripadne odstranit $item['price']['price_with_vat'] = $item['value_with_vat']; $item['price']['price_without_vat'] = $item['value_without_vat']; $item['price']['value_with_vat'] = $item['value_with_vat']; $item['price']['value_without_vat'] = $item['value_without_vat']; $item['totalPrice']['value_with_vat'] = $item['total_price']['value_with_vat']; $item['totalPrice']['value_without_vat'] = $item['total_price']['value_without_vat']; $item['inStore'] = $item['in_store']; $this->order->products[$item['id']] = $item; $this->order->products[$item['id']]['id'] = $item['id_product']; } unset($this->order->items); } protected function getDefaultCtrl() { return [ 'id_level' => 0, 'discount' => 0, 'discount_discount' => 0, 'add' => false, 'title' => 'Pokladna', 'unit' => 'perc', 'sections' => [], 'products' => [], 'producers' => [], ]; } protected function loadUser() { global $ctrl; $userKey = Cart::getCartID(); // Kvůli classCart, která načítá uživatele z ctrl['id'] $ctrl['id'] = null; $user_id = getVal('user_id', null, getVal('pos_user_id', $_SESSION, null)); if (!empty($user_id)) { $_SESSION['pos_user_id'] = $user_id; } $ctrl['dealerPricelevel'] = null; if ($user_id == -1) { $this->user = null; } if ($user_id > 0) { $this->user = User::createFromId($user_id); if ($this->user) { $this->user->activateUser(); } $userContext = \KupShop\KupShopBundle\Util\Contexts::get(\KupShop\KupShopBundle\Context\UserContext::class); $userContext->activate($this->user); } } public const RESULT_TYPE_OK = 1; public const RESULT_TYPE_ERROR = 0; /** * @return ArrayAccess */ protected function getResultInfo() { return $this->result_info; } protected function addErrorInfo($type, $msg) { if (!empty($type)) { $this->result_info['type'] = self::RESULT_TYPE_OK; $this->result_info['msg'] = $msg; } else { $this->result_info['type'] = self::RESULT_TYPE_ERROR; $this->result_info['msg'] = 'Chyba: '.(($type) ? $type : $msg); } } protected function editOrderEvent() { $this->order->createFromDB($this->id_order); $this->order->fetchItems(); $eventDispatcher = ServiceContainer::getService('event_dispatcher'); $event = new OrderEvent($this->order); $eventDispatcher->dispatch($event, OrderEvent::ORDER_EDITED); } protected function getErrorMsg($error) { switch ($error) { // Discount case 2: return translate_shop('error', 'order')['failed']; break; // User errors case 5: return translate_shop('error', 'order')['missing_transport']; break; case 6: return translate_shop('error', 'user')['missing_name_invoice']; break; case 7: return translate_shop('error', 'user')['missing_street_invoice']; break; case 8: return translate_shop('error', 'user')['missing_city_invoice']; break; case 9: return translate_shop('error', 'user')['missing_zip_invoice']; break; case 10: return translate_shop('error', 'user')['missing_email_invoice']; break; case 11: return translate_shop('error', 'user')['missing_phone_invoice']; break; case 12: return translate_shop('error', 'order')['missing_products']; break; case 13: return translate_shop('error', 'user')['false_zip']; break; case 14: return translate_shop('error', 'user')['false_phone']; break; case 15: return replacePlaceholders( translate_shop('error', 'user')['login_exists'], ['URL' => path('kupshop_user_login_login')] ); break; case 16: return translate_shop('error', 'user')['false_length_passw']; break; case 17: return translate_shop('error', 'order')['false_country']; break; case 18: return translate_shop('error', 'order')['disabled_country']; break; case 19: return translate_shop('error', 'order')['invalid_variation']; break; } return 'Unknown error'; } protected function checkBuyingProcess() { return true; } protected function changeOrderStatus() { $statusHandled = findModule('pos', 'status_handled', getStatuses('handled')[0]); $this->order->changeStatus($statusHandled, '', false); } }