Vous consultez les produits pour
6 produits
Afficher tous les produits: Skoda Karoq 2018- · 776
Votre voiture
Reset
  • Accessoires Universels
  • brand iconAcura
  • brand iconAlfa Romeo
  • Avatr
  • brand iconAudi
  • brand iconBMW
  • brand iconBentley
  • brand iconBrilliance
  • brand iconBugatti
  • brand iconBuick
  • brand iconBYD
  • brand iconChangan
  • brand iconCadillac
  • brand iconChery
  • brand iconChevrolet
  • brand iconChrysler
  • brand iconCitroen
  • Cowin
  • brand iconCupra
  • brand iconDAF
  • brand iconDacia
  • brand iconDaewoo
  • brand iconDaihatsu
  • Denza
  • brand iconDongfeng
  • brand iconDatsun
  • brand iconDodge
  • brand iconDS Automobiles
  • brand iconFAW
  • brand iconFerrari
  • brand iconFiat
  • brand iconFisker
  • brand iconFord
  • brand iconGAC Motor (Trumpchi)
  • brand iconGeely
  • brand iconGenesis
  • brand iconGreat Wall
  • brand iconGMC
  • brand iconHonda
  • brand iconHongqi
  • brand iconHyundai
  • brand iconHummer
  • brand iconJAC
  • brand iconJaguar
  • brand iconJeep
  • brand iconJetour
  • brand iconInfiniti
  • brand iconIsuzu
  • brand iconIveco
  • brand iconKIA
  • brand iconLamborghini
  • brand iconLada
  • brand iconLancia
  • brand iconLandRover
  • brand iconLeapmotor
  • Leopaard
  • brand iconLexus
  • brand iconLi Auto
  • brand iconLifan
  • brand iconLincoln
  • brand iconLotus
  • brand iconLucid
  • brand iconMAN
  • brand iconMaserati
  • brand iconMazda
  • brand iconMcLaren
  • brand iconMG
  • brand iconMercedes-Benz
  • brand iconMitsubishi
  • brand iconMini
  • Moskvitch
  • brand iconNissan
  • brand iconOpel
  • brand iconPeugeot
  • brand iconPolestar
  • brand iconPorsche
  • brand iconRivian
  • RAVON
  • brand iconRenault
  • brand iconRolls-Royce
  • brand iconSaab
  • brand iconScania
  • brand iconSinotruk
  • brand iconSeat
  • brand iconSkoda
    • 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
  • brand iconSmart
  • brand iconSsangYong
  • StreetScooter
  • brand iconSubaru
  • brand iconSuzuki
  • brand iconTesla
  • brand iconToyota
  • brand iconVolkswagen
  • brand iconVolvo
  • brand iconVolga
  • brand iconXiaomi
  • brand iconXpeng
  • brand iconWuling
  • brand iconZeekr
  • brand iconGAZ
  • brand iconUAZ
Catégorie
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
Afficher les produits

Monogrammes & lettrages pour Skoda Karoq 2018-

Filtres
Reset
Prix EUR
Pays d’origine
Fabricant
Fabricant
@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-.
@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

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