From 86a3885cedeebf9cf585d76c084bdf33eea65563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=2E=20Nagy=20Gerg=C5=91?= Date: Fri, 15 Dec 2023 13:17:21 +0100 Subject: [PATCH] rework totals --- .../views/emails/customer-new-order.blade.php | 2 +- src/Interfaces/Itemable.php | 18 +++- src/Interfaces/LineItem.php | 8 +- src/Models/Item.php | 35 +++++--- src/Models/Order.php | 4 +- src/Models/Shipping.php | 22 ++--- src/Traits/AsCustomer.php | 2 +- src/Traits/InteractsWithItems.php | 88 ++++++++++++++----- tests/Models/CartTest.php | 4 +- tests/Models/ItemTest.php | 10 +-- tests/Models/OrderTest.php | 6 +- tests/Models/ShippingTest.php | 10 +-- 12 files changed, 137 insertions(+), 72 deletions(-) diff --git a/resources/views/emails/customer-new-order.blade.php b/resources/views/emails/customer-new-order.blade.php index de3492af..f794ae2c 100644 --- a/resources/views/emails/customer-new-order.blade.php +++ b/resources/views/emails/customer-new-order.blade.php @@ -24,7 +24,7 @@ **Tax**: {{ $order->formattedTax }} -**Net. Total**: {{ $order->formattedNetTotal }} +**Subtotal**: {{ $order->formattedSubtotal }} **Gross Total**: {{ $order->formattedTotal }} diff --git a/src/Interfaces/Itemable.php b/src/Interfaces/Itemable.php index 0ceb5f6e..a5edb4a8 100644 --- a/src/Interfaces/Itemable.php +++ b/src/Interfaces/Itemable.php @@ -34,14 +34,24 @@ public function getTotal(): float; public function getFormattedTotal(): string; /** - * Get the net total. + * Get the subtotal. */ - public function getNetTotal(): float; + public function getSubtotal(): float; /** - * Get the formatted net total. + * Get the formatted subtotal. */ - public function getFormattedNetTotal(): string; + public function getFormattedSubtotal(): string; + + /** + * Get the itemable model's fee total. + */ + public function getFeeTotal(): float; + + /** + * Get the formatted fee total. + */ + public function getFormattedFeeTotal(): string; /** * Get the tax. diff --git a/src/Interfaces/LineItem.php b/src/Interfaces/LineItem.php index 30563eb6..84ed9d45 100644 --- a/src/Interfaces/LineItem.php +++ b/src/Interfaces/LineItem.php @@ -30,14 +30,14 @@ public function getTotal(): float; public function getFormattedTotal(): string; /** - * Get the net total. + * Get the subtotal. */ - public function getNetTotal(): float; + public function getSubtotal(): float; /** - * Get the formatted net total. + * Get the formatted subtotal. */ - public function getFormattedNetTotal(): string; + public function getFormattedSubtotal(): string; /** * Get the quantity. diff --git a/src/Models/Item.php b/src/Models/Item.php index 5eeefcf7..0bfc2e06 100644 --- a/src/Models/Item.php +++ b/src/Models/Item.php @@ -3,6 +3,7 @@ namespace Cone\Bazar\Models; use Cone\Bazar\Database\Factories\ItemFactory; +use Cone\Bazar\Interfaces\Buyable; use Cone\Bazar\Interfaces\Models\Item as Contract; use Cone\Bazar\Traits\InteractsWithTaxes; use Cone\Root\Traits\InteractsWithProxy; @@ -27,7 +28,7 @@ class Item extends Model implements Contract * @var array */ protected $appends = [ - 'net_total', + 'subtotal', 'total', ]; @@ -149,22 +150,22 @@ protected function formattedTotal(): Attribute } /** - * Get the net total attribute. + * Get the subtotal attribute. */ - protected function netTotal(): Attribute + protected function subtotal(): Attribute { return new Attribute( - get: fn (): float => $this->getNetTotal(), + get: fn (): float => $this->getSubtotal(), ); } /** - * Get the formatted net total attribute. + * Get the formatted subtotal attribute. */ - protected function formattedNetTotal(): Attribute + protected function formattedSubtotal(): Attribute { return new Attribute( - get: fn (): string => $this->getFormattedNetTotal(), + get: fn (): string => $this->getFormattedSubtotal(), ); } @@ -209,19 +210,19 @@ public function getFormattedTotal(): string } /** - * Get the net total. + * Get the subtotal. */ - public function getNetTotal(): float + public function getSubtotal(): float { return $this->getPrice() * $this->getQuantity(); } /** - * Get the formatted net total. + * Get the formatted subtotal. */ - public function getFormattedNetTotal(): string + public function getFormattedSubtotal(): string { - return Str::currency($this->getNetTotal(), $this->itemable->getCurrency()); + return Str::currency($this->getSubtotal(), $this->itemable->getCurrency()); } /** @@ -232,11 +233,19 @@ public function getQuantity(): float return $this->quantity; } + /** + * Determine if the item is a line item. + */ + public function isLineItem(): bool + { + return $this->buyable instanceof Buyable; + } + /** * Determine if the item is a fee. */ public function isFee(): bool { - return is_null($this->buyable); + return ! $this->isLineItem(); } } diff --git a/src/Models/Order.php b/src/Models/Order.php index 11d00ade..b0a704fd 100644 --- a/src/Models/Order.php +++ b/src/Models/Order.php @@ -54,10 +54,10 @@ class Order extends Model implements Contract */ protected $appends = [ 'formatted_discount', - 'formatted_net_total', + 'formatted_subtotal', 'formatted_tax', 'formatted_total', - 'net_total', + 'subtotal', 'status_name', 'tax', 'total', diff --git a/src/Models/Shipping.php b/src/Models/Shipping.php index e745140b..ea549058 100644 --- a/src/Models/Shipping.php +++ b/src/Models/Shipping.php @@ -134,25 +134,25 @@ protected function formattedTotal(): Attribute } /** - * Get the net total attribute. + * Get the subtotal attribute. */ - protected function netTotal(): Attribute + protected function subtotal(): Attribute { return new Attribute( get: function (): float { - return $this->getNetTotal(); + return $this->getSubtotal(); } ); } /** - * Get the formatted net total attribute. + * Get the formatted subtotal attribute. */ - protected function formattedNetTotal(): Attribute + protected function formattedSubtotal(): Attribute { return new Attribute( get: function (): string { - return $this->getFormattedNetTotal(); + return $this->getFormattedSubtotal(); } ); } @@ -214,19 +214,19 @@ public function getFormattedTotal(): string } /** - * Get the shipping's net total. + * Get the shipping's subtotal. */ - public function getNetTotal(): float + public function getSubtotal(): float { return $this->getPrice(); } /** - * Get the shipping's formatted net total. + * Get the shipping's formatted subtotal. */ - public function getFormattedNetTotal(): string + public function getFormattedSubtotal(): string { - return Str::currency($this->getNetTotal(), $this->shippable->getCurrency()); + return Str::currency($this->getSubtotal(), $this->shippable->getCurrency()); } /** diff --git a/src/Traits/AsCustomer.php b/src/Traits/AsCustomer.php index c58ca736..956f45df 100644 --- a/src/Traits/AsCustomer.php +++ b/src/Traits/AsCustomer.php @@ -52,6 +52,6 @@ public function address(): MorphOne return $this->addresses()->one()->ofMany([ 'default' => 'max', 'id' => 'min', - ]); + ])->withDefault(); } } diff --git a/src/Traits/InteractsWithItems.php b/src/Traits/InteractsWithItems.php index 34dfea93..c97d645f 100644 --- a/src/Traits/InteractsWithItems.php +++ b/src/Traits/InteractsWithItems.php @@ -76,6 +76,30 @@ protected function currency(): Attribute * Get the line items attribute. */ protected function lineItems(): Attribute + { + return new Attribute( + get: function (): Collection { + return $this->items->filter->isLineItem(); + } + ); + } + + /** + * Get the fees attribute. + */ + protected function fees(): Attribute + { + return new Attribute( + get: function (): Collection { + return $this->items->filter->isFee(); + } + ); + } + + /** + * Get the taxables attribute. + */ + protected function taxables(): Attribute { return new Attribute( get: function (): Collection { @@ -111,25 +135,25 @@ protected function formattedTotal(): Attribute } /** - * Get the net total attribute. + * Get the subtotal attribute. */ - protected function netTotal(): Attribute + protected function subtotal(): Attribute { return new Attribute( get: function (): float { - return $this->getNetTotal(); + return $this->getSubtotal(); } ); } /** - * Get the formatted net total attribute. + * Get the formatted subtotal attribute. */ - protected function formattedNetTotal(): Attribute + protected function formattedSubtotal(): Attribute { return new Attribute( get: function (): string { - return $this->getFormattedNetTotal(); + return $this->getFormattedSubtotal(); } ); } @@ -183,9 +207,13 @@ public function getCurrency(): string */ public function getTotal(): float { - $value = $this->lineItems->reduce(static function (float $value, LineItem $item): float { - return $value + $item->getTotal(); - }, -$this->discount); + $value = $this->items->sum(static function (LineItem $item): float { + return $item->getTotal(); + }); + + $value += $this->shipping->getTotal(); + + $value -= $this->discount; return round($value < 0 ? 0 : $value, 2); } @@ -195,27 +223,47 @@ public function getTotal(): float */ public function getFormattedTotal(): string { - return Str::currency($this->getNetTotal(), $this->getCurrency()); + return Str::currency($this->getTotal(), $this->getCurrency()); } /** - * Get the itemable model's total. + * Get the itemable model's subtotal. + */ + public function getSubtotal(): float + { + $value = $this->lineItems->sum(static function (LineItem $item): float { + return $item->getSubtotal(); + }); + + return round($value < 0 ? 0 : $value, 2); + } + + /** + * Get the formatted subtotal. */ - public function getNetTotal(): float + public function getFormattedSubtotal(): string { - $value = $this->lineItems->reduce(static function (float $value, LineItem $item): float { - return $value + $item->getNetTotal(); - }, -$this->discount); + return Str::currency($this->getSubtotal(), $this->getCurrency()); + } + + /** + * Get the itemable model's fee total. + */ + public function getFeeTotal(): float + { + $value = $this->fees->sum(static function (LineItem $item): float { + return $item->getSubtotal(); + }); return round($value < 0 ? 0 : $value, 2); } /** - * Get the formatted net total. + * Get the formatted fee total. */ - public function getFormattedNetTotal(): string + public function getFormattedFeeTotal(): string { - return Str::currency($this->getNetTotal(), $this->getCurrency()); + return Str::currency($this->getFeeTotal(), $this->getCurrency()); } /** @@ -243,7 +291,7 @@ public function getFormattedTax(): string */ public function calculateTax(bool $update = true): float { - return $this->lineItems->sum(static function (LineItem $item) use ($update): float { + return $this->taxables->sum(static function (LineItem $item) use ($update): float { return $item->calculateTax($update) * $item->getQuantity(); }); } @@ -296,7 +344,7 @@ public function mergeItem(Item $item): Item public function syncItems(): void { $this->items->each(static function (Item $item): void { - if (! $item->isFee() && ! is_null($item->itemable)) { + if ($item->isLineItem() && ! is_null($item->itemable)) { $data = $item->buyable->toItem($item->itemable, $item->only('properties'))->only('price'); $item->fill($data)->calculateTax(); diff --git a/tests/Models/CartTest.php b/tests/Models/CartTest.php index 9d127a56..56f2569a 100644 --- a/tests/Models/CartTest.php +++ b/tests/Models/CartTest.php @@ -83,7 +83,7 @@ public function test_cart_has_total_attribute(): void $this->assertEquals($total, $this->cart->total); } - public function test_cart_has_net_total_attribute(): void + public function test_cart_has_subtotal_attribute(): void { $total = $this->cart->items->sum(function ($item) { return $item->price * $item->quantity; @@ -91,7 +91,7 @@ public function test_cart_has_net_total_attribute(): void $total -= $this->cart->discount; - $this->assertEquals($total, $this->cart->netTotal); + $this->assertEquals($total, $this->cart->subtotal); } public function test_cart_can_be_locked(): void diff --git a/tests/Models/ItemTest.php b/tests/Models/ItemTest.php index 7abb47c3..6041cbd5 100644 --- a/tests/Models/ItemTest.php +++ b/tests/Models/ItemTest.php @@ -60,12 +60,12 @@ public function test_item_has_total_attribute(): void $this->item->getFormattedTotal() ); $this->assertSame($this->item->getFormattedTotal(), $this->item->formattedTotal); - $this->assertSame($this->item->price * $this->item->quantity, $this->item->getNetTotal()); - $this->assertSame($this->item->getNetTotal(), $this->item->netTotal); + $this->assertSame($this->item->price * $this->item->quantity, $this->item->getSubtotal()); + $this->assertSame($this->item->getSubtotal(), $this->item->subtotal); $this->assertSame( - Str::currency($this->item->netTotal, $this->item->itemable->currency), - $this->item->getFormattedNetTotal() + Str::currency($this->item->subtotal, $this->item->itemable->currency), + $this->item->getFormattedSubtotal() ); - $this->assertSame($this->item->getFormattedNetTotal(), $this->item->formattedNetTotal); + $this->assertSame($this->item->getFormattedSubtotal(), $this->item->formattedSubtotal); } } diff --git a/tests/Models/OrderTest.php b/tests/Models/OrderTest.php index a015088a..be213f1e 100644 --- a/tests/Models/OrderTest.php +++ b/tests/Models/OrderTest.php @@ -82,15 +82,13 @@ public function test_order_has_total_attribute(): void $this->assertEquals($total, $this->order->total); } - public function test_order_has_net_total_attribute(): void + public function test_order_has_subtotal_attribute(): void { $total = $this->order->items->sum(function ($item) { return $item->price * $item->quantity; }); - $total -= $this->order->discount; - - $this->assertEquals($total, $this->order->netTotal); + $this->assertEquals($total, $this->order->subtotal); } public function test_order_has_query_scopes(): void diff --git a/tests/Models/ShippingTest.php b/tests/Models/ShippingTest.php index a6b416a4..e2884755 100644 --- a/tests/Models/ShippingTest.php +++ b/tests/Models/ShippingTest.php @@ -101,13 +101,13 @@ public function testt_has_total_attribute(): void $this->shipping->getFormattedTotal() ); $this->assertSame($this->shipping->getFormattedTotal(), $this->shipping->formattedTotal); - $this->assertSame($this->shipping->cost, $this->shipping->getNetTotal()); - $this->assertSame($this->shipping->getNetTotal(), $this->shipping->netTotal); + $this->assertSame($this->shipping->cost, $this->shipping->getSubtotal()); + $this->assertSame($this->shipping->getSubtotal(), $this->shipping->subtotal); $this->assertSame( - Str::currency($this->shipping->netTotal, $this->shipping->shippable->currency), - $this->shipping->getFormattedNetTotal() + Str::currency($this->shipping->subtotal, $this->shipping->shippable->currency), + $this->shipping->getFormattedSubtotal() ); - $this->assertSame($this->shipping->getFormattedNetTotal(), $this->shipping->formattedNetTotal); + $this->assertSame($this->shipping->getFormattedSubtotal(), $this->shipping->formattedSubtotal); } public function testt_has_driver_name(): void