Votre voiture
Marque et modèle
SkodaSkoda Karoq 2018-
Choisissez votre voiture
Choisir un modèle
Reset- Accessoires Universels
Acura
Alfa Romeo- Avatr
Audi
BMW
Bentley
BrillianceBugatti
Buick
BYD
ChanganCadillac
CheryChevrolet
Chrysler
Citroen
- Cowin
CupraDAF
Dacia
Daewoo
Daihatsu- Denza
Dongfeng
Datsun
DodgeDS Automobiles
FAWFerrari
Fiat
FiskerFord
GAC Motor (Trumpchi)
Geely
Genesis
Great WallGMC
Honda
HongqiHyundai
Hummer
JAC
JaguarJeep
JetourInfiniti
IsuzuIveco
KIA
Lamborghini
Lada
Lancia
LandRover
Leapmotor- Leopaard
Lexus
Li Auto
Lifan
Lincoln
LotusLucid
MAN
Maserati
Mazda
McLaren
MG
Mercedes-Benz
Mitsubishi
Mini
- Moskvitch
Nissan
Opel
Peugeot
Polestar
Porsche
Rivian- RAVON
Renault
Rolls-Royce
SaabScania
SinotrukSeat
Skoda
- Skoda Citigo 2011-2020
- Skoda Elroq 2025-
- Skoda Enyaq 2020-
- Skoda Fabia 2000-2007
- Skoda Fabia 2007-2014
- Skoda Fabia 2014-2021
- Skoda Fabia 2021–
- Skoda Kamiq 2019-
- Skoda Karoq 2018-vous êtes ici
- Skoda Kodiaq 2016-2023
- Skoda Kodiaq 2023-
- Skoda Octavia I Tour A4 1996-2010
- Skoda Octavia II A5 2006-2010
- Skoda Octavia II A5 2010-2013
- Skoda Octavia III A7 2013-2019
- Skoda Octavia IV A8 2020-
- Skoda Rapid 2012-2019
- Skoda Roomster 2006-2015
- Skoda Scala 2018-
- Skoda Superb 2001-2009
- Skoda Superb 2009-2015
- Skoda Superb 2015-2024
- Skoda Superb 2024-
- Skoda Yeti 2009-2017
- Skywell
Smart
SsangYong- StreetScooter
Subaru
Suzuki
Tesla
Toyota
Volkswagen
Volvo
VolgaXiaomi
Xpeng
Wuling
Zeekr
GAZ
UAZ
Catégorie
Catégorie et sous-catégorie
Logos AutomobilesMonogrammes & lettrages
Catégories
ResetAfficher tous les produits Vous ne devez pas choisir de catégorie- Ampoules auto
- Produits chimiques auto
- Éclairage automobile
- Accessoires d'intérieur
- Bavettes (garde‑boue)
- Déflecteurs & protections
- Barres de toit / porte-bagages de toit
- Pièces de carrosserie
- Pare‑buffles & marchepieds
- Tapis de voiture
- Kits carrosserie & style extérieur
- Accessoires tout‑terrain
- Accessoires auto universels
- Garnitures chromées
- Logos Automobiles
- Emblèmes & badges
- Cabochons de roue (centre)
- Monogrammes & lettrages
- Insonorisation & amortissement des vibrations
- Housses & bâches auto
- Plaques plastique ABS
- Jantes
Monogrammes & lettrages pour Skoda Karoq 2018-
Filtres actifs :Reset
@php use EvolutionCMS\Ddaudio\Facades\Domain; use EvolutionCMS\Ddaudio\Services\CustomerLoyaltyProgram; use EvolutionCMS\Ddaudio\Services\Product\StorefrontPricePresentation; use EvolutionCMS\Ddaudio\Services\Product\StorefrontFreeShipping; use EvolutionCMS\Ddaudio\Services\Product\StorefrontCardTerms; use EvolutionCMS\Ddaudio\Services\Product\StorefrontDeliveryTerms; $item = StorefrontDeliveryTerms::forProduct($item); $url = (string)($item['url'] ?? '#'); $title = trim((string)($item['pagetitle'] ?? '')); $normalizeImage = static function (mixed $image): string { $image = is_string($image) ? trim($image) : ''; return $image ==='' || preg_match('~^(?:https?:)?//|^data:|^/~i', $image) ? $image : '/' . ltrim($image, '/'); }; $sliderLarge = array_values((array)($item['sliderLarge'] ?? [])); $largeImages = []; $images = collect(array_values((array)($item['slider'] ?? []))) ->map(static fn(mixed $image, int $index): array => [$normalizeImage($image), $normalizeImage($sliderLarge[$index] ?? '')]) ->filter(static fn(array $pair): bool => $pair[0] !=='') ->unique(static fn(array $pair): string => $pair[0]) ->take(4) ->values() ->map(static function (array $pair) use (&$largeImages): string { if ($pair[1] !=='') { $largeImages[$pair[0]] = $pair[1]; } return $pair[0]; }) ->all(); $imageSrcset = static fn(string $image): string => isset($largeImages[$image]) ? $image . ' 320w, ' . $largeImages[$image] . ' 920w' : ''; $imageSizes ='(max-width: 575px) 100vw, (max-width: 1199px) 50vw, 360px'; if ($images === [] && is_string($item['image'] ?? null) && trim($item['image']) !=='') { $fallbackImage = trim($item['image']); $images[] = preg_match('~^(?:https?:)?//|^data:|^/~i', $fallbackImage) ? $fallbackImage : '/' . ltrim($fallbackImage, '/'); } $image = (string)($images[0] ?? ''); /* Фото немає — сірий логотип замість картинки товару. Клас is-placeholder не дає заглушці розтягнутись на всю плитку (див. card-product.scss). */ $imageIsPlaceholder = $image ===''; if ($imageIsPlaceholder) { $image = rtrim($storefrontThemeUrl, '/') . '/img/icons/logo-gray.svg'; } $hasImageSlider = count($images) > 1; $labels = (array)($item['labels'] ?? []); $rating = (array)($item['rating'] ?? []); $ratingValue = (float)($rating['rating'] ?? 0); $ratingTotal = 5.0; // total is the sum of votes, not the scale maximum. $ratingVotes = max(0, (int)($rating['votes'] ?? 0)); $ratingFilled = max(0, min(5, (int)round($ratingValue))); $currency = strtoupper((string)($_SESSION['currency'] ?? 'UAH')); $currencyLabel = \EvolutionCMS\Ddaudio\Support\StorefrontMoney::label($currency); $forSale = (bool)($item['forSale'] ?? false); $price = $item['price'] ?? null; $oldPrice = $item['price_old'] ?? null; $isAuthenticatedCustomer = (bool)evo()->getLoginUserID('web') && empty($_SESSION['webSellerId']); $pricePresentation = StorefrontPricePresentation::forProduct($item, $isAuthenticatedCustomer); $oldPrice = $pricePresentation['old_price']; $discountPercent = $pricePresentation['discount_percent']; $clientDiscountPercent = $pricePresentation['client_percent']; $isClientOnlyDiscount = $clientDiscountPercent > 0; $promoUntil = $pricePresentation['promo_until']; $formatCardAmount = static fn(float|int $amount): string => \EvolutionCMS\Ddaudio\Support\StorefrontMoney::format($amount); $showCardLoyaltyBonus = empty($_SESSION['webSellerId']) && Domain::isCountry('UA'); $stockCount = (int)($item['inStockCount'] ?? 0); $availabilityBadge = (array)($item['availabilityBadge'] ?? []); $availability = trim((string)($availabilityBadge['label'] ?? '')); $availabilityCta = (array)($availabilityBadge['cta'] ?? []); $availabilityCtaType = (string)($availabilityCta['type'] ?? ($stockCount > 0 ? 'cart' : 'request')); $isOrderable = (bool)($availabilityBadge['is_orderable'] ?? ($stockCount > 0)); $cardTerms = StorefrontCardTerms::forProduct($item); $productOrderId = (int)($item['productId'] ?? 0); $favoriteProductId = (int)($item['id'] ?? $productOrderId); if ((int)($item['template'] ?? 0) === 32) { $favoriteProductId = (int)($item['parent'] ?? $favoriteProductId); } $favoriteIds = array_map('intval', (array)($_SESSION['favorite'] ?? [])); $isFavorite = $favoriteProductId > 0 && in_array($favoriteProductId, $favoriteIds, true); $favoriteAddLabel = __('dd::global.favorite_add'); $favoriteRemoveLabel = __('dd::global.favorite_remove'); $canAddToCart = $forSale && $isOrderable && $availabilityCtaType ==='cart' && $productOrderId > 0; $canRequestProduct = $forSale && $isOrderable && $availabilityCtaType ==='request' && $productOrderId > 0; $managerEntries = []; $managerId = (int)evo()->getLoginUserID('mgr'); $isManager = $managerId > 0; $isWholesaler = !empty($_SESSION['webSellerId']); $cardMetaSku = trim((string)($item['sku'] ?? '')); $hasStaffVariants = ($isManager || $isWholesaler) && count((array)($item['children'] ?? [])) > 1; $cardMetaId = $hasStaffVariants ? $productOrderId : (int)($item['id'] ?? 0); $cardShowMetaId = $cardMetaId > 0 && \EvolutionCMS\Ddaudio\Services\Storefront\StorefrontProductIdVisibility::visible(); $hasKitComposition = !empty($item['stock']['data']['sku']['related']) && $productOrderId > 0; $canViewKitComposition = \EvolutionCMS\Ddaudio\Helpers\DevHelper::isManager() && $hasKitComposition; $hasCardNuances = ($isManager || $isWholesaler) && !empty($item['nuances']['full']); $cardMarginPercent = ($isManager || $isWholesaler) ? trim((string)($item['marginality']['percent'] ?? '')) : ''; $canPrintLabels = \EvolutionCMS\Ddaudio\Helpers\DevHelper::isManager() && $productOrderId > 0; $showCardManagerTools = ($isManager && $cardMetaId > 0) || $canViewKitComposition || $canPrintLabels || $hasCardNuances || $cardMarginPercent !==''; // "від" the lowest retail price; managers and wholesalers also see each variant's SKU and stock. $cardVariantLabel = trim((string)($item['variant_option_name'] ?? '')) ?: __('dd::storefront.variants'); $cardVariantOptions = []; if (count((array)($item['children'] ?? [])) > 1) { foreach ((array)($item['children'] ?? []) as $child) { if (!is_array($child) || (int)($child['id'] ?? 0) <= 0) { continue; } $child = StorefrontDeliveryTerms::forProduct($child); $childId = (int)$child['id']; $childBadge = (array)($child['availabilityBadge'] ?? []); $childStockCount = (int)($child['inStockCount'] ?? 0); $childCta = (array)($childBadge['cta'] ?? []); $childCtaType = (string)($childCta['type'] ?? ($childStockCount > 0 ? 'cart' : 'request')); $childOrderable = (bool)($childBadge['is_orderable'] ?? ($childStockCount > 0)); $childUnavailableForDomain = (Domain::isEuMarket() && (int)($child['not_sale_europe'] ?? 0) === 1) || (int)($child['not_sale_' . Domain::siteKey()] ?? 0) === 1 || (int)($child['not_sale'] ?? 0) === 1; $childForSale = (int)($child['hidemenu'] ?? 0) === 0 && !$childUnavailableForDomain; $childAvailability = trim((string)($childBadge['label'] ?? '')); if ($childAvailability ==='') { $childAvailability = trim(strip_tags((string)($child['inStock'] ?? ''))); } $childPrice = $child['price'] ?? ''; if (!$childForSale) { $childPrice = (int)($child['hidemenu'] ?? 0) === 1 ? __('dd::global.out_of_production') : __('dd::global.not_sale'); } $childPreorder = (array)($child['preorder'] ?? []); $childPreorder['hint'] = \EvolutionCMS\Ddaudio\Services\Preorder\PreorderHintText::for($childPreorder); $childPresentation = StorefrontPricePresentation::forProduct($child, $isAuthenticatedCustomer); $childOldPrice = $childForSale ? ($childPresentation['old_price'] ?? '') : ''; $childBonusAmount = $showCardLoyaltyBonus && $childForSale && is_numeric($childPrice) ? CustomerLoyaltyProgram::purchaseBonusAmount((float)$childPrice) : 0; $cardVariantOptions[] = [ 'id' => $childId, 'title' => trim((string)($child['pagetitle'] ?? '')) ?: '#' . $childId, 'url' => trim((string)($child['alias'] ?? '')) !=='' ? (string)$child['alias'] . '.html' : $url, 'price' => (string)$childPrice, 'oldPrice' => (string)$childOldPrice, 'discountPercent' => $childForSale ? $childPresentation['discount_percent'] : 0, 'clientPercent' => $childForSale ? $childPresentation['client_percent'] : 0, 'clientBasePrice' => $childForSale ? $childPresentation['client_base_price'] : null, 'delivery' => StorefrontCardTerms::forProduct($child), 'dispatch' => \EvolutionCMS\Ddaudio\Services\Product\StorefrontCardDispatch::forProduct(array_replace($child, ['forSale' => $childForSale])), 'promoUntil' => $childForSale ? $childPresentation['promo_until'] : '', 'priceRefreshRequired' => $childForSale && $childPresentation['refresh_required'], 'bonusAmount' => $childBonusAmount, 'forSale' => $childForSale, 'freeShipping' => StorefrontFreeShipping::forProduct(['price' => $childPrice, 'forSale' => $childForSale]), 'isMarkdown' => \EvolutionCMS\Ddaudio\Services\Product\StorefrontMarkdown::forProduct($child), 'availability' => $childAvailability, 'stockCount' => $childStockCount, 'sku' => trim((string)($child['sku'] ?? '')), 'sku2' => trim((string)($child['sku2'] ?? '')), 'canCart' => $childForSale && $childOrderable && $childCtaType ==='cart', 'canRequest' => $childForSale && $childOrderable && $childCtaType ==='request', 'preorder' => $childPreorder, ]; } } $hasCardVariants = count($cardVariantOptions) > 1; // it); the card it returns has that variant chosen. Until then, as for retail, nothing is chosen. $staffVariantChosen = $hasStaffVariants && $hasCardVariants && !empty($item['variantChosen']); $cardVariantPending = $hasCardVariants && !$staffVariantChosen; $minimumCardVariantPrice = collect($cardVariantOptions) ->filter(static fn(array $option): bool => $option['forSale'] && is_numeric($option['price'])) ->min(static fn(array $option): float => (float)$option['price']); $variantHasCartAction = $cardVariantPending && collect($cardVariantOptions)->contains(static fn(array $option): bool => $option['canCart']); $variantHasRequestAction = $cardVariantPending && collect($cardVariantOptions)->contains(static fn(array $option): bool => $option['canRequest']); $loyaltyBonusPrice = $cardVariantPending && $minimumCardVariantPrice !== null ? $minimumCardVariantPrice : $price; $loyaltyBonusEligible = $cardVariantPending ? $minimumCardVariantPrice !== null : $forSale; $freeShipping = StorefrontFreeShipping::forProduct([ 'price' => $loyaltyBonusPrice, 'forSale' => $loyaltyBonusEligible, ]); $loyaltyBonusAmount = $showCardLoyaltyBonus && $loyaltyBonusEligible && is_numeric($loyaltyBonusPrice) ? CustomerLoyaltyProgram::purchaseBonusAmount((float)$loyaltyBonusPrice) : 0; $loyaltyBonusTemplate = __('dd::product.loyalty_bonus_amount', [ 'amount' => '__AMOUNT__', 'currency' => $currencyLabel, ]); $wholesaleStock = (array)($item['stock'] ?? []); $wholesaleStockItems = array_values(array_filter( (array)($wholesaleStock['data'] ?? []), static fn(mixed $stockItem): bool => is_array($stockItem) )); $hasWholesaleDetails = $isWholesaler && $wholesaleStockItems !== []; $showCardManagerTools = $showCardManagerTools || $hasWholesaleDetails; if ($isManager) { $contentId = (int)($item['id'] ?? 0); if ($contentId > 0) $managerEntries[$contentId] = $title !=='' ? $title : '#' . $contentId; foreach ((array)($item['child @php use EvolutionCMS\Ddaudio\Facades\Domain; use EvolutionCMS\Ddaudio\Services\CustomerLoyaltyProgram; use EvolutionCMS\Ddaudio\Services\Product\StorefrontPricePresentation; use EvolutionCMS\Ddaudio\Services\Product\StorefrontFreeShipping; use EvolutionCMS\Ddaudio\Services\Product\StorefrontCardTerms; use EvolutionCMS\Ddaudio\Services\Product\StorefrontDeliveryTerms; $item = StorefrontDeliveryTerms::forProduct($item); $url = (string)($item['url'] ?? '#'); $title = trim((string)($item['pagetitle'] ?? '')); $normalizeImage = static function (mixed $image): string { $image = is_string($image) ? trim($image) : ''; return $image ==='' || preg_match('~^(?:https?:)?//|^data:|^/~i', $image) ? $image : '/' . ltrim($image, '/'); }; $sliderLarge = array_values((array)($item['sliderLarge'] ?? [])); $largeImages = []; $images = collect(array_values((array)($item['slider'] ?? []))) ->map(static fn(mixed $image, int $index): array => [$normalizeImage($image), $normalizeImage($sliderLarge[$index] ?? '')]) ->filter(static fn(array $pair): bool => $pair[0] !=='') ->unique(static fn(array $pair): string => $pair[0]) ->take(4) ->values() ->map(static function (array $pair) use (&$largeImages): string { if ($pair[1] !=='') { $largeImages[$pair[0]] = $pair[1]; } return $pair[0]; }) ->all(); $imageSrcset = static fn(string $image): string => isset($largeImages[$image]) ? $image . ' 320w, ' . $largeImages[$image] . ' 920w' : ''; $imageSizes ='(max-width: 575px) 100vw, (max-width: 1199px) 50vw, 360px'; if ($images === [] && is_string($item['image'] ?? null) && trim($item['image']) !=='') { $fallbackImage = trim($item['image']); $images[] = preg_match('~^(?:https?:)?//|^data:|^/~i', $fallbackImage) ? $fallbackImage : '/' . ltrim($fallbackImage, '/'); } $image = (string)($images[0] ?? ''); /* Фото немає — сірий логотип замість картинки товару. Клас is-placeholder не дає заглушці розтягнутись на всю плитку (див. card-product.scss). */ $imageIsPlaceholder = $image ===''; if ($imageIsPlaceholder) { $image = rtrim($storefrontThemeUrl, '/') . '/img/icons/logo-gray.svg'; } $hasImageSlider = count($images) > 1; $labels = (array)($item['labels'] ?? []); $rating = (array)($item['rating'] ?? []); $ratingValue = (float)($rating['rating'] ?? 0); $ratingTotal = 5.0; // total is the sum of votes, not the scale maximum. $ratingVotes = max(0, (int)($rating['votes'] ?? 0)); $ratingFilled = max(0, min(5, (int)round($ratingValue))); $currency = strtoupper((string)($_SESSION['currency'] ?? 'UAH')); $currencyLabel = \EvolutionCMS\Ddaudio\Support\StorefrontMoney::label($currency); $forSale = (bool)($item['forSale'] ?? false); $price = $item['price'] ?? null; $oldPrice = $item['price_old'] ?? null; $isAuthenticatedCustomer = (bool)evo()->getLoginUserID('web') && empty($_SESSION['webSellerId']); $pricePresentation = StorefrontPricePresentation::forProduct($item, $isAuthenticatedCustomer); $oldPrice = $pricePresentation['old_price']; $discountPercent = $pricePresentation['discount_percent']; $clientDiscountPercent = $pricePresentation['client_percent']; $isClientOnlyDiscount = $clientDiscountPercent > 0; $promoUntil = $pricePresentation['promo_until']; $formatCardAmount = static fn(float|int $amount): string => \EvolutionCMS\Ddaudio\Support\StorefrontMoney::format($amount); $showCardLoyaltyBonus = empty($_SESSION['webSellerId']) && Domain::isCountry('UA'); $stockCount = (int)($item['inStockCount'] ?? 0); $availabilityBadge = (array)($item['availabilityBadge'] ?? []); $availability = trim((string)($availabilityBadge['label'] ?? '')); $availabilityCta = (array)($availabilityBadge['cta'] ?? []); $availabilityCtaType = (string)($availabilityCta['type'] ?? ($stockCount > 0 ? 'cart' : 'request')); $isOrderable = (bool)($availabilityBadge['is_orderable'] ?? ($stockCount > 0)); $cardTerms = StorefrontCardTerms::forProduct($item); $productOrderId = (int)($item['productId'] ?? 0); $favoriteProductId = (int)($item['id'] ?? $productOrderId); if ((int)($item['template'] ?? 0) === 32) { $favoriteProductId = (int)($item['parent'] ?? $favoriteProductId); } $favoriteIds = array_map('intval', (array)($_SESSION['favorite'] ?? [])); $isFavorite = $favoriteProductId > 0 && in_array($favoriteProductId, $favoriteIds, true); $favoriteAddLabel = __('dd::global.favorite_add'); $favoriteRemoveLabel = __('dd::global.favorite_remove'); $canAddToCart = $forSale && $isOrderable && $availabilityCtaType ==='cart' && $productOrderId > 0; $canRequestProduct = $forSale && $isOrderable && $availabilityCtaType ==='request' && $productOrderId > 0; $managerEntries = []; $managerId = (int)evo()->getLoginUserID('mgr'); $isManager = $managerId > 0; $isWholesaler = !empty($_SESSION['webSellerId']); $cardMetaSku = trim((string)($item['sku'] ?? '')); $hasStaffVariants = ($isManager || $isWholesaler) && count((array)($item['children'] ?? [])) > 1; $cardMetaId = $hasStaffVariants ? $productOrderId : (int)($item['id'] ?? 0); $cardShowMetaId = $cardMetaId > 0 && \EvolutionCMS\Ddaudio\Services\Storefront\StorefrontProductIdVisibility::visible(); $hasKitComposition = !empty($item['stock']['data']['sku']['related']) && $productOrderId > 0; $canViewKitComposition = \EvolutionCMS\Ddaudio\Helpers\DevHelper::isManager() && $hasKitComposition; $hasCardNuances = ($isManager || $isWholesaler) && !empty($item['nuances']['full']); $cardMarginPercent = ($isManager || $isWholesaler) ? trim((string)($item['marginality']['percent'] ?? '')) : ''; $canPrintLabels = \EvolutionCMS\Ddaudio\Helpers\DevHelper::isManager() && $productOrderId > 0; $showCardManagerTools = ($isManager && $cardMetaId > 0) || $canViewKitComposition || $canPrintLabels || $hasCardNuances || $cardMarginPercent !==''; // "від" the lowest retail price; managers and wholesalers also see each variant's SKU and stock. $cardVariantLabel = trim((string)($item['variant_option_name'] ?? '')) ?: __('dd::storefront.variants'); $cardVariantOptions = []; if (count((array)($item['children'] ?? [])) > 1) { foreach ((array)($item['children'] ?? []) as $child) { if (!is_array($child) || (int)($child['id'] ?? 0) <= 0) { continue; } $child = StorefrontDeliveryTerms::forProduct($child); $childId = (int)$child['id']; $childBadge = (array)($child['availabilityBadge'] ?? []); $childStockCount = (int)($child['inStockCount'] ?? 0); $childCta = (array)($childBadge['cta'] ?? []); $childCtaType = (string)($childCta['type'] ?? ($childStockCount > 0 ? 'cart' : 'request')); $childOrderable = (bool)($childBadge['is_orderable'] ?? ($childStockCount > 0)); $childUnavailableForDomain = (Domain::isEuMarket() && (int)($child['not_sale_europe'] ?? 0) === 1) || (int)($child['not_sale_' . Domain::siteKey()] ?? 0) === 1 || (int)($child['not_sale'] ?? 0) === 1; $childForSale = (int)($child['hidemenu'] ?? 0) === 0 && !$childUnavailableForDomain; $childAvailability = trim((string)($childBadge['label'] ?? '')); if ($childAvailability ==='') { $childAvailability = trim(strip_tags((string)($child['inStock'] ?? ''))); } $childPrice = $child['price'] ?? ''; if (!$childForSale) { $childPrice = (int)($child['hidemenu'] ?? 0) === 1 ? __('dd::global.out_of_production') : __('dd::global.not_sale'); } $childPreorder = (array)($child['preorder'] ?? []); $childPreorder['hint'] = \EvolutionCMS\Ddaudio\Services\Preorder\PreorderHintText::for($childPreorder); $childPresentation = StorefrontPricePresentation::forProduct($child, $isAuthenticatedCustomer); $childOldPrice = $childForSale ? ($childPresentation['old_price'] ?? '') : ''; $childBonusAmount = $showCardLoyaltyBonus && $childForSale && is_numeric($childPrice) ? CustomerLoyaltyProgram::purchaseBonusAmount((float)$childPrice) : 0; $cardVariantOptions[] = [ 'id' => $childId, 'title' => trim((string)($child['pagetitle'] ?? '')) ?: '#' . $childId, 'url' => trim((string)($child['alias'] ?? '')) !=='' ? (string)$child['alias'] . '.html' : $url, 'price' => (string)$childPrice, 'oldPrice' => (string)$childOldPrice, 'discountPercent' => $childForSale ? $childPresentation['discount_percent'] : 0, 'clientPercent' => $childForSale ? $childPresentation['client_percent'] : 0, 'clientBasePrice' => $childForSale ? $childPresentation['client_base_price'] : null, 'delivery' => StorefrontCardTerms::forProduct($child), 'dispatch' => \EvolutionCMS\Ddaudio\Services\Product\StorefrontCardDispatch::forProduct(array_replace($child, ['forSale' => $childForSale])), 'promoUntil' => $childForSale ? $childPresentation['promo_until'] : '', 'priceRefreshRequired' => $childForSale && $childPresentation['refresh_required'], 'bonusAmount' => $childBonusAmount, 'forSale' => $childForSale, 'freeShipping' => StorefrontFreeShipping::forProduct(['price' => $childPrice, 'forSale' => $childForSale]), 'isMarkdown' => \EvolutionCMS\Ddaudio\Services\Product\StorefrontMarkdown::forProduct($child), 'availability' => $childAvailability, 'stockCount' => $childStockCount, 'sku' => trim((string)($child['sku'] ?? '')), 'sku2' => trim((string)($child['sku2'] ?? '')), 'canCart' => $childForSale && $childOrderable && $childCtaType ==='cart', 'canRequest' => $childForSale && $childOrderable && $childCtaType ==='request', 'preorder' => $childPreorder, ]; } } $hasCardVariants = count($cardVariantOptions) > 1; // it); the card it returns has that variant chosen. Until then, as for retail, nothing is chosen. $staffVariantChosen = $hasStaffVariants && $hasCardVariants && !empty($item['variantChosen']); $cardVariantPending = $hasCardVariants && !$staffVariantChosen; $minimumCardVariantPrice = collect($cardVariantOptions) ->filter(static fn(array $option): bool => $option['forSale'] && is_numeric($option['price'])) ->min(static fn(array $option): float => (float)$option['price']); $variantHasCartAction = $cardVariantPending && collect($cardVariantOptions)->contains(static fn(array $option): bool => $option['canCart']); $variantHasRequestAction = $cardVariantPending && collect($cardVariantOptions)->contains(static fn(array $option): bool => $option['canRequest']); $loyaltyBonusPrice = $cardVariantPending && $minimumCardVariantPrice !== null ? $minimumCardVariantPrice : $price; $loyaltyBonusEligible = $cardVariantPending ? $minimumCardVariantPrice !== null : $forSale; $freeShipping = StorefrontFreeShipping::forProduct([ 'price' => $loyaltyBonusPrice, 'forSale' => $loyaltyBonusEligible, ]); $loyaltyBonusAmount = $showCardLoyaltyBonus && $loyaltyBonusEligible && is_numeric($loyaltyBonusPrice) ? CustomerLoyaltyProgram::purchaseBonusAmount((float)$loyaltyBonusPrice) : 0; $loyaltyBonusTemplate = __('dd::product.loyalty_bonus_amount', [ 'amount' => '__AMOUNT__', 'currency' => $currencyLabel, ]); $wholesaleStock = (array)($item['stock'] ?? []); $wholesaleStockItems = array_values(array_filter( (array)($wholesaleStock['data'] ?? []), static fn(mixed $stockItem): bool => is_array($stockItem) )); $hasWholesaleDetails = $isWholesaler && $wholesaleStockItems !== []; $showCardManagerTools = $showCardManagerTools || $hasWholesaleDetails; if ($isManager) { $contentId = (int)($item['id'] ?? 0); if ($contentId > 0) $managerEntries[$contentId] = $title !=='' ? $title : '#' . $contentId; foreach ((array)($item['child @php use EvolutionCMS\Ddaudio\Facades\Domain; use EvolutionCMS\Ddaudio\Services\CustomerLoyaltyProgram; use EvolutionCMS\Ddaudio\Services\Product\StorefrontPricePresentation; use EvolutionCMS\Ddaudio\Services\Product\StorefrontFreeShipping; use EvolutionCMS\Ddaudio\Services\Product\StorefrontCardTerms; use EvolutionCMS\Ddaudio\Services\Product\StorefrontDeliveryTerms; $item = StorefrontDeliveryTerms::forProduct($item); $url = (string)($item['url'] ?? '#'); $title = trim((string)($item['pagetitle'] ?? '')); $normalizeImage = static function (mixed $image): string { $image = is_string($image) ? trim($image) : ''; return $image ==='' || preg_match('~^(?:https?:)?//|^data:|^/~i', $image) ? $image : '/' . ltrim($image, '/'); }; $sliderLarge = array_values((array)($item['sliderLarge'] ?? [])); $largeImages = []; $images = collect(array_values((array)($item['slider'] ?? []))) ->map(static fn(mixed $image, int $index): array => [$normalizeImage($image), $normalizeImage($sliderLarge[$index] ?? '')]) ->filter(static fn(array $pair): bool => $pair[0] !=='') ->unique(static fn(array $pair): string => $pair[0]) ->take(4) ->values() ->map(static function (array $pair) use (&$largeImages): string { if ($pair[1] !=='') { $largeImages[$pair[0]] = $pair[1]; } return $pair[0]; }) ->all(); $imageSrcset = static fn(string $image): string => isset($largeImages[$image]) ? $image . ' 320w, ' . $largeImages[$image] . ' 920w' : ''; $imageSizes ='(max-width: 575px) 100vw, (max-width: 1199px) 50vw, 360px'; if ($images === [] && is_string($item['image'] ?? null) && trim($item['image']) !=='') { $fallbackImage = trim($item['image']); $images[] = preg_match('~^(?:https?:)?//|^data:|^/~i', $fallbackImage) ? $fallbackImage : '/' . ltrim($fallbackImage, '/'); } $image = (string)($images[0] ?? ''); /* Фото немає — сірий логотип замість картинки товару. Клас is-placeholder не дає заглушці розтягнутись на всю плитку (див. card-product.scss). */ $imageIsPlaceholder = $image ===''; if ($imageIsPlaceholder) { $image = rtrim($storefrontThemeUrl, '/') . '/img/icons/logo-gray.svg'; } $hasImageSlider = count($images) > 1; $labels = (array)($item['labels'] ?? []); $rating = (array)($item['rating'] ?? []); $ratingValue = (float)($rating['rating'] ?? 0); $ratingTotal = 5.0; // total is the sum of votes, not the scale maximum. $ratingVotes = max(0, (int)($rating['votes'] ?? 0)); $ratingFilled = max(0, min(5, (int)round($ratingValue))); $currency = strtoupper((string)($_SESSION['currency'] ?? 'UAH')); $currencyLabel = \EvolutionCMS\Ddaudio\Support\StorefrontMoney::label($currency); $forSale = (bool)($item['forSale'] ?? false); $price = $item['price'] ?? null; $oldPrice = $item['price_old'] ?? null; $isAuthenticatedCustomer = (bool)evo()->getLoginUserID('web') && empty($_SESSION['webSellerId']); $pricePresentation = StorefrontPricePresentation::forProduct($item, $isAuthenticatedCustomer); $oldPrice = $pricePresentation['old_price']; $discountPercent = $pricePresentation['discount_percent']; $clientDiscountPercent = $pricePresentation['client_percent']; $isClientOnlyDiscount = $clientDiscountPercent > 0; $promoUntil = $pricePresentation['promo_until']; $formatCardAmount = static fn(float|int $amount): string => \EvolutionCMS\Ddaudio\Support\StorefrontMoney::format($amount); $showCardLoyaltyBonus = empty($_SESSION['webSellerId']) && Domain::isCountry('UA'); $stockCount = (int)($item['inStockCount'] ?? 0); $availabilityBadge = (array)($item['availabilityBadge'] ?? []); $availability = trim((string)($availabilityBadge['label'] ?? '')); $availabilityCta = (array)($availabilityBadge['cta'] ?? []); $availabilityCtaType = (string)($availabilityCta['type'] ?? ($stockCount > 0 ? 'cart' : 'request')); $isOrderable = (bool)($availabilityBadge['is_orderable'] ?? ($stockCount > 0)); $cardTerms = StorefrontCardTerms::forProduct($item); $productOrderId = (int)($item['productId'] ?? 0); $favoriteProductId = (int)($item['id'] ?? $productOrderId); if ((int)($item['template'] ?? 0) === 32) { $favoriteProductId = (int)($item['parent'] ?? $favoriteProductId); } $favoriteIds = array_map('intval', (array)($_SESSION['favorite'] ?? [])); $isFavorite = $favoriteProductId > 0 && in_array($favoriteProductId, $favoriteIds, true); $favoriteAddLabel = __('dd::global.favorite_add'); $favoriteRemoveLabel = __('dd::global.favorite_remove'); $canAddToCart = $forSale && $isOrderable && $availabilityCtaType ==='cart' && $productOrderId > 0; $canRequestProduct = $forSale && $isOrderable && $availabilityCtaType ==='request' && $productOrderId > 0; $managerEntries = []; $managerId = (int)evo()->getLoginUserID('mgr'); $isManager = $managerId > 0; $isWholesaler = !empty($_SESSION['webSellerId']); $cardMetaSku = trim((string)($item['sku'] ?? '')); $hasStaffVariants = ($isManager || $isWholesaler) && count((array)($item['children'] ?? [])) > 1; $cardMetaId = $hasStaffVariants ? $productOrderId : (int)($item['id'] ?? 0); $cardShowMetaId = $cardMetaId > 0 && \EvolutionCMS\Ddaudio\Services\Storefront\StorefrontProductIdVisibility::visible(); $hasKitComposition = !empty($item['stock']['data']['sku']['related']) && $productOrderId > 0; $canViewKitComposition = \EvolutionCMS\Ddaudio\Helpers\DevHelper::isManager() && $hasKitComposition; $hasCardNuances = ($isManager || $isWholesaler) && !empty($item['nuances']['full']); $cardMarginPercent = ($isManager || $isWholesaler) ? trim((string)($item['marginality']['percent'] ?? '')) : ''; $canPrintLabels = \EvolutionCMS\Ddaudio\Helpers\DevHelper::isManager() && $productOrderId > 0; $showCardManagerTools = ($isManager && $cardMetaId > 0) || $canViewKitComposition || $canPrintLabels || $hasCardNuances || $cardMarginPercent !==''; // "від" the lowest retail price; managers and wholesalers also see each variant's SKU and stock. $cardVariantLabel = trim((string)($item['variant_option_name'] ?? '')) ?: __('dd::storefront.variants'); $cardVariantOptions = []; if (count((array)($item['children'] ?? [])) > 1) { foreach ((array)($item['children'] ?? []) as $child) { if (!is_array($child) || (int)($child['id'] ?? 0) <= 0) { continue; } $child = StorefrontDeliveryTerms::forProduct($child); $childId = (int)$child['id']; $childBadge = (array)($child['availabilityBadge'] ?? []); $childStockCount = (int)($child['inStockCount'] ?? 0); $childCta = (array)($childBadge['cta'] ?? []); $childCtaType = (string)($childCta['type'] ?? ($childStockCount > 0 ? 'cart' : 'request')); $childOrderable = (bool)($childBadge['is_orderable'] ?? ($childStockCount > 0)); $childUnavailableForDomain = (Domain::isEuMarket() && (int)($child['not_sale_europe'] ?? 0) === 1) || (int)($child['not_sale_' . Domain::siteKey()] ?? 0) === 1 || (int)($child['not_sale'] ?? 0) === 1; $childForSale = (int)($child['hidemenu'] ?? 0) === 0 && !$childUnavailableForDomain; $childAvailability = trim((string)($childBadge['label'] ?? '')); if ($childAvailability ==='') { $childAvailability = trim(strip_tags((string)($child['inStock'] ?? ''))); } $childPrice = $child['price'] ?? ''; if (!$childForSale) { $childPrice = (int)($child['hidemenu'] ?? 0) === 1 ? __('dd::global.out_of_production') : __('dd::global.not_sale'); } $childPreorder = (array)($child['preorder'] ?? []); $childPreorder['hint'] = \EvolutionCMS\Ddaudio\Services\Preorder\PreorderHintText::for($childPreorder); $childPresentation = StorefrontPricePresentation::forProduct($child, $isAuthenticatedCustomer); $childOldPrice = $childForSale ? ($childPresentation['old_price'] ?? '') : ''; $childBonusAmount = $showCardLoyaltyBonus && $childForSale && is_numeric($childPrice) ? CustomerLoyaltyProgram::purchaseBonusAmount((float)$childPrice) : 0; $cardVariantOptions[] = [ 'id' => $childId, 'title' => trim((string)($child['pagetitle'] ?? '')) ?: '#' . $childId, 'url' => trim((string)($child['alias'] ?? '')) !=='' ? (string)$child['alias'] . '.html' : $url, 'price' => (string)$childPrice, 'oldPrice' => (string)$childOldPrice, 'discountPercent' => $childForSale ? $childPresentation['discount_percent'] : 0, 'clientPercent' => $childForSale ? $childPresentation['client_percent'] : 0, 'clientBasePrice' => $childForSale ? $childPresentation['client_base_price'] : null, 'delivery' => StorefrontCardTerms::forProduct($child), 'dispatch' => \EvolutionCMS\Ddaudio\Services\Product\StorefrontCardDispatch::forProduct(array_replace($child, ['forSale' => $childForSale])), 'promoUntil' => $childForSale ? $childPresentation['promo_until'] : '', 'priceRefreshRequired' => $childForSale && $childPresentation['refresh_required'], 'bonusAmount' => $childBonusAmount, 'forSale' => $childForSale, 'freeShipping' => StorefrontFreeShipping::forProduct(['price' => $childPrice, 'forSale' => $childForSale]), 'isMarkdown' => \EvolutionCMS\Ddaudio\Services\Product\StorefrontMarkdown::forProduct($child), 'availability' => $childAvailability, 'stockCount' => $childStockCount, 'sku' => trim((string)($child['sku'] ?? '')), 'sku2' => trim((string)($child['sku2'] ?? '')), 'canCart' => $childForSale && $childOrderable && $childCtaType ==='cart', 'canRequest' => $childForSale && $childOrderable && $childCtaType ==='request', 'preorder' => $childPreorder, ]; } } $hasCardVariants = count($cardVariantOptions) > 1; // it); the card it returns has that variant chosen. Until then, as for retail, nothing is chosen. $staffVariantChosen = $hasStaffVariants && $hasCardVariants && !empty($item['variantChosen']); $cardVariantPending = $hasCardVariants && !$staffVariantChosen; $minimumCardVariantPrice = collect($cardVariantOptions) ->filter(static fn(array $option): bool => $option['forSale'] && is_numeric($option['price'])) ->min(static fn(array $option): float => (float)$option['price']); $variantHasCartAction = $cardVariantPending && collect($cardVariantOptions)->contains(static fn(array $option): bool => $option['canCart']); $variantHasRequestAction = $cardVariantPending && collect($cardVariantOptions)->contains(static fn(array $option): bool => $option['canRequest']); $loyaltyBonusPrice = $cardVariantPending && $minimumCardVariantPrice !== null ? $minimumCardVariantPrice : $price; $loyaltyBonusEligible = $cardVariantPending ? $minimumCardVariantPrice !== null : $forSale; $freeShipping = StorefrontFreeShipping::forProduct([ 'price' => $loyaltyBonusPrice, 'forSale' => $loyaltyBonusEligible, ]); $loyaltyBonusAmount = $showCardLoyaltyBonus && $loyaltyBonusEligible && is_numeric($loyaltyBonusPrice) ? CustomerLoyaltyProgram::purchaseBonusAmount((float)$loyaltyBonusPrice) : 0; $loyaltyBonusTemplate = __('dd::product.loyalty_bonus_amount', [ 'amount' => '__AMOUNT__', 'currency' => $currencyLabel, ]); $wholesaleStock = (array)($item['stock'] ?? []); $wholesaleStockItems = array_values(array_filter( (array)($wholesaleStock['data'] ?? []), static fn(mixed $stockItem): bool => is_array($stockItem) )); $hasWholesaleDetails = $isWholesaler && $wholesaleStockItems !== []; $showCardManagerTools = $showCardManagerTools || $hasWholesaleDetails; if ($isManager) { $contentId = (int)($item['id'] ?? 0); if ($contentId > 0) $managerEntries[$contentId] = $title !=='' ? $title : '#' . $contentId; foreach ((array)($item['child @php use EvolutionCMS\Ddaudio\Facades\Domain; use EvolutionCMS\Ddaudio\Services\CustomerLoyaltyProgram; use EvolutionCMS\Ddaudio\Services\Product\StorefrontPricePresentation; use EvolutionCMS\Ddaudio\Services\Product\StorefrontFreeShipping; use EvolutionCMS\Ddaudio\Services\Product\StorefrontCardTerms; use EvolutionCMS\Ddaudio\Services\Product\StorefrontDeliveryTerms; $item = StorefrontDeliveryTerms::forProduct($item); $url = (string)($item['url'] ?? '#'); $title = trim((string)($item['pagetitle'] ?? '')); $normalizeImage = static function (mixed $image): string { $image = is_string($image) ? trim($image) : ''; return $image ==='' || preg_match('~^(?:https?:)?//|^data:|^/~i', $image) ? $image : '/' . ltrim($image, '/'); }; $sliderLarge = array_values((array)($item['sliderLarge'] ?? [])); $largeImages = []; $images = collect(array_values((array)($item['slider'] ?? []))) ->map(static fn(mixed $image, int $index): array => [$normalizeImage($image), $normalizeImage($sliderLarge[$index] ?? '')]) ->filter(static fn(array $pair): bool => $pair[0] !=='') ->unique(static fn(array $pair): string => $pair[0]) ->take(4) ->values() ->map(static function (array $pair) use (&$largeImages): string { if ($pair[1] !=='') { $largeImages[$pair[0]] = $pair[1]; } return $pair[0]; }) ->all(); $imageSrcset = static fn(string $image): string => isset($largeImages[$image]) ? $image . ' 320w, ' . $largeImages[$image] . ' 920w' : ''; $imageSizes ='(max-width: 575px) 100vw, (max-width: 1199px) 50vw, 360px'; if ($images === [] && is_string($item['image'] ?? null) && trim($item['image']) !=='') { $fallbackImage = trim($item['image']); $images[] = preg_match('~^(?:https?:)?//|^data:|^/~i', $fallbackImage) ? $fallbackImage : '/' . ltrim($fallbackImage, '/'); } $image = (string)($images[0] ?? ''); /* Фото немає — сірий логотип замість картинки товару. Клас is-placeholder не дає заглушці розтягнутись на всю плитку (див. card-product.scss). */ $imageIsPlaceholder = $image ===''; if ($imageIsPlaceholder) { $image = rtrim($storefrontThemeUrl, '/') . '/img/icons/logo-gray.svg'; } $hasImageSlider = count($images) > 1; $labels = (array)($item['labels'] ?? []); $rating = (array)($item['rating'] ?? []); $ratingValue = (float)($rating['rating'] ?? 0); $ratingTotal = 5.0; // total is the sum of votes, not the scale maximum. $ratingVotes = max(0, (int)($rating['votes'] ?? 0)); $ratingFilled = max(0, min(5, (int)round($ratingValue))); $currency = strtoupper((string)($_SESSION['currency'] ?? 'UAH')); $currencyLabel = \EvolutionCMS\Ddaudio\Support\StorefrontMoney::label($currency); $forSale = (bool)($item['forSale'] ?? false); $price = $item['price'] ?? null; $oldPrice = $item['price_old'] ?? null; $isAuthenticatedCustomer = (bool)evo()->getLoginUserID('web') && empty($_SESSION['webSellerId']); $pricePresentation = StorefrontPricePresentation::forProduct($item, $isAuthenticatedCustomer); $oldPrice = $pricePresentation['old_price']; $discountPercent = $pricePresentation['discount_percent']; $clientDiscountPercent = $pricePresentation['client_percent']; $isClientOnlyDiscount = $clientDiscountPercent > 0; $promoUntil = $pricePresentation['promo_until']; $formatCardAmount = static fn(float|int $amount): string => \EvolutionCMS\Ddaudio\Support\StorefrontMoney::format($amount); $showCardLoyaltyBonus = empty($_SESSION['webSellerId']) && Domain::isCountry('UA'); $stockCount = (int)($item['inStockCount'] ?? 0); $availabilityBadge = (array)($item['availabilityBadge'] ?? []); $availability = trim((string)($availabilityBadge['label'] ?? '')); $availabilityCta = (array)($availabilityBadge['cta'] ?? []); $availabilityCtaType = (string)($availabilityCta['type'] ?? ($stockCount > 0 ? 'cart' : 'request')); $isOrderable = (bool)($availabilityBadge['is_orderable'] ?? ($stockCount > 0)); $cardTerms = StorefrontCardTerms::forProduct($item); $productOrderId = (int)($item['productId'] ?? 0); $favoriteProductId = (int)($item['id'] ?? $productOrderId); if ((int)($item['template'] ?? 0) === 32) { $favoriteProductId = (int)($item['parent'] ?? $favoriteProductId); } $favoriteIds = array_map('intval', (array)($_SESSION['favorite'] ?? [])); $isFavorite = $favoriteProductId > 0 && in_array($favoriteProductId, $favoriteIds, true); $favoriteAddLabel = __('dd::global.favorite_add'); $favoriteRemoveLabel = __('dd::global.favorite_remove'); $canAddToCart = $forSale && $isOrderable && $availabilityCtaType ==='cart' && $productOrderId > 0; $canRequestProduct = $forSale && $isOrderable && $availabilityCtaType ==='request' && $productOrderId > 0; $managerEntries = []; $managerId = (int)evo()->getLoginUserID('mgr'); $isManager = $managerId > 0; $isWholesaler = !empty($_SESSION['webSellerId']); $cardMetaSku = trim((string)($item['sku'] ?? '')); $hasStaffVariants = ($isManager || $isWholesaler) && count((array)($item['children'] ?? [])) > 1; $cardMetaId = $hasStaffVariants ? $productOrderId : (int)($item['id'] ?? 0); $cardShowMetaId = $cardMetaId > 0 && \EvolutionCMS\Ddaudio\Services\Storefront\StorefrontProductIdVisibility::visible(); $hasKitComposition = !empty($item['stock']['data']['sku']['related']) && $productOrderId > 0; $canViewKitComposition = \EvolutionCMS\Ddaudio\Helpers\DevHelper::isManager() && $hasKitComposition; $hasCardNuances = ($isManager || $isWholesaler) && !empty($item['nuances']['full']); $cardMarginPercent = ($isManager || $isWholesaler) ? trim((string)($item['marginality']['percent'] ?? '')) : ''; $canPrintLabels = \EvolutionCMS\Ddaudio\Helpers\DevHelper::isManager() && $productOrderId > 0; $showCardManagerTools = ($isManager && $cardMetaId > 0) || $canViewKitComposition || $canPrintLabels || $hasCardNuances || $cardMarginPercent !==''; // "від" the lowest retail price; managers and wholesalers also see each variant's SKU and stock. $cardVariantLabel = trim((string)($item['variant_option_name'] ?? '')) ?: __('dd::storefront.variants'); $cardVariantOptions = []; if (count((array)($item['children'] ?? [])) > 1) { foreach ((array)($item['children'] ?? []) as $child) { if (!is_array($child) || (int)($child['id'] ?? 0) <= 0) { continue; } $child = StorefrontDeliveryTerms::forProduct($child); $childId = (int)$child['id']; $childBadge = (array)($child['availabilityBadge'] ?? []); $childStockCount = (int)($child['inStockCount'] ?? 0); $childCta = (array)($childBadge['cta'] ?? []); $childCtaType = (string)($childCta['type'] ?? ($childStockCount > 0 ? 'cart' : 'request')); $childOrderable = (bool)($childBadge['is_orderable'] ?? ($childStockCount > 0)); $childUnavailableForDomain = (Domain::isEuMarket() && (int)($child['not_sale_europe'] ?? 0) === 1) || (int)($child['not_sale_' . Domain::siteKey()] ?? 0) === 1 || (int)($child['not_sale'] ?? 0) === 1; $childForSale = (int)($child['hidemenu'] ?? 0) === 0 && !$childUnavailableForDomain; $childAvailability = trim((string)($childBadge['label'] ?? '')); if ($childAvailability ==='') { $childAvailability = trim(strip_tags((string)($child['inStock'] ?? ''))); } $childPrice = $child['price'] ?? ''; if (!$childForSale) { $childPrice = (int)($child['hidemenu'] ?? 0) === 1 ? __('dd::global.out_of_production') : __('dd::global.not_sale'); } $childPreorder = (array)($child['preorder'] ?? []); $childPreorder['hint'] = \EvolutionCMS\Ddaudio\Services\Preorder\PreorderHintText::for($childPreorder); $childPresentation = StorefrontPricePresentation::forProduct($child, $isAuthenticatedCustomer); $childOldPrice = $childForSale ? ($childPresentation['old_price'] ?? '') : ''; $childBonusAmount = $showCardLoyaltyBonus && $childForSale && is_numeric($childPrice) ? CustomerLoyaltyProgram::purchaseBonusAmount((float)$childPrice) : 0; $cardVariantOptions[] = [ 'id' => $childId, 'title' => trim((string)($child['pagetitle'] ?? '')) ?: '#' . $childId, 'url' => trim((string)($child['alias'] ?? '')) !=='' ? (string)$child['alias'] . '.html' : $url, 'price' => (string)$childPrice, 'oldPrice' => (string)$childOldPrice, 'discountPercent' => $childForSale ? $childPresentation['discount_percent'] : 0, 'clientPercent' => $childForSale ? $childPresentation['client_percent'] : 0, 'clientBasePrice' => $childForSale ? $childPresentation['client_base_price'] : null, 'delivery' => StorefrontCardTerms::forProduct($child), 'dispatch' => \EvolutionCMS\Ddaudio\Services\Product\StorefrontCardDispatch::forProduct(array_replace($child, ['forSale' => $childForSale])), 'promoUntil' => $childForSale ? $childPresentation['promo_until'] : '', 'priceRefreshRequired' => $childForSale && $childPresentation['refresh_required'], 'bonusAmount' => $childBonusAmount, 'forSale' => $childForSale, 'freeShipping' => StorefrontFreeShipping::forProduct(['price' => $childPrice, 'forSale' => $childForSale]), 'isMarkdown' => \EvolutionCMS\Ddaudio\Services\Product\StorefrontMarkdown::forProduct($child), 'availability' => $childAvailability, 'stockCount' => $childStockCount, 'sku' => trim((string)($child['sku'] ?? '')), 'sku2' => trim((string)($child['sku2'] ?? '')), 'canCart' => $childForSale && $childOrderable && $childCtaType ==='cart', 'canRequest' => $childForSale && $childOrderable && $childCtaType ==='request', 'preorder' => $childPreorder, ]; } } $hasCardVariants = count($cardVariantOptions) > 1; // it); the card it returns has that variant chosen. Until then, as for retail, nothing is chosen. $staffVariantChosen = $hasStaffVariants && $hasCardVariants && !empty($item['variantChosen']); $cardVariantPending = $hasCardVariants && !$staffVariantChosen; $minimumCardVariantPrice = collect($cardVariantOptions) ->filter(static fn(array $option): bool => $option['forSale'] && is_numeric($option['price'])) ->min(static fn(array $option): float => (float)$option['price']); $variantHasCartAction = $cardVariantPending && collect($cardVariantOptions)->contains(static fn(array $option): bool => $option['canCart']); $variantHasRequestAction = $cardVariantPending && collect($cardVariantOptions)->contains(static fn(array $option): bool => $option['canRequest']); $loyaltyBonusPrice = $cardVariantPending && $minimumCardVariantPrice !== null ? $minimumCardVariantPrice : $price; $loyaltyBonusEligible = $cardVariantPending ? $minimumCardVariantPrice !== null : $forSale; $freeShipping = StorefrontFreeShipping::forProduct([ 'price' => $loyaltyBonusPrice, 'forSale' => $loyaltyBonusEligible, ]); $loyaltyBonusAmount = $showCardLoyaltyBonus && $loyaltyBonusEligible && is_numeric($loyaltyBonusPrice) ? CustomerLoyaltyProgram::purchaseBonusAmount((float)$loyaltyBonusPrice) : 0; $loyaltyBonusTemplate = __('dd::product.loyalty_bonus_amount', [ 'amount' => '__AMOUNT__', 'currency' => $currencyLabel, ]); $wholesaleStock = (array)($item['stock'] ?? []); $wholesaleStockItems = array_values(array_filter( (array)($wholesaleStock['data'] ?? []), static fn(mixed $stockItem): bool => is_array($stockItem) )); $hasWholesaleDetails = $isWholesaler && $wholesaleStockItems !== []; $showCardManagerTools = $showCardManagerTools || $hasWholesaleDetails; if ($isManager) { $contentId = (int)($item['id'] ?? 0); if ($contentId > 0) $managerEntries[$contentId] = $title !=='' ? $title : '#' . $contentId; foreach ((array)($item['child @php use EvolutionCMS\Ddaudio\Facades\Domain; use EvolutionCMS\Ddaudio\Services\CustomerLoyaltyProgram; use EvolutionCMS\Ddaudio\Services\Product\StorefrontPricePresentation; use EvolutionCMS\Ddaudio\Services\Product\StorefrontFreeShipping; use EvolutionCMS\Ddaudio\Services\Product\StorefrontCardTerms; use EvolutionCMS\Ddaudio\Services\Product\StorefrontDeliveryTerms; $item = StorefrontDeliveryTerms::forProduct($item); $url = (string)($item['url'] ?? '#'); $title = trim((string)($item['pagetitle'] ?? '')); $normalizeImage = static function (mixed $image): string { $image = is_string($image) ? trim($image) : ''; return $image ==='' || preg_match('~^(?:https?:)?//|^data:|^/~i', $image) ? $image : '/' . ltrim($image, '/'); }; $sliderLarge = array_values((array)($item['sliderLarge'] ?? [])); $largeImages = []; $images = collect(array_values((array)($item['slider'] ?? []))) ->map(static fn(mixed $image, int $index): array => [$normalizeImage($image), $normalizeImage($sliderLarge[$index] ?? '')]) ->filter(static fn(array $pair): bool => $pair[0] !=='') ->unique(static fn(array $pair): string => $pair[0]) ->take(4) ->values() ->map(static function (array $pair) use (&$largeImages): string { if ($pair[1] !=='') { $largeImages[$pair[0]] = $pair[1]; } return $pair[0]; }) ->all(); $imageSrcset = static fn(string $image): string => isset($largeImages[$image]) ? $image . ' 320w, ' . $largeImages[$image] . ' 920w' : ''; $imageSizes ='(max-width: 575px) 100vw, (max-width: 1199px) 50vw, 360px'; if ($images === [] && is_string($item['image'] ?? null) && trim($item['image']) !=='') { $fallbackImage = trim($item['image']); $images[] = preg_match('~^(?:https?:)?//|^data:|^/~i', $fallbackImage) ? $fallbackImage : '/' . ltrim($fallbackImage, '/'); } $image = (string)($images[0] ?? ''); /* Фото немає — сірий логотип замість картинки товару. Клас is-placeholder не дає заглушці розтягнутись на всю плитку (див. card-product.scss). */ $imageIsPlaceholder = $image ===''; if ($imageIsPlaceholder) { $image = rtrim($storefrontThemeUrl, '/') . '/img/icons/logo-gray.svg'; } $hasImageSlider = count($images) > 1; $labels = (array)($item['labels'] ?? []); $rating = (array)($item['rating'] ?? []); $ratingValue = (float)($rating['rating'] ?? 0); $ratingTotal = 5.0; // total is the sum of votes, not the scale maximum. $ratingVotes = max(0, (int)($rating['votes'] ?? 0)); $ratingFilled = max(0, min(5, (int)round($ratingValue))); $currency = strtoupper((string)($_SESSION['currency'] ?? 'UAH')); $currencyLabel = \EvolutionCMS\Ddaudio\Support\StorefrontMoney::label($currency); $forSale = (bool)($item['forSale'] ?? false); $price = $item['price'] ?? null; $oldPrice = $item['price_old'] ?? null; $isAuthenticatedCustomer = (bool)evo()->getLoginUserID('web') && empty($_SESSION['webSellerId']); $pricePresentation = StorefrontPricePresentation::forProduct($item, $isAuthenticatedCustomer); $oldPrice = $pricePresentation['old_price']; $discountPercent = $pricePresentation['discount_percent']; $clientDiscountPercent = $pricePresentation['client_percent']; $isClientOnlyDiscount = $clientDiscountPercent > 0; $promoUntil = $pricePresentation['promo_until']; $formatCardAmount = static fn(float|int $amount): string => \EvolutionCMS\Ddaudio\Support\StorefrontMoney::format($amount); $showCardLoyaltyBonus = empty($_SESSION['webSellerId']) && Domain::isCountry('UA'); $stockCount = (int)($item['inStockCount'] ?? 0); $availabilityBadge = (array)($item['availabilityBadge'] ?? []); $availability = trim((string)($availabilityBadge['label'] ?? '')); $availabilityCta = (array)($availabilityBadge['cta'] ?? []); $availabilityCtaType = (string)($availabilityCta['type'] ?? ($stockCount > 0 ? 'cart' : 'request')); $isOrderable = (bool)($availabilityBadge['is_orderable'] ?? ($stockCount > 0)); $cardTerms = StorefrontCardTerms::forProduct($item); $productOrderId = (int)($item['productId'] ?? 0); $favoriteProductId = (int)($item['id'] ?? $productOrderId); if ((int)($item['template'] ?? 0) === 32) { $favoriteProductId = (int)($item['parent'] ?? $favoriteProductId); } $favoriteIds = array_map('intval', (array)($_SESSION['favorite'] ?? [])); $isFavorite = $favoriteProductId > 0 && in_array($favoriteProductId, $favoriteIds, true); $favoriteAddLabel = __('dd::global.favorite_add'); $favoriteRemoveLabel = __('dd::global.favorite_remove'); $canAddToCart = $forSale && $isOrderable && $availabilityCtaType ==='cart' && $productOrderId > 0; $canRequestProduct = $forSale && $isOrderable && $availabilityCtaType ==='request' && $productOrderId > 0; $managerEntries = []; $managerId = (int)evo()->getLoginUserID('mgr'); $isManager = $managerId > 0; $isWholesaler = !empty($_SESSION['webSellerId']); $cardMetaSku = trim((string)($item['sku'] ?? '')); $hasStaffVariants = ($isManager || $isWholesaler) && count((array)($item['children'] ?? [])) > 1; $cardMetaId = $hasStaffVariants ? $productOrderId : (int)($item['id'] ?? 0); $cardShowMetaId = $cardMetaId > 0 && \EvolutionCMS\Ddaudio\Services\Storefront\StorefrontProductIdVisibility::visible(); $hasKitComposition = !empty($item['stock']['data']['sku']['related']) && $productOrderId > 0; $canViewKitComposition = \EvolutionCMS\Ddaudio\Helpers\DevHelper::isManager() && $hasKitComposition; $hasCardNuances = ($isManager || $isWholesaler) && !empty($item['nuances']['full']); $cardMarginPercent = ($isManager || $isWholesaler) ? trim((string)($item['marginality']['percent'] ?? '')) : ''; $canPrintLabels = \EvolutionCMS\Ddaudio\Helpers\DevHelper::isManager() && $productOrderId > 0; $showCardManagerTools = ($isManager && $cardMetaId > 0) || $canViewKitComposition || $canPrintLabels || $hasCardNuances || $cardMarginPercent !==''; // "від" the lowest retail price; managers and wholesalers also see each variant's SKU and stock. $cardVariantLabel = trim((string)($item['variant_option_name'] ?? '')) ?: __('dd::storefront.variants'); $cardVariantOptions = []; if (count((array)($item['children'] ?? [])) > 1) { foreach ((array)($item['children'] ?? []) as $child) { if (!is_array($child) || (int)($child['id'] ?? 0) <= 0) { continue; } $child = StorefrontDeliveryTerms::forProduct($child); $childId = (int)$child['id']; $childBadge = (array)($child['availabilityBadge'] ?? []); $childStockCount = (int)($child['inStockCount'] ?? 0); $childCta = (array)($childBadge['cta'] ?? []); $childCtaType = (string)($childCta['type'] ?? ($childStockCount > 0 ? 'cart' : 'request')); $childOrderable = (bool)($childBadge['is_orderable'] ?? ($childStockCount > 0)); $childUnavailableForDomain = (Domain::isEuMarket() && (int)($child['not_sale_europe'] ?? 0) === 1) || (int)($child['not_sale_' . Domain::siteKey()] ?? 0) === 1 || (int)($child['not_sale'] ?? 0) === 1; $childForSale = (int)($child['hidemenu'] ?? 0) === 0 && !$childUnavailableForDomain; $childAvailability = trim((string)($childBadge['label'] ?? '')); if ($childAvailability ==='') { $childAvailability = trim(strip_tags((string)($child['inStock'] ?? ''))); } $childPrice = $child['price'] ?? ''; if (!$childForSale) { $childPrice = (int)($child['hidemenu'] ?? 0) === 1 ? __('dd::global.out_of_production') : __('dd::global.not_sale'); } $childPreorder = (array)($child['preorder'] ?? []); $childPreorder['hint'] = \EvolutionCMS\Ddaudio\Services\Preorder\PreorderHintText::for($childPreorder); $childPresentation = StorefrontPricePresentation::forProduct($child, $isAuthenticatedCustomer); $childOldPrice = $childForSale ? ($childPresentation['old_price'] ?? '') : ''; $childBonusAmount = $showCardLoyaltyBonus && $childForSale && is_numeric($childPrice) ? CustomerLoyaltyProgram::purchaseBonusAmount((float)$childPrice) : 0; $cardVariantOptions[] = [ 'id' => $childId, 'title' => trim((string)($child['pagetitle'] ?? '')) ?: '#' . $childId, 'url' => trim((string)($child['alias'] ?? '')) !=='' ? (string)$child['alias'] . '.html' : $url, 'price' => (string)$childPrice, 'oldPrice' => (string)$childOldPrice, 'discountPercent' => $childForSale ? $childPresentation['discount_percent'] : 0, 'clientPercent' => $childForSale ? $childPresentation['client_percent'] : 0, 'clientBasePrice' => $childForSale ? $childPresentation['client_base_price'] : null, 'delivery' => StorefrontCardTerms::forProduct($child), 'dispatch' => \EvolutionCMS\Ddaudio\Services\Product\StorefrontCardDispatch::forProduct(array_replace($child, ['forSale' => $childForSale])), 'promoUntil' => $childForSale ? $childPresentation['promo_until'] : '', 'priceRefreshRequired' => $childForSale && $childPresentation['refresh_required'], 'bonusAmount' => $childBonusAmount, 'forSale' => $childForSale, 'freeShipping' => StorefrontFreeShipping::forProduct(['price' => $childPrice, 'forSale' => $childForSale]), 'isMarkdown' => \EvolutionCMS\Ddaudio\Services\Product\StorefrontMarkdown::forProduct($child), 'availability' => $childAvailability, 'stockCount' => $childStockCount, 'sku' => trim((string)($child['sku'] ?? '')), 'sku2' => trim((string)($child['sku2'] ?? '')), 'canCart' => $childForSale && $childOrderable && $childCtaType ==='cart', 'canRequest' => $childForSale && $childOrderable && $childCtaType ==='request', 'preorder' => $childPreorder, ]; } } $hasCardVariants = count($cardVariantOptions) > 1; // it); the card it returns has that variant chosen. Until then, as for retail, nothing is chosen. $staffVariantChosen = $hasStaffVariants && $hasCardVariants && !empty($item['variantChosen']); $cardVariantPending = $hasCardVariants && !$staffVariantChosen; $minimumCardVariantPrice = collect($cardVariantOptions) ->filter(static fn(array $option): bool => $option['forSale'] && is_numeric($option['price'])) ->min(static fn(array $option): float => (float)$option['price']); $variantHasCartAction = $cardVariantPending && collect($cardVariantOptions)->contains(static fn(array $option): bool => $option['canCart']); $variantHasRequestAction = $cardVariantPending && collect($cardVariantOptions)->contains(static fn(array $option): bool => $option['canRequest']); $loyaltyBonusPrice = $cardVariantPending && $minimumCardVariantPrice !== null ? $minimumCardVariantPrice : $price; $loyaltyBonusEligible = $cardVariantPending ? $minimumCardVariantPrice !== null : $forSale; $freeShipping = StorefrontFreeShipping::forProduct([ 'price' => $loyaltyBonusPrice, 'forSale' => $loyaltyBonusEligible, ]); $loyaltyBonusAmount = $showCardLoyaltyBonus && $loyaltyBonusEligible && is_numeric($loyaltyBonusPrice) ? CustomerLoyaltyProgram::purchaseBonusAmount((float)$loyaltyBonusPrice) : 0; $loyaltyBonusTemplate = __('dd::product.loyalty_bonus_amount', [ 'amount' => '__AMOUNT__', 'currency' => $currencyLabel, ]); $wholesaleStock = (array)($item['stock'] ?? []); $wholesaleStockItems = array_values(array_filter( (array)($wholesaleStock['data'] ?? []), static fn(mixed $stockItem): bool => is_array($stockItem) )); $hasWholesaleDetails = $isWholesaler && $wholesaleStockItems !== []; $showCardManagerTools = $showCardManagerTools || $hasWholesaleDetails; if ($isManager) { $contentId = (int)($item['id'] ?? 0); if ($contentId > 0) $managerEntries[$contentId] = $title !=='' ? $title : '#' . $contentId; foreach ((array)($item['child @php use EvolutionCMS\Ddaudio\Facades\Domain; use EvolutionCMS\Ddaudio\Services\CustomerLoyaltyProgram; use EvolutionCMS\Ddaudio\Services\Product\StorefrontPricePresentation; use EvolutionCMS\Ddaudio\Services\Product\StorefrontFreeShipping; use EvolutionCMS\Ddaudio\Services\Product\StorefrontCardTerms; use EvolutionCMS\Ddaudio\Services\Product\StorefrontDeliveryTerms; $item = StorefrontDeliveryTerms::forProduct($item); $url = (string)($item['url'] ?? '#'); $title = trim((string)($item['pagetitle'] ?? '')); $normalizeImage = static function (mixed $image): string { $image = is_string($image) ? trim($image) : ''; return $image ==='' || preg_match('~^(?:https?:)?//|^data:|^/~i', $image) ? $image : '/' . ltrim($image, '/'); }; $sliderLarge = array_values((array)($item['sliderLarge'] ?? [])); $largeImages = []; $images = collect(array_values((array)($item['slider'] ?? []))) ->map(static fn(mixed $image, int $index): array => [$normalizeImage($image), $normalizeImage($sliderLarge[$index] ?? '')]) ->filter(static fn(array $pair): bool => $pair[0] !=='') ->unique(static fn(array $pair): string => $pair[0]) ->take(4) ->values() ->map(static function (array $pair) use (&$largeImages): string { if ($pair[1] !=='') { $largeImages[$pair[0]] = $pair[1]; } return $pair[0]; }) ->all(); $imageSrcset = static fn(string $image): string => isset($largeImages[$image]) ? $image . ' 320w, ' . $largeImages[$image] . ' 920w' : ''; $imageSizes ='(max-width: 575px) 100vw, (max-width: 1199px) 50vw, 360px'; if ($images === [] && is_string($item['image'] ?? null) && trim($item['image']) !=='') { $fallbackImage = trim($item['image']); $images[] = preg_match('~^(?:https?:)?//|^data:|^/~i', $fallbackImage) ? $fallbackImage : '/' . ltrim($fallbackImage, '/'); } $image = (string)($images[0] ?? ''); /* Фото немає — сірий логотип замість картинки товару. Клас is-placeholder не дає заглушці розтягнутись на всю плитку (див. card-product.scss). */ $imageIsPlaceholder = $image ===''; if ($imageIsPlaceholder) { $image = rtrim($storefrontThemeUrl, '/') . '/img/icons/logo-gray.svg'; } $hasImageSlider = count($images) > 1; $labels = (array)($item['labels'] ?? []); $rating = (array)($item['rating'] ?? []); $ratingValue = (float)($rating['rating'] ?? 0); $ratingTotal = 5.0; // total is the sum of votes, not the scale maximum. $ratingVotes = max(0, (int)($rating['votes'] ?? 0)); $ratingFilled = max(0, min(5, (int)round($ratingValue))); $currency = strtoupper((string)($_SESSION['currency'] ?? 'UAH')); $currencyLabel = \EvolutionCMS\Ddaudio\Support\StorefrontMoney::label($currency); $forSale = (bool)($item['forSale'] ?? false); $price = $item['price'] ?? null; $oldPrice = $item['price_old'] ?? null; $isAuthenticatedCustomer = (bool)evo()->getLoginUserID('web') && empty($_SESSION['webSellerId']); $pricePresentation = StorefrontPricePresentation::forProduct($item, $isAuthenticatedCustomer); $oldPrice = $pricePresentation['old_price']; $discountPercent = $pricePresentation['discount_percent']; $clientDiscountPercent = $pricePresentation['client_percent']; $isClientOnlyDiscount = $clientDiscountPercent > 0; $promoUntil = $pricePresentation['promo_until']; $formatCardAmount = static fn(float|int $amount): string => \EvolutionCMS\Ddaudio\Support\StorefrontMoney::format($amount); $showCardLoyaltyBonus = empty($_SESSION['webSellerId']) && Domain::isCountry('UA'); $stockCount = (int)($item['inStockCount'] ?? 0); $availabilityBadge = (array)($item['availabilityBadge'] ?? []); $availability = trim((string)($availabilityBadge['label'] ?? '')); $availabilityCta = (array)($availabilityBadge['cta'] ?? []); $availabilityCtaType = (string)($availabilityCta['type'] ?? ($stockCount > 0 ? 'cart' : 'request')); $isOrderable = (bool)($availabilityBadge['is_orderable'] ?? ($stockCount > 0)); $cardTerms = StorefrontCardTerms::forProduct($item); $productOrderId = (int)($item['productId'] ?? 0); $favoriteProductId = (int)($item['id'] ?? $productOrderId); if ((int)($item['template'] ?? 0) === 32) { $favoriteProductId = (int)($item['parent'] ?? $favoriteProductId); } $favoriteIds = array_map('intval', (array)($_SESSION['favorite'] ?? [])); $isFavorite = $favoriteProductId > 0 && in_array($favoriteProductId, $favoriteIds, true); $favoriteAddLabel = __('dd::global.favorite_add'); $favoriteRemoveLabel = __('dd::global.favorite_remove'); $canAddToCart = $forSale && $isOrderable && $availabilityCtaType ==='cart' && $productOrderId > 0; $canRequestProduct = $forSale && $isOrderable && $availabilityCtaType ==='request' && $productOrderId > 0; $managerEntries = []; $managerId = (int)evo()->getLoginUserID('mgr'); $isManager = $managerId > 0; $isWholesaler = !empty($_SESSION['webSellerId']); $cardMetaSku = trim((string)($item['sku'] ?? '')); $hasStaffVariants = ($isManager || $isWholesaler) && count((array)($item['children'] ?? [])) > 1; $cardMetaId = $hasStaffVariants ? $productOrderId : (int)($item['id'] ?? 0); $cardShowMetaId = $cardMetaId > 0 && \EvolutionCMS\Ddaudio\Services\Storefront\StorefrontProductIdVisibility::visible(); $hasKitComposition = !empty($item['stock']['data']['sku']['related']) && $productOrderId > 0; $canViewKitComposition = \EvolutionCMS\Ddaudio\Helpers\DevHelper::isManager() && $hasKitComposition; $hasCardNuances = ($isManager || $isWholesaler) && !empty($item['nuances']['full']); $cardMarginPercent = ($isManager || $isWholesaler) ? trim((string)($item['marginality']['percent'] ?? '')) : ''; $canPrintLabels = \EvolutionCMS\Ddaudio\Helpers\DevHelper::isManager() && $productOrderId > 0; $showCardManagerTools = ($isManager && $cardMetaId > 0) || $canViewKitComposition || $canPrintLabels || $hasCardNuances || $cardMarginPercent !==''; // "від" the lowest retail price; managers and wholesalers also see each variant's SKU and stock. $cardVariantLabel = trim((string)($item['variant_option_name'] ?? '')) ?: __('dd::storefront.variants'); $cardVariantOptions = []; if (count((array)($item['children'] ?? [])) > 1) { foreach ((array)($item['children'] ?? []) as $child) { if (!is_array($child) || (int)($child['id'] ?? 0) <= 0) { continue; } $child = StorefrontDeliveryTerms::forProduct($child); $childId = (int)$child['id']; $childBadge = (array)($child['availabilityBadge'] ?? []); $childStockCount = (int)($child['inStockCount'] ?? 0); $childCta = (array)($childBadge['cta'] ?? []); $childCtaType = (string)($childCta['type'] ?? ($childStockCount > 0 ? 'cart' : 'request')); $childOrderable = (bool)($childBadge['is_orderable'] ?? ($childStockCount > 0)); $childUnavailableForDomain = (Domain::isEuMarket() && (int)($child['not_sale_europe'] ?? 0) === 1) || (int)($child['not_sale_' . Domain::siteKey()] ?? 0) === 1 || (int)($child['not_sale'] ?? 0) === 1; $childForSale = (int)($child['hidemenu'] ?? 0) === 0 && !$childUnavailableForDomain; $childAvailability = trim((string)($childBadge['label'] ?? '')); if ($childAvailability ==='') { $childAvailability = trim(strip_tags((string)($child['inStock'] ?? ''))); } $childPrice = $child['price'] ?? ''; if (!$childForSale) { $childPrice = (int)($child['hidemenu'] ?? 0) === 1 ? __('dd::global.out_of_production') : __('dd::global.not_sale'); } $childPreorder = (array)($child['preorder'] ?? []); $childPreorder['hint'] = \EvolutionCMS\Ddaudio\Services\Preorder\PreorderHintText::for($childPreorder); $childPresentation = StorefrontPricePresentation::forProduct($child, $isAuthenticatedCustomer); $childOldPrice = $childForSale ? ($childPresentation['old_price'] ?? '') : ''; $childBonusAmount = $showCardLoyaltyBonus && $childForSale && is_numeric($childPrice) ? CustomerLoyaltyProgram::purchaseBonusAmount((float)$childPrice) : 0; $cardVariantOptions[] = [ 'id' => $childId, 'title' => trim((string)($child['pagetitle'] ?? '')) ?: '#' . $childId, 'url' => trim((string)($child['alias'] ?? '')) !=='' ? (string)$child['alias'] . '.html' : $url, 'price' => (string)$childPrice, 'oldPrice' => (string)$childOldPrice, 'discountPercent' => $childForSale ? $childPresentation['discount_percent'] : 0, 'clientPercent' => $childForSale ? $childPresentation['client_percent'] : 0, 'clientBasePrice' => $childForSale ? $childPresentation['client_base_price'] : null, 'delivery' => StorefrontCardTerms::forProduct($child), 'dispatch' => \EvolutionCMS\Ddaudio\Services\Product\StorefrontCardDispatch::forProduct(array_replace($child, ['forSale' => $childForSale])), 'promoUntil' => $childForSale ? $childPresentation['promo_until'] : '', 'priceRefreshRequired' => $childForSale && $childPresentation['refresh_required'], 'bonusAmount' => $childBonusAmount, 'forSale' => $childForSale, 'freeShipping' => StorefrontFreeShipping::forProduct(['price' => $childPrice, 'forSale' => $childForSale]), 'isMarkdown' => \EvolutionCMS\Ddaudio\Services\Product\StorefrontMarkdown::forProduct($child), 'availability' => $childAvailability, 'stockCount' => $childStockCount, 'sku' => trim((string)($child['sku'] ?? '')), 'sku2' => trim((string)($child['sku2'] ?? '')), 'canCart' => $childForSale && $childOrderable && $childCtaType ==='cart', 'canRequest' => $childForSale && $childOrderable && $childCtaType ==='request', 'preorder' => $childPreorder, ]; } } $hasCardVariants = count($cardVariantOptions) > 1; // it); the card it returns has that variant chosen. Until then, as for retail, nothing is chosen. $staffVariantChosen = $hasStaffVariants && $hasCardVariants && !empty($item['variantChosen']); $cardVariantPending = $hasCardVariants && !$staffVariantChosen; $minimumCardVariantPrice = collect($cardVariantOptions) ->filter(static fn(array $option): bool => $option['forSale'] && is_numeric($option['price'])) ->min(static fn(array $option): float => (float)$option['price']); $variantHasCartAction = $cardVariantPending && collect($cardVariantOptions)->contains(static fn(array $option): bool => $option['canCart']); $variantHasRequestAction = $cardVariantPending && collect($cardVariantOptions)->contains(static fn(array $option): bool => $option['canRequest']); $loyaltyBonusPrice = $cardVariantPending && $minimumCardVariantPrice !== null ? $minimumCardVariantPrice : $price; $loyaltyBonusEligible = $cardVariantPending ? $minimumCardVariantPrice !== null : $forSale; $freeShipping = StorefrontFreeShipping::forProduct([ 'price' => $loyaltyBonusPrice, 'forSale' => $loyaltyBonusEligible, ]); $loyaltyBonusAmount = $showCardLoyaltyBonus && $loyaltyBonusEligible && is_numeric($loyaltyBonusPrice) ? CustomerLoyaltyProgram::purchaseBonusAmount((float)$loyaltyBonusPrice) : 0; $loyaltyBonusTemplate = __('dd::product.loyalty_bonus_amount', [ 'amount' => '__AMOUNT__', 'currency' => $currencyLabel, ]); $wholesaleStock = (array)($item['stock'] ?? []); $wholesaleStockItems = array_values(array_filter( (array)($wholesaleStock['data'] ?? []), static fn(mixed $stockItem): bool => is_array($stockItem) )); $hasWholesaleDetails = $isWholesaler && $wholesaleStockItems !== []; $showCardManagerTools = $showCardManagerTools || $hasWholesaleDetails; if ($isManager) { $contentId = (int)($item['id'] ?? 0); if ($contentId > 0) $managerEntries[$contentId] = $title !=='' ? $title : '#' . $contentId; foreach ((array)($item['child
Ensuite — produits universelsIls ne sont pas liés à un modèle précis et conviennent à différents véhicules, dont Skoda Karoq 2018-.
Dans le catalogue de notre boutique en ligne propose un grand choix de produits pour Tuning Skoda Karoq 2018- prix 6 EUR et à 18 EUR.
Tuning Skoda Karoq 2018- – vous êtes unique. Sur les avantages
- La possibilité de payer votre commande de plusieurs façons.
- Étonnamment bas prix des produits Skoda Karoq 2018-, qui sont encore plus réduites grâce à des promotions mensuelles.
- Des services haut de gamme et personnalisé à ses clients.
- La garantie à long terme sur les accessoires auto pour Skoda Karoq 2018-.
- Pas de contrefaçons de marchandises Skoda Karoq 2018- seulement de l'officiel du fabricant.
- Le prix est moins cher que partout: 1 à 3 EUR.
