Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Calculator with Pipelines #873

Merged
merged 16 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 1 addition & 9 deletions src/Contracts/Calculator.php
Expand Up @@ -4,13 +4,5 @@

interface Calculator
{
public function calculate(Order $order): array;

public function calculateLineItem(array $data, array $lineItem): array;

public function calculateLineItemTax(array $data, array $lineItem): array;

public function calculateOrderShipping(array $data): array;

public function calculateOrderCoupons(array $data): array;
public static function calculate(Order $order): Order;
}
3 changes: 2 additions & 1 deletion src/Contracts/TaxEngine.php
Expand Up @@ -2,11 +2,12 @@

namespace DoubleThreeDigital\SimpleCommerce\Contracts;

use DoubleThreeDigital\SimpleCommerce\Orders\LineItem;
use DoubleThreeDigital\SimpleCommerce\Tax\TaxCalculation;

interface TaxEngine
{
public function name(): string;

public function calculate(Order $order, array $lineItem): TaxCalculation;
public function calculate(Order $order, LineItem $lineItem): TaxCalculation;
}
191 changes: 0 additions & 191 deletions src/Orders/Calculator.php

This file was deleted.

22 changes: 22 additions & 0 deletions src/Orders/Calculator/CalculateGrandTotal.php
@@ -0,0 +1,22 @@
<?php

namespace DoubleThreeDigital\SimpleCommerce\Orders\Calculator;

use Closure;
use DoubleThreeDigital\SimpleCommerce\Contracts\Order;

class CalculateGrandTotal
{
public function handle(Order $order, Closure $next)
{
$order->grandTotal(
(($order->itemsTotal() + $order->taxTotal()) - $order->couponTotal()) + $order->shippingTotal()
);

$order->grandTotal(
(int) $order->grandTotal()
);

return $next($order);
}
}
19 changes: 19 additions & 0 deletions src/Orders/Calculator/CalculateItemsTotal.php
@@ -0,0 +1,19 @@
<?php

namespace DoubleThreeDigital\SimpleCommerce\Orders\Calculator;

use Closure;
use DoubleThreeDigital\SimpleCommerce\Contracts\Order;
use DoubleThreeDigital\SimpleCommerce\Orders\LineItem;

class CalculateItemsTotal
{
public function handle(Order $order, Closure $next)
{
$order->itemsTotal(
$order->lineItems()->map(fn (LineItem $lineItem) => $lineItem->total())->sum()
);

return $next($order);
}
}
24 changes: 24 additions & 0 deletions src/Orders/Calculator/Calculator.php
@@ -0,0 +1,24 @@
<?php

namespace DoubleThreeDigital\SimpleCommerce\Orders\Calculator;

use DoubleThreeDigital\SimpleCommerce\Contracts\Calculator as Contract;
use DoubleThreeDigital\SimpleCommerce\Contracts\Order;
use Illuminate\Support\Facades\Pipeline;

class Calculator implements Contract
{
public static function calculate(Order $order): Order
{
return Pipeline::send($order)
->through([
LineItemCalculator::class,
LineItemTaxCalculator::class,
CalculateItemsTotal::class,
CouponCalculator::class,
ShippingCalculator::class,
CalculateGrandTotal::class,
])
->thenReturn();
}
}
43 changes: 43 additions & 0 deletions src/Orders/Calculator/CouponCalculator.php
@@ -0,0 +1,43 @@
<?php

namespace DoubleThreeDigital\SimpleCommerce\Orders\Calculator;

use Closure;
use DoubleThreeDigital\SimpleCommerce\Contracts\Order;
use DoubleThreeDigital\SimpleCommerce\Coupons\CouponType;

class CouponCalculator
{
public function handle(Order $order, Closure $next)
{
if ($coupon = $order->coupon()) {
$value = (int) $coupon->value();

// Double check coupon is still valid
if (! $coupon->isValid($order)) {
return $next($order);
}

$baseAmount = $order->itemsTotal() + $order->taxTotal();

// Otherwise do all the other stuff...
if ($coupon->type() === CouponType::Percentage) {
$order->couponTotal(
(int) ($value * $baseAmount) / 100
);
}

if ($coupon->type() === CouponType::Fixed) {
$order->couponTotal(
(int) $baseAmount - ($baseAmount - $value)
);
}

$order->couponTotal(
(int) round($order->couponTotal())
);
}

return $next($order);
}
}
64 changes: 64 additions & 0 deletions src/Orders/Calculator/LineItemCalculator.php
@@ -0,0 +1,64 @@
<?php

namespace DoubleThreeDigital\SimpleCommerce\Orders\Calculator;

use Closure;
use DoubleThreeDigital\SimpleCommerce\Contracts\Order;
use DoubleThreeDigital\SimpleCommerce\Orders\LineItem;
use DoubleThreeDigital\SimpleCommerce\Products\ProductType;
use DoubleThreeDigital\SimpleCommerce\SimpleCommerce;

class LineItemCalculator
{
public function handle(Order $order, Closure $next)
{
$order->lineItems()
->transform(function (LineItem $lineItem) use ($order) {
$product = $lineItem->product();

if ($product->purchasableType() === ProductType::Variant) {
$variant = $product->variant(
isset($lineItem->variant()['variant']) ? $lineItem->variant()['variant'] : $lineItem->variant()
);

if (SimpleCommerce::$productVariantPriceHook) {
$productPrice = (SimpleCommerce::$productVariantPriceHook)($order, $product, $variant);
} else {
$productPrice = $variant->price();
}

// If $productPrice contains a decimal, we need to strip it & ensure we have two decimal places.
if (str_contains($productPrice, '.')) {
$productPrice = number_format($productPrice, 2, '.', '');
$productPrice = (int) str_replace('.', '', (string) $productPrice);
}

$lineItem->total(
$productPrice * $lineItem->quantity()
);
}

if ($product->purchasableType() === ProductType::Product) {
if (SimpleCommerce::$productPriceHook) {
$productPrice = (SimpleCommerce::$productPriceHook)($order, $product);
} else {
$productPrice = $product->price();
}

// If $productPrice contains a decimal, we need to strip it & ensure we have two decimal places.
if (str_contains($productPrice, '.')) {
$productPrice = number_format($productPrice, 2, '.', '');
$productPrice = (int) str_replace('.', '', (string) $productPrice);
}

$lineItem->total(
$productPrice * $lineItem->quantity()
);
}

return $lineItem;
});

return $next($order);
}
}