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;
}
}