From a44fa7445d0d5d78e00a29453074f6ffa4240fdf Mon Sep 17 00:00:00 2001 From: Steffen Mahler Date: Sat, 4 Jul 2020 15:20:59 +0200 Subject: [PATCH] refactoring of validateCartItem methods of product types --- .../BookingProduct/src/Helpers/Booking.php | 56 +++-- .../src/Helpers/EventTicket.php | 21 +- .../BookingProduct/src/Helpers/RentalSlot.php | 29 ++- .../BookingProduct/src/Type/Booking.php | 31 ++- packages/Webkul/Checkout/src/Cart.php | 59 +++--- .../Datatypes/CartItemValidationResult.php | 39 ++++ .../Webkul/Product/src/Type/AbstractType.php | 197 ++++++++++++------ packages/Webkul/Product/src/Type/Bundle.php | 28 ++- .../Webkul/Product/src/Type/Configurable.php | 25 ++- .../Webkul/Product/src/Type/Downloadable.php | 21 +- .../Webkul/Shop/src/Resources/lang/ar/app.php | 4 +- .../Webkul/Shop/src/Resources/lang/es/app.php | 4 +- .../Webkul/Shop/src/Resources/lang/fa/app.php | 4 +- .../Webkul/Shop/src/Resources/lang/ja/app.php | 4 +- .../Shop/src/Resources/lang/pt_BR/app.php | 4 +- tests/unit/Checkout/Cart/CartCest.php | 5 + 16 files changed, 373 insertions(+), 158 deletions(-) create mode 100644 packages/Webkul/Product/src/Datatypes/CartItemValidationResult.php diff --git a/packages/Webkul/BookingProduct/src/Helpers/Booking.php b/packages/Webkul/BookingProduct/src/Helpers/Booking.php index 0e944cc6b..b148043b2 100644 --- a/packages/Webkul/BookingProduct/src/Helpers/Booking.php +++ b/packages/Webkul/BookingProduct/src/Helpers/Booking.php @@ -12,12 +12,15 @@ use Webkul\BookingProduct\Repositories\BookingProductEventTicketRepository; use Webkul\BookingProduct\Repositories\BookingProductRentalSlotRepository; use Webkul\BookingProduct\Repositories\BookingProductTableSlotRepository; use Webkul\BookingProduct\Repositories\BookingRepository; +use Webkul\Product\Datatypes\CartItemValidationResult; +use Webkul\Checkout\Models\CartItem; + class Booking { /** * BookingProductRepository - * + * * @return \Webkul\BookingProduct\Repositories\BookingProductRepository */ protected $bookingProductRepository; @@ -29,7 +32,7 @@ class Booking /** * BookingRepository - * + * * @return \Webkul\BookingProduct\Repositories\BookingRepository */ protected $bookingRepository; @@ -93,7 +96,7 @@ class Booking * @param string $type * @return array */ - public function getTypeHepler($type) + public function getTypeHelper($type) { return $this->typeHelpers[$type]; } @@ -316,10 +319,10 @@ class Booking if ($qty && $currentTime <= $from) { $slots[] = [ - 'from' => $from->format('h:i A'), - 'to' => $to->format('h:i A'), - 'timestamp' => $from->getTimestamp() . '-' . $to->getTimestamp(), - 'qty' => $qty, + 'from' => $from->format('h:i A'), + 'to' => $to->format('h:i A'), + 'timestamp' => $from->getTimestamp() . '-' . $to->getTimestamp(), + 'qty' => $qty, ]; } } else { @@ -368,7 +371,7 @@ class Booking public function isSlotExpired($cartItem) { $bookingProduct = $this->bookingProductRepository->findOneByField('product_id', $cartItem['product_id']); - + $typeHelper = app($this->typeHelpers[$bookingProduct->type]); $slots = $typeHelper->getSlotsByDate($bookingProduct, $cartItem['additional']['booking']['date']); @@ -432,7 +435,7 @@ class Booking 'option_label' => Carbon::createFromTimeString($bookingProduct->available_to)->format('d F, Y'), ] ]; - + break; case 'rental': @@ -465,7 +468,7 @@ class Booking ]; break; - + case 'table': $timestamps = explode('-', $data['booking']['slot']); @@ -490,7 +493,7 @@ class Booking } break; - + default: $timestamps = explode('-', $data['booking']['slot']); @@ -529,12 +532,20 @@ class Booking * @param \Webkul\Checkout\Contracts\CartItem $item * @return void */ - public function validateCartItem($item) + public function validateCartItem(CartItem $item): CartItemValidationResult { + $result = new CartItemValidationResult(); + + if ($this->isCartItemInactive($item)) { + $result->itemIsInactive(); + + return $result; + } + $price = $item->product->getTypeInstance()->getFinalPrice($item->quantity); if ($price == $item->base_price) { - return; + return $result; } $item->base_price = $price; @@ -544,5 +555,24 @@ class Booking $item->total = core()->convertPrice($price * $item->quantity); $item->save(); + $result->cartIsDirty(); + + return $result; + } + + /** + * Returns true, if cart item is inactive + * + * @param \Webkul\Checkout\Contracts\CartItem $item + * + * @return bool + */ + public function isCartItemInactive(\Webkul\Checkout\Contracts\CartItem $item): bool + { + if ($item->product->status === 0) { + return true; + } + + return false; } } \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Helpers/EventTicket.php b/packages/Webkul/BookingProduct/src/Helpers/EventTicket.php index f1116614f..1341b498c 100644 --- a/packages/Webkul/BookingProduct/src/Helpers/EventTicket.php +++ b/packages/Webkul/BookingProduct/src/Helpers/EventTicket.php @@ -5,6 +5,8 @@ namespace Webkul\BookingProduct\Helpers; use Illuminate\Support\Facades\DB; use Carbon\Carbon; use Webkul\Checkout\Facades\Cart; +use Webkul\Product\Datatypes\CartItemValidationResult; +use Webkul\Checkout\Models\CartItem; class EventTicket extends Booking { @@ -131,8 +133,16 @@ class EventTicket extends Booking * @param \Webkul\Checkout\Contracts\CartItem $item * @return float */ - public function validateCartItem($item) + public function validateCartItem(CartItem $item): CartItemValidationResult { + $result = new CartItemValidationResult(); + + if (parent::isCartItemInactive($item)) { + $result->itemIsInactive(); + + return $result; + } + $price = $item->product->getTypeInstance()->getFinalPrice($item->quantity); $bookingProduct = $this->bookingProductRepository->findOneByField('product_id', $item->product_id); @@ -140,9 +150,9 @@ class EventTicket extends Booking $ticket = $bookingProduct->event_tickets()->find($item->additional['booking']['ticket_id']); if (! $ticket) { - Cart::removeItem($item->id); + $result->itemIsInactive(); - return true; + return $result; } if ($this->isInSale($ticket)) { @@ -152,7 +162,7 @@ class EventTicket extends Booking } if ($price === $item->base_price) { - return; + return $result; } $item->base_price = $price; @@ -162,6 +172,9 @@ class EventTicket extends Booking $item->total = core()->convertPrice($price * $item->quantity); $item->save(); + $result->cartIsDirty(); + + return $result; } /** diff --git a/packages/Webkul/BookingProduct/src/Helpers/RentalSlot.php b/packages/Webkul/BookingProduct/src/Helpers/RentalSlot.php index 71d1a937e..cdc54cdf0 100644 --- a/packages/Webkul/BookingProduct/src/Helpers/RentalSlot.php +++ b/packages/Webkul/BookingProduct/src/Helpers/RentalSlot.php @@ -5,6 +5,8 @@ namespace Webkul\BookingProduct\Helpers; use Illuminate\Support\Facades\DB; use Carbon\Carbon; use Webkul\Checkout\Facades\Cart; +use Webkul\Checkout\Models\CartItem; +use Webkul\Product\Datatypes\CartItemValidationResult; class RentalSlot extends Booking { @@ -159,7 +161,7 @@ class RentalSlot extends Booking return $isExpired; } else { $currentTime = Carbon::now(); - + $requestedFromDate = Carbon::createFromTimeString($cartItem['additional']['booking']['date_from'] . " 00:00:00"); $requestedToDate = Carbon::createFromTimeString($cartItem['additional']['booking']['date_to'] . " 23:59:59"); @@ -222,8 +224,16 @@ class RentalSlot extends Booking * @param \Webkul\Checkout\Contracts\CartItem $item * @return void|null */ - public function validateCartItem($item) + public function validateCartItem(CartItem $item): CartItemValidationResult { + $result = new CartItemValidationResult(); + + if (parent::isCartItemInactive($item)) { + $result->itemIsInactive(); + + return $result; + } + $price = $item->product->getTypeInstance()->getFinalPrice($item->quantity); $bookingProduct = $this->bookingProductRepository->findOneByField('product_id', $item->product_id); @@ -234,11 +244,11 @@ class RentalSlot extends Booking if (! isset($item->additional['booking']['date_from']) || ! isset($item->additional['booking']['date_to']) ) { - Cart::removeItem($item->id); + $result->itemIsInactive(); - return true; + return $result; } - + $from = Carbon::createFromTimeString($item->additional['booking']['date_from'] . " 00:00:00"); $to = Carbon::createFromTimeString($item->additional['booking']['date_to'] . " 24:00:00"); @@ -247,9 +257,9 @@ class RentalSlot extends Booking if (! isset($item->additional['booking']['slot']['from']) || ! isset($item->additional['booking']['slot']['to']) ) { - Cart::removeItem($item->id); + $result->itemIsInactive(); - return true; + return $result; } $from = Carbon::createFromTimestamp($item->additional['booking']['slot']['from']); @@ -259,7 +269,7 @@ class RentalSlot extends Booking } if ($price == $item->base_price) { - return; + return $result; } $item->base_price = $price; @@ -269,5 +279,8 @@ class RentalSlot extends Booking $item->total = core()->convertPrice($price * $item->quantity); $item->save(); + $result->cartIsDirty(); + + return $result; } } \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Type/Booking.php b/packages/Webkul/BookingProduct/src/Type/Booking.php index 293f65aa0..ed8f7159d 100644 --- a/packages/Webkul/BookingProduct/src/Type/Booking.php +++ b/packages/Webkul/BookingProduct/src/Type/Booking.php @@ -4,6 +4,7 @@ namespace Webkul\BookingProduct\Type; use Illuminate\Support\Arr; use Webkul\Attribute\Repositories\AttributeRepository; +use Webkul\Product\Datatypes\CartItemValidationResult; use Webkul\Product\Repositories\ProductRepository; use Webkul\Product\Repositories\ProductAttributeValueRepository; use Webkul\Product\Repositories\ProductInventoryRepository; @@ -13,6 +14,7 @@ use Webkul\BookingProduct\Repositories\BookingProductRepository; use Webkul\BookingProduct\Helpers\Booking as BookingHelper; use Webkul\Product\Type\Virtual; use Carbon\Carbon; +use Webkul\Checkout\Models\CartItem; class Booking extends Virtual { @@ -133,7 +135,7 @@ class Booking extends Virtual if (! $bookingProduct) { return false; } - + if (in_array($bookingProduct->type, ['default', 'rental', 'table'])) { return true; } @@ -149,7 +151,7 @@ class Booking extends Virtual { $bookingProduct = $this->getBookingProduct($this->product->id); - return app($this->bookingHelper->getTypeHepler($bookingProduct->type))->isItemHaveQuantity($cartItem); + return app($this->bookingHelper->getTypeHelper($bookingProduct->type))->isItemHaveQuantity($cartItem); } /** @@ -171,7 +173,7 @@ class Booking extends Virtual if ($bookingProduct->type == 'event') { if (Carbon::now() > $bookingProduct->available_from && Carbon::now() > $bookingProduct->available_to) { return trans('shop::app.checkout.cart.event.expired'); - } + } $filtered = Arr::where($data['booking']['qty'], function ($qty, $key) { return $qty != 0; @@ -197,14 +199,14 @@ class Booking extends Virtual if (is_string($cartProducts)) { return $cartProducts; } - + $products = array_merge($products, $cartProducts); } } else { $products = parent::prepareForCart($data); } - $typeHelper = app($this->bookingHelper->getTypeHepler($bookingProduct->type)); + $typeHelper = app($this->bookingHelper->getTypeHelper($bookingProduct->type)); if (! $typeHelper->isSlotAvailable($products)) { return trans('shop::app.checkout.cart.quantity.inventory_warning'); @@ -250,17 +252,26 @@ class Booking extends Virtual /** * Validate cart item product price * - * @param \Webkul\Checkout\Contracts\CartItem $item - * @return float + * @param \Webkul\Checkout\Models\CartItem $item + * + * @return \Webkul\Product\Datatypes\CartItemValidationResult */ - public function validateCartItem($item) + public function validateCartItem(CartItem $item): CartItemValidationResult { + $result = new CartItemValidationResult(); + + if (parent::isCartItemInactive($item)) { + $result->itemIsInactive(); + + return $result; + } + $bookingProduct = $this->getBookingProduct($item->product_id); if (! $bookingProduct) { - return; + return $result; } - return app($this->bookingHelper->getTypeHepler($bookingProduct->type))->validateCartItem($item); + return app($this->bookingHelper->getTypeHelper($bookingProduct->type))->validateCartItem($item); } } \ No newline at end of file diff --git a/packages/Webkul/Checkout/src/Cart.php b/packages/Webkul/Checkout/src/Cart.php index b25d705e0..b9dcc7bdc 100755 --- a/packages/Webkul/Checkout/src/Cart.php +++ b/packages/Webkul/Checkout/src/Cart.php @@ -571,16 +571,14 @@ class Cart * * @return void */ - public function collectTotals() + public function collectTotals(): void { - $validated = $this->validateItems(); - - if (! $validated) { - return false; + if (! $this->validateItems()) { + return; } if (! $cart = $this->getCart()) { - return false; + return; } Event::dispatch('checkout.cart.collect.totals.before', $cart); @@ -637,37 +635,42 @@ class Cart * * @return bool */ - public function validateItems() + public function validateItems(): bool { + $result = false; + if (! $cart = $this->getCart()) { - return; + return false; } if (count($cart->items) == 0) { $this->cartRepository->delete($cart->id); return false; - } else { - foreach ($cart->items as $item) { - $response = $item->product->getTypeInstance()->validateCartItem($item); - // ToDo: refactoring of all validateCartItem functions, at the moment they return nothing - if ($response) { - return; - } + } - $price = ! is_null($item->custom_price) ? $item->custom_price : $item->base_price; + foreach ($cart->items as $item) { + $validationResult = $item->product->getTypeInstance()->validateCartItem($item); - $this->cartItemRepository->update([ - 'price' => core()->convertPrice($price), - 'base_price' => $price, - 'total' => core()->convertPrice($price * $item->quantity), - 'base_total' => $price * $item->quantity, - ], $item->id); + if ($validationResult->isItemInactive()) { + $this->removeItem($item->id); + session()->flash('info', __('shop::app.checkout.cart.item.inactive')); } - return true; + $price = ! is_null($item->custom_price) ? $item->custom_price : $item->base_price; + + $this->cartItemRepository->update([ + 'price' => core()->convertPrice($price), + 'base_price' => $price, + 'total' => core()->convertPrice($price * $item->quantity), + 'base_total' => $price * $item->quantity, + ], $item->id); + + $result |= $validationResult->isCartDirty(); } + + return $result; } /** @@ -1131,15 +1134,7 @@ class Cart * @return bool */ private function isCartItemInactive(\Webkul\Checkout\Contracts\CartItem $item): bool { - if ($item->product->status === 0) { - return true; - } - - if ($item->product->type === 'configurable' && $item->child && $item->child->product->status === 0) { - return true; - } - - return false; + return $item->product->getTypeInstance()->isCartItemInactive($item); } /** diff --git a/packages/Webkul/Product/src/Datatypes/CartItemValidationResult.php b/packages/Webkul/Product/src/Datatypes/CartItemValidationResult.php new file mode 100644 index 000000000..6e8ea9585 --- /dev/null +++ b/packages/Webkul/Product/src/Datatypes/CartItemValidationResult.php @@ -0,0 +1,39 @@ +cartIsDirty; + } + + /** + * @return bool + */ + public function isItemInactive(): bool + { + return $this->itemIsInactive; + } + + public function itemIsInactive(): void + { + $this->itemIsInactive = true; + $this->cartIsDirty = true; + } + + public function cartIsDirty(): void + { + $this->cartIsDirty = true; + } +} diff --git a/packages/Webkul/Product/src/Type/AbstractType.php b/packages/Webkul/Product/src/Type/AbstractType.php index df3e84172..3e631adb2 100644 --- a/packages/Webkul/Product/src/Type/AbstractType.php +++ b/packages/Webkul/Product/src/Type/AbstractType.php @@ -3,8 +3,8 @@ namespace Webkul\Product\Type; use Illuminate\Support\Facades\Storage; -use phpDocumentor\Reflection\Types\Boolean; use Webkul\Attribute\Repositories\AttributeRepository; +use Webkul\Product\Datatypes\CartItemValidationResult; use Webkul\Product\Repositories\ProductRepository; use Webkul\Product\Repositories\ProductAttributeValueRepository; use Webkul\Product\Repositories\ProductInventoryRepository; @@ -12,6 +12,7 @@ use Webkul\Product\Repositories\ProductImageRepository; use Webkul\Product\Models\ProductAttributeValue; use Webkul\Product\Helpers\ProductImage; use Webkul\Checkout\Facades\Cart; +use Webkul\Checkout\Models\CartItem; abstract class AbstractType { @@ -54,7 +55,7 @@ abstract class AbstractType * Product Image helper instance * * @var \Webkul\Product\Helpers\ProductImage - */ + */ protected $productImageHelper; /** @@ -121,12 +122,13 @@ abstract class AbstractType /** * Create a new product type instance. * - * @param \Webkul\Attribute\Repositories\AttributeRepository $attributeRepository - * @param \Webkul\Product\Repositories\ProductRepository $productRepository - * @param \Webkul\Product\Repositories\ProductAttributeValueRepository $attributeValueRepository - * @param \Webkul\Product\Repositories\ProductInventoryRepository $productInventoryRepository - * @param \Webkul\Product\Repositories\ProductImageRepository $productImageRepository - * @param \Webkul\Product\Helpers\ProductImage $productImageHelper + * @param \Webkul\Attribute\Repositories\AttributeRepository $attributeRepository + * @param \Webkul\Product\Repositories\ProductRepository $productRepository + * @param \Webkul\Product\Repositories\ProductAttributeValueRepository $attributeValueRepository + * @param \Webkul\Product\Repositories\ProductInventoryRepository $productInventoryRepository + * @param \Webkul\Product\Repositories\ProductImageRepository $productImageRepository + * @param \Webkul\Product\Helpers\ProductImage $productImageHelper + * * @return void */ public function __construct( @@ -136,8 +138,7 @@ abstract class AbstractType ProductInventoryRepository $productInventoryRepository, ProductImageRepository $productImageRepository, ProductImage $productImageHelper - ) - { + ) { $this->attributeRepository = $attributeRepository; $this->productRepository = $productRepository; @@ -152,7 +153,8 @@ abstract class AbstractType } /** - * @param array $data + * @param array $data + * * @return \Webkul\Product\Contracts\Product */ public function create(array $data) @@ -161,9 +163,10 @@ abstract class AbstractType } /** - * @param array $data - * @param int $id - * @param string $attribute + * @param array $data + * @param int $id + * @param string $attribute + * * @return \Webkul\Product\Contracts\Product */ public function update(array $data, $id, $attribute = "id") @@ -179,7 +182,7 @@ abstract class AbstractType $data[$attribute->code] = isset($data[$attribute->code]) && $data[$attribute->code] ? 1 : 0; } - if (! isset($data[$attribute->code])) { + if (!isset($data[$attribute->code])) { continue; } @@ -197,8 +200,8 @@ abstract class AbstractType if ($attribute->type == 'image' || $attribute->type == 'file') { $data[$attribute->code] = gettype($data[$attribute->code]) == 'object' - ? request()->file($attribute->code)->store('product/' . $product->id) - : NULL; + ? request()->file($attribute->code)->store('product/' . $product->id) + : null; } $attributeValue = $this->attributeValueRepository->findOneWhere([ @@ -208,7 +211,7 @@ abstract class AbstractType 'locale' => $attribute->value_per_locale ? $data['locale'] : null, ]); - if (! $attributeValue) { + if (!$attributeValue) { $this->attributeValueRepository->create([ 'product_id' => $product->id, 'attribute_id' => $attribute->id, @@ -218,8 +221,8 @@ abstract class AbstractType ]); } else { $this->attributeValueRepository->update([ - ProductAttributeValue::$attributeTypeFields[$attribute->type] => $data[$attribute->code] - ], $attributeValue->id + ProductAttributeValue::$attributeTypeFields[$attribute->type] => $data[$attribute->code], + ], $attributeValue->id ); if ($attribute->type == 'image' || $attribute->type == 'file') { @@ -231,7 +234,7 @@ abstract class AbstractType $route = request()->route() ? request()->route()->getName() : ""; if ($route != 'admin.catalog.products.massupdate') { - if (! isset($data['categories'])) { + if (!isset($data['categories'])) { $data['categories'] = []; } @@ -247,7 +250,8 @@ abstract class AbstractType $this->productImageRepository->uploadImages($data, $product); - app('Webkul\Product\Repositories\ProductCustomerGroupPriceRepository')->saveCustomerGroupPrices($data, $product); + app('Webkul\Product\Repositories\ProductCustomerGroupPriceRepository')->saveCustomerGroupPrices($data, + $product); } return $product; @@ -256,7 +260,8 @@ abstract class AbstractType /** * Specify type instance product * - * @param \Webkul\Product\Contracts\Product $product + * @param \Webkul\Product\Contracts\Product $product + * * @return \Webkul\Product\Type\AbstractType */ public function setProduct($product) @@ -293,7 +298,7 @@ abstract class AbstractType */ public function isSaleable() { - if (! $this->product->status) { + if (!$this->product->status) { return false; } @@ -341,7 +346,8 @@ abstract class AbstractType } /** - * @param int $qty + * @param int $qty + * * @return bool */ public function haveSufficientQuantity($qty) @@ -360,7 +366,8 @@ abstract class AbstractType } /** - * @param \Webkul\Checkout\Contracts\CartItem $cartItem + * @param \Webkul\Checkout\Contracts\CartItem $cartItem + * * @return bool */ public function isItemHaveQuantity($cartItem) @@ -376,9 +383,9 @@ abstract class AbstractType $total = 0; $channelInventorySourceIds = core()->getCurrentChannel() - ->inventory_sources() - ->where('status', 1) - ->pluck('id'); + ->inventory_sources() + ->where('status', 1) + ->pluck('id'); foreach ($this->product->inventories as $inventory) { if (is_numeric($index = $channelInventorySourceIds->search($inventory->inventory_source_id))) { @@ -387,8 +394,8 @@ abstract class AbstractType } $orderedInventory = $this->product->ordered_inventories() - ->where('channel_id', core()->getCurrentChannel()->id) - ->first(); + ->where('channel_id', core()->getCurrentChannel()->id) + ->first(); if ($orderedInventory) { $total -= $orderedInventory->qty; @@ -400,7 +407,8 @@ abstract class AbstractType /** * Return true if item can be moved to cart from wishlist * - * @param \Webkul\Checkout\Contracts\CartItem $item + * @param \Webkul\Checkout\Contracts\CartItem $item + * * @return bool */ public function canBeMovedFromWishlistToCart($item) @@ -411,18 +419,21 @@ abstract class AbstractType /** * Retrieve product attributes * - * @param \Webkul\Attribute\Contracts\Group $group - * @param bool $skipSuperAttribute + * @param \Webkul\Attribute\Contracts\Group $group + * @param bool $skipSuperAttribute + * * @return \Illuminate\Support\Collection */ public function getEditableAttributes($group = null, $skipSuperAttribute = true) { if ($skipSuperAttribute) { - $this->skipAttributes = array_merge($this->product->super_attributes->pluck('code')->toArray(), $this->skipAttributes); + $this->skipAttributes = array_merge($this->product->super_attributes->pluck('code')->toArray(), + $this->skipAttributes); } - if (! $group) { - return $this->product->attribute_family->custom_attributes()->whereNotIn('attributes.code', $this->skipAttributes)->get(); + if (!$group) { + return $this->product->attribute_family->custom_attributes()->whereNotIn('attributes.code', + $this->skipAttributes)->get(); } return $group->custom_attributes()->whereNotIn('code', $this->skipAttributes)->get(); @@ -451,7 +462,8 @@ abstract class AbstractType /** * Get product minimal price * - * @param int $qty + * @param int $qty + * * @return float */ public function getMinimalPrice($qty = null) @@ -476,7 +488,8 @@ abstract class AbstractType /** * Get product minimal price * - * @param int $qty + * @param int $qty + * * @return float */ public function getFinalPrice($qty = null) @@ -487,7 +500,8 @@ abstract class AbstractType /** * Returns the product's minimal price * - * @param int $qty + * @param int $qty + * * @return float */ public function getSpecialPrice($qty = null) @@ -496,7 +510,8 @@ abstract class AbstractType } /** - * @param int $qty + * @param int $qty + * * @return bool */ public function haveSpecialPrice($qty = null) @@ -505,8 +520,8 @@ abstract class AbstractType $rulePrice = app('Webkul\CatalogRule\Helpers\CatalogRuleProductPrice')->getRulePrice($this->product); - if ((is_null($this->product->special_price) || ! (float) $this->product->special_price) - && ! $rulePrice + if ((is_null($this->product->special_price) || !(float)$this->product->special_price) + && !$rulePrice && $customerGroupPrice == $this->product->price ) { return false; @@ -514,7 +529,7 @@ abstract class AbstractType $haveSpecialPrice = false; - if (! (float) $this->product->special_price) { + if (!(float)$this->product->special_price) { if ($rulePrice && $rulePrice->price < $this->product->price) { $this->product->special_price = $rulePrice->price; @@ -526,7 +541,8 @@ abstract class AbstractType $haveSpecialPrice = true; } else { - if (core()->isChannelDateInInterval($this->product->special_price_from, $this->product->special_price_to)) { + if (core()->isChannelDateInInterval($this->product->special_price_from, + $this->product->special_price_to)) { $haveSpecialPrice = true; } elseif ($rulePrice) { $this->product->special_price = $rulePrice->price; @@ -570,11 +586,11 @@ abstract class AbstractType $customerGroupPrices = $product->customer_group_prices()->where(function ($query) use ($customerGroupId) { $query->where('customer_group_id', $customerGroupId) - ->orWhereNull('customer_group_id'); - } + ->orWhereNull('customer_group_id'); + } )->get(); - if (! $customerGroupPrices->count()) { + if (!$customerGroupPrices->count()) { return $product->price; } @@ -635,7 +651,7 @@ abstract class AbstractType 'final_price' => [ 'price' => core()->convertPrice($this->getMinimalPrice()), 'formated_price' => core()->currency($this->getMinimalPrice()), - ] + ], ]; } @@ -660,7 +676,8 @@ abstract class AbstractType /** * Add product. Returns error message if can't prepare product. * - * @param array $data + * @param array $data + * * @return array */ public function prepareForCart($data) @@ -669,7 +686,7 @@ abstract class AbstractType $data = $this->getQtyRequest($data); - if (! $this->haveSufficientQuantity($data['quantity'])) { + if (!$this->haveSufficientQuantity($data['quantity'])) { return trans('shop::app.checkout.cart.quantity.inventory_warning'); } @@ -690,7 +707,7 @@ abstract class AbstractType 'base_total_weight' => ($this->product->weight ?? 0) * $data['quantity'], 'type' => $this->product->type, 'additional' => $this->getAdditionalOptions($data), - ] + ], ]; return $products; @@ -699,7 +716,8 @@ abstract class AbstractType /** * Get request quantity * - * @param array $data + * @param array $data + * * @return array */ public function getQtyRequest($data) @@ -713,8 +731,9 @@ abstract class AbstractType /** * - * @param array $options1 - * @param array $options2 + * @param array $options1 + * @param array $options2 + * * @return bool */ public function compareOptions($options1, $options2) @@ -728,9 +747,9 @@ abstract class AbstractType } else { return false; } - } elseif (isset($options1['parent_id']) && ! isset($options2['parent_id'])) { + } elseif (isset($options1['parent_id']) && !isset($options2['parent_id'])) { return false; - } elseif (isset($options2['parent_id']) && ! isset($options1['parent_id'])) { + } elseif (isset($options2['parent_id']) && !isset($options1['parent_id'])) { return false; } } @@ -741,7 +760,8 @@ abstract class AbstractType /** * Returns additional information for items * - * @param array $data + * @param array $data + * * @return array */ public function getAdditionalOptions($data) @@ -752,7 +772,8 @@ abstract class AbstractType /** * Get actual ordered item * - * @param \Webkul\Checkout\Contracts\CartItem $item + * @param \Webkul\Checkout\Contracts\CartItem $item + * * @return \Webkul\Checkout\Contracts\CartItem|\Webkul\Sales\Contracts\OrderItem|\Webkul\Sales\Contracts\InvoiceItem|\Webkul\Sales\Contracts\ShipmentItem|\Webkul\Customer\Contracts\Wishlist */ public function getOrderedItem($item) @@ -763,7 +784,8 @@ abstract class AbstractType /** * Get product base image * - * @param \Webkul\Customer\Contracts\CartItem|\Webkul\Checkout\Contracts\CartItem $item + * @param \Webkul\Customer\Contracts\CartItem|\Webkul\Checkout\Contracts\CartItem $item + * * @return array */ public function getBaseImage($item) @@ -772,17 +794,26 @@ abstract class AbstractType } /** - * Validate cart item product price + * Validate cart item product price and other things * - * @param \Webkul\Customer\Contracts\CartItem $item - * @return void + * @param \Webkul\Checkout\Models\CartItem $item + * + * @return \Webkul\Product\Datatypes\CartItemValidationResult */ - public function validateCartItem($item) + public function validateCartItem(CartItem $item): CartItemValidationResult { + $result = new CartItemValidationResult(); + + if ($this->isCartItemInactive($item)) { + $result->itemIsInactive(); + + return $result; + } + $price = $item->product->getTypeInstance()->getFinalPrice($item->quantity); if ($price == $item->base_price) { - return; + return $result; } $item->base_price = $price; @@ -792,6 +823,9 @@ abstract class AbstractType $item->total = core()->convertPrice($price * $item->quantity); $item->save(); + $result->cartIsDirty(); + + return $result; } //get product options @@ -799,4 +833,37 @@ abstract class AbstractType { return $this->productOptions; } + + /** + * Returns true, if cart item is inactive + * + * @param \Webkul\Checkout\Contracts\CartItem $item + * + * @return bool + */ + public function isCartItemInactive(\Webkul\Checkout\Contracts\CartItem $item): bool + { + if ($item->product->status === 0) { + return true; + } + + switch ($item->product->type) { + case 'bundle': + foreach ($item->children as $child) { + if ($child->product->status === 0) { + return true; + } + } + break; + + case 'configurable': + if ($item->child && $item->child->product->status === 0) { + return true; + } + break; + } + + return false; + } + } \ No newline at end of file diff --git a/packages/Webkul/Product/src/Type/Bundle.php b/packages/Webkul/Product/src/Type/Bundle.php index 9ad09181c..5b0d503ef 100644 --- a/packages/Webkul/Product/src/Type/Bundle.php +++ b/packages/Webkul/Product/src/Type/Bundle.php @@ -3,6 +3,7 @@ namespace Webkul\Product\Type; use Webkul\Attribute\Repositories\AttributeRepository; +use Webkul\Product\Datatypes\CartItemValidationResult; use Webkul\Product\Repositories\ProductRepository; use Webkul\Product\Repositories\ProductAttributeValueRepository; use Webkul\Product\Repositories\ProductInventoryRepository; @@ -11,6 +12,7 @@ use Webkul\Product\Repositories\ProductBundleOptionRepository; use Webkul\Product\Repositories\ProductBundleOptionProductRepository; use Webkul\Product\Helpers\ProductImage; use Webkul\Product\Helpers\BundleOption; +use Webkul\Checkout\Models\CartItem; class Bundle extends AbstractType { @@ -278,7 +280,7 @@ class Bundle extends AbstractType if (! $bundleOptionProduct->product->getTypeInstance()->isSaleable()) { continue; } - + if (in_array($option->type, ['multiselect', 'checkbox'])) { if (! isset($optionPrices[$option->id][0])) { $optionPrices[$option->id][0] = 0; @@ -315,7 +317,7 @@ class Bundle extends AbstractType if (! $bundleOptionProduct->product->getTypeInstance()->isSaleable()) { continue; } - + if (in_array($option->type, ['multiselect', 'checkbox'])) { if (! isset($optionPrices[$option->id][0])) { $optionPrices[$option->id][0] = 0; @@ -634,15 +636,23 @@ class Bundle extends AbstractType } /** - * Validate cart item product price + * Validate cart item product price and other things * - * @param \Webkul\Checkout\Contracts\CartItem $item - * @return void + * @param \Webkul\Checkout\Models\CartItem $item + * + * @return \Webkul\Product\Datatypes\CartItemValidationResult */ - public function validateCartItem($item) + public function validateCartItem(CartItem $item): CartItemValidationResult { + $result = new CartItemValidationResult(); $price = 0; + if (parent::isCartItemInactive($item)) { + $result->itemIsInactive(); + + return $result; + } + foreach ($item->children as $childItem) { $childItem->product->getTypeInstance()->validateCartItem($childItem); @@ -650,7 +660,7 @@ class Bundle extends AbstractType } if ($price == $item->base_price) { - return; + return $result; } $item->base_price = $price; @@ -662,6 +672,9 @@ class Bundle extends AbstractType $item->additional = $this->getAdditionalOptions($item->additional); $item->save(); + $result->cartIsDirty(); + + return $result; } /** @@ -674,4 +687,5 @@ class Bundle extends AbstractType return $options; } + } \ No newline at end of file diff --git a/packages/Webkul/Product/src/Type/Configurable.php b/packages/Webkul/Product/src/Type/Configurable.php index 2d1d23273..6c166892e 100644 --- a/packages/Webkul/Product/src/Type/Configurable.php +++ b/packages/Webkul/Product/src/Type/Configurable.php @@ -3,9 +3,11 @@ namespace Webkul\Product\Type; use Webkul\Customer\Contracts\CartItem; +use Webkul\Product\Datatypes\CartItemValidationResult; use Webkul\Product\Models\ProductAttributeValue; use Webkul\Product\Models\ProductFlat; use Illuminate\Support\Str; +use Webkul\Checkout\Models\CartItem; class Configurable extends AbstractType { @@ -539,18 +541,28 @@ class Configurable extends AbstractType /** * Validate cart item product price * - * @param \Webkul\Checkout\Contracts\CartItem $item + * @param \Webkul\Product\Type\CartItem $item + * + * @return \Webkul\Product\Datatypes\CartItemValidationResult */ - public function validateCartItem($item) + public function validateCartItem(CartItem $item): CartItemValidationResult { - if (! $item || ! $item->child) { - return; + $result = new CartItemValidationResult(); + + if ($this->isCartItemInactive($item)) { + $result->itemIsInactive(); + + return $result; } +// if (! $item || ! $item->child) { +// return; +// } + $price = $item->child->product->getTypeInstance()->getFinalPrice($item->quantity); if ($price == $item->base_price) { - return; + return $result; } $item->base_price = $price; @@ -560,6 +572,9 @@ class Configurable extends AbstractType $item->total = core()->convertPrice($price * $item->quantity); $item->save(); + $result->cartIsDirty(); + + return $result; } /** diff --git a/packages/Webkul/Product/src/Type/Downloadable.php b/packages/Webkul/Product/src/Type/Downloadable.php index f4487c151..ff584f4fe 100644 --- a/packages/Webkul/Product/src/Type/Downloadable.php +++ b/packages/Webkul/Product/src/Type/Downloadable.php @@ -3,6 +3,7 @@ namespace Webkul\Product\Type; use Webkul\Attribute\Repositories\AttributeRepository; +use Webkul\Product\Datatypes\CartItemValidationResult; use Webkul\Product\Repositories\ProductRepository; use Webkul\Product\Repositories\ProductAttributeValueRepository; use Webkul\Product\Repositories\ProductInventoryRepository; @@ -230,11 +231,20 @@ class Downloadable extends AbstractType /** * Validate cart item product price * - * @param \Webkul\Checkout\Contracts\CartItem $item - * @return float + * @param \Webkul\Checkout\Models\CartItem $item + * + * @return \Webkul\Product\Datatypes\CartItemValidationResult */ - public function validateCartItem($item) + public function validateCartItem(CartItem $item): CartItemValidationResult { + $result = new CartItemValidationResult(); + + if (parent::isCartItemInactive($item)) { + $result->itemIsInactive(); + + return $result; + } + $price = $item->product->getTypeInstance()->getFinalPrice($item->quantity); foreach ($item->product->downloadable_links as $link) { @@ -246,7 +256,7 @@ class Downloadable extends AbstractType } if ($price == $item->base_price) { - return; + return $result; } $item->base_price = $price; @@ -256,5 +266,8 @@ class Downloadable extends AbstractType $item->total = core()->convertPrice($price * $item->quantity); $item->save(); + $result->cartIsDirty(); + + return $result; } } \ No newline at end of file diff --git a/packages/Webkul/Shop/src/Resources/lang/ar/app.php b/packages/Webkul/Shop/src/Resources/lang/ar/app.php index 3745244dd..4dda93582 100644 --- a/packages/Webkul/Shop/src/Resources/lang/ar/app.php +++ b/packages/Webkul/Shop/src/Resources/lang/ar/app.php @@ -464,8 +464,8 @@ return [ 'success' => 'تم بنجاح إضافة العنصر إلى العربة', 'success-remove' => 'تم إزالة العنصر بنجاح من العربة', 'error-add' => 'لا يمكن إضافة العنصر إلى العربة ، رجاء حاول مرة أخرى ', - 'inactive' => 'An item is inactive and was removed from cart.', - 'inactive-add' => 'Inactive item cannot be added to cart.', + 'inactive' => 'An item is inactive and was removed from cart', + 'inactive-add' => 'Inactive item cannot be added to cart', ], 'quantity-error' => 'الكمية المطلوبة غير متوفرة', 'cart-subtotal' => 'المجموع الفرعي للمشتريات', diff --git a/packages/Webkul/Shop/src/Resources/lang/es/app.php b/packages/Webkul/Shop/src/Resources/lang/es/app.php index 419be465e..ece9b4d0d 100644 --- a/packages/Webkul/Shop/src/Resources/lang/es/app.php +++ b/packages/Webkul/Shop/src/Resources/lang/es/app.php @@ -431,8 +431,8 @@ return [ 'success' => 'El artículp se añadió a la cesta', 'success-remove' => 'El artículo se eliminó de la cesta', 'error-add' => 'El artículo no se puede añadir a la cesta, inténtelo más tarde', - 'inactive' => 'An item is inactive and was removed from cart.', - 'inactive-add' => 'Inactive item cannot be added to cart.', + 'inactive' => 'An item is inactive and was removed from cart', + 'inactive-add' => 'Inactive item cannot be added to cart', ], 'quantity-error' => 'La cantidad solicitada no está disponible', 'cart-subtotal' => 'Total parcial', diff --git a/packages/Webkul/Shop/src/Resources/lang/fa/app.php b/packages/Webkul/Shop/src/Resources/lang/fa/app.php index 73b2a7804..6a6f5222f 100644 --- a/packages/Webkul/Shop/src/Resources/lang/fa/app.php +++ b/packages/Webkul/Shop/src/Resources/lang/fa/app.php @@ -463,8 +463,8 @@ return [ 'success' => 'مورد با موفقیت به سبد خرید اضافه شد', 'success-remove' => 'مورد با موفقیت از سبد خرید حذف شد', 'error-add' => 'لطفاً موردی را به سبد خرید اضافه نکرد ، لطفا بعداً دوباره امتحان کنید', - 'inactive' => 'An item is inactive and was removed from cart.', - 'inactive-add' => 'Inactive item cannot be added to cart.', + 'inactive' => 'An item is inactive and was removed from cart', + 'inactive-add' => 'Inactive item cannot be added to cart', ], 'quantity-error' => 'مقدار درخواستی در دسترس نیست', diff --git a/packages/Webkul/Shop/src/Resources/lang/ja/app.php b/packages/Webkul/Shop/src/Resources/lang/ja/app.php index ca525c8cd..b8f2ab8f7 100644 --- a/packages/Webkul/Shop/src/Resources/lang/ja/app.php +++ b/packages/Webkul/Shop/src/Resources/lang/ja/app.php @@ -425,8 +425,8 @@ return [ 'success' => 'アイテムがカートに追加されました。', 'success-remove' => 'アイテムがカートから削除されました。', 'error-add' => 'アイテムをカートに追加できません。しばらくしてから再度お試し下さい。', - 'inactive' => 'An item is inactive and was removed from cart.', - 'inactive-add' => 'Inactive item cannot be added to cart.', + 'inactive' => 'An item is inactive and was removed from cart', + 'inactive-add' => 'Inactive item cannot be added to cart', ], 'quantity-error' => 'ご希望の数量の在庫が現在ございません。', 'cart-subtotal' => '小計', diff --git a/packages/Webkul/Shop/src/Resources/lang/pt_BR/app.php b/packages/Webkul/Shop/src/Resources/lang/pt_BR/app.php index f2472cc58..ea887d4e5 100755 --- a/packages/Webkul/Shop/src/Resources/lang/pt_BR/app.php +++ b/packages/Webkul/Shop/src/Resources/lang/pt_BR/app.php @@ -453,8 +453,8 @@ return [ 'success' => 'Item foi adicionado com sucesso ao carrinho', 'success-remove' => 'Item foi removido com sucesso do carrinho', 'error-add' => 'Item não pode ser adicionado ao carrinho, por favor, tente novamente mais tarde', - 'inactive' => 'An item is inactive and was removed from cart.', - 'inactive-add' => 'Inactive item cannot be added to cart.', + 'inactive' => 'An item is inactive and was removed from cart', + 'inactive-add' => 'Inactive item cannot be added to cart', ], 'quantity-error' => 'Quantidade solicitada não está disponível', 'cart-subtotal' => 'Subtotal do carrinho', diff --git a/tests/unit/Checkout/Cart/CartCest.php b/tests/unit/Checkout/Cart/CartCest.php index 967188e36..8a11cf4b4 100644 --- a/tests/unit/Checkout/Cart/CartCest.php +++ b/tests/unit/Checkout/Cart/CartCest.php @@ -75,6 +75,8 @@ class CartCest $I->assertFalse(cart()->hasError()); $I->comment('sP2 is inactive'); + + cart()->validateItems(); $I->assertEquals(3, count(cart()->getCart()->items)); $I->comment('add dP2 to cart'); @@ -106,6 +108,7 @@ class CartCest 'links' => $this->downloadableProduct1->downloadable_links->pluck('id')->all(), ]); + cart()->validateItems(); $I->assertEquals(4, count(cart()->getCart()->items)); $I->comment('deactivate vP2'); @@ -125,6 +128,8 @@ class CartCest $cartItemId => 5 ], ]); + + // now lets check without validating cart before $I->assertEquals(3, count(cart()->getCart()->items)); $I->assertEquals(5, cart()->getCart()->items()->find($cartItemId)->quantity);