diff --git a/config/products.php b/config/products.php index 6eb93e2fd..a84a63e78 100644 --- a/config/products.php +++ b/config/products.php @@ -1,14 +1,23 @@ catalog->products->copy product). // you can also add every relation that should not be copied here to skip them. // defaults to none (which means everything is copied). 'skipAttributesOnCopy' => [ - ], // Make the original and source product 'related' via the 'product_relations' table 'linkProductsOnCopy' => false, -]; \ No newline at end of file + + // Ability to set a global callable that defines if a product is saleable. + // Return neither true nor false but null by default to not interrupt the default chain that + // defines if a product is saleable. It depends on the isSaleable() method of the product + // type if this callable is obeyed. + 'isSaleable' => function (Product $product): ?bool { + return null; + }, +]; diff --git a/packages/Webkul/Core/src/Helpers/Laravel5Helper.php b/packages/Webkul/Core/src/Helpers/Laravel5Helper.php index 528a062d9..028f7ab01 100644 --- a/packages/Webkul/Core/src/Helpers/Laravel5Helper.php +++ b/packages/Webkul/Core/src/Helpers/Laravel5Helper.php @@ -5,24 +5,25 @@ namespace Webkul\Core\Helpers; // here you can define custom actions // all public methods declared in helper class will be available in $I +use StdClass; use Faker\Factory; use Codeception\Module\Laravel5; -use Webkul\Attribute\Models\Attribute; -use Webkul\Attribute\Models\AttributeOption; -use Webkul\BookingProduct\Models\BookingProduct; -use Webkul\BookingProduct\Models\BookingProductEventTicket; use Webkul\Checkout\Models\Cart; -use Webkul\Checkout\Models\CartAddress; +use Webkul\Product\Models\Product; +use Illuminate\Support\Facades\DB; use Webkul\Checkout\Models\CartItem; use Webkul\Customer\Models\Customer; +use Illuminate\Support\Facades\Event; +use Webkul\Attribute\Models\Attribute; +use Webkul\Checkout\Models\CartAddress; use Webkul\Customer\Models\CustomerAddress; -use Webkul\Product\Models\Product; +use Webkul\Product\Models\ProductInventory; +use Webkul\Attribute\Models\AttributeOption; +use Webkul\BookingProduct\Models\BookingProduct; use Webkul\Product\Models\ProductAttributeValue; use Webkul\Product\Models\ProductDownloadableLink; +use Webkul\BookingProduct\Models\BookingProductEventTicket; use Webkul\Product\Models\ProductDownloadableLinkTranslation; -use Webkul\Product\Models\ProductInventory; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Event; /** * Class Laravel5Helper @@ -54,6 +55,7 @@ class Laravel5Helper extends Laravel5 'textarea' => 'text_value', 'price' => 'float_value', 'date' => 'date_value', + 'checkbox' => 'text_value', ]; return $possibleTypes[$type]; @@ -127,7 +129,7 @@ class Laravel5Helper extends Laravel5 // actually set the cart to the user's session // when in an functional test: - $stub = new \StdClass(); + $stub = new StdClass(); $stub->id = $cart->id; $I->setSession(['cart' => $stub]); diff --git a/packages/Webkul/Product/src/Type/AbstractType.php b/packages/Webkul/Product/src/Type/AbstractType.php index fffda16a9..e07a39147 100644 --- a/packages/Webkul/Product/src/Type/AbstractType.php +++ b/packages/Webkul/Product/src/Type/AbstractType.php @@ -177,9 +177,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") @@ -199,20 +200,20 @@ abstract class AbstractType continue; } - if ($attribute->type == 'price' && isset($data[$attribute->code]) && $data[$attribute->code] == '') { + if ($attribute->type === 'price' && isset($data[$attribute->code]) && $data[$attribute->code] === '') { $data[$attribute->code] = null; } - if ($attribute->type == 'date' && $data[$attribute->code] == '' && $route != 'admin.catalog.products.massupdate') { + if ($attribute->type === 'date' && $data[$attribute->code] === '' && $route !== 'admin.catalog.products.massupdate') { $data[$attribute->code] = null; } - if ($attribute->type == 'multiselect' || $attribute->type == 'checkbox') { + if ($attribute->type === 'multiselect' || $attribute->type === 'checkbox') { $data[$attribute->code] = implode(",", $data[$attribute->code]); } - if ($attribute->type == 'image' || $attribute->type == 'file') { - $data[$attribute->code] = gettype($data[$attribute->code]) == 'object' + if ($attribute->type === 'image' || $attribute->type === 'file') { + $data[$attribute->code] = gettype($data[$attribute->code]) === 'object' ? request()->file($attribute->code)->store('product/' . $product->id) : null; } @@ -315,6 +316,11 @@ abstract class AbstractType return false; } + if (is_callable(config('products.isSaleable')) && + call_user_func(config('products.isSaleable'), $this->product) === false) { + return false; + } + return true; } @@ -533,8 +539,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; @@ -542,7 +548,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; diff --git a/packages/Webkul/Product/src/Type/Downloadable.php b/packages/Webkul/Product/src/Type/Downloadable.php index 55e6957f2..5a01c35e1 100644 --- a/packages/Webkul/Product/src/Type/Downloadable.php +++ b/packages/Webkul/Product/src/Type/Downloadable.php @@ -4,14 +4,13 @@ namespace Webkul\Product\Type; use Webkul\Attribute\Repositories\AttributeRepository; use Webkul\Product\Datatypes\CartItemValidationResult; +use Webkul\Product\Helpers\ProductImage; use Webkul\Product\Repositories\ProductRepository; -use Webkul\Product\Repositories\ProductAttributeValueRepository; -use Webkul\Product\Repositories\ProductInventoryRepository; use Webkul\Product\Repositories\ProductImageRepository; +use Webkul\Product\Repositories\ProductInventoryRepository; +use Webkul\Product\Repositories\ProductAttributeValueRepository; use Webkul\Product\Repositories\ProductDownloadableLinkRepository; use Webkul\Product\Repositories\ProductDownloadableSampleRepository; -use Webkul\Product\Helpers\ProductImage; -use Webkul\Checkout\Models\CartItem; class Downloadable extends AbstractType { @@ -19,14 +18,14 @@ class Downloadable extends AbstractType * ProductDownloadableLinkRepository instance * * @var \Webkul\Product\Repositories\ProductDownloadableLinkRepository - */ + */ protected $productDownloadableLinkRepository; /** * ProductDownloadableSampleRepository instance * * @var \Webkul\Product\Repositories\ProductDownloadableSampleRepository - */ + */ protected $productDownloadableSampleRepository; /** @@ -46,7 +45,7 @@ class Downloadable extends AbstractType 'admin::catalog.products.accordians.categories', 'admin::catalog.products.accordians.downloadable', 'admin::catalog.products.accordians.channels', - 'admin::catalog.products.accordians.product-links' + 'admin::catalog.products.accordians.product-links', ]; /** @@ -64,14 +63,15 @@ class Downloadable extends 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\Repositories\ProductDownloadableLinkRepository $productDownloadableLinkRepository - * @param \Webkul\Product\Repositories\ProductDownloadableSampleRepository $productDownloadableSampleRepository - * @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\Repositories\ProductDownloadableLinkRepository $productDownloadableLinkRepository + * @param \Webkul\Product\Repositories\ProductDownloadableSampleRepository $productDownloadableSampleRepository + * @param \Webkul\Product\Helpers\ProductImage $productImageHelper + * * @return void */ public function __construct( @@ -100,9 +100,10 @@ class Downloadable extends 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") @@ -129,6 +130,11 @@ class Downloadable extends AbstractType return false; } + if (is_callable(config('products.isSaleable')) && + call_user_func(config('products.isSaleable'), $this->product) === false) { + return false; + } + if ($this->product->downloadable_links()->count()) { return true; } @@ -157,7 +163,8 @@ class Downloadable extends AbstractType /** * Add product. Returns error message if can't prepare product. * - * @param array $data + * @param array $data + * * @return array */ public function prepareForCart($data) @@ -184,8 +191,9 @@ class Downloadable extends AbstractType /** * - * @param array $options1 - * @param array $options2 + * @param array $options1 + * @param array $options2 + * * @return bool */ public function compareOptions($options1, $options2) @@ -206,7 +214,8 @@ class Downloadable extends AbstractType /** * Returns additional information for items * - * @param array $data + * @param array $data + * * @return array */ public function getAdditionalOptions($data) diff --git a/packages/Webkul/Product/src/Type/Simple.php b/packages/Webkul/Product/src/Type/Simple.php index 6b5659108..67e34a67d 100644 --- a/packages/Webkul/Product/src/Type/Simple.php +++ b/packages/Webkul/Product/src/Type/Simple.php @@ -42,6 +42,11 @@ class Simple extends AbstractType return false; } + if (is_callable(config('products.isSaleable')) && + call_user_func(config('products.isSaleable'), $this->product) === false) { + return false; + } + if ($this->haveSufficientQuantity(1)) { return true; } diff --git a/packages/Webkul/Product/src/Type/Virtual.php b/packages/Webkul/Product/src/Type/Virtual.php index ede642cb0..9c804be1d 100644 --- a/packages/Webkul/Product/src/Type/Virtual.php +++ b/packages/Webkul/Product/src/Type/Virtual.php @@ -49,6 +49,11 @@ class Virtual extends AbstractType return false; } + if (is_callable(config('products.isSaleable')) && + call_user_func(config('products.isSaleable'), $this->product) === false) { + return false; + } + if ($this->haveSufficientQuantity(1)) { return true; } @@ -57,7 +62,8 @@ class Virtual extends AbstractType } /** - * @param int $qty + * @param int $qty + * * @return bool */ public function haveSufficientQuantity(int $qty): bool