diff --git a/packages/admin/resources/views/partials/orders/activity/email-notification.blade.php b/packages/admin/resources/views/partials/orders/activity/email-notification.blade.php
index 06ea013e83..feb0eb6fd3 100644
--- a/packages/admin/resources/views/partials/orders/activity/email-notification.blade.php
+++ b/packages/admin/resources/views/partials/orders/activity/email-notification.blade.php
@@ -1,5 +1,5 @@
- @livewire('hub.components.orders.emil-notification', [
+ @livewire('hub.components.orders.email-notification', [
'log' => $log,
])
diff --git a/packages/admin/src/Models/Staff.php b/packages/admin/src/Models/Staff.php
index 4cfc9546e8..35b76d0c7b 100644
--- a/packages/admin/src/Models/Staff.php
+++ b/packages/admin/src/Models/Staff.php
@@ -58,6 +58,13 @@ protected static function newFactory(): StaffFactory
'email_verified_at' => 'datetime',
];
+ /**
+ * Append attributes to the model.
+ *
+ * @var array
+ */
+ protected $appends = ['fullName', 'gravatar'];
+
/**
* Create a new instance of the Model.
*/
@@ -124,7 +131,12 @@ public function getFullNameAttribute()
*/
public function getGravatarAttribute()
{
- $hash = md5(strtolower(trim($this->attributes['email'])));
+ if (isset($this->attributes['email'])) {
+ $hash = md5(strtolower(trim($this->attributes['email'])));
+ }
+ else {
+ $hash = '';
+ }
return "https://www.gravatar.com/avatar/$hash?d=mp";
}
diff --git a/packages/core/config/pricing.php b/packages/core/config/pricing.php
index 6a2a99c09a..3283cc51d6 100644
--- a/packages/core/config/pricing.php
+++ b/packages/core/config/pricing.php
@@ -12,7 +12,7 @@
| Specify whether the prices entered into the system include tax or not.
|
*/
- 'stored_inclusive_of_tax' => false,
+ 'stored_inclusive_of_tax' => env('LUNAR_STORE_INCLUSIVE_OF_TAX', false),
/*
|--------------------------------------------------------------------------
diff --git a/packages/core/resources/lang/de/exceptions.php b/packages/core/resources/lang/de/exceptions.php
new file mode 100644
index 0000000000..5de03deb85
--- /dev/null
+++ b/packages/core/resources/lang/de/exceptions.php
@@ -0,0 +1,18 @@
+ 'Das Model ":class" implementiert nicht das "Bestellbar (purchasable)" Interface.',
+ 'cart_line_id_mismatch' => 'Die Position gehört nicht zu diesem Warenkorb.',
+ 'invalid_cart_line_quantity' => 'Die Bestellmenge muss mindestens "1" sein, angegeben wurden ":quantity".',
+ 'maximum_cart_line_quantity' => 'Die Bestellmenge darf nicht mehr als ":quantity" betragen.',
+ 'carts.shipping_missing' => 'Eine Lieferadresse ist erforderlich',
+ 'carts.billing_missing' => 'Eine Rechnungsadresse ist erforderlich',
+ 'carts.billing_incomplete' => 'Die Rechnungsadresse ist unvollständig',
+ 'carts.order_exists' => 'Für diesen Warenkorb existiert bereits eine Bestellung',
+ 'carts.shipping_option_missing' => 'Eine gültige Versandart fehlt',
+ 'missing_currency_price' => 'Es existiert kein Preis für die Währung ":currency"',
+ 'fieldtype_missing' => 'Der FeldType ":class" existiert nicht',
+ 'invalid_fieldtype' => 'Die Klasse ":class" implementiert nicht das Feldtyp-Interface.',
+ 'discounts.invalid_type' => 'Die Liste der Rabatte darf nur ":expected" enthalten, gefunden wurde ":actual"',
+ 'disallow_multiple_cart_orders' => 'Ein Warenkorb kann nur zu einer Bestellung gehören.',
+];
diff --git a/packages/core/src/Base/ShippingModifiers.php b/packages/core/src/Base/ShippingModifiers.php
index 3f1b06e3db..d89fda1cf0 100644
--- a/packages/core/src/Base/ShippingModifiers.php
+++ b/packages/core/src/Base/ShippingModifiers.php
@@ -48,6 +48,6 @@ public function add($modifier)
*/
public function remove($modifier)
{
- $this->modifiers->forget($modifier);
+ $this->modifiers = $this->modifiers->reject(fn ($value) => $value == $modifier);
}
}
diff --git a/packages/core/src/Observers/OrderLineObserver.php b/packages/core/src/Observers/OrderLineObserver.php
index 91d3fbe700..7486adc21b 100644
--- a/packages/core/src/Observers/OrderLineObserver.php
+++ b/packages/core/src/Observers/OrderLineObserver.php
@@ -15,7 +15,7 @@ class OrderLineObserver
*/
public function creating(OrderLine $orderLine)
{
- if ($orderLine->type != 'shipping' && ! $orderLine->purchasable instanceof Purchasable) {
+ if (! in_array(Purchasable::class, class_implements($orderLine->purchasable_type, true))) {
throw new NonPurchasableItemException($orderLine->purchasable_type);
}
}
@@ -27,7 +27,7 @@ public function creating(OrderLine $orderLine)
*/
public function updating(OrderLine $orderLine)
{
- if ($orderLine->type != 'shipping' && ! $orderLine->purchasable instanceof Purchasable) {
+ if (! in_array(Purchasable::class, class_implements($orderLine->purchasable_type, true))) {
throw new NonPurchasableItemException($orderLine->purchasable_type);
}
}
diff --git a/packages/core/tests/Stubs/DataTypes/TestPurchasable.php b/packages/core/tests/Stubs/DataTypes/TestPurchasable.php
new file mode 100644
index 0000000000..d572b65337
--- /dev/null
+++ b/packages/core/tests/Stubs/DataTypes/TestPurchasable.php
@@ -0,0 +1,139 @@
+price;
+ }
+
+ /**
+ * Get prices for the purchasable item.
+ */
+ public function getPrices(): Collection
+ {
+ return collect([
+ $this->price,
+ ]);
+ }
+
+ /**
+ * Return the purchasable unit quantity.
+ */
+ public function getUnitQuantity(): int
+ {
+ return 1;
+ }
+
+ /**
+ * Return the purchasable tax class.
+ */
+ public function getTaxClass(): TaxClass
+ {
+ return $this->taxClass;
+ }
+
+ /**
+ * Return the purchasable tax reference.
+ *
+ * @return string|null
+ */
+ public function getTaxReference()
+ {
+ return $this->taxReference;
+ }
+
+ /**
+ * Return what type of purchasable this is, i.e. physical,digital,shipping.
+ *
+ * @return string
+ */
+ public function getType()
+ {
+ return 'test-purchsable';
+ }
+
+ /**
+ * Return the name for the purchasable.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Return the description for the purchasable.
+ *
+ * @return string
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Return the option for this purchasable.
+ *
+ * @return string|null
+ */
+ public function getOption()
+ {
+ return $this->option;
+ }
+
+ /**
+ * Return a unique string which identifies the purchasable item.
+ *
+ * @return string
+ */
+ public function getIdentifier()
+ {
+ return $this->identifier;
+ }
+
+ /**
+ * Returns whether the purchasable item is shippable.
+ *
+ * @return bool
+ */
+ public function isShippable()
+ {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getThumbnail()
+ {
+ return null;
+ }
+}
diff --git a/packages/core/tests/Unit/Base/ShippingModifiersTest.php b/packages/core/tests/Unit/Base/ShippingModifiersTest.php
new file mode 100644
index 0000000000..4aa2cb1dd5
--- /dev/null
+++ b/packages/core/tests/Unit/Base/ShippingModifiersTest.php
@@ -0,0 +1,61 @@
+create([
+ 'decimal_places' => 2,
+ ]);
+
+ $this->cart = Cart::factory()->create([
+ 'currency_id' => $currency->id,
+ ]);
+
+ $this->class = new class extends ShippingModifier
+ {
+ public function handle(Cart $cart)
+ {
+ //
+ }
+ };
+
+ $this->shippingModifiers = new ShippingModifiers();
+ }
+
+ /** @test */
+ public function can_add_modifier()
+ {
+ $this->shippingModifiers->add($this->class::class);
+
+ $this->assertCount(1, $this->shippingModifiers->getModifiers());
+ }
+
+
+ public function can_remove_modifier()
+ {
+ $this->shippingModifiers->remove($this->class::class);
+
+ $this->assertCount(0, $this->shippingModifiers->getModifiers());
+ }
+
+}
diff --git a/packages/core/tests/Unit/Models/OrderLineTest.php b/packages/core/tests/Unit/Models/OrderLineTest.php
index 627452e129..1589cc492a 100644
--- a/packages/core/tests/Unit/Models/OrderLineTest.php
+++ b/packages/core/tests/Unit/Models/OrderLineTest.php
@@ -3,6 +3,8 @@
namespace Lunar\Tests\Unit\Models;
use Illuminate\Foundation\Testing\RefreshDatabase;
+use Lunar\DataTypes\Price;
+use Lunar\DataTypes\ShippingOption;
use Lunar\Exceptions\NonPurchasableItemException;
use Lunar\Models\CartLine;
use Lunar\Models\Channel;
@@ -10,6 +12,8 @@
use Lunar\Models\Order;
use Lunar\Models\OrderLine;
use Lunar\Models\ProductVariant;
+use Lunar\Models\TaxClass;
+use Lunar\Tests\Stubs\DataTypes\TestPurchasable;
use Lunar\Tests\TestCase;
/**
@@ -91,4 +95,72 @@ public function only_purchasables_can_be_added_to_an_order()
$this->assertDatabaseMissing((new CartLine())->getTable(), $data);
}
+
+ /** @test */
+ public function purchasable_non_eloquent_models_can_be_added_to_an_order()
+ {
+ $order = Order::factory()->create();
+
+ $currency = Currency::factory()->create([
+ 'default' => true,
+ ]);
+
+ $taxClass = TaxClass::factory()->create();
+
+ $shippingOption = new ShippingOption(
+ name: 'Basic Delivery',
+ description: 'Basic Delivery',
+ identifier: 'BASDEL',
+ price: new Price(500, $currency, 1),
+ taxClass: $taxClass
+ );
+
+ $data = [
+ 'order_id' => $order->id,
+ 'quantity' => 1,
+ 'type' => $shippingOption->getType(),
+ 'purchasable_type' => ShippingOption::class,
+ 'purchasable_id' => $shippingOption->getIdentifier(),
+ 'unit_price' => $shippingOption->getPrice()->value,
+ 'unit_quantity' => $shippingOption->getUnitQuantity(),
+ ];
+
+ $orderLine = OrderLine::factory()->create($data);
+
+ $this->assertDatabaseHas(
+ (new OrderLine())->getTable(),
+ $data
+ );
+
+ $this->assertEquals(5.0, $orderLine->unit_price->decimal);
+ $this->assertEquals(5.0, $orderLine->unit_price->unitDecimal);
+
+ $testPurchasable = new TestPurchasable(
+ name: 'Test Purchasable',
+ description: 'Test Purchasable',
+ identifier: 'TESTPUR',
+ price: new Price(650, $currency, 1),
+ taxClass: $taxClass
+ );
+
+ $data = [
+ 'order_id' => $order->id,
+ 'quantity' => 1,
+ 'type' => $testPurchasable->getType(),
+ 'purchasable_type' => TestPurchasable::class,
+ 'purchasable_id' => $testPurchasable->getIdentifier(),
+ 'unit_price' => $testPurchasable->getPrice()->value,
+ 'unit_quantity' => $testPurchasable->getUnitQuantity(),
+ ];
+
+ $orderLine = OrderLine::factory()->create($data);
+
+ $this->assertDatabaseHas(
+ (new OrderLine())->getTable(),
+ $data
+ );
+
+ $this->assertEquals(6.5, $orderLine->unit_price->decimal);
+ $this->assertEquals(6.5, $orderLine->unit_price->unitDecimal);
+ }
}