Skip to content
11 changes: 9 additions & 2 deletions packages/api/src/ApiServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
use Lunar\Base\CartSessionInterface;
use Lunar\Facades\ModelManifest;

class ApiServiceProvider extends ServiceProvider
Expand Down Expand Up @@ -202,6 +201,14 @@ protected function setPaymentOptionsConfig(): void

Config::set('lunar.cart.pipelines.cart', $cartPipelines);

Config::set(
'lunar.cart.pipelines.cart_lines',
array_merge(
Config::get('lunar.cart.pipelines.cart_lines'),
[Domain\CartLines\Pipelines\GetUnitPriceExTax::class]
)
);

Config::set(
'lunar.cart.validators.set_payment_option',
[Domain\Carts\Validation\PaymentOptionValidator::class],
Expand Down Expand Up @@ -449,7 +456,7 @@ protected function bindModels(): void
Domain\Carts\Contracts\CurrentSessionCart::class,
function (Application $app): ?\Lunar\Models\Contracts\Cart {
/** @var \Lunar\Managers\CartSessionManager $cartSession */
$cartSession = $this->app->make(CartSessionInterface::class);
$cartSession = $this->app->make(\Lunar\Base\CartSessionInterface::class);

return $cartSession->current();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
use Dystore\Api\Domain\JsonApi\Resources\JsonApiResource;
use Illuminate\Http\Request;

/**
* @param \Dystore\Api\Domain\CartLines\Models\CartLine $resource
*/
class CartLineResource extends JsonApiResource
{
/**
Expand Down
21 changes: 5 additions & 16 deletions packages/api/src/Domain/CartLines/JsonApi/V1/CartLineSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

namespace Dystore\Api\Domain\CartLines\JsonApi\V1;

use Dystore\Api\Domain\JsonApi\Eloquent\Fields\CartLinePricing;
use Dystore\Api\Domain\JsonApi\Eloquent\Schema;
use Dystore\Api\Support\Models\Actions\SchemaType;
use LaravelJsonApi\Eloquent\Fields\ArrayHash;
use LaravelJsonApi\Eloquent\Fields\Map;
use LaravelJsonApi\Eloquent\Fields\Number;
use LaravelJsonApi\Eloquent\Fields\Relations\BelongsTo;
use LaravelJsonApi\Eloquent\Fields\Relations\MorphTo;
Expand Down Expand Up @@ -55,24 +55,13 @@ public function fields(): array
$this->idField(),

Number::make('purchasable_id'),
Str::make('purchasable_type'),

Map::make('prices', [
Number::make('unit_price', 'unitPrice')
->serializeUsing(static fn ($value) => $value?->decimal()),
Number::make('quantity', 'quantity'),
Number::make('sub_total', 'subTotal')->serializeUsing(static fn ($value) => $value?->decimal()),
Number::make('sub_total_discounted', 'subTotalDiscounted')
->serializeUsing(static fn ($value) => $value?->decimal()),
Number::make('total', 'total')
->serializeUsing(static fn ($value) => $value?->decimal()),
Number::make('tax_amount', 'taxAmount')
->serializeUsing(static fn ($value) => $value?->decimal()),
Number::make('discount_total', 'discounTotal')
->serializeUsing(static fn ($value) => $value?->decimal()),
]),
Str::make('purchasable_type'),

Number::make('quantity'),

CartLinePricing::make('pricing'),

ArrayHash::make('meta'),

BelongsTo::make('cart')
Expand Down
15 changes: 15 additions & 0 deletions packages/api/src/Domain/CartLines/Models/CartLine.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,24 @@

use Dystore\Api\Domain\CartLines\Concerns\InteractsWithDystoreApi;
use Dystore\Api\Domain\CartLines\Contracts\CartLine as CartLineContract;
use Lunar\DataTypes\Price;
use Lunar\Models\CartLine as LunarCartLine;

/**
* @property ?Price $unitPriceExTax
* @property ?Price $unitTax
*/
class CartLine extends LunarCartLine implements CartLineContract
{
use InteractsWithDystoreApi;

/**
* Unit price excluding tax.
*/
public ?Price $unitPriceExTax = null;

/**
* Unit tax.
*/
public ?Price $unitTax = null;
}
57 changes: 57 additions & 0 deletions packages/api/src/Domain/CartLines/Pipelines/GetUnitPriceExTax.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Dystore\Api\Domain\CartLines\Pipelines;

use Closure;
use Lunar\DataTypes\Price;
use Lunar\Facades\Pricing;
use Lunar\Models\CartLine;
use Lunar\Models\Contracts\CartLine as CartLineContract;
use Spatie\LaravelBlink\BlinkFacade as Blink;

class GetUnitPriceExTax
{
/**
* Called just before cart totals are calculated.
*
* @param Closure(CartLineContract): mixed $next
* @return Closure
*/
public function handle(CartLineContract $cartLine, Closure $next)
{
/** @var CartLine $cart */
$purchasable = $cartLine->purchasable;
$cart = $cartLine->cart;

if ($customer = $cart->customer) {
$customerGroups = $customer->customerGroups;
} else {
$customerGroups = $cart->user?->customers->pluck('customerGroups')->flatten();
}

$currency = Blink::once('currency_'.$cart->currency_id, function () use ($cart) {
return $cart->currency;
});

$priceResponse = Pricing::currency($currency)
->qty($cartLine->quantity)
->currency($cart->currency)
->customerGroups($customerGroups)
->for($purchasable)
->get();

$cartLine->unitPriceExTax = new Price(
$priceResponse->matched->priceExTax()->value,
$cart->currency,
$purchasable->getUnitQuantity()
);

$cartLine->unitTax = new Price(
$cartLine->unitPriceInclTax->value - $cartLine->unitPriceExTax->value,
$cart->currency,
$purchasable->getUnitQuantity()
);

return $next($cartLine);
}
}
48 changes: 4 additions & 44 deletions packages/api/src/Domain/Carts/JsonApi/V1/CartSchema.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,16 @@

namespace Dystore\Api\Domain\Carts\JsonApi\V1;

use Dystore\Api\Domain\Discounts\Data\DiscountBreakdown;
use Dystore\Api\Domain\JsonApi\Eloquent\Fields\CartPricing;
use Dystore\Api\Domain\JsonApi\Eloquent\Schema;
use Dystore\Api\Support\Models\Actions\SchemaType;
use LaravelJsonApi\Eloquent\Fields\ArrayHash;
use LaravelJsonApi\Eloquent\Fields\Boolean;
use LaravelJsonApi\Eloquent\Fields\Map;
use LaravelJsonApi\Eloquent\Fields\Number;
use LaravelJsonApi\Eloquent\Fields\Relations\BelongsTo;
use LaravelJsonApi\Eloquent\Fields\Relations\HasMany;
use LaravelJsonApi\Eloquent\Fields\Relations\HasOne;
use LaravelJsonApi\Eloquent\Fields\Str;
use LaravelJsonApi\Eloquent\Resources\Relation;
use Lunar\Base\ValueObjects\Cart\DiscountBreakdown as LunarDiscountBreakdown;
use Lunar\Models\Contracts\Cart;
use Lunar\Models\Contracts\CartAddress;
use Lunar\Models\Contracts\CartLine;
Expand Down Expand Up @@ -93,54 +90,17 @@ public function fields(): array
return [
$this->idField(),

Map::make('prices', [
Number::make('sub_total', 'subTotal')
->serializeUsing(static fn ($value) => $value?->decimal),

Number::make('sub_total_discounted', 'subTotalDiscounted')
->serializeUsing(static fn ($value) => $value?->decimal),

Number::make('total', 'total')
->serializeUsing(static fn ($value) => $value?->decimal),

Number::make('shipping_sub_total', 'shippingSubTotal')
->serializeUsing(static fn ($value) => $value?->decimal),

Number::make('shipping_total', 'shippingTotal')
->serializeUsing(static fn ($value) => $value?->decimal),

Number::make('payment_sub_total', 'paymentSubTotal')
->serializeUsing(static fn ($value) => $value?->decimal),

Number::make('payment_total', 'paymentTotal')
->serializeUsing(static fn ($value) => $value?->decimal),

Number::make('tax_total', 'taxTotal')
->serializeUsing(static fn ($value) => $value?->decimal),

Number::make('discount_total', 'discountTotal')
->serializeUsing(static fn ($value) => $value?->decimal),

ArrayHash::make('tax_breakdown', 'taxBreakdown')
->serializeUsing(static fn ($value) => $value?->amounts),

ArrayHash::make('discount_breakdown', 'discountBreakdown')
->serializeUsing(static fn ($value) => $value?->map(
fn (LunarDiscountBreakdown $discountBreakdown) => (new DiscountBreakdown($discountBreakdown))->toArray(),
)),
]),
CartPricing::make('pricing'),

Str::make('coupon_code'),

Str::make('payment_option'),

// NOTE: Attributes used for setting shipping options to current session cart
Str::make('shipping_option')->hidden(),
Str::make('shipping_option')->hidden(), // NOTE: Attributes used for setting shipping options to current session cart

Str::make('address_type')->hidden(),

// NOTE: Attributes used for determining if user should be created during checkout
Boolean::make('create_user')->hidden(),
Boolean::make('create_user')->hidden(), // NOTE: Attributes used for determining if user should be created during checkout

Boolean::make('agree')->hidden(),

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace Dystore\Api\Domain\JsonApi\Eloquent\Fields;

use Closure;
use LaravelJsonApi\Contracts\Resources\Serializer\Attribute as SerializableContract;
use LaravelJsonApi\Eloquent\Fields\Boolean;
use LaravelJsonApi\Eloquent\Fields\Map;
use LaravelJsonApi\Eloquent\Fields\Number;

class CartLinePricing extends Map implements SerializableContract
{
private ?Closure $extractor = null;

private ?string $related = null;

public function __construct(private string $fieldName, private array $map = [])
{
parent::__construct($fieldName, $map);
}

public static function make(string $fieldName, array $map = []): self
{
return new self($fieldName, $map);
}

/**
* {@inheritDoc}
*/
public function serialize(object $model): ?array
{
/** @var \Lunar\Models\Contracts\CartLine $owner */
$owner = $this->related ? $model->{$this->related} : $model;
$values = [];

$fields = [
Boolean::make('incl_tax')->extractUsing(fn () => prices_inc_tax()),
Price::make(fieldName: 'unit_price', price: $owner->unitPrice),
Price::make(fieldName: 'unit_price_incl_tax', price: $owner->unitPriceInclTax),
Price::make(fieldName: 'sub_total', price: $owner->subTotal),
Price::make(fieldName: 'sub_total_discounted', price: $owner->subTotalDiscounted),
Price::make(fieldName: 'total', price: $owner->total),
Price::make(fieldName: 'tax_total', price: $owner->taxAmount),
Price::make(fieldName: 'discount_total', price: $owner->discountTotal),
Number::make('quantity'),
Number::make('unit_quantity'),
];

if ($owner) {
foreach ($fields as $attr) {
if ($attr instanceof SerializableContract) {
$values[$attr->serializedFieldName()] = $attr->serialize($owner);
}
}
}

ksort($values);

return $values ?: null;
}
}
77 changes: 77 additions & 0 deletions packages/api/src/Domain/JsonApi/Eloquent/Fields/CartPricing.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace Dystore\Api\Domain\JsonApi\Eloquent\Fields;

use Closure;
use Dystore\Api\Domain\Discounts\Data\DiscountBreakdown;
use LaravelJsonApi\Contracts\Resources\Serializer\Attribute as SerializableContract;
use LaravelJsonApi\Eloquent\Fields\ArrayHash;
use LaravelJsonApi\Eloquent\Fields\Boolean;
use LaravelJsonApi\Eloquent\Fields\Map;
use Lunar\Base\ValueObjects\Cart\DiscountBreakdown as LunarDiscountBreakdown;

class CartPricing extends Map implements SerializableContract
{
private ?Closure $extractor = null;

private ?string $related = null;

public function __construct(private string $fieldName, private array $map = [])
{
parent::__construct($fieldName, $map);
}

public static function make(string $fieldName, array $map = []): self
{
return new self($fieldName, $map);
}

/**
* {@inheritDoc}
*/
public function serialize(object $model): ?array
{
/** @var \Lunar\Models\Contracts\Cart $owner */
$owner = $this->related ? $model->{$this->related} : $model;
$values = [];

$fields = [
Boolean::make('incl_tax')->extractUsing(fn () => prices_inc_tax()),
Price::make(fieldName: 'shipping_sub_total', price: $owner->shippingSubTotal),
Price::make(fieldName: 'shipping_tax_total', price: $owner->shippingTaxTotal),
Price::make(fieldName: 'shipping_total', price: $owner->shippingTotal),
Price::make(fieldName: 'payment_sub_total', price: $owner->paymentSubTotal),
Price::make(fieldName: 'payment_tax_total', price: $owner->paymentTaxTotal),
Price::make(fieldName: 'payment_total', price: $owner->paymentTotal),
Price::make(fieldName: 'sub_total', price: $owner->subTotal),
Price::make(fieldName: 'sub_total_discounted', price: $owner->subTotalDiscounted),
Price::make(fieldName: 'total', price: $owner->total),
Price::make(fieldName: 'tax_total', price: $owner->taxTotal),
Price::make(fieldName: 'discount_total', price: $owner->discountTotal),

ArrayHash::make('tax_breakdown', 'taxBreakdown')
->serializeUsing(
static fn ($value) => $value?->amounts
),

ArrayHash::make('discount_breakdown', 'discountBreakdown')
->serializeUsing(
static fn ($value) => $value?->map(
fn (LunarDiscountBreakdown $discountBreakdown) => (new DiscountBreakdown($discountBreakdown))->toArray(),
)
),
];

if ($owner) {
foreach ($fields as $attr) {
if ($attr instanceof SerializableContract) {
$values[$attr->serializedFieldName()] = $attr->serialize($owner);
}
}
}

ksort($values);

return $values ?: null;
}
}
Loading