session = $session; $this->cart = $cart; } /** * @required */ public function setUserContent(UserContent $userContext): void { $this->userContent = $userContext; } /** * @required */ public function setUserContext(UserContext $userContext): void { $this->userContext = $userContext; } /** * @required */ public function setPriceLevelContext(PriceLevelContext $pricelistLevelContext): void { $this->priceLevelContext = $pricelistLevelContext; } #[Required] final public function setCartImporter(CartImporter $cartImporter): void { $this->cartImporter = $cartImporter; } public function getBodyVariables() { $vars = parent::getBodyVariables(); $this->act = $this->request->get('act', null); $this->error = getVal('error'); // Act Eraseall if ($this->act == 'eraseall') { $this->cart->clear(); redirection('REFERER'); } $this->cart->load(); // Set Step $newStepName = $this->cart->visitStep($this->stepName, $this->request->get('OrderNext', false) || $this->request->get('SubmitOrder', false), $this->request->getMethod() == 'POST'); if ($newStepName) { $step = $this->cart->findStep($newStepName); redirection($step['url']); } $this->step = $this->cart->findStep($this->stepName); $this->template = $this->step['template']; $this->title = $this->step['title']; // Act Erase if ($this->act == 'erase') { $id = $this->request->get('IDInCart'); if ($id) { $this->cart->deleteItem($id); } } // Act recount $products = getVal('products'); if (!empty($products)) { $this->cart->recalculate($products); if (findModule(\Modules::JS_SHOP)) { $this->session->set(JsShopRefreshListener::SESSION_NAME, true); } } // Act add if ($this->act == 'add') { $this->actAdd(); } // Act import if ($this->act == 'import') { $this->actImport(); } // Act fix out of stock if ($this->act == 'fixOutOfStock') { $updated = $this->cart->ensureCartInStore(); addUserMessage(translate('fixedOutOfStock', 'ordering'), 'success', ['id' => 'fixedOutOfStock', 'products' => $updated]); redirection('REFERER'); } // Discounts if ($couponNo = getVal('couponNo')) { $vars['couponNo'] = $couponNo; if (getVal('AddDiscount')) { $this->applyDiscountCoupon(strval($couponNo)); redirection($this->step['url']); } } if ($this->stepName == 'cart' && $couponNo = getVal('ApplyCouponNo')) { if ($this->applyDiscountCoupon(strval($couponNo))) { redirection($this->step['url']); } $vars['couponNo'] = $couponNo; } if (getVal('DeleteDiscount')) { $couponNo = null; if (getVal('couponNo')) { $couponNo = getVal('couponNo'); } $this->cart->deleteCoupon($couponNo); $this->cart->save(); redirection($this->step['url']); } // Set addresses $invoice = getVal('invoice'); $delivery = getVal('delivery'); if (!empty($invoice) || !empty($delivery)) { if (getVal('different_address') === '0') { $delivery = []; $this->cart->delivery = []; } if (getVal('firm_address') === '0') { $invoice['firm'] = ''; $invoice['ico'] = ''; $invoice['dic'] = ''; $invoice['copy_email'] = ''; } try { $addrError = $this->cart->updateAddresses($invoice, $delivery); if (!$this->errorId) { $this->errorId = $addrError; } } catch (\Exception $e) { addUserMessage($e->getMessage(), 'danger'); if (isLocalDevelopment()) { throw $e; } if (!$this->errorId) { $this->errorId = -1; } } } $password = getVal('password'); if ($password !== null) { $this->setFutureUser($password); $pwdError = $this->cart->registerUser($password); if (!$this->errorId) { $this->errorId = $pwdError; } } // Set delivery and payment $delivery_id = getVal('delivery_id'); $payment_id = getVal('payment_id'); if (!empty($delivery_id) || !empty($payment_id)) { $this->cart->setDeliveryAndPayment($delivery_id, $payment_id); } // Set transport $transport = getVal('transport'); if ($transport) { $this->cart->setTransport($transport); } // Set note $note = getVal('noteUser'); if (!is_null($note)) { $this->cart->setNote($note); } // Set note $userOrderNo = getVal('userOrderNo'); if (!is_null($userOrderNo)) { $this->cart->setUserOrderNo($userOrderNo); } // Set newsletter $newsletter = getVal('news'); if (!is_null($newsletter)) { $this->cart->setNewsletter(!empty($newsletter)); } // Set cart data $cart_data = getVal('cart_data'); if (!is_null($cart_data)) { $this->cart->setData(null, $cart_data); } $actionHandlersData = getVal('action_handler'); if ($actionHandlersData !== null) { $this->cart->setActionHandlersData( array_replace( $this->cart->getActionHandlersData(), $actionHandlersData, ) ); $this->cart->invalidatePurchaseState(); } if (($this->stepName == 'summary') || ($this->stepName == 'cart' && empty($this->act))) { $this->cart->invalidatePurchaseState(); } // Load cart from DB $this->cart->createFromDB(); // Save to session $this->cart->save(); try { $inStoreError = QueryHint::withRouteToMaster(function () { return $this->cart->checkCartItems(); }); if (!$this->errorId) { $this->errorId = $inStoreError; } } catch (\Exception $e) { // rethrow RedirectException if ($e instanceof RedirectException) { throw $e; } // vyskocila DeliveryException nebo PaymentException, coz znamena, ze mam vybranou dopravu / platbu, ktera je disabled / ma nejakou chybu // takovou dopravu / platbu kvuli novemu kosiku nechci mit zaselectovanou, takze ji odeselectuju, aby se zobrazily vsechny moznosti, ktere jsou dostupne // zkousime to vyhazovat jen kdyz to je DELIVERY_DISABLED/HARD exception, nechcem resetovat delivery type, kdyz je spatny PSC if ($e instanceof DeliveryException && $e->isDeliveryDisabled()) { $this->cart->resetDeliveryType(!($e instanceof PaymentException)); } addUserMessage($e->getMessage(), 'danger', method_exists($e, 'getData') ? $e->getData() : []); if (!$this->errorId) { $this->errorId = -1; } } if (\Settings::getDefault()['cart_price_change_notification'] != 'N') { // notifikace o zmene ceny // pokud mam predesly purchase state a nejsem na prvnim kroku kosiku nebo jdu na dalsi step, a neodesilam // objednavku, tak provedu kontrolu zmeny cen v kosiku a pripadne zobrazim info o zmene if ($this->cart->getPreviousPurchaseState() && ($this->stepName !== 'cart' || getVal('OrderNext')) && !getVal('SubmitOrder')) { foreach ($this->cart->getPurchaseState()->getProducts() as $purchaseItem) { // pokud v previous statu nemam polozku, tak skipuju if (!($previousPurchaseItem = ($this->cart->getPreviousPurchaseState()->getProducts()[$purchaseItem->getId()] ?? false))) { continue; } $newPrice = $purchaseItem->getPiecePrice(); $prevPrice = $previousPurchaseItem->getPiecePrice(); // prevedu ceny do stejne meny PriceCalculator::makeCompatible($newPrice, $prevPrice, false); // spocitam rozdil mezi novou cenou a starou cenou $diff = $newPrice->getPriceWithVat()->sub($prevPrice->getPriceWithVat())->abs(); // pokud se cena zmenila o vic jak 0.1, tak zobrazim hlasku o zmene ceny if ($diff->asFloat() >= 0.1) { addUserMessage( sprintf(translate('error', 'order')['price_changed'] ?? 'Product price changed', $purchaseItem->getName(), "{$previousPurchaseItem->getPiecePrice()->getPriceWithVat()->asString()} {$previousPurchaseItem->getPiecePrice()->getCurrency()->getSymbol()}", printPrice($newPrice)) ); } } } } if (!empty($this->errorId)) { $this->unsetFutureUser(); } if (empty($this->errorId) && getVal('SubmitOrder')) { // re-create PurchaseState before order submit $this->cart->invalidatePurchaseState(); $this->cart->getPurchaseState(); $this->submitOrder(); } // Should we redirect to another address if (empty($this->errorId)) { if (getVal('OrderNext')) { $this->step = $this->cart->findNextStep($this->stepName); $this->cart->save(); redirection($this->step['url']); } } if (getVal('OrderPrevious')) { $this->step = $this->cart->findPreviousStep($this->stepName); $this->cart->save(); redirection($this->step['url']); } $this->getError(); $vars['returnNav'] = $this->getReturnNav(); $vars['error'] = $this->error; $vars['act'] = $this->act; // $vars['cart'] = $this->cart; // v SMARTY_DEBUG = 3 to delalo: Recursion detected $vars['step'] = $this->step; $vars['stepName'] = $this->stepName; $vars['error_id'] = $this->errorId; $vars['lowestDeliveryPrice'] = $this->cart->getLowestDeliveryPrice(); foreach ($vars as $key => $value) { $this->cart->$key = &$value; unset($value); } return $this->cart; } public function getPaymentPrice($paymentId) { $deliveryType = $this->cart->getDeliveryType(); if ($deliveryType) { $deliveryId = $deliveryType->id_delivery; } else { $deliveryId = $this->cart->delivery_id; } foreach ($this->cart->delivery_types as $type) { if (($type->id_payment == $paymentId) && (($type->id_delivery == $deliveryId) || empty($deliveryId))) { $price = PriceCalculator::sub($type->getPrice(), $type->getDelivery()->getPrice($type->vat ?? null)); return ServiceContainer::getService(PriceWrapper::class)->setObject($price); } } return null; } public function getDeliveryPrice($deliveryId) { $deliveryType = null; if ($selectedDeliveryType = $this->cart->getDeliveryType()) { // uz je vybrana nejaka kombinace dopravy a platby // zkusime najit deliveryType s dopravou $deliveryId a vybranou platbou foreach ($this->cart->delivery_types as $type) { if ($type->id_payment == $selectedDeliveryType->id_payment && $deliveryId == $type->id_delivery) { $deliveryType = $type; break; } } } if (!$deliveryType) { // zkusime najit prvni deliveryType s dopravou $deliveryId foreach ($this->cart->delivery_types as $type) { if ($deliveryId == $type->id_delivery) { $deliveryType = $type; break; } } } if ($deliveryType) { $deliveryType->getDelivery()->applyToCart($this->getCart()); $type_price = $deliveryType->getPrice(); $price = $deliveryType->price_delivery; if (PriceCalculator::firstGreater($price, $type_price)) { // cena za dopravu nemuze byt vetsi nez celkova cena zpusobu doruceni (kdyz je spatne predefinovana) $price = $type_price; } return ServiceContainer::getService(PriceWrapper::class)->setObject($price); } return null; } public function getTemplate(): string { if (\Settings::getDefault()->orders_closed == 'Y') { return 'ordersClosed.tpl'; } return parent::getTemplate(); } public function submitOrder() { try { /** @var \Order $order */ $order = $this->cart->submitOrder(); } catch (\Doctrine\DBAL\Exception\DriverException $ex) { $error_code = $ex->getErrorCode(); $order = -1; if (($error_code == 1205) || ($error_code == 1213)) { addUserMessage(translate('order_failed_try_again', 'order_error'), 'danger'); } else { addUserMessage(translate('order_failed_permanent', 'order_error'), 'danger'); $sentryLogger = ServiceContainer::getService(SentryLogger::class); $sentryLogger->captureException($ex); } } catch (\RuntimeException|CartValidationException|DeliveryException|DICException $e) { addUserMessage($e->getMessage(), 'danger'); $order = -1; } if (is_object($order)) { // reset userContent data when order is sent $this->userContent->setData('cart', null); if (findModule(\Modules::JS_SHOP)) { $this->session->set('orderCreated', true); } $payment = $order->getDeliveryType()->getPayment(); if (findModule('orders', 'payment_redirect') && $payment->hasOnlinePayment()) { $pathData = ['id' => $order->id]; if (!\User::getCurrentUser()) { $pathData['cf'] = $order->getSecurityCode(); } throw new RedirectException( path('payment-redirect', $pathData) ); } else { throw new RedirectException($order->getDetailUrl(1)); } } $this->errorId = $order; } public function getError() { switch ($this->errorId) { // Discount case 2: $this->error = translate('error', 'order')['failed']; break; // User info case 5: $this->error = translate('error', 'order')['missing_transport']; break; case 6: $this->error = translate('error', 'user')['missing_name_invoice']; break; case 7: $this->error = translate('error', 'user')['missing_street_invoice']; break; case 8: $this->error = translate('error', 'user')['missing_city_invoice']; break; case 9: $this->error = translate('error', 'user')['missing_zip_invoice']; break; case 10: $this->error = translate('error', 'user')['missing_email_invoice']; break; case 11: $this->error = translate('error', 'user')['missing_phone_invoice']; break; case 12: $this->error = translate('error', 'order')['missing_products']; break; case 13: $this->error = translate('error', 'user')['false_zip']; break; case 14: $this->error = translate('error', 'user')['false_phone']; break; case 15: $this->error = replacePlaceholders( translate('error', 'user')['login_exists'], ['URL' => path('kupshop_user_login_login')] ); break; case 16: $this->error = translate('error', 'user')['false_length_passw']; break; case 17: $this->error = translate('error', 'order')['false_country']; break; case 18: $this->error = translate('error', 'order')['disabled_country']; break; case 19: $this->error = translate('error', 'order')['invalid_variation']; break; case 20: $this->error = translate('error', 'user')['login_exists_edit']; break; // Products // TODO: uz by se nemelo nikde pouzivat, mohlo by se zahodit :-) case 30: $products = $this->cart->getErrors(Cart::$ERR_OUT_OF_STOCK); $productName = ''; if (count($products) > 0) { $product = new \Product(); $product->createFromDB(key($products)); $variaton = reset($products); if (!is_null($variaton)) { $product->fetchVariations(); } if (($variaton = $product->findVariation($variaton)) !== null) { $productName = $product->title.' '.$variaton['title']; } else { $productName = $product->title; } } addUserMessage(str_replace('{PRODUCT}', $productName, translate('error', 'order')['not_stocked_products']), 'danger', ['id' => 'outOfStock', 'products' => $products]); break; case 31: $products = $this->cart->getErrors(Cart::$ERR_NOT_SELECTED_VARIATION); $productName = ''; if (count($products) > 0) { $product = new \Product(); $product->createFromDB(reset($products)); $productName = $product->title; } $this->error = str_replace('{PRODUCT}', $productName, translate('error', 'order')['invalid_products']); break; case 32: $this->error = 'Pro odeslání objednávky musíte být přihlášeni.'; break; case 33: $products = $this->cart->getErrors(Cart::$ERR_CANNOT_BE_PURCHASED); $product_names = []; foreach ($products as $id => $product) { $product = new \Product(); $product->createFromDB($id); $product_names[] = $product->title; } addUserMessage( str_replace('{PRODUCTS}', implode(', ', $product_names), translate('error', 'order')['cannot_be_purchased']), 'danger', ['id' => 'cannotBePurchased', 'products' => $products] ); break; } $this->error = replacePlaceholders($this->error, [], function ($placeholder) { switch ($placeholder) { case 'step_delivery': return createScriptURL(['s' => 'ordering', 'step' => 'delivery']); case 'step_user': return createScriptURL(['s' => 'ordering', 'step' => 'user']); } return ''; }); } public function actImport() { $count = 0; $errors = []; $data = getVal('data'); if ($data) { [$tmpCount, $tmpErrors] = $this->cartImporter->importFromString($data); $count += $tmpCount; $errors = array_merge($errors, $tmpErrors); unset($data, $tmpCount, $tmpErrors); } foreach ($_FILES as $file) { if (!$file['tmp_name']) { continue; } [$tmpCount, $tmpErrors] = $this->cartImporter->importFromFile($file['tmp_name'], $file['name']); $count += $tmpCount; $errors = array_merge($errors, $tmpErrors); unset($tmpCount, $tmpErrors); } if (isAjax()) { exit( json_encode( [ 'result' => empty($errors), 'errors' => $errors, ] ) ); } if ($count) { addUserMessage(sprintf(translate('importedItems', 'ordering'), $count), 'success'); } else { addUserMessage(translate('importedNoItem', 'ordering'), 'danger'); } if ($errors) { $message = sprintf(translate('importedWithError', 'ordering'), count($errors)).':
'.join('
', array_slice($errors, 0, 10)); addUserMessage($message, 'warning'); } redirection('REFERER'); } /** * @throws \SmartyException * @throws ResponseException */ public function actAdd() { global $cfg; $params = [ 'id_product' => intval($this->request->get('IDproduct', null)), 'id_variation' => $this->request->get('IDvariation', null), 'pieces' => max(floatval($this->request->get('No', null)), 0), 'note' => $this->request->get('note', ''), ]; $products = []; $items = getVal('items'); if (!empty($items)) { foreach ($items as $id_variation => $item) { if ($item['pieces'] > 0) { $products[] = array_merge($params, $item); } } } else { $products[] = $params; } $newIds = []; foreach ($products as $product) { $item = $product; try { $id = $this->cart->addItem($item); } catch (InvalidCartItemException $e) { $id = 0; // ajax neumře, ale nic se nevloží do košíku } if ($id <= 0) { addUserMessage(translate('error', 'order')['invalid_variation'], 'success'); } $newIds[$id] = [ 'id_cart' => $id, 'quantity' => $product['pieces'], ]; } if (isAjax()) { $addedToCart = [ 'id_cart' => array_values($newIds)[0]['id_cart'], 'pieces' => array_values($newIds)[0]['quantity'], 'items' => $newIds, ]; $this->session->set('addedToCart', $addedToCart); require_once $cfg['Path']['shared_version'].'web/block.cartInfo.php'; $smarty = createSmarty(false, true); $this->cart->createFromDB(); $smarty->assign([ 'cartInfo' => block_cartInfo(), 'cart' => $this->cart, 'view' => $this, ]); throw new ResponseException(new Response($smarty->fetch('block.cartInfo.tpl'), 200)); } $redir = getVal('redir'); if (!empty($redir)) { $addedToCart = [ 'id_cart' => array_values($newIds)[0]['id_cart'], 'pieces' => array_values($newIds)[0]['quantity'], 'items' => $newIds, ]; $this->session->set('addedToCart', $addedToCart); } $next = getVal('next'); if ($next) { redirection($next); } // pokud mame po pridani presmerovat if (!empty($redir) && !empty($_SERVER['HTTP_REFERER'])) { redirection($_SERVER['HTTP_REFERER']); } } public function getCart() { return $this->cart; } public function setStepName($stepName) { $this->stepName = $stepName; } public function getReturnNav() { $retNav = [['text' => translate('returnNav', 'order')[1]]]; if ($this->stepName != 'cart') { $url = createScriptURL([ 'URL' => 'launch.php', 's' => 'cart', ]); $retNav[] = ['text' => $this->step['title']]; $retNav[0]['link'] = $url; } return getReturnNavigation(-1, 'NO_TYPE', $retNav); } public function __get($property) { if (property_exists($this, $property)) { return $this->$property; } } protected function setFutureUser(string $password): void { if ($password != '' && !empty($this->cart->invoice['email'])) { $this->priceLevelContext->activate(null); $user = new \User(); $user->email = $this->cart->invoice['email']; $this->userContext->activate($user); $user->activateUser(); } $this->cart->invalidatePurchaseState(); } protected function unsetFutureUser(): void { // fixuje bug, ze kdyz se chci registrovat, ale vyskoci mi chyba, tak se to tvari jako ze jsem prihlasenej if ($user = $this->userContext->getActive()) { if ($user->id <= 0) { $this->userContext->activate(false); } } } protected function applyDiscountCoupon(string $couponNumber): bool { $success = false; try { if ($this->cart->addCoupon(preg_replace('/[[:^print:]]/', '', $couponNumber))) { addUserMessage(translate('error', 'order')['coupon_added'], 'success', ['id' => 'coupon'.$couponNumber]); $success = true; } else { addUserMessage(translate('error', 'order')['false_coupon'], 'danger', ['id' => 'coupon'.$couponNumber]); } } catch (CartValidationException $e) { addUserMessage($e->getMessage(), 'danger', ['id' => 'coupon'.$couponNumber]); } $this->cart->save(); return $success; } }