$value) { if ($key == 'data') { $this->custom_data = $value; } else { $this->$key = $value; } } if (!empty($this->price_registered) && User::getCurrentUser()) { $this->price = $this->price_registered; } // Potrebuju menu z dopravy, nechci tu z delivery pricelistu $this->deliveryCurrency = $this->currency; if (findModule(Modules::DELIVERY_PRICELIST)) { if (isset($this->id_pricelist) && !empty($this->pricelist_currency)) { $this->currency = $this->pricelist_currency; } } $priceUtil = ServiceContainer::getService(PriceUtil::class); if ($this->getPriceDontCountinFrom() !== null) { $this->price_dont_countin_from = new Price(toDecimal($this->getPriceDontCountinFrom()), $priceUtil->getCurrencyById($this->delivery_type_currency), 0); } if (User::getCurrentUser() && $this->price_dont_countin_from_reg) { $this->price_dont_countin_from = new Price(toDecimal($this->price_dont_countin_from_reg), $priceUtil->getCurrencyById($this->delivery_type_currency), 0); } if ($this->getPriceDontCountinFromDelivery() !== null) { $this->price_dont_countin_from_delivery = new Price(toDecimal($this->getPriceDontCountinFromDelivery()), $priceUtil->getCurrencyById($this->deliveryCurrency), 0); } if (User::getCurrentUser() && $this->price_dont_countin_from_reg_delivery) { $this->price_dont_countin_from_delivery = new Price(toDecimal($this->price_dont_countin_from_reg_delivery), $priceUtil->getCurrencyById($this->deliveryCurrency), 0); } if ($this->price_dont_countin_from_reg_delivery !== null) { $this->price_dont_countin_from_reg_delivery = new Price(toDecimal($this->price_dont_countin_from_reg_delivery), $priceUtil->getCurrencyById($this->deliveryCurrency), 0); } if (!empty($this->custom_data) && is_string($this->custom_data)) { $this->custom_data = json_decode($this->custom_data, true); } } /** Get payment HTML when payment selected. */ public function getCartDescription() { if ($this->templateDescription) { $smarty = createSmarty(false, true); $smarty->assign([ 'object' => $this, 'deliveryPrefix' => "delivery_data[{$this->id}]", ]); return $smarty->fetch($this->templateDescription); } return null; } public function getInitTemplate($smarty) { if ($this->templateInit) { $params = [ 'object' => $this, ]; $_smarty_tpl_vars = $smarty->tpl_vars; $ret = $smarty->_subTemplateRender($this->templateInit, $smarty->cache_id, $smarty->compile_id, 0, null, $params, 0, false); $smarty->tpl_vars = $_smarty_tpl_vars; return $ret; } return null; } public function getName() { return static::$className; } public function getClassName() { return static::$className; } public function isInPerson() { return static::$type === self::TYPE_IN_PERSON; } public function isVirtual() { return static::$type === self::TYPE_VIRTUAL; } public function getType() { return static::$type; } public function __construct() { $this->loadConfig(); } public function loadConfig() { if (!static::$className) { return; } global $cfg; $this->config = array_merge($this->config, getVal(get_class($this), $cfg['Modules']['deliveries'] ?? [], [])); } public function storeDeliveryInfo($data) { $this->data = []; if (findModule(Modules::DELIVERY_PRICELIST)) { $this->data += $this->getPackageInfo(); } return $this->data; } public function loadDeliveryInfo($data) { foreach ($data as $key => $value) { $this->$key = $value; } $this->data = $data; } public function printDeliveryInfo() { return ''; } public function getPointId() { return null; } public function setPointId($id): self { if ($this->getType() === Delivery::TYPE_POINT) { throw new RuntimeException('Not implemented!'); } return $this; } public function printDeliverySettings($order) { if (!$this->AdminTemplateSettings) { return ''; } $info = $this->getInfo(); $smarty = createSmarty(false, true); $smarty->assign([ 'info' => $info, 'delivery_data' => $this['data'], 'object' => $this, 'order' => $order, ]); return $smarty->fetch($this->AdminTemplateSettings); } /** * @param $order Order */ public function applyToOrder(&$data, $order) { if (findModule(Modules::PRODUCTS, Modules::SUB_WEIGHT)) { $this->totalWeight = $order->getTotalWeight(); $this->maxItemWeight = $order->getMaxItemWeight(); } } public function applyToCart(CartBase $cart) { if (findModule(Modules::PRODUCTS, Modules::SUB_WEIGHT)) { $this->totalWeight = $cart->getTotalWeight(); $this->maxItemWeight = $cart->getMaxItemWeight(); } } public function getPackageInfo() { if (isset($this->id_pricelist) && findModule(Modules::DELIVERY_PRICELIST)) { $priceList = $this->getPriceList(); $priceList->setDivide(false); $priceList->setPricelistId($this->id_pricelist); // set weight of cart $priceList->setTotalWeight($this->totalWeight); $priceList->setMaxItemWeight($this->maxItemWeight); return $priceList->getCountOfPackages(); } return []; } public function getPriceFromPricelist($pricelistId, $totalCartWeight, $maxItemWeight) { $priceList = $this->getPriceList(); $priceList->setDivide(false); $priceList->setPricelistId($pricelistId); // set weight of cart $priceList->setTotalWeight($totalCartWeight); $priceList->setMaxItemWeight($maxItemWeight); // set country $countryContext = Contexts::get(CountryContext::class); $priceList->setCountry($countryContext->getActive()->getId()); if ($priceList->isDeliverySupported()) { // check weightgroup maxweight if ($checkWg = $priceList->checkWeightGroup()) { // if weight of cart > maxweight of weightgroup && onMaxWeight is set to divide if ($checkWg === 'divide') { $priceList->setDivide(true); } } else { // Vahovy limit pro tento typ dopravy byl prekrocen $this->exception = new DeliveryException(translate_shop('weight_limit', 'delivery'), translate_shop('weight_limit_short', 'delivery'), DeliveryException::ERROR_DELIVERY_DISABLED); } } else { $this->exception = new DeliveryException(translate_shop('unavailable', 'delivery'), translate_shop('unavailable_short', 'delivery'), DeliveryException::ERROR_DELIVERY_DISABLED); } // returns price without vat return $priceList->getPriceWithoutVat(getAdminVat()['value'])->round(4); } public function processAdminWindowData($data) { return $data; } public function getDispatchDate() { $today = true; if (DateUtil::compareTimes($this->time_hours)) { $today = false; } return $this->calcWorkingDays($today ? 0 : 1, new DateTime()); } // Delivery date public function getDeliveryDate() { $days = $this->getDeliveryDays()['min'] ?? null; if ($days === null) { return false; } $fromDay = new DateTime(); return $this->calcWorkingDays($days, $fromDay); } // Returns delivery days - min & max public function getDeliveryDays(?DateTime $fromDay = null) { if (findModule(Modules::DELIVERY_PRICELIST)) { $days = $this->getDeliveryTimeFromPricelist(); } if (is_null($this->time_days) && !isset($days)) { return false; } if (!isset($days)) { $days = ['min' => intval($this->time_days)]; } $fromDay ??= new DateTime(); if ((!empty($this->time_hours) && DateUtil::compareTimes($this->time_hours, $fromDay->format('H:i:s'))) || !$this->isWorkday($fromDay) ) { $days['min']++; if (isset($days['max']) && $days['max'] != null) { $days['max']++; } } return $days; } /** * @param $date DateTime * * @return bool */ public function isWorkday($date, ?array $countries = null) { return DateUtil::isWorkday($date, $countries); } /** * @param $fromDay DateTime * * @return DateTime */ public function calcWorkingDays($days, $fromDay = null) { $dbcfg = Settings::getDefault(); switch ($dbcfg->delivery_config['holidaysByCountry'] ?? null) { // zeme doruceni case 'delivery': $countries = [Contexts::get(CountryContext::class)->getActiveId()]; break; // zeme doruceni + zeme expedice case 'dispatchDelivery': $countries = array_unique(['CZ', Contexts::get(CountryContext::class)->getActiveId()]); break; // zeme expedice default: $countries = ['CZ']; } return DateUtil::calcWorkingDays($days, $fromDay, [$this, 'isWorkday'], $countries); } public function getSupportedCountries() { if (isset($this->countries)) { return $this->countries; } $countries = $this->getCustomData()['restrictions']['countries'] ?? null; $supported = array_keys(Contexts::get(CountryContext::class)->getSupported()); if (!$countries) { $this->countries = $supported; } else { $invert = $this->getCustomData()['restrictions']['countries_invert'] ?? false; if ($invert) { $this->countries = array_diff($supported, $countries); } else { $this->countries = array_intersect($supported, $countries); } } return $this->countries; } protected function getRestrictionUtils() { if (!isset($this->restrictionUtils)) { $this->restrictionUtils = ServiceContainer::getService(RestrictionUtils::class); } return $this->restrictionUtils->setType('delivery'); } /** * @param Price $totalPrice * * @return bool */ public function accept($totalPrice, $freeDelivery, ?PurchaseState $purchaseState = null) { $this->totalPrice = $totalPrice; $this->freeShipping = $freeDelivery; if ($purchaseState) { $this->purchaseState = $purchaseState; } if ($freeDelivery || ($totalPrice && $this->getPriceDontCountinFromDelivery() && !PriceCalculator::firstLower($totalPrice, $this->getPriceDontCountinFromDelivery())) || ($totalPrice && $this->getPriceDontCountinFrom() && !PriceCalculator::firstLower($totalPrice, $this->getPriceDontCountinFrom()))) { $this->freeDelivery = true; } return $this->getRestrictionUtils()->checkHardRequirements($this); } public function isCountrySupported($countryCode) { if (!$countryCode) { return false; } return array_search(strtoupper($countryCode), $this->getSupportedCountries()) !== false; } /** * @return Delivery[] */ public static function getAll($figure = true) { $language = Contexts::get(LanguageContext::class)->getActiveId(); if (!isset(self::$cache[$language])) { $qb = sqlQueryBuilder()->select('dtd.*, MIN(dt.price_dont_countin_from) AS price_dont_countin_from, MIN(dt.price_dont_countin_from_reg) AS price_dont_countin_from_reg, dt.currency AS delivery_type_currency, dtd.price_dont_countin_from AS price_dont_countin_from_delivery, dtd.price_dont_countin_from_reg as price_dont_countin_from_reg_delivery, MAX(dt.figure) as figure, dtd.date_updated as date_updated') ->from('delivery_type_delivery', 'dtd') ->leftJoin('dtd', 'delivery_type', 'dt', 'dt.id_delivery = dtd.id') ->andWhere(Translation::coalesceTranslatedFields(DeliveryTypeDeliveriesTranslation::class)) ->groupBy('dtd.id') ->orderBy('dtd.position'); if (findModule(Modules::DELIVERY_PRICELIST)) { $qb->addSelect('dp.id_currency as pricelist_currency') ->leftJoin('dtd', 'delivery_pricelists', 'dp', 'dtd.id_pricelist = dp.id'); } $deliveries = []; foreach ($qb->execute() as $row) { $delivery = Delivery::getClass(getVal('class', $row)); $delivery->createFromArray($row); $deliveries[$delivery->id] = $delivery; } if (findModule(Modules::DELIVERY_PRICELIST)) { $deliveryPriceCalculator = ServiceContainer::getService(DeliveryPriceCalculator::class); $deliveryPriceCalculator->multiFetchDeliveryTime($deliveries); } self::$cache[$language] = $deliveries; } $deliveries = self::$cache[$language]; if ($figure) { $deliveries = array_filter($deliveries, function ($d) {return $d['figure'] == 'Y'; }); } return $deliveries; } public static function clearCache() { self::$cache = null; } public function getHeurekaDeliveryID() { if (!empty($this->heureka_delivery_id)) { return $this->heureka_delivery_id; } return null; } public function getZboziDeliveryID() { if (!empty($this->zbozi_delivery_id)) { return $this->zbozi_delivery_id; } return null; } public function getAdminWindowTemplate() { return $this->adminWindowTemplate; } public function requiresDeliveryAddress() { return static::$type === self::TYPE_ADDRESS; } protected function getDeliveryTimeFromPricelist() { if (findModule(Modules::DELIVERY_PRICELIST)) { return $this->pricelistDeliveryTime ?? null; } return null; } public function getTrackAndTraceLink($package_id, ?Order $order = null) { if (empty($this->track_and_trace)) { return null; } if (is_array($this->track_and_trace) && $order) { $tat = $this->track_and_trace[$order->delivery_country] ?? reset($this->track_and_trace); } else { $tat = is_array($this->track_and_trace) ? reset($this->track_and_trace) : $this->track_and_trace; } if (!empty($package_id)) { $tat = str_replace('[PACKAGE_ID]', $package_id, $tat); } return $tat; } public function setPrice(Price $price) { $this->priceCache = $price; } private function preparePrice() { if (isset($this->id_pricelist) && findModule(Modules::DELIVERY_PRICELIST)) { return $this->getPriceFromPricelist($this->id_pricelist, $this->totalWeight, $this->maxItemWeight); } return toDecimal($this->price); } public function getPrice(?float $vat = null): Price { $vat = $this->getDeliveryVat($vat); if (!empty($this->priceCache)) { if ($this->priceCache->getVat()->equals(toDecimal($vat))) { return $this->priceCache; } } $price = $this->preparePrice(); // free delivery if ($this->freeDelivery) { $price = toDecimal(0); } return $this->priceCache = $this->getDeliveryPrice($price, $vat); } public function getPriceSelected(DeliveryType $deliveryType) { $totalPrice = $this->totalPrice; $price = $this->preparePrice(); if ($this->freeShipping || ($totalPrice && $this->getPriceDontCountinFromDelivery() && !PriceCalculator::firstLower($totalPrice, $this->getPriceDontCountinFromDelivery())) || ($totalPrice && $deliveryType->getPriceDontCountinFrom() && !PriceCalculator::firstLower($totalPrice, $deliveryType->getPriceDontCountinFrom()))) { $price = toDecimal(0); } return $this->getDeliveryPrice($price, $deliveryType->vat); } protected function getDeliveryPrice($price, $vat = null) { $dbcfg = Settings::getDefault(); $vat = $this->getDeliveryVat($vat); $vatContext = Contexts::get(VatContext::class); $noVat = ($vat == 0) && ($vatContext->getActive() == $vatContext::NO_VAT); if (($dbcfg->delivery_config['vats_from_top'] ?? 'N') == 'Y') { $price = $price->addVat(getAdminVat()['value'])->removeVat($vat)->value(); if ($noVat) { $defaultVat = $vatContext->getDefault()['vat'] ?? 0; $price = $price->removeVat($defaultVat)->value(); } } $priceUtil = ServiceContainer::getService(PriceUtil::class); $currency = $priceUtil->getCurrencyById($this->currency); $price = new Price($price, $currency, $vat); $activeCurrency = Contexts::get(CurrencyContext::class)->getActive(); $price = PriceCalculator::convert($price, $activeCurrency); $priceWithoutVat = $price->getPriceWithoutVat(); // kvuli spravnemu zaokrouhleni $price = new Price($priceWithoutVat, $activeCurrency, $price->getVat()); return $price; } protected function getDeliveryVat($vat = null) { $dbcfg = Settings::getDefault(); return (($dbcfg->delivery_config['from_products'] ?? 'N') == 'Y' && $this->purchaseState) ? $this->purchaseState->getDeliveryVat() ?? $vat ?? getVat() : $vat ?? getVat(); } public function getCustomData() { return $this->custom_data; } /** * @return Price */ public function getPriceDontCountinFromDelivery($reg = null) { if ($reg === true) { return $this->price_dont_countin_from_reg_delivery; } return $this->price_dont_countin_from_delivery; } /** * @return Price */ public function getPriceDontCountinFrom() { return $this->price_dont_countin_from; } /** * Implements ArrayAccess interface. */ public function offsetSet($offset, $value): void { $this->{$offset} = $value; } public function offsetExists($offset): bool { return isset($this->{$offset}); } public function offsetUnset($offset): void { unset($this->{$offset}); } public function offsetGet($offset): mixed { if ($offset == 'price') { return ServiceContainer::getService(\KupShop\KupShopBundle\Wrapper\PriceWrapper::class)->setObject($this->getPrice()); } if ($offset == 'photo') { return $this->getPhoto(); } return isset($this->{$offset}) ? $this->{$offset} : null; } public function setException(Exception $exception) { $this->exception = $exception; } public function check(Cart $cart) { $deliveryAddress = array_merge($cart->invoice, array_filter($cart->delivery)); if ($cart->hasData('user') && $cart->hasData('delivery', false) && !$this->isCountrySupported($deliveryAddress['country'] ?? '')) { return 18; } if ($this->exception && $cart->hasData('delivery', false)) { throw $this->exception; } try { $this->checkRestrictions($cart->getPurchaseState()); } catch (DeliveryException $exception) { $this->exception = $exception; throw $this->exception; } try { ServiceContainer::getService('event_dispatcher')->dispatch( new DeliveryCheckEvent($this, $cart) ); } catch (DeliveryException $e) { $this->exception = $e; throw $e; } return null; } public function checkRestrictions(PurchaseState $purchaseState) { $customData = $this->getCustomData(); $this->checkSoftRestrictions($purchaseState); if ($productsFilter = $customData['restrictions']['productsFilter'] ?? null) { $this->getRestrictionUtils()->checkProductsFilter($productsFilter, $purchaseState->getProductList()); } return true; } public function checkSoftRestrictions(PurchaseState $purchaseState): void { $customData = $this->getCustomData(); if (($customData['restrictions']['enabled'] ?? false) !== 'Y') { return; } if (!isset($this->restrictionParams)) { $this->restrictionParams = $this->getRestrictionUtils() ->createParamsFromConfig($this->config ?? [], $customData['restrictions'] ?? []); } $this->getRestrictionUtils()->checkSoftRequirements( $this->getRestrictionParams($purchaseState), $this->restrictionParams ); } protected function getRestrictionParams(PurchaseState $purchaseState): RestrictionParams { return $purchaseState->getDeliveryRestrictionParams(); } public function checkSelected(Cart $cart, ?Payment $payment = null) { // check selected combination of delivery and payment $error = $this->check($cart); if ($error) { return $error; } if ($payment) { try { $error = $payment->check($cart); } catch (PaymentException $exception) { $payment->exception = $exception; throw $payment->exception; } } if (($cart->max_step != 0) && ($payment instanceof Dobirka)) { if (isset($this->restrictionParams) && ($limit = $this->restrictionParams->getMaxCOD())) { // zapnute Přepravní omezení a nastavena Maximální hodnota dobírky $rParams = new RestrictionParams(); $rParams->setMaxCOD($cart->getPurchaseState()->getTotalPrice()->getPriceWithVat()->asFloat()); try { $this->getRestrictionUtils()->checkSoftRequirements($rParams, $this->restrictionParams); } catch (DeliveryException $e) { $printPrice = printPrice($limit); throw new PaymentException( sprintf($e->getMessage(), $printPrice), sprintf($e->getShortMessage(), $printPrice), DeliveryException::ERROR_DELIVERY_DISABLED ); } } } return $error; } public function zipExists($invoice) { if (!empty($invoice['zip']) && in_array($invoice['country'], ['CZ', 'SK'])) { $zipInvoice = sqlQueryBuilder()->select('zip')->from('kupshop_shared.delivery_zip_codes') ->where(\Query\Operator::equals(['zip' => $invoice['zip']])) ->andWhere(\Query\Operator::equals(['country' => $invoice['country']])) ->execute()->fetch(); if (!$zipInvoice) { throw new \KupShop\OrderingBundle\Exception\DeliveryException(translate('false_zip', 'order_error')); } } } public static function isEnabled() { return true; } public function importFromBalikobot($url = null, $where = null) { increaseMemoryLimit(1024); $url = ($url ?: static::$jsonDeliveryBalikobot); if (empty($url)) { return false; } $json = file_get_contents($url); $data = json_decode_strict($json, true); if (!empty($data['branches'])) { sqlGetConnection()->transactional(function () use ($data, $where) { sqlQueryBuilder()->delete(static::$tableName)->andWhere($where)->execute(); $tableColumns = static::$tableColumns ?? []; if (empty($tableColumns)) { $tableColumns = [ 'type' => 'type', 'id' => 'id', 'zip' => 'zip', 'name' => 'name', 'street' => 'street', 'city' => 'city', 'country' => 'country', ]; } $excludeColumns = static::$excludeColumns ?? []; $faultyBranches = []; foreach ($data['branches'] as $id => $branch) { $insert = []; $custom_data = []; foreach ($branch as $title => $value) { if (array_key_exists($title, $tableColumns)) { if ($title == 'zip') { $value = str_replace(' ', '', $value); } $insert[$tableColumns[$title]] = $value; } elseif (!in_array($title, $excludeColumns)) { $custom_data[$title] = $value; } } $insert = $this->importCustomData($custom_data, $insert); try { sqlQueryBuilder()->insert(static::$tableName)->directValues($insert)->execute(); } catch (\Exception $exception) { $faultyBranches[] = $insert; } } if (!empty($faultyBranches)) { addActivityLog(ActivityLog::SEVERITY_NOTICE, ActivityLog::TYPE_SYNC, 'Duplicitní výdejní místa dopravy', $faultyBranches); } }); return true; } return false; } public function importCustomData($custom_data, $insert) { if ($custom_data) { $insert['data'] = json_encode($custom_data); } return $insert; } public function getPriceList(): DeliveryPriceCalculator { if (!$this->pricelist) { $this->pricelist = ServiceContainer::getService(DeliveryPriceCalculator::class); } return $this->pricelist; } public function getPhoto() { if (empty($this->id)) { return null; } $icon = $this->getPhotoPath(); return getImage($this->id, basename($icon), dirname($icon), 9, $this->name, strtotime($this->date_updated)); } public function getPhotoPath() { if ($this->photo) { return '../delivery/'.$this->photo; } return $this->defaultIcon; } public function __toString() { return static::class; } } if (empty($subclass)) { class Delivery extends DeliveryBase { } }