diff --git a/packages/core/src/DiscountTypes/BuyXGetY.php b/packages/core/src/DiscountTypes/BuyXGetY.php index 32ceb017cd..d1d40606a3 100644 --- a/packages/core/src/DiscountTypes/BuyXGetY.php +++ b/packages/core/src/DiscountTypes/BuyXGetY.php @@ -4,6 +4,7 @@ use Illuminate\Pipeline\Pipeline; use Illuminate\Support\Collection; +use Lunar\Base\Purchasable; use Lunar\Base\ValueObjects\Cart\DiscountBreakdown; use Lunar\Base\ValueObjects\Cart\DiscountBreakdownLine; use Lunar\DataTypes\Price; @@ -251,6 +252,8 @@ private function processAutomaticRewards(CartContract $cart, int $remainingRewar $product = $selectedRewardItem->products()->inRandomOrder()->first(); $purchasable = $product?->variants()->first(); $selectedRewardItem = $product; + } elseif ($selectedRewardItem instanceof Purchasable) { + $purchasable = $selectedRewardItem; } else { $purchasable = $selectedRewardItem->variants->first(); } diff --git a/tests/core/Unit/DiscountTypes/BuyXGetYTest.php b/tests/core/Unit/DiscountTypes/BuyXGetYTest.php index 68db8e0f0d..31a4b67da4 100644 --- a/tests/core/Unit/DiscountTypes/BuyXGetYTest.php +++ b/tests/core/Unit/DiscountTypes/BuyXGetYTest.php @@ -1548,3 +1548,95 @@ // productC (not in collection) should not be discounted expect($lineC->discountTotal->value)->toEqual(0); }); + +test('can add an eligible variant reward when not in cart', function () { + $customerGroup = CustomerGroup::factory()->create([ + 'default' => true, + ]); + + $channel = Channel::factory()->create([ + 'default' => true, + ]); + + $currency = Currency::factory()->create([ + 'code' => 'GBP', + ]); + + $cart = Cart::factory()->create([ + 'channel_id' => $channel->id, + 'currency_id' => $currency->id, + ]); + + $productA = Product::factory()->create(); + $productB = Product::factory()->create(); + + $purchasableA = ProductVariant::factory()->create([ + 'product_id' => $productA->id, + ]); + $purchasableB = ProductVariant::factory()->create([ + 'product_id' => $productB->id, + ]); + + Price::factory()->create([ + 'price' => 1000, // £10 + 'min_quantity' => 1, + 'currency_id' => $currency->id, + 'priceable_type' => $purchasableA->getMorphClass(), + 'priceable_id' => $purchasableA->id, + ]); + + Price::factory()->create([ + 'price' => 1000, // £10 + 'min_quantity' => 1, + 'currency_id' => $currency->id, + 'priceable_type' => $purchasableB->getMorphClass(), + 'priceable_id' => $purchasableB->id, + ]); + + $cart->lines()->create([ + 'purchasable_type' => $purchasableA->getMorphClass(), + 'purchasable_id' => $purchasableA->id, + 'quantity' => 1, + ]); + + $discount = Discount::factory()->create([ + 'type' => BuyXGetY::class, + 'name' => 'Test Variant Reward Discount', + 'data' => [ + 'min_qty' => 1, + 'reward_qty' => 2, + 'automatically_add_rewards' => true, + ], + ]); + + $discount->customerGroups()->sync([ + $customerGroup->id => [ + 'enabled' => true, + 'starts_at' => now(), + ], + ]); + + $discount->channels()->sync([ + $channel->id => [ + 'enabled' => true, + 'starts_at' => now()->subHour(), + ], + ]); + + $discount->discountableConditions()->create([ + 'discountable_type' => $productA->getMorphClass(), + 'discountable_id' => $productA->id, + ]); + + // The reward is a variant, which the admin allows selecting. + $discount->discountableRewards()->create([ + 'discountable_type' => $purchasableB->getMorphClass(), + 'discountable_id' => $purchasableB->id, + 'type' => 'reward', + ]); + + $cart = $cart->calculate(); + + $this->assertEquals(1200, $cart->total->value); + $this->assertCount(1, $cart->freeItems); +});