From 04267660821ee8b0e75523b92627dd787c47ca36 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 4 Sep 2024 07:53:58 +0100 Subject: [PATCH 1/3] Support selection of the highest tax rate in the basket for shipping --- .../config/shipping-tables.php | 7 ++ .../src/Models/ShippingRate.php | 28 ++++- .../Resolvers/ShippingOptionResolverTest.php | 118 ++++++++++++++++++ 3 files changed, 152 insertions(+), 1 deletion(-) diff --git a/packages/table-rate-shipping/config/shipping-tables.php b/packages/table-rate-shipping/config/shipping-tables.php index 1e2ce9a3f9..b11a7c328b 100644 --- a/packages/table-rate-shipping/config/shipping-tables.php +++ b/packages/table-rate-shipping/config/shipping-tables.php @@ -2,4 +2,11 @@ return [ 'enabled' => env('LUNAR_SHIPPING_TABLES_ENABLED', true), + + /* + * What method should we use for a shipping rate tax calculation? + * Options are 'default' for the system-wide default tax rate, + * or 'highest' to select the highest tax rate in the cart + */ + 'shipping_rate_tax_calculation' => 'default', ]; diff --git a/packages/table-rate-shipping/src/Models/ShippingRate.php b/packages/table-rate-shipping/src/Models/ShippingRate.php index bcb5a08975..9b97222d40 100644 --- a/packages/table-rate-shipping/src/Models/ShippingRate.php +++ b/packages/table-rate-shipping/src/Models/ShippingRate.php @@ -10,6 +10,7 @@ use Lunar\Base\Purchasable; use Lunar\Base\Traits\HasPrices; use Lunar\DataTypes\ShippingOption; +use Lunar\Facades\Taxes; use Lunar\Models\Cart; use Lunar\Models\TaxClass; use Lunar\Shipping\Database\Factories\ShippingRateFactory; @@ -28,6 +29,8 @@ class ShippingRate extends BaseModel implements Contracts\ShippingRate, Purchasa */ protected $guarded = []; + private ?TaxClass $resolvedTaxClass; + protected static function booted() { self::deleting(function (self $shippingRate) { @@ -73,7 +76,7 @@ public function getUnitQuantity(): int */ public function getTaxClass(): TaxClass { - return TaxClass::getDefault(); + return $this->resolvedTaxClass ?? TaxClass::getDefault(); } public function getTaxReference(): ?string @@ -139,6 +142,10 @@ public function getThumbnail(): ?string */ public function getShippingOption(Cart $cart): ?ShippingOption { + if (config('lunar.shipping-tables.shipping_rate_tax_calculation') == 'highest') { + $this->resolvedTaxClass = $this->resolveHighestTaxRateInCart($cart); + } + return $this->shippingMethod->driver()->resolve( new ShippingOptionRequest( shippingRate: $this, @@ -156,4 +163,23 @@ public function getTotalInventory(): int { return 1; } + + private function resolveHighestTaxRateInCart(Cart $cart): ?TaxClass + { + $highestRate = false; + $highestTaxClass = null; + + foreach ($cart->lines as $cartLine) { + if ($cartLine->purchasable->taxClass) { + foreach ($cartLine->purchasable->taxClass->taxRateAmounts as $amount) { + if ($highestRate === false || $amount->percentage > $highestRate) { + $highestRate = $amount->percentage; + $highestTaxClass = $cartLine->purchasable->taxClass; + } + } + } + } + + return $highestTaxClass; + } } diff --git a/tests/shipping/Unit/Resolvers/ShippingOptionResolverTest.php b/tests/shipping/Unit/Resolvers/ShippingOptionResolverTest.php index 68861802cf..8ae92314e0 100644 --- a/tests/shipping/Unit/Resolvers/ShippingOptionResolverTest.php +++ b/tests/shipping/Unit/Resolvers/ShippingOptionResolverTest.php @@ -5,7 +5,10 @@ use Lunar\Models\CartAddress; use Lunar\Models\Country; use Lunar\Models\Currency; +use Lunar\Models\Price; +use Lunar\Models\ProductVariant; use Lunar\Models\TaxClass; +use Lunar\Models\TaxRateAmount; use Lunar\Shipping\DataTransferObjects\ShippingOptionLookup; use Lunar\Shipping\Facades\Shipping; use Lunar\Shipping\Models\ShippingMethod; @@ -94,3 +97,118 @@ $this->assertcount(1, $options); }); + +test('sets tax rate to the highest basket rate', function () { + config()->set('lunar.shipping-tables.shipping_rate_tax_calculation', 'highest'); + + $currency = Currency::factory()->create([ + 'default' => true, + ]); + + $country = Country::factory()->create(); + + TaxClass::factory()->create([ + 'default' => true, + 'name' => 'default', + ]); + + $higherRate = TaxClass::factory()->create([ + 'default' => false, + 'name' => 'higher' + ]); + + $taxAmount = TaxRateAmount::factory()->create(); + $taxAmount->percentage = 90; + + $higherRate->taxRateAmounts()->save($taxAmount); + + $customerGroup = \Lunar\Models\CustomerGroup::factory()->create([ + 'default' => true, + ]); + + $shippingZone = ShippingZone::factory()->create([ + 'type' => 'countries', + ]); + + $shippingZone->countries()->attach($country); + + $shippingMethod = ShippingMethod::factory()->create([ + 'driver' => 'ship-by', + 'data' => [ + 'minimum_spend' => [ + "{$currency->code}" => 200, + ], + ], + ]); + + $shippingMethod->customerGroups()->sync([ + $customerGroup->id => ['enabled' => true, 'visible' => true, 'starts_at' => now(), 'ends_at' => null], + ]); + + $shippingRate = \Lunar\Shipping\Models\ShippingRate::factory() + ->create([ + 'shipping_method_id' => $shippingMethod->id, + 'shipping_zone_id' => $shippingZone->id, + ]); + + $shippingRate->prices()->createMany([ + [ + 'price' => 600, + 'min_quantity' => 1, + 'currency_id' => $currency->id, + ], + [ + 'price' => 500, + 'min_quantity' => 700, + 'currency_id' => $currency->id, + ], + [ + 'price' => 0, + 'min_quantity' => 800, + 'currency_id' => $currency->id, + ], + ]); + + $cart = $this->createCart($currency, 500); + + $purchasable = ProductVariant::factory()->create(); + $purchasable->stock = 50; + $purchasable->tax_class_id = $higherRate->id; + $purchasable->save(); + + Price::factory()->create([ + 'price' => 300, + 'min_quantity' => 1, + 'currency_id' => $currency->id, + 'priceable_type' => $purchasable->getMorphClass(), + 'priceable_id' => $purchasable->id, + ]); + + $cart->lines()->create([ + 'purchasable_type' => $purchasable->getMorphClass(), + 'purchasable_id' => $purchasable->id, + 'quantity' => 1, + ]); + + $cart->shippingAddress()->create( + CartAddress::factory()->make([ + 'country_id' => $country->id, + 'state' => null, + ])->toArray() + ); + + $shippingRates = Shipping::shippingRates( + $cart->refresh()->calculate() + )->get(); + + $options = Shipping::shippingOptions()->cart( + $cart->refresh()->calculate() + )->get( + new ShippingOptionLookup( + shippingRates: $shippingRates + ) + ); + + $this->assertcount(1, $options); + $this->assertSame($options->first()->option->taxClass->id, $higherRate->id); +}); From 83e1fb87e4e185ee5fcd5bc088e8d39da9c4d367 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 4 Sep 2024 07:55:11 +0100 Subject: [PATCH 2/3] :beer: --- packages/table-rate-shipping/src/Models/ShippingRate.php | 1 - tests/shipping/Unit/Resolvers/ShippingOptionResolverTest.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/table-rate-shipping/src/Models/ShippingRate.php b/packages/table-rate-shipping/src/Models/ShippingRate.php index 9b97222d40..a0769d76b6 100644 --- a/packages/table-rate-shipping/src/Models/ShippingRate.php +++ b/packages/table-rate-shipping/src/Models/ShippingRate.php @@ -10,7 +10,6 @@ use Lunar\Base\Purchasable; use Lunar\Base\Traits\HasPrices; use Lunar\DataTypes\ShippingOption; -use Lunar\Facades\Taxes; use Lunar\Models\Cart; use Lunar\Models\TaxClass; use Lunar\Shipping\Database\Factories\ShippingRateFactory; diff --git a/tests/shipping/Unit/Resolvers/ShippingOptionResolverTest.php b/tests/shipping/Unit/Resolvers/ShippingOptionResolverTest.php index 8ae92314e0..c12fd40998 100644 --- a/tests/shipping/Unit/Resolvers/ShippingOptionResolverTest.php +++ b/tests/shipping/Unit/Resolvers/ShippingOptionResolverTest.php @@ -114,7 +114,7 @@ $higherRate = TaxClass::factory()->create([ 'default' => false, - 'name' => 'higher' + 'name' => 'higher', ]); $taxAmount = TaxRateAmount::factory()->create(); From 80b9c1826918b41e2fe9d8785e9c08efdbe91ebb Mon Sep 17 00:00:00 2001 From: Author Date: Tue, 22 Oct 2024 10:18:07 +0000 Subject: [PATCH 3/3] chore: fix code style --- .../CustomerResource/RelationManagers/OrdersRelationManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/admin/src/Filament/Resources/CustomerResource/RelationManagers/OrdersRelationManager.php b/packages/admin/src/Filament/Resources/CustomerResource/RelationManagers/OrdersRelationManager.php index 99b114b773..b01eea5116 100644 --- a/packages/admin/src/Filament/Resources/CustomerResource/RelationManagers/OrdersRelationManager.php +++ b/packages/admin/src/Filament/Resources/CustomerResource/RelationManagers/OrdersRelationManager.php @@ -5,8 +5,8 @@ use Filament\Tables; use Filament\Tables\Table; use Lunar\Admin\Filament\Resources\OrderResource; -use Lunar\Admin\Support\RelationManagers\BaseRelationManager; use Lunar\Admin\Filament\Resources\OrderResource\Pages\ManageOrder; +use Lunar\Admin\Support\RelationManagers\BaseRelationManager; use Lunar\Models\Order; class OrdersRelationManager extends BaseRelationManager