¨4.0.1¨
This commit is contained in:
@@ -8,9 +8,14 @@ use Modules\Tax\Entities\TaxRate;
|
||||
use Illuminate\Support\Collection;
|
||||
use Modules\Coupon\Entities\Coupon;
|
||||
use Modules\Product\Entities\Product;
|
||||
use Modules\Product\Entities\ProductVariant;
|
||||
use Modules\Shipping\Facades\ShippingMethod;
|
||||
use Darryldecode\Cart\Cart as DarryldecodeCart;
|
||||
use Modules\Variation\Entities\VariationValue;
|
||||
use Modules\Product\Services\ChosenProductOptions;
|
||||
use Modules\Product\Services\ChosenProductVariations;
|
||||
use Darryldecode\Cart\Exceptions\InvalidItemException;
|
||||
use Darryldecode\Cart\Exceptions\InvalidConditionException;
|
||||
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
||||
|
||||
class Cart extends DarryldecodeCart implements JsonSerializable
|
||||
@@ -20,51 +25,78 @@ class Cart extends DarryldecodeCart implements JsonSerializable
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function instance()
|
||||
public function instance(): static
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear the cart.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clear()
|
||||
public function clear(): void
|
||||
{
|
||||
parent::clear();
|
||||
|
||||
$this->clearCartConditions();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Store a new item to the cart.
|
||||
*
|
||||
* @param int $productId
|
||||
* @param $variantId
|
||||
* @param int $qty
|
||||
* @param array $options
|
||||
*
|
||||
* @return void
|
||||
* @throws InvalidItemException
|
||||
*/
|
||||
public function store($productId, $qty, $options = [])
|
||||
public function store($productId, $variantId, $qty, $options = []): void
|
||||
{
|
||||
$options = array_filter($options);
|
||||
$variations = [];
|
||||
|
||||
$product = Product::with('files', 'categories', 'taxClass')->findOrFail($productId);
|
||||
$variant = ProductVariant::find($variantId);
|
||||
$item = $variant ?? $product;
|
||||
|
||||
if ($variant) {
|
||||
$uids = collect(explode('.', $variant->uids));
|
||||
$rawVariations = $uids->map(function ($uid) {
|
||||
return VariationValue::where('uid', $uid)->get()->pluck('id', 'variation.id');
|
||||
});
|
||||
|
||||
foreach ($rawVariations as $variation) {
|
||||
foreach ($variation as $variationId => $variationValueId) {
|
||||
$variations[$variationId] = $variationValueId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$chosenVariations = new ChosenProductVariations($product, $variations);
|
||||
$chosenOptions = new ChosenProductOptions($product, $options);
|
||||
|
||||
$this->add([
|
||||
'id' => md5("product_id.{$product->id}:options." . serialize($options)),
|
||||
'id' => md5("product_id.{$product->id}:options." . serialize($options) . "product_id.{$product->id}:variations." . serialize($variations)),
|
||||
'name' => $product->name,
|
||||
'price' => $product->selling_price->amount(),
|
||||
'price' => $item->selling_price->amount(),
|
||||
'quantity' => $qty,
|
||||
'attributes' => [
|
||||
'product' => $product,
|
||||
'variant' => $variant,
|
||||
'item' => $item,
|
||||
'variations' => $chosenVariations->getEntities(),
|
||||
'options' => $chosenOptions->getEntities(),
|
||||
'created_at' => time(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function updateQuantity($id, $qty)
|
||||
{
|
||||
$this->update($id, [
|
||||
@@ -75,6 +107,29 @@ class Cart extends DarryldecodeCart implements JsonSerializable
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Total quantity of the given cartItem
|
||||
* in the Cart.
|
||||
*
|
||||
* @param CartItem $cartItem
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function addedQty(CartItem $cartItem): int
|
||||
{
|
||||
$items = $this->items()->filter(function ($cartItemAlias) use ($cartItem) {
|
||||
if ($cartItem->variant && $cartItemAlias->variant) {
|
||||
return $cartItemAlias->variant->id === $cartItem->variant->id;
|
||||
}
|
||||
|
||||
return $cartItemAlias->product->id === $cartItem->product->id;
|
||||
});
|
||||
|
||||
return $items->sum('qty');
|
||||
}
|
||||
|
||||
|
||||
public function items()
|
||||
{
|
||||
return $this->getContent()
|
||||
@@ -84,17 +139,6 @@ class Cart extends DarryldecodeCart implements JsonSerializable
|
||||
});
|
||||
}
|
||||
|
||||
public function addedQty($productId)
|
||||
{
|
||||
return $this->findByProductId($productId)->sum('qty');
|
||||
}
|
||||
|
||||
public function findByProductId($productId)
|
||||
{
|
||||
return $this->items()->filter(function ($cartItem) use ($productId) {
|
||||
return $cartItem->product->id == $productId;
|
||||
});
|
||||
}
|
||||
|
||||
public function crossSellProducts()
|
||||
{
|
||||
@@ -108,94 +152,36 @@ class Cart extends DarryldecodeCart implements JsonSerializable
|
||||
->flatten();
|
||||
}
|
||||
|
||||
public function getAllProducts()
|
||||
|
||||
public function getAllProducts(): EloquentCollection
|
||||
{
|
||||
return $this->items()
|
||||
->map(function ($cartItem) {
|
||||
return $cartItem->product;
|
||||
})
|
||||
->flatten()
|
||||
->pipe(function ($products) {
|
||||
return new EloquentCollection($products);
|
||||
});
|
||||
return $this->items()->map(function ($cartItem) {
|
||||
return $cartItem->product;
|
||||
})->flatten()->pipe(function ($products) {
|
||||
return new EloquentCollection($products);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function reduceStock()
|
||||
{
|
||||
$this->manageStock(function ($cartItem) {
|
||||
$cartItem->product->decrement('qty', $cartItem->qty);
|
||||
|
||||
if ($cartItem->refreshStock()->product->qty === 0) {
|
||||
$cartItem->product->markAsOutOfStock();
|
||||
}
|
||||
$cartItem->item->decrement('qty', $cartItem->qty);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function restoreStock()
|
||||
{
|
||||
$this->manageStock(function ($cartItem) {
|
||||
$cartItem->product->increment('qty', $cartItem->qty);
|
||||
|
||||
if ($cartItem->product->qty > 0) {
|
||||
$cartItem->product->markAsInStock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function manageStock($callback)
|
||||
{
|
||||
$this->items()
|
||||
->filter(function ($cartItem) {
|
||||
return $cartItem->product->manage_stock;
|
||||
})
|
||||
->each($callback);
|
||||
}
|
||||
|
||||
public function quantity()
|
||||
{
|
||||
return $this->getTotalQuantity();
|
||||
}
|
||||
|
||||
public function hasAvailableShippingMethod()
|
||||
{
|
||||
return $this->availableShippingMethods()->isNotEmpty();
|
||||
}
|
||||
|
||||
public function availableShippingMethods()
|
||||
{
|
||||
if ($this->allItemsAreVirtual()) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
return ShippingMethod::available();
|
||||
}
|
||||
|
||||
public function allItemsAreVirtual()
|
||||
{
|
||||
return $this->items()->every(function (CartItem $cartItem) {
|
||||
return $cartItem->product->is_virtual;
|
||||
});
|
||||
}
|
||||
|
||||
public function hasShippingMethod()
|
||||
{
|
||||
return $this->getConditionsByType('shipping_method')->isNotEmpty();
|
||||
}
|
||||
|
||||
public function shippingMethod()
|
||||
{
|
||||
if (!$this->hasShippingMethod()) {
|
||||
return new NullCartShippingMethod();
|
||||
}
|
||||
|
||||
return new CartShippingMethod($this, $this->getConditionsByType('shipping_method')->first());
|
||||
}
|
||||
|
||||
public function shippingCost()
|
||||
{
|
||||
return $this->shippingMethod()->cost();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidConditionException
|
||||
*/
|
||||
public function addShippingMethod($shippingMethod)
|
||||
{
|
||||
$this->removeShippingMethod();
|
||||
@@ -205,7 +191,7 @@ class Cart extends DarryldecodeCart implements JsonSerializable
|
||||
'name' => $shippingMethod->label,
|
||||
'type' => 'shipping_method',
|
||||
'target' => 'total',
|
||||
'value' => "+{$shippingMethod->cost->amount()}",
|
||||
'value' => $this->coupon()?->free_shipping ? 0 : $shippingMethod->cost->amount(),
|
||||
'order' => 1,
|
||||
'attributes' => [
|
||||
'shipping_method' => $shippingMethod,
|
||||
@@ -218,18 +204,26 @@ class Cart extends DarryldecodeCart implements JsonSerializable
|
||||
return $this->shippingMethod();
|
||||
}
|
||||
|
||||
|
||||
public function removeShippingMethod()
|
||||
{
|
||||
$this->removeConditionsByType('shipping_method');
|
||||
}
|
||||
|
||||
private function refreshFreeShippingCoupon()
|
||||
|
||||
public function coupon()
|
||||
{
|
||||
if ($this->coupon()->isFreeShipping()) {
|
||||
$this->applyCoupon($this->coupon()->entity());
|
||||
if (!$this->hasCoupon()) {
|
||||
return new NullCartCoupon();
|
||||
}
|
||||
|
||||
$couponCondition = $this->getConditionsByType('coupon')->first();
|
||||
$coupon = Coupon::with('products', 'categories')->find($couponCondition->getAttribute('coupon_id'));
|
||||
|
||||
return new CartCoupon($this, $coupon, $couponCondition);
|
||||
}
|
||||
|
||||
|
||||
public function hasCoupon()
|
||||
{
|
||||
if ($this->getConditionsByType('coupon')->isEmpty()) {
|
||||
@@ -243,69 +237,165 @@ class Cart extends DarryldecodeCart implements JsonSerializable
|
||||
return Coupon::where('id', $couponId)->exists();
|
||||
}
|
||||
|
||||
public function couponAlreadyApplied(Coupon $coupon)
|
||||
{
|
||||
return $this->coupon()->code() === $coupon->code;
|
||||
}
|
||||
|
||||
public function coupon()
|
||||
{
|
||||
if (!$this->hasCoupon()) {
|
||||
return new NullCartCoupon();
|
||||
}
|
||||
|
||||
$couponCondition = $this->getConditionsByType('coupon')->first();
|
||||
$coupon = Coupon::with('products', 'categories')->find($couponCondition->getAttribute('coupon_id'));
|
||||
|
||||
return new CartCoupon($this, $coupon, $couponCondition);
|
||||
}
|
||||
|
||||
public function discount()
|
||||
{
|
||||
return $this->coupon()->value();
|
||||
}
|
||||
|
||||
public function applyCoupon(Coupon $coupon)
|
||||
{
|
||||
$this->removeCoupon();
|
||||
|
||||
$this->condition(
|
||||
new CartCondition([
|
||||
'name' => $coupon->code,
|
||||
'type' => 'coupon',
|
||||
'target' => 'total',
|
||||
'value' => $this->getCouponValue($coupon),
|
||||
'order' => 2,
|
||||
'attributes' => [
|
||||
'coupon_id' => $coupon->id,
|
||||
],
|
||||
]),
|
||||
);
|
||||
try {
|
||||
$this->condition(
|
||||
new CartCondition([
|
||||
'name' => $coupon->code,
|
||||
'type' => 'coupon',
|
||||
'target' => 'total',
|
||||
'value' => $this->getCouponValue($coupon),
|
||||
'order' => 2,
|
||||
'attributes' => [
|
||||
'coupon_id' => $coupon->id,
|
||||
],
|
||||
]),
|
||||
);
|
||||
} catch (InvalidConditionException $e) {
|
||||
if (app()->hasDebugModeEnabled()) {
|
||||
$message = $e->getMessage();
|
||||
} else {
|
||||
$message = trans('core::something_went_wrong');
|
||||
}
|
||||
|
||||
if (request()->ajax()) {
|
||||
return response()->json(
|
||||
[
|
||||
'message' => $message,
|
||||
],
|
||||
400
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getCouponValue($coupon)
|
||||
{
|
||||
if ($coupon->free_shipping) {
|
||||
return "-{$this->shippingMethod()->cost()->amount()}";
|
||||
}
|
||||
|
||||
if ($coupon->is_percent) {
|
||||
return "-{$coupon->value}%";
|
||||
}
|
||||
|
||||
return "-{$coupon->value->amount()}";
|
||||
}
|
||||
|
||||
public function removeCoupon()
|
||||
{
|
||||
$this->removeConditionsByType('coupon');
|
||||
}
|
||||
|
||||
public function hasTax()
|
||||
|
||||
public function shippingMethod()
|
||||
{
|
||||
return $this->getConditionsByType('tax')->isNotEmpty();
|
||||
if (!$this->hasShippingMethod()) {
|
||||
return new NullCartShippingMethod();
|
||||
}
|
||||
|
||||
return new CartShippingMethod($this, $this->getConditionsByType('shipping_method')->first());
|
||||
}
|
||||
|
||||
|
||||
public function hasShippingMethod()
|
||||
{
|
||||
return $this->getConditionsByType('shipping_method')->isNotEmpty();
|
||||
}
|
||||
|
||||
|
||||
public function couponAlreadyApplied(Coupon $coupon)
|
||||
{
|
||||
return $this->coupon()->code() === $coupon->code;
|
||||
}
|
||||
|
||||
|
||||
public function discount()
|
||||
{
|
||||
return $this->coupon()->value();
|
||||
}
|
||||
|
||||
|
||||
public function addTaxes($request)
|
||||
{
|
||||
$this->removeTaxes();
|
||||
|
||||
$this->findTaxes($request)->each(/**
|
||||
* @throws InvalidConditionException
|
||||
*/
|
||||
function ($taxRate) {
|
||||
$this->condition(
|
||||
new CartCondition([
|
||||
'name' => $taxRate->id,
|
||||
'type' => 'tax',
|
||||
'target' => 'total',
|
||||
'value' => "+{$taxRate->rate}%",
|
||||
'order' => 3,
|
||||
'attributes' => [
|
||||
'tax_rate_id' => $taxRate->id,
|
||||
],
|
||||
]),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function removeTaxes()
|
||||
{
|
||||
$this->removeConditionsByType('tax');
|
||||
}
|
||||
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return json_encode($this->jsonSerialize());
|
||||
}
|
||||
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'items' => $this->items(),
|
||||
'quantity' => $this->getTotalQuantity(),
|
||||
'availableShippingMethods' => $this->availableShippingMethods(),
|
||||
'subTotal' => $this->subTotal(),
|
||||
'shippingMethodName' => $this->shippingMethod()->name(),
|
||||
'shippingCost' => $this->shippingCost(),
|
||||
'coupon' => $this->coupon(),
|
||||
'taxes' => $this->taxes(),
|
||||
'total' => $this->total(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function availableShippingMethods(): Collection
|
||||
{
|
||||
if ($this->allItemsAreVirtual()) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
return ShippingMethod::available();
|
||||
}
|
||||
|
||||
|
||||
public function allItemsAreVirtual()
|
||||
{
|
||||
return $this->items()->every(function (CartItem $cartItem) {
|
||||
return $cartItem->product->is_virtual;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public function subTotal()
|
||||
{
|
||||
return Money::inDefaultCurrency($this->getSubTotal())->add($this->optionsPrice());
|
||||
}
|
||||
|
||||
|
||||
public function shippingCost()
|
||||
{
|
||||
return $this->shippingMethod()->cost();
|
||||
}
|
||||
|
||||
|
||||
public function taxes()
|
||||
{
|
||||
if (!$this->hasTax()) {
|
||||
@@ -322,50 +412,56 @@ class Cart extends DarryldecodeCart implements JsonSerializable
|
||||
});
|
||||
}
|
||||
|
||||
private function getTaxRateIds($taxConditions)
|
||||
|
||||
public function hasTax()
|
||||
{
|
||||
return $taxConditions->map(function ($taxCondition) {
|
||||
return $taxCondition->getAttribute('tax_rate_id');
|
||||
});
|
||||
return $this->getConditionsByType('tax')->isNotEmpty();
|
||||
}
|
||||
|
||||
|
||||
public function total()
|
||||
{
|
||||
return $this->subTotal()
|
||||
->add($this->shippingMethod()->cost())
|
||||
->subtract($this->coupon()->value())
|
||||
->add($this->tax());
|
||||
}
|
||||
|
||||
|
||||
public function tax()
|
||||
{
|
||||
return Money::inDefaultCurrency($this->calculateTax());
|
||||
}
|
||||
|
||||
private function calculateTax()
|
||||
|
||||
private function manageStock($callback)
|
||||
{
|
||||
return $this->taxes()->sum(function ($cartTax) {
|
||||
return $cartTax->amount()->amount();
|
||||
});
|
||||
$this->items()
|
||||
->filter(function ($cartItem) {
|
||||
return $cartItem->item->manage_stock;
|
||||
})
|
||||
->each($callback);
|
||||
}
|
||||
|
||||
public function addTaxes($request)
|
||||
{
|
||||
$this->removeTaxes();
|
||||
|
||||
$this->findTaxes($request)->each(function ($taxRate) {
|
||||
$this->condition(
|
||||
new CartCondition([
|
||||
'name' => $taxRate->id,
|
||||
'type' => 'tax',
|
||||
'target' => 'total',
|
||||
'value' => "+{$taxRate->rate}%",
|
||||
'order' => 3,
|
||||
'attributes' => [
|
||||
'tax_rate_id' => $taxRate->id,
|
||||
],
|
||||
]),
|
||||
);
|
||||
});
|
||||
private function refreshFreeShippingCoupon()
|
||||
{
|
||||
if ($this->coupon()->isFreeShipping()) {
|
||||
$this->applyCoupon($this->coupon()->entity());
|
||||
}
|
||||
}
|
||||
|
||||
public function removeTaxes()
|
||||
|
||||
private function getCouponValue($coupon)
|
||||
{
|
||||
$this->removeConditionsByType('tax');
|
||||
if ($coupon->is_percent) {
|
||||
return "-{$coupon->value}%";
|
||||
}
|
||||
|
||||
return "-{$coupon->value->amount()}";
|
||||
}
|
||||
|
||||
|
||||
private function findTaxes($request)
|
||||
{
|
||||
return $this->items()
|
||||
@@ -377,16 +473,13 @@ class Cart extends DarryldecodeCart implements JsonSerializable
|
||||
->filter();
|
||||
}
|
||||
|
||||
public function subTotal()
|
||||
{
|
||||
return Money::inDefaultCurrency($this->getSubTotal())->add($this->optionsPrice());
|
||||
}
|
||||
|
||||
private function optionsPrice()
|
||||
{
|
||||
return Money::inDefaultCurrency($this->calculateOptionsPrice());
|
||||
}
|
||||
|
||||
|
||||
private function calculateOptionsPrice()
|
||||
{
|
||||
return $this->items()->sum(function ($cartItem) {
|
||||
@@ -397,36 +490,19 @@ class Cart extends DarryldecodeCart implements JsonSerializable
|
||||
});
|
||||
}
|
||||
|
||||
public function total()
|
||||
|
||||
private function getTaxRateIds($taxConditions)
|
||||
{
|
||||
return $this->subTotal()
|
||||
->add($this->shippingMethod()->cost())
|
||||
->subtract($this->coupon()->value())
|
||||
->add($this->tax());
|
||||
return $taxConditions->map(function ($taxCondition) {
|
||||
return $taxCondition->getAttribute('tax_rate_id');
|
||||
});
|
||||
}
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
return [
|
||||
'items' => $this->items(),
|
||||
'quantity' => $this->quantity(),
|
||||
'availableShippingMethods' => $this->availableShippingMethods(),
|
||||
'subTotal' => $this->subTotal(),
|
||||
'shippingMethodName' => $this->shippingMethod()->name(),
|
||||
'shippingCost' => $this->shippingCost(),
|
||||
'coupon' => $this->coupon(),
|
||||
'taxes' => $this->taxes(),
|
||||
'total' => $this->total(),
|
||||
];
|
||||
}
|
||||
|
||||
public function jsonSerialize()
|
||||
private function calculateTax()
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return json_encode($this->jsonSerialize());
|
||||
return $this->taxes()->sum(function ($cartTax) {
|
||||
return $cartTax->amount()->amount();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user