{{ __('Shopping Cart') }}

@csrf
@if (count($products) > 0)
{{ __('Updating cart') }} {{ __('Please wait while totals are refreshed') }}
@php /* Pre-load promotion data for all gift items (one pass, no duplicate queries) */ $promotionMap = []; $qualifyingProductIds = []; $discountedLineOffers = []; $cartContents = Cart::instance('cart')->content(); foreach ($cartContents as $_ci) { if (!(bool)($_ci->options['is_free_gift'] ?? false)) continue; $pid = (int)($_ci->options['promotion_id'] ?? 0); if (!$pid || isset($promotionMap[$pid])) continue; $promo = \Botble\Ecommerce\Models\Discount::with('products')->find($pid); if (!$promo) continue; $targetSpecific = in_array((string)$promo->target, [ \Botble\Ecommerce\Enums\DiscountTargetEnum::SPECIFIC_PRODUCT, \Botble\Ecommerce\Enums\DiscountTargetEnum::PRODUCT_VARIANT, ]); $promoProductIds = $targetSpecific ? $promo->products->pluck('id')->map(fn($id) => (int)$id)->toArray() : []; $promotionMap[$pid] = [ 'title' => $promo->title, 'product_ids' => $promoProductIds, 'all_items' => !$targetSpecific, ]; $qualifyingProductIds = array_merge($qualifyingProductIds, $promoProductIds); } $qualifyingProductIds = array_unique($qualifyingProductIds); $buyXGetYPercentPromotions = \Botble\Ecommerce\Models\Discount::query() ->where('type', \Botble\Ecommerce\Enums\DiscountTypeEnum::PROMOTION) ->where('type_option', \Botble\Ecommerce\Enums\DiscountTypeOptionEnum::BUY_X_GET_Y_DISCOUNT) ->where('start_date', '<=', \Carbon\Carbon::now()) ->where(fn($query) => $query->whereNull('end_date')->orWhere('end_date', '>=', \Carbon\Carbon::now())) ->with('products') ->get(); foreach ($buyXGetYPercentPromotions as $promotion) { $buyProductIds = $promotion->products->pluck('id')->map(fn($id) => (int)$id)->all(); if (empty($buyProductIds)) continue; $buyQuantity = max((int)$promotion->product_quantity, 1); $buyCartQuantity = 0; foreach ($cartContents as $ci) { if ((bool)($ci->options['is_free_gift'] ?? false)) continue; $ciProduct = $products->find($ci->id); if (!$ciProduct) continue; if ( in_array((int)$ci->id, $buyProductIds, true) || in_array((int)$ciProduct->original_product->id, $buyProductIds, true) ) { $buyCartQuantity += (int)$ci->qty; } } $eligibleQty = (int)floor($buyCartQuantity / $buyQuantity); if ($eligibleQty < 1) continue; foreach (\Illuminate\Support\Arr::wrap(data_get($promotion->apply_conditions, 'free_products', [])) as $offerProduct) { $offerProductId = (int)data_get($offerProduct, 'product_id'); $discountPercent = min(max((float)data_get($offerProduct, 'discount_percent', $promotion->value), 1), 100); $remainingQty = $eligibleQty; foreach ($cartContents as $ci) { if ($remainingQty < 1 || (bool)($ci->options['is_free_gift'] ?? false)) continue; $ciProduct = $products->find($ci->id); if (!$ciProduct) continue; if ( (int)$ci->id !== $offerProductId && (int)$ciProduct->original_product->id !== $offerProductId ) { continue; } $discountQty = min((int)$ci->qty, $remainingQty); $unitPrice = EcommerceHelper::isTaxEnabled() && $ci->priceTax > 0 ? (float)$ci->priceTax : (float)$ci->price; $discountAmount = $unitPrice * ($discountPercent / 100) * $discountQty; if ($discountAmount > 0) { $discountedLineOffers[$ci->rowId] = [ 'amount' => ($discountedLineOffers[$ci->rowId]['amount'] ?? 0) + $discountAmount, 'percent' => $discountPercent, 'discount_qty' => ($discountedLineOffers[$ci->rowId]['discount_qty'] ?? 0) + $discountQty, 'type' => 'buy_x_get_y_percent', ]; } $remainingQty -= $discountQty; } } } $quantityBasedPromotions = \Botble\Ecommerce\Models\Discount::query() ->where('type', \Botble\Ecommerce\Enums\DiscountTypeEnum::PROMOTION) ->where('type_option', \Botble\Ecommerce\Enums\DiscountTypeOptionEnum::QUANTITY_BASED) ->where('start_date', '<=', \Carbon\Carbon::now()) ->where(fn($query) => $query->whereNull('end_date')->orWhere('end_date', '>=', \Carbon\Carbon::now())) ->with(['products', 'productCollections', 'productCategories']) ->get(); $cartRawTotal = (float) Cart::instance('cart')->rawSubTotal(); foreach ($quantityBasedPromotions as $promotion) { if ( $promotion->target === \Botble\Ecommerce\Enums\DiscountTargetEnum::MINIMUM_ORDER_AMOUNT && (float) $promotion->min_order_price > $cartRawTotal ) { continue; } $eligibleCartItems = $cartContents ->reject(fn($ci) => (bool)($ci->options['is_free_gift'] ?? false)) ->filter(function ($ci) use ($promotion, $products) { $ciProduct = $products->find($ci->id); return match ($promotion->target) { \Botble\Ecommerce\Enums\DiscountTargetEnum::SPECIFIC_PRODUCT, \Botble\Ecommerce\Enums\DiscountTargetEnum::PRODUCT_VARIANT => in_array( (int)$ci->id, $promotion->products->pluck('id')->map(fn($id) => (int)$id)->all(), true ) || ($ciProduct && in_array( (int)$ciProduct->original_product->id, $promotion->products->pluck('id')->map(fn($id) => (int)$id)->all(), true )), \Botble\Ecommerce\Enums\DiscountTargetEnum::PRODUCT_COLLECTIONS => $ciProduct && $ciProduct->original_product ->productCollections() ->pluck('ec_product_collections.id') ->intersect($promotion->productCollections->pluck('id')) ->isNotEmpty(), \Botble\Ecommerce\Enums\DiscountTargetEnum::PRODUCT_CATEGORIES => $ciProduct && $ciProduct->original_product ->categories() ->pluck('ec_product_categories.id') ->intersect($promotion->productCategories->pluck('id')) ->isNotEmpty(), default => true, }; }); $requiredQuantity = max((int)$promotion->product_quantity, 1); $discountPercent = min(max((float)$promotion->value, 0), 100); if ($discountPercent <= 0 || $eligibleCartItems->sum('qty') < $requiredQuantity) { continue; } foreach ($eligibleCartItems as $ci) { $unitPrice = EcommerceHelper::isTaxEnabled() && $ci->priceTax > 0 ? (float)$ci->priceTax : (float)$ci->price; $discountAmount = $unitPrice * (int)$ci->qty * ($discountPercent / 100); if ($discountAmount > 0) { $discountedLineOffers[$ci->rowId] = [ 'amount' => ($discountedLineOffers[$ci->rowId]['amount'] ?? 0) + $discountAmount, 'percent' => $discountedLineOffers[$ci->rowId]['percent'] ?? $discountPercent, 'discount_qty' => (int) $ci->qty, 'type' => $discountedLineOffers[$ci->rowId]['type'] ?? 'quantity_based', ]; } } } /* Sort: regular items first, free gifts at the bottom */ $cartContents = $cartContents->sortBy(fn($ci) => (bool)($ci->options['is_free_gift'] ?? false)); @endphp @foreach($cartContents as $key => $cartItem) @php $product = $products->find($cartItem->id); $isFreeGift = (bool) ($cartItem->options['is_free_gift'] ?? false); $giftMessage = $cartItem->options['gift_message'] ?? __('Free gift with offer'); @endphp @if (!empty($product)) @if ($isFreeGift) {{-- Free gift row --}} @php $giftPromoId = (int)($cartItem->options['promotion_id'] ?? 0); $giftPromoInfo = $promotionMap[$giftPromoId] ?? null; $giftUnitPrice = (float)($cartItem->options['gift_original_price'] ?? 0); /* Find qualifying non-gift cart items for this promotion */ $qualifyingCartItems = collect(); if ($giftPromoInfo) { $qualifyingCartItems = $cartContents ->reject(fn($ci) => (bool)($ci->options['is_free_gift'] ?? false)) ->filter(function($ci) use ($giftPromoInfo) { if ($giftPromoInfo['all_items']) return true; return in_array((int)$ci->id, $giftPromoInfo['product_ids']); }); } @endphp
{{-- Qualifying products header --}} @if ($qualifyingCartItems->isNotEmpty())
🛒 {{ __('Free with your purchase of:') }}
@foreach($qualifyingCartItems as $qci) @php $qProduct = $products->find($qci->id); @endphp @if($qProduct)
{!! RvMedia::image($qci->options['image'] ?? null, $qProduct->original_product->name, 'thumb') !!}
{{ $qProduct->original_product->name }} ×{{ $qci->qty }}
@endif @endforeach
@endif {{-- Gift product row --}}
{{ $product->original_product->name }} 🎁 {{ __('Free Gift') }}
@if (is_plugin_active('marketplace') && $product->original_product->store->id)

{{ __('Sold by') }}: {{ $product->original_product->store->name }}

@endif @if (!empty($cartItem->options['attributes']))

{{ $cartItem->options['attributes'] }}

@endif

{{ __('Qty') }}: {{ $cartItem->qty }}

{{ __('FREE') }} @if ($giftUnitPrice > 0) {{ format_price($giftUnitPrice) }} @endif
@else {{-- Regular cart row --}} @php $isQualifying = in_array((int)$cartItem->id, $qualifyingProductIds) || collect($promotionMap)->contains('all_items', true); $lineUnitPrice = EcommerceHelper::isTaxEnabled() ? (float) $cartItem->priceTax : (float) $cartItem->price; $lineTotal = $lineUnitPrice * (int) $cartItem->qty; $lineOffer = $discountedLineOffers[$cartItem->rowId] ?? null; $lineOfferAmount = $lineOffer ? min((float)$lineOffer['amount'], (float)$lineTotal) : 0; $lineOfferPercent = $lineOffer ? (float) $lineOffer['percent'] : 0; $lineDiscountQty = $lineOffer ? min((int) ($lineOffer['discount_qty'] ?? $cartItem->qty), (int) $cartItem->qty) : 0; $shouldSplitOfferLine = $lineOfferAmount > 0 && ($lineOffer['type'] ?? null) === 'buy_x_get_y_percent' && $lineDiscountQty > 0 && $lineDiscountQty < (int) $cartItem->qty; $lineRegularQty = (int) $cartItem->qty - $lineDiscountQty; $lineDiscountedTotal = ($lineUnitPrice * $lineDiscountQty) - $lineOfferAmount; @endphp @if ($shouldSplitOfferLine)
@if (is_plugin_active('marketplace') && $product->original_product->store->id)

{{ __('Sold by') }}: {{ $product->original_product->store->name }}

@endif @if (!empty($cartItem->options['attributes']))

{{ $cartItem->options['attributes'] }}

@endif @if (!empty($cartItem->options['options']))
{!! render_product_options_info($cartItem->options['options'], $product, false) !!}
@endif @if (!empty($cartItem->options['extras']) && is_array($cartItem->options['extras'])) @foreach($cartItem->options['extras'] as $option) @if (!empty($option['key']) && !empty($option['value']) && strtolower($option['key']) !== 'price')

{{ $option['key'] }}: {{ $option['value'] }}

@endif @endforeach @endif

{{ __('Qty') }}: {{ $lineDiscountQty }}

{{ format_price($lineUnitPrice * $lineDiscountQty) }} {{ format_price($lineDiscountedTotal) }} {{ __('Offer') }} {{ $lineOfferPercent }}% {{ __('off') }}
@if (is_plugin_active('marketplace') && $product->original_product->store->id)

{{ __('Sold by') }}: {{ $product->original_product->store->name }}

@endif @if (!empty($cartItem->options['attributes']))

{{ $cartItem->options['attributes'] }}

@endif @if (!empty($cartItem->options['options']))
{!! render_product_options_info($cartItem->options['options'], $product, false) !!}
@endif @if (!empty($cartItem->options['extras']) && is_array($cartItem->options['extras'])) @foreach($cartItem->options['extras'] as $option) @if (!empty($option['key']) && !empty($option['value']) && strtolower($option['key']) !== 'price')

{{ $option['key'] }}: {{ $option['value'] }}

@endif @endforeach @endif
{{ format_price($lineUnitPrice * $lineRegularQty) }}
@else
@if (is_plugin_active('marketplace') && $product->original_product->store->id)

{{ __('Sold by') }}: {{ $product->original_product->store->name }}

@endif @if (!empty($cartItem->options['attributes']))

{{ $cartItem->options['attributes'] }}

@endif @if (!empty($cartItem->options['options']))
{!! render_product_options_info($cartItem->options['options'], $product, false) !!}
@endif @if (!empty($cartItem->options['extras']) && is_array($cartItem->options['extras'])) @foreach($cartItem->options['extras'] as $option) @if (!empty($option['key']) && !empty($option['value']) && strtolower($option['key']) !== 'price')

{{ $option['key'] }}: {{ $option['value'] }}

@endif @endforeach @endif
@php $lineTotalAfterOffer = max((float)$lineTotal - $lineOfferAmount, 0); @endphp
@if ($lineOfferAmount > 0) {{ format_price($lineTotal) }} {{ format_price($lineTotalAfterOffer) }} {{ (float)$lineOffer['percent'] }}% {{ __('off') }} @else {{ format_price($lineTotal) }} @endif
@endif @endif @endif @endforeach
@else

{{ __('Your cart is empty!') }}

@endif
@if (count($products) > 0)
{{-- Coupon card --}}
{{ __('Coupon / Promo Code') }}
@if ($couponDiscountAmount > 0 && session('applied_coupon_code'))
{{ session('applied_coupon_code') }} -{{ format_price($couponDiscountAmount) }}
@else
@endif
{{-- Order summary card --}}
{{ __('Order Summary') }}
{{ __('Subtotal') }} {{ format_price(Cart::instance('cart')->rawSubTotal()) }}
@if (EcommerceHelper::isTaxEnabled())
{{ __('Tax') }} {{ format_price(Cart::instance('cart')->rawTax()) }}
@endif @php $freeGiftDiscountAmount = app(\Botble\Ecommerce\Services\BuyXGetYGiftSyncService::class)->getCartFreeGiftDiscountAmount(); if ($freeGiftDiscountAmount > 0 && abs((float) $promotionDiscountAmount - (float) $freeGiftDiscountAmount) < 0.01) { $displayPromotionDiscountAmount = 0; } elseif ((float) $promotionDiscountAmount > (float) $freeGiftDiscountAmount) { $displayPromotionDiscountAmount = (float) $promotionDiscountAmount - (float) $freeGiftDiscountAmount; } else { $displayPromotionDiscountAmount = (float) $promotionDiscountAmount; } @endphp @if ($displayPromotionDiscountAmount)
{{ __('Promotion discount') }} -{{ format_price($displayPromotionDiscountAmount) }}
@endif @if ($couponDiscountAmount > 0 && session('applied_coupon_code'))
{{ __('Coupon') }} ({{ session('applied_coupon_code') }}) -{{ format_price($couponDiscountAmount) }}
@endif
{{ __('Total') }} {{ ($displayPromotionDiscountAmount + $couponDiscountAmount) > Cart::instance('cart')->rawTotal() ? format_price(0) : format_price(Cart::instance('cart')->rawTotal() - $displayPromotionDiscountAmount - $couponDiscountAmount) }}

{{ __('Shipping fees not included') }}

{{-- Action buttons --}}
@endif {!! Theme::partial('cross-sell-products', compact('crossSellProducts')) !!}