Db schema, models and repositories added for bundle type product

This commit is contained in:
jitendra 2019-08-20 17:57:39 +05:30
parent e2bd18e916
commit ec567d624f
51 changed files with 581 additions and 371 deletions

View File

@ -13,8 +13,6 @@ class Product extends JsonResource
*/
public function __construct($resource)
{
$this->productPriceHelper = app('Webkul\Product\Helpers\Price');
$this->productImageHelper = app('Webkul\Product\Helpers\ProductImage');
$this->productReviewHelper = app('Webkul\Product\Helpers\Review');
@ -37,25 +35,25 @@ class Product extends JsonResource
'type' => $product->type,
'name' => $this->name,
'url_key' => $this->url_key,
'price' => $product->type == 'configurable' ? $this->productPriceHelper->getVariantMinPrice($product) : $this->price,
'formated_price' => $product->type == 'configurable' ? core()->currency($this->productPriceHelper->getVariantMinPrice($product)) : core()->currency($this->price),
'price' => $product->getTypeInstance()->getMinimalPrice(),
'formated_price' => core()->currency($product->getTypeInstance()->getMinimalPrice()),
'short_description' => $this->short_description,
'description' => $this->description,
'sku' => $this->sku,
'images' => ProductImage::collection($product->images),
'base_image' => $this->productImageHelper->getProductBaseImage($product),
'variants' => Self::collection($this->variants),
'in_stock' => $product->type == 'configurable' ? 1 : $product->haveSufficientQuantity(1),
'in_stock' => $product->haveSufficientQuantity(1),
$this->mergeWhen($product->type == 'configurable', [
'super_attributes' => Attribute::collection($product->super_attributes),
]),
'special_price' => $this->when(
$this->productPriceHelper->haveSpecialPrice($product),
$this->productPriceHelper->getSpecialPrice($product)
$product->getTypeInstance()->haveSpecialPrice(),
$product->getTypeInstance()->getSpecialPrice()
),
'formated_special_price' => $this->when(
$this->productPriceHelper->haveSpecialPrice($product),
core()->currency($this->productPriceHelper->getSpecialPrice($product))
$product->getTypeInstance()->haveSpecialPrice(),
core()->currency($product->getTypeInstance()->getSpecialPrice())
),
'reviews' => [
'total' => $total = $this->productReviewHelper->getTotalReviews($product),

View File

@ -4,7 +4,6 @@ namespace Webkul\Admin\Listeners;
use Webkul\Product\Repositories\ProductRepository;
use Webkul\Product\Repositories\ProductFlatRepository;
use Webkul\Product\Helpers\Price;
/**
* Products Event handler
@ -18,13 +17,6 @@ class Product {
*/
protected $product;
/**
* Price Object
*
* @var array
*/
protected $price;
/**
* Product Flat Object
*
@ -39,15 +31,12 @@ class Product {
public function __construct(
ProductRepository $product,
ProductFlatRepository $productFlat,
Price $price
ProductFlatRepository $productFlat
)
{
$this->product = $product;
$this->productFlat = $productFlat;
$this->price = $price;
}
/**
@ -63,7 +52,7 @@ class Product {
'type' => $product->type,
'name' => $product->name,
'attribute_family_name' => $product->toArray()['attribute_family']['name'],
'price' => $this->price->getMinimalPrice($product),
'price' => $product->getTypeInstance()->getMinimalPrice(),
'status' => $product->status
];

View File

@ -386,7 +386,8 @@ return [
'searching' => 'Searching ...',
'grouped-products' => 'Grouped Products',
'search-products' => 'Search Products',
'no-result-found' => 'Products not found with same name.'
'no-result-found' => 'Products not found with same name.',
'bundle-items' => 'Bundle Items'
],
'attributes' => [
'title' => 'الصفات',

View File

@ -416,7 +416,8 @@ return [
'grouped-products' => 'Grouped Products',
'search-products' => 'Search Products',
'no-result-found' => 'Products not found with same name.',
'channel' => 'Channels'
'channel' => 'Channels',
'bundle-items' => 'Bundle Items'
],
'attributes' => [

View File

@ -398,7 +398,8 @@ return [
'related-products' => 'محصولات مرتبط',
'product-search-hint' => 'شروع به تایپ نام محصول کنید',
'no-result-found' => 'محصولاتی با همین نام یافت نشد',
'searching' => 'جست و جو ...'
'searching' => 'جست و جو ...',
'bundle-items' => 'Bundle Items'
],
'attributes' => [

View File

@ -392,7 +392,8 @@ return [
'searching' => 'Procurando ...',
'grouped-products' => 'Grouped Products',
'search-products' => 'Search Products',
'no-result-found' => 'Products not found with same name.'
'no-result-found' => 'Products not found with same name.',
'bundle-items' => 'Bundle Items'
],
'attributes' => [
'title' => 'Atributos',

View File

@ -0,0 +1,11 @@
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.bundle.before', ['product' => $product]) !!}
<accordian :title="'{{ __('admin::app.catalog.products.bundle-items') }}'" :active="true">
<div slot="body">
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.bundle.controls.before', ['product' => $product]) !!}
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.bundle.controls.after', ['product' => $product]) !!}
</div>
</accordian>

View File

@ -11,7 +11,6 @@ use Webkul\Checkout\Models\CartItem;
use Webkul\Checkout\Models\CartPayment;
use Webkul\Customer\Repositories\WishlistRepository;
use Webkul\Customer\Repositories\CustomerAddressRepository;
use Webkul\Product\Helpers\Price;
use Illuminate\Support\Facades\Event;
/**
@ -72,11 +71,6 @@ class Cart {
*/
protected $customerAddressRepository;
/**
* Product price helper instance
*/
protected $price;
/**
* Create a new controller instance.
*
@ -98,8 +92,7 @@ class Cart {
ProductRepository $productRepository,
TaxCategoryRepository $taxCategoryRepository,
WishlistRepository $wishlistRepository,
CustomerAddressRepository $customerAddressRepository,
Price $price
CustomerAddressRepository $customerAddressRepository
)
{
$this->cartRepository = $cartRepository;
@ -115,8 +108,6 @@ class Cart {
$this->wishlistRepository = $wishlistRepository;
$this->customerAddressRepository = $customerAddressRepository;
$this->price = $price;
}
/**
@ -242,7 +233,9 @@ class Cart {
throw new \Exception(trans('shop::app.checkout.cart.quantity.illegal'));
}
if ($item->product->isStockable() && ! $item->product->haveSufficientQuantity($quantity))
$item->quantity = $quantity;
if (! $this->isItemHaveQuantity($item))
throw new \Exception(trans('shop::app.checkout.cart.quantity.inventory_warning'));
Event::fire('checkout.cart.update.before', $item);
@ -343,9 +336,9 @@ class Cart {
if (! $cartItem->product->getTypeInstance()->compareOptions($cartItem->additional, $guestCartItem->additional))
continue;
$newQuantity = $cartItem->quantity + $guestCartItem->quantity;
$cartItem->quantity = $newQuantity = $cartItem->quantity + $guestCartItem->quantity;
if ($cartItem->product->isStockable() && ! $cartItem->product->haveSufficientQuantity($newQuantity)) {
if ($this->isItemHaveQuantity($cartItem)) {
$this->cartItemRepository->delete($guestCartItem->id);
continue;
@ -667,7 +660,7 @@ class Cart {
if ($item->product_flat->getTypeInstance()->getMinimalPrice($item) == $item->price)
continue;
$price = ! is_null($item->custom_price) ? $item->custom_price : $this->price->getMinimalPrice($productFlat);
$price = ! is_null($item->custom_price) ? $item->custom_price : $productFlat->getTypeInstance()->getMinimalPrice();
$this->cartItemRepository->update([
'price' => core()->convertPrice($price),

View File

@ -109,7 +109,7 @@ class Cart extends Model implements CartContract
*/
public function haveStockableItems()
{
foreach ($this->all_items as $item) {
foreach ($this->items as $item) {
if ($item->product->isStockable())
return true;
}

View File

@ -30,5 +30,11 @@ return [
'name' => 'Downloadable',
'class' => 'Webkul\Product\Type\Downloadable',
'sort' => 5
],
'bundle' => [
'key' => 'bundle',
'name' => 'Bundle',
'class' => 'Webkul\Product\Type\Bundle',
'sort' => 6
]
];

View File

@ -0,0 +1,7 @@
<?php
namespace Webkul\Product\Contracts;
interface ProductBundleOption
{
}

View File

@ -0,0 +1,7 @@
<?php
namespace Webkul\Product\Contracts;
interface ProductBundleOptionTranslation
{
}

View File

@ -0,0 +1,7 @@
<?php
namespace Webkul\Product\Contracts;
interface ProductBundleProduct
{
}

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductBundleOptionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('product_bundle_options', function (Blueprint $table) {
$table->increments('id');
$table->string('type');
$table->boolean('is_required')->default(1);
$table->integer('sort_order')->default(0);
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('product_bundle_options');
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductBundleOptionTranslationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('product_bundle_option_translations', function (Blueprint $table) {
$table->increments('id');
$table->string('locale');
$table->text('label')->nullable();
$table->integer('product_bundle_option_id')->unsigned();
$table->unique(['product_bundle_option_id', 'locale'], 'product_bundle_option_translations_option_id_locale_unique');
$table->foreign('product_bundle_option_id', 'product_bundle_option_translations_option_id_foreign')->references('id')->on('product_bundle_options')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('product_bundle_option_translations');
}
}

View File

@ -0,0 +1,38 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductBundleProductsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('product_bundle_products', function (Blueprint $table) {
$table->increments('id');
$table->integer('qty')->default(0);
$table->integer('sort_order')->default(0);
$table->integer('product_bundle_option_id')->unsigned();
$table->foreign('product_bundle_option_id')->references('id')->on('product_bundle_options')->onDelete('cascade');
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('product_bundle_products');
}
}

View File

@ -3,8 +3,6 @@
namespace Webkul\Product\Helpers;
use Webkul\Attribute\Repositories\AttributeOptionRepository as AttributeOption;
use Webkul\Product\Helpers\ProductImage;
use Webkul\Product\Helpers\Price;
use Webkul\Product\Models\Product;
use Webkul\Product\Models\ProductAttributeValue;
@ -30,32 +28,21 @@ class ConfigurableOption extends AbstractProduct
*/
protected $productImage;
/**
* Price object
*
* @var array
*/
protected $price;
/**
* Create a new controller instance.
*
* @param Webkul\Attribute\Repositories\AttributeOptionRepository $attributeOption
* @param Webkul\Product\Helpers\ProductImage $productImage
* @param Webkul\Product\Helpers\Price $price
* @return void
*/
public function __construct(
AttributeOption $attributeOption,
ProductImage $productImage,
Price $price
ProductImage $productImage
)
{
$this->attributeOption = $attributeOption;
$this->productImage = $productImage;
$this->price = $price;
}
/**
@ -94,8 +81,8 @@ class ConfigurableOption extends AbstractProduct
'attributes' => $this->getAttributesData($product, $options),
'index' => isset($options['index']) ? $options['index'] : [],
'regular_price' => [
'formated_price' => core()->currency($this->price->getMinimalPrice($product)),
'price' => $this->price->getMinimalPrice($product)
'formated_price' => core()->currency($product->getTypeInstance()->getMinimalPrice()),
'price' => $product->getTypeInstance()->getMinimalPrice()
],
'variant_prices' => $this->getVariantPrices($product),
'variant_images' => $this->getVariantImages($product),
@ -237,8 +224,8 @@ class ConfigurableOption extends AbstractProduct
'price' => $variant->price
],
'final_price' => [
'formated_price' => core()->currency($this->price->getMinimalPrice($variant)),
'price' => $this->price->getMinimalPrice($variant)
'formated_price' => core()->currency($variant->getTypeInstance()->getMinimalPrice()),
'price' => $variant->getTypeInstance()->getMinimalPrice()
]
];
}

View File

@ -1,32 +0,0 @@
<?php
namespace Webkul\Product\Helpers;
use Webkul\Product\Repositories\ProductGroupedProductRepository;
/**
* Grouped Product Helper
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class GroupedProduct extends AbstractProduct
{
/**
* ProductGroupedProductRepository object
*
* @var object
*/
protected $productGroupedProductRepository;
/**
* Create a new helper instance.
*
* @param Webkul\Product\Repositories\ProductGroupedProductRepository $productGroupedProductRepository
* @return void
*/
public function __construct(ProductGroupedProductRepository $productGroupedProductRepository)
{
$this->productGroupedProductRepository = $productGroupedProductRepository;
}
}

View File

@ -1,137 +0,0 @@
<?php
namespace Webkul\Product\Helpers;
use Webkul\Attribute\Repositories\AttributeRepository as Attribute;
use Webkul\Product\Models\ProductAttributeValue;
use Webkul\Product\Models\Product;
use Webkul\Product\Models\ProductFlat;
/**
* Price Helper
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class Price extends AbstractProduct
{
/**
* AttributeRepository object
*
* @var array
*/
protected $attribute;
/**
* Create a new controller instance.
*
* @param Webkul\Attribute\Repositories\AttributeRepository $attribute
* @return void
*/
public function __construct(Attribute $attribute)
{
$this->attribute = $attribute;
}
/**
* Returns the product's minimal price
*
* @param Product $product
* @return float
*/
public function getMinimalPrice($product)
{
static $price = [];
if(array_key_exists($product->id, $price))
return $price[$product->id];
if ($product->type == 'configurable') {
return $price[$product->id] = $this->getVariantMinPrice($product);
} else {
if ($this->haveSpecialPrice($product)) {
return $price[$product->id] = $product->special_price;
}
return $price[$product->id] = $product->price;
}
}
/**
* Returns the product's minimal price
*
* @param Product $product
* @return float
*/
public function getVariantMinPrice($product)
{
static $price = [];
$finalPrice = [];
if (array_key_exists($product->id, $price))
return $price[$product->id];
if ($product instanceof ProductFlat) {
$productId = $product->product_id;
} else {
$productId = $product->id;
}
$qb = ProductFlat::join('products', 'product_flat.product_id', '=', 'products.id')
->where('products.parent_id', $productId);
$result = $qb
->distinct()
->selectRaw('IF( product_flat.special_price_from IS NOT NULL
AND product_flat.special_price_to IS NOT NULL , IF( NOW( ) >= product_flat.special_price_from
AND NOW( ) <= product_flat.special_price_to, IF( product_flat.special_price IS NULL OR product_flat.special_price = 0 , product_flat.price, LEAST( product_flat.special_price, product_flat.price ) ) , product_flat.price ) , IF( product_flat.special_price_from IS NULL , IF( product_flat.special_price_to IS NULL , IF( product_flat.special_price IS NULL OR product_flat.special_price = 0 , product_flat.price, LEAST( product_flat.special_price, product_flat.price ) ) , IF( NOW( ) <= product_flat.special_price_to, IF( product_flat.special_price IS NULL OR product_flat.special_price = 0 , product_flat.price, LEAST( product_flat.special_price, product_flat.price ) ) , product_flat.price ) ) , IF( product_flat.special_price_to IS NULL , IF( NOW( ) >= product_flat.special_price_from, IF( product_flat.special_price IS NULL OR product_flat.special_price = 0 , product_flat.price, LEAST( product_flat.special_price, product_flat.price ) ) , product_flat.price ) , product_flat.price ) ) ) AS final_price')
->where('product_flat.channel', core()->getCurrentChannelCode())
->where('product_flat.locale', app()->getLocale())
->get();
foreach ($result as $price) {
$finalPrice[] = $price->final_price;
}
if (empty($finalPrice))
return $price[$product->id] = 0;
return $price[$product->id] = min($finalPrice);
}
/**
* Returns the product's minimal price
*
* @param Product $product
* @return float
*/
public function getSpecialPrice($product)
{
static $price = [];
if(array_key_exists($product->id, $price))
return $price[$product->id];
if ($this->haveSpecialPrice($product)) {
return $price[$product->id] = $product->special_price;
} else {
return $price[$product->id] = $product->price;
}
}
/**
* @param Product $product
* @return boolean
*/
public function haveSpecialPrice($product)
{
if (is_null($product->special_price) || ! (float) $product->special_price)
return false;
if (core()->isChannelDateInInterval($product->special_price_from, $product->special_price_to))
return true;
return false;
}
}

View File

@ -229,11 +229,7 @@ class ProductFlat
}
}
if ($product->type == 'configurable' && $attribute->code == 'price') {
$productFlat->{$attribute->code} = app('Webkul\Product\Helpers\Price')->getVariantMinPrice($product);
} else {
$productFlat->{$attribute->code} = $productAttributeValue[ProductAttributeValue::$attributeTypeFields[$attribute->type]];
}
$productFlat->{$attribute->code} = $productAttributeValue[ProductAttributeValue::$attributeTypeFields[$attribute->type]];
if ($attribute->type == 'select') {
$attributeOption = $this->attributeOptionRepository->find($product->{$attribute->code});

View File

@ -0,0 +1,23 @@
<?php
namespace Webkul\Product\Models;
use Webkul\Core\Eloquent\TranslatableModel;
use Webkul\Product\Contracts\ProductBundleOption as ProductBundleOptionContract;
class ProductBundleOption extends TranslatableModel implements ProductBundleOptionContract
{
public $timestamps = false;
public $translatedAttributes = ['label'];
protected $fillable = ['type', 'is_required', 'sort_order', 'product_id'];
/**
* Get the product that owns the image.
*/
public function product()
{
return $this->belongsTo(ProductProxy::modelClass());
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Webkul\Product\Models;
use Konekt\Concord\Proxies\ModelProxy;
class ProductBundleOptionProxy extends ModelProxy
{
}

View File

@ -0,0 +1,13 @@
<?php
namespace Webkul\Product\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Product\Contracts\ProductBundleOptionTranslation as ProductBundleOptionTranslationContract;
class ProductBundleOptionTranslation extends Model implements ProductBundleOptionTranslationContract
{
public $timestamps = false;
protected $fillable = ['label'];
}

View File

@ -0,0 +1,10 @@
<?php
namespace Webkul\Product\Models;
use Konekt\Concord\Proxies\ModelProxy;
class ProductBundleOptionTranslationProxy extends ModelProxy
{
}

View File

@ -0,0 +1,21 @@
<?php
namespace Webkul\Product\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Product\Contracts\ProductBundleProduct as ProductBundleProductContract;
class ProductBundleProduct extends Model implements ProductBundleProductContract
{
public $timestamps = false;
protected $fillable = ['qty', 'sort_order', 'product_bundle_option_id', 'product_id'];
/**
* Get the product that owns the image.
*/
public function product()
{
return $this->belongsTo(ProductProxy::modelClass());
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace Webkul\Product\Models;
use Konekt\Concord\Proxies\ModelProxy;
class ProductBundleProductProxy extends ModelProxy
{
}

View File

@ -7,9 +7,9 @@ use Webkul\Product\Contracts\ProductGroupedProduct as ProductGroupedProductContr
class ProductGroupedProduct extends Model implements ProductGroupedProductContract
{
protected $fillable = ['qty', 'sort_order', 'product_id', 'associated_product_id'];
public $timestamps = false;
protected $fillable = ['qty', 'sort_order', 'product_id', 'associated_product_id'];
/**
* Get the product that owns the image.

View File

@ -17,6 +17,9 @@ class ModuleServiceProvider extends BaseModuleServiceProvider
\Webkul\Product\Models\ProductSalableInventory::class,
\Webkul\Product\Models\ProductDownloadableSample::class,
\Webkul\Product\Models\ProductDownloadableLink::class,
\Webkul\Product\Models\ProductGroupedProduct::class
\Webkul\Product\Models\ProductGroupedProduct::class,
\Webkul\Product\Models\ProductBundleOption::class,
\Webkul\Product\Models\ProductBundleOptionTranslation::class,
\Webkul\Product\Models\ProductBundleProduct::class,
];
}

View File

@ -0,0 +1,19 @@
<?php
namespace Webkul\Product\Repositories;
use Webkul\Core\Eloquent\Repository;
/**
* ProductBundleOption Repository
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class ProductBundleOptionRepository extends Repository
{
public function model()
{
return 'Webkul\Product\Contracts\ProductBundleOption';
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Webkul\Product\Repositories;
use Webkul\Core\Eloquent\Repository;
/**
* ProductBundleProduct Repository
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class ProductBundleProductRepository extends Repository
{
public function model()
{
return 'Webkul\Product\Contracts\ProductBundleProduct';
}
}

View File

@ -2,15 +2,14 @@
namespace Webkul\Product\Type;
use Illuminate\Support\Facades\Storage;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Product\Repositories\ProductRepository;
use Webkul\Product\Repositories\ProductAttributeValueRepository;
use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Models\ProductAttributeValue;
use Webkul\Product\Helpers\Price;
use Webkul\Product\Helpers\ProductImage;
use Illuminate\Support\Facades\Storage;
use Cart;
/**
@ -56,13 +55,6 @@ abstract class AbstractType
*/
protected $productImageRepository;
/**
* Product price helper instance
*
* @var Price
*/
protected $priceHelper;
/**
* Product Image helper instance
*
@ -85,7 +77,6 @@ abstract class AbstractType
* @param Webkul\Product\Repositories\ProductAttributeValueRepository $attributeValueRepository
* @param Webkul\Product\Repositories\ProductInventoryRepository $productInventoryRepository
* @param Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param Webkul\Product\Helpers\Price $priceHelper
* @param Webkul\Product\Helpers\ProductImage $productImageHelper
* @return void
*/
@ -95,7 +86,6 @@ abstract class AbstractType
ProductAttributeValueRepository $attributeValueRepository,
ProductInventoryRepository $productInventoryRepository,
ProductImageRepository $productImageRepository,
Price $priceHelper,
ProductImage $productImageHelper
)
{
@ -109,8 +99,6 @@ abstract class AbstractType
$this->productImageRepository = $productImageRepository;
$this->priceHelper = $priceHelper;
$this->productImageHelper = $productImageHelper;
}
@ -225,7 +213,7 @@ abstract class AbstractType
*/
public function isStockable()
{
return false;
return true;
}
/**
@ -236,6 +224,16 @@ abstract class AbstractType
{
return true;
}
/**
* Return true if this product can have inventory
*
* @return boolean
*/
public function showQuantityBox()
{
return false;
}
/**
* @param CartItem $cartItem
@ -294,6 +292,71 @@ abstract class AbstractType
return [];
}
/**
* Get product minimal price
*
* @return float
*/
public function getMinimalPrice()
{
if ($this->haveSpecialPrice())
return $this->product->special_price;
return $this->product->price;
}
/**
* Get product maximam price
*
* @return float
*/
public function getMaximamPrice()
{
return $this->product->price;
}
/**
* Returns the product's minimal price
*
* @return float
*/
public function getSpecialPrice()
{
return $this->haveSpecialPrice() ? $this->product->special_price : $this->product->price;
}
/**
* @return boolean
*/
public function haveSpecialPrice()
{
if (is_null($this->product->special_price) || ! (float) $this->product->special_price)
return false;
if (core()->isChannelDateInInterval($this->product->special_price_from, $this->product->special_price_to))
return true;
return false;
}
/**
* Get product minimal price
*
* @return string
*/
public function getPriceHtml()
{
if ($this->haveSpecialPrice()) {
$html = '<div class="sticker sale">' . trans('shop::app.products.sale') . '</div>'
. '<span class="regular-price">' . core()->currency($this->product->price) . '</span>'
. '<span class="special-price">' . core()->currency($this->getSpecialPrice()) . '</span>';
} else {
$html = '<span>' . core()->currency($this->product->price) . '</span>';
}
return $html;
}
/**
* Add product. Returns error message if can't prepare product.
*
@ -307,7 +370,7 @@ abstract class AbstractType
if ($this->isStockable() && ! $this->haveSufficientQuantity($data['quantity']))
return trans('shop::app.checkout.cart.quantity.inventory_warning');
$price = $this->priceHelper->getMinimalPrice($this->product);
$price = $this->getMinimalPrice();
$products = [
[
@ -398,15 +461,4 @@ abstract class AbstractType
{
return $this->productImageHelper->getProductBaseImage($item->product);
}
/**
* Get product base image
*
* @param CartItem $item
* @return float
*/
public function getMinimalPrice($item)
{
return $this->priceHelper->getMinimalPrice($item->product);
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Webkul\Product\Type;
/**
* Class Bundle.
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class Bundle extends AbstractType
{
/**
* Skip attribute for Bundle product type
*
* @var array
*/
protected $skipAttributes = ['price', 'cost', 'special_price', 'special_price_from', 'special_price_to', 'width', 'height', 'depth', 'weight'];
/**
* These blade files will be included in product edit page
*
* @var array
*/
protected $additionalViews = [
'admin::catalog.products.accordians.images',
'admin::catalog.products.accordians.categories',
'admin::catalog.products.accordians.bundle-items',
'admin::catalog.products.accordians.product-links'
];
}

View File

@ -3,6 +3,7 @@
namespace Webkul\Product\Type;
use Webkul\Product\Models\ProductAttributeValue;
use Webkul\Product\Models\ProductFlat;
/**
* Class Configurable.
@ -263,6 +264,16 @@ class Configurable extends AbstractType
{
return $cartItem->child->product->getTypeInstance()->haveSufficientQuantity($cartItem->quantity);
}
/**
* Return true if this product can have inventory
*
* @return boolean
*/
public function showQuantityBox()
{
return true;
}
/**
* Returns validation rules
@ -293,6 +304,64 @@ class Configurable extends AbstractType
return false;
}
/**
* Get product minimal price
*
* @return float
*/
public function getMinimalPrice()
{
$minPrices = [];
$result = ProductFlat::join('products', 'product_flat.product_id', '=', 'products.id')
->distinct()
->where('products.parent_id', $this->product->id)
->selectRaw('IF( product_flat.special_price_from IS NOT NULL
AND product_flat.special_price_to IS NOT NULL , IF( NOW( ) >= product_flat.special_price_from
AND NOW( ) <= product_flat.special_price_to, IF( product_flat.special_price IS NULL OR product_flat.special_price = 0 , product_flat.price, LEAST( product_flat.special_price, product_flat.price ) ) , product_flat.price ) , IF( product_flat.special_price_from IS NULL , IF( product_flat.special_price_to IS NULL , IF( product_flat.special_price IS NULL OR product_flat.special_price = 0 , product_flat.price, LEAST( product_flat.special_price, product_flat.price ) ) , IF( NOW( ) <= product_flat.special_price_to, IF( product_flat.special_price IS NULL OR product_flat.special_price = 0 , product_flat.price, LEAST( product_flat.special_price, product_flat.price ) ) , product_flat.price ) ) , IF( product_flat.special_price_to IS NULL , IF( NOW( ) >= product_flat.special_price_from, IF( product_flat.special_price IS NULL OR product_flat.special_price = 0 , product_flat.price, LEAST( product_flat.special_price, product_flat.price ) ) , product_flat.price ) , product_flat.price ) ) ) AS min_price')
->where('product_flat.channel', core()->getCurrentChannelCode())
->where('product_flat.locale', app()->getLocale())
->get();
foreach ($result as $price) {
$minPrices[] = $price->min_price;
}
if (empty($minPrices))
return 0;
return min($minPrices);
}
/**
* Get product maximam price
*
* @return float
*/
public function getMaximamPrice()
{
$productFlat = ProductFlat::join('products', 'product_flat.product_id', '=', 'products.id')
->distinct()
->where('products.parent_id', $this->product->id)
->selectRaw('MAX(product_flat.price) AS max_price')
->where('product_flat.channel', core()->getCurrentChannelCode())
->where('product_flat.locale', app()->getLocale())
->first();
return $productFlat ? $productFlat->max_price : 0;
}
/**
* Get product minimal price
*
* @return string
*/
public function getPriceHtml()
{
return '<span class="price-label">' . trans('shop::app.products.price-label') . '</span>'
. '<span class="final-price">' . core()->currency($this->getMinimalPrice()) . '</span>';
}
/**
* Add product. Returns error message if can't prepare product.
*
@ -311,7 +380,7 @@ class Configurable extends AbstractType
if (! $childProduct->haveSufficientQuantity($data['quantity']))
return trans('shop::app.checkout.cart.quantity.inventory_warning');
$price = $this->priceHelper->getMinimalPrice($childProduct);
$price = $this->getMinimalPrice();
$products = [
[
@ -419,15 +488,4 @@ class Configurable extends AbstractType
return $this->productImageHelper->getProductBaseImage($product);
}
/**
* Get product base image
*
* @param CartItem $item
* @return array
*/
public function getMinimalPrice($item)
{
return $this->priceHelper->getMinimalPrice($item->child->product);
}
}

View File

@ -10,7 +10,6 @@ use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductDownloadableLinkRepository;
use Webkul\Product\Repositories\ProductDownloadableSampleRepository;
use Webkul\Product\Models\ProductAttributeValue;
use Webkul\Product\Helpers\Price;
use Webkul\Product\Helpers\ProductImage;
use Webkul\Checkout\Models\CartItem;
@ -71,13 +70,6 @@ class Downloadable extends AbstractType
*/
protected $productDownloadableSampleRepository;
/**
* Product price helper instance
*
* @var Price
*/
protected $priceHelper;
/**
* Product Image helper instance
*
@ -114,7 +106,6 @@ class Downloadable extends AbstractType
* @param Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param Webkul\Product\Repositories\ProductDownloadableLinkRepository $productDownloadableLinkRepository
* @param Webkul\Product\Repositories\ProductDownloadableSampleRepository $productDownloadableSampleRepository
* @param Webkul\Product\Helpers\Price $priceHelper
* @param Webkul\Product\Helpers\ProductImage $productImageHelper
* @return void
*/
@ -126,7 +117,6 @@ class Downloadable extends AbstractType
productImageRepository $productImageRepository,
ProductDownloadableLinkRepository $productDownloadableLinkRepository,
ProductDownloadableSampleRepository $productDownloadableSampleRepository,
Price $priceHelper,
ProductImage $productImageHelper
)
{
@ -136,7 +126,6 @@ class Downloadable extends AbstractType
$attributeValueRepository,
$productInventoryRepository,
$productImageRepository,
$priceHelper,
$productImageHelper
);
@ -180,6 +169,16 @@ class Downloadable extends AbstractType
return false;
}
/**
* Return true if this product can have inventory
*
* @return boolean
*/
public function isStockable()
{
return false;
}
/**
* Returns validation rules
*

View File

@ -8,9 +8,9 @@ use Webkul\Product\Repositories\ProductAttributeValueRepository;
use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductGroupedProductRepository;
use Webkul\Product\Models\ProductAttributeValue;
use Webkul\Product\Helpers\Price;
use Webkul\Product\Helpers\ProductImage;
use Webkul\Product\Models\ProductAttributeValue;
use Webkul\Product\Models\ProductFlat;
/**
* Class Grouped.
@ -62,13 +62,6 @@ class Grouped extends AbstractType
*/
protected $productGroupedProductRepository;
/**
* Product price helper instance
*
* @var Price
*/
protected $priceHelper;
/**
* Product Image helper instance
*
@ -104,7 +97,6 @@ class Grouped extends AbstractType
* @param Webkul\Product\Repositories\ProductInventoryRepository $productInventoryRepository
* @param Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param Webkul\Product\Repositories\ProductGroupedProductRepository $productGroupedProductRepository
* @param Webkul\Product\Helpers\Price $priceHelper
* @param Webkul\Product\Helpers\ProductImage $productImageHelper
* @return void
*/
@ -115,7 +107,6 @@ class Grouped extends AbstractType
ProductInventoryRepository $productInventoryRepository,
ProductImageRepository $productImageRepository,
ProductGroupedProductRepository $productGroupedProductRepository,
Price $priceHelper,
ProductImage $productImageHelper
)
{
@ -125,7 +116,6 @@ class Grouped extends AbstractType
$attributeValueRepository,
$productInventoryRepository,
$productImageRepository,
$priceHelper,
$productImageHelper
);
@ -149,14 +139,42 @@ class Grouped extends AbstractType
}
/**
* Returns validation rules
* Get product minimal price
*
* @return array
* @return float
*/
public function getTypeValidationRules()
public function getMinimalPrice()
{
return [
];
$minPrices = [];
$result = $this->product->grouped_products()
->join('product_flat', 'product_grouped_products.associated_product_id', '=', 'product_flat.product_id')
->selectRaw('IF( product_flat.special_price_from IS NOT NULL
AND product_flat.special_price_to IS NOT NULL , IF( NOW( ) >= product_flat.special_price_from
AND NOW( ) <= product_flat.special_price_to, IF( product_flat.special_price IS NULL OR product_flat.special_price = 0 , product_flat.price, LEAST( product_flat.special_price, product_flat.price ) ) , product_flat.price ) , IF( product_flat.special_price_from IS NULL , IF( product_flat.special_price_to IS NULL , IF( product_flat.special_price IS NULL OR product_flat.special_price = 0 , product_flat.price, LEAST( product_flat.special_price, product_flat.price ) ) , IF( NOW( ) <= product_flat.special_price_to, IF( product_flat.special_price IS NULL OR product_flat.special_price = 0 , product_flat.price, LEAST( product_flat.special_price, product_flat.price ) ) , product_flat.price ) ) , IF( product_flat.special_price_to IS NULL , IF( NOW( ) >= product_flat.special_price_from, IF( product_flat.special_price IS NULL OR product_flat.special_price = 0 , product_flat.price, LEAST( product_flat.special_price, product_flat.price ) ) , product_flat.price ) , product_flat.price ) ) ) AS min_price')
->where('product_flat.channel', core()->getCurrentChannelCode())
->where('product_flat.locale', app()->getLocale())
->get();
foreach ($result as $price) {
$minPrices[] = $price->min_price;
}
if (empty($minPrices))
return 0;
return min($minPrices);
}
/**
* Get product minimal price
*
* @return string
*/
public function getPriceHtml()
{
return '<span class="price-label">' . trans('shop::app.products.starting-at') . '</span>'
. '<span class="final-price">' . core()->currency($this->getMinimalPrice()) . '</span>';
}
/**

View File

@ -45,16 +45,6 @@ class Simple extends AbstractType
return false;
}
/**
* Return true if this product can have inventory
*
* @return boolean
*/
public function isStockable()
{
return true;
}
/**
* @param integer $qty
*
@ -64,6 +54,16 @@ class Simple extends AbstractType
{
return $qty <= $this->totalQuantity() ? true : (core()->getConfigData('catalog.inventory.stock_options.backorders') ? true : false);
}
/**
* Return true if this product can have inventory
*
* @return boolean
*/
public function showQuantityBox()
{
return true;
}
/**
* @return integer

View File

@ -27,4 +27,14 @@ class Virtual extends AbstractType
'admin::catalog.products.accordians.categories',
'admin::catalog.products.accordians.product-links'
];
/**
* Return true if this product can have inventory
*
* @return boolean
*/
public function isStockable()
{
return false;
}
}

View File

@ -154,7 +154,7 @@ class Order extends Model implements OrderContract
*/
public function haveStockableItems()
{
foreach ($this->all_items as $item) {
foreach ($this->items as $item) {
if ($item->getTypeInstance()->isStockable())
return true;
}

View File

@ -47,7 +47,7 @@ class OrderItem extends Model implements OrderItemContract
*/
public function canShip()
{
if ($this->type != 'configurable' && ! $this->isStockable())
if (! $this->isStockable())
return false;
if ($this->qty_to_ship > 0)
@ -61,7 +61,7 @@ class OrderItem extends Model implements OrderItemContract
*/
public function getQtyToShipAttribute()
{
if ($this->type != 'configurable' && ! $this->isStockable())
if (! $this->isStockable())
return 0;
return $this->qty_ordered - $this->qty_shipped - $this->qty_refunded - $this->qty_canceled;

View File

@ -181,7 +181,7 @@ class OrderRepository extends Repository
$totalQtyOrdered += $item->qty_ordered;
$totalQtyInvoiced += $item->qty_invoiced;
if ($item->type != 'configurable' && ! $item->isStockable()) {
if (! $item->isStockable()) {
$totalQtyShipped += $item->qty_ordered;
} else {
$totalQtyShipped += $item->qty_shipped;

View File

@ -44,7 +44,7 @@ class FlatRate extends AbstractShipping
if ($this->getConfigData('type') == 'per_unit') {
foreach ($cart->items as $item) {
if ($item->type == 'configurable' || $item->type == 'configurable') {
if ($item->product->getTypeInstance()->isStockable()) {
$object->price += core()->convertPrice($this->getConfigData('default_rate')) * $item->quantity;
$object->base_price += $this->getConfigData('default_rate') * $item->quantity;
}

View File

@ -192,6 +192,7 @@ input {
.price-label {
font-size: 14px;
font-weight: 400;
margin-right: 5px;
}
.regular-price {
@ -341,6 +342,7 @@ input {
.product-card {
position: relative;
padding: 15px;
.product-image {
max-height: 350px;
@ -421,13 +423,10 @@ input {
}
.product-card:hover {
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
-webkit-box-shadow: 0px 2px 16px 4px rgba(40, 44, 63, 0.07);
-moz-box-shadow: 0px 2px 16px 4px rgba(40, 44, 63, 0.07);
box-shadow: 0px 2px 16px 4px rgba(40, 44, 63, 0.07);
padding: 10px;
transition: .3s;
outline: 1px solid $outline-hvr;
outline: 1px solid #eaeaec;
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
-webkit-box-shadow: 0 2px 16px 4px rgba(40, 44, 63, .07);
box-shadow: 0 2px 16px 4px rgba(40, 44, 63, .07)
}
@media only screen and (max-width: 580px) {
@ -2786,6 +2785,8 @@ section.review {
}
.product-price {
margin-top: 10px;
.pro-price {
color: $disc-price;
}

View File

@ -323,7 +323,8 @@ return [
'in-stock' => 'في الأسهم',
'out-of-stock' => 'خارج الأسهم',
'view-all' => 'عرض الكل',
'less-quantity' => 'Quantity can not be less than one.'
'less-quantity' => 'Quantity can not be less than one.',
'starting-at' => 'Starting at'
],
'wishlist' => [

View File

@ -353,7 +353,8 @@ return [
'links' => 'Links',
'sample' => 'Sample',
'name' => 'Name',
'qty' => 'Qty'
'qty' => 'Qty',
'starting-at' => 'Starting at'
],
'wishlist' => [

View File

@ -333,7 +333,8 @@ return [
'out-of-stock' => 'تمام شده',
'view-all' => 'مشاهده همه',
'select-above-options' => 'لطفا ابتدا گزینه های بالا را انتخاب کنید',
'less-quantity' => 'کمیت نمی تواند کمتر از یک باشد.'
'less-quantity' => 'کمیت نمی تواند کمتر از یک باشد.',
'starting-at' => 'Starting at'
],
'wishlist' => [

View File

@ -328,7 +328,8 @@ return [
'out-of-stock' => 'Fora de Estoque',
'view-all' => 'Ver Tudo',
'select-above-options' => 'Por favor, selecione as opções acima primeiro.',
'less-quantity' => 'Quantity can not be less than one.'
'less-quantity' => 'Quantity can not be less than one.',
'starting-at' => 'Starting at'
],
'wishlist' => [

View File

@ -1,25 +1,7 @@
{!! view_render_event('bagisto.shop.products.price.before', ['product' => $product]) !!}
<div class="product-price">
@inject ('priceHelper', 'Webkul\Product\Helpers\Price')
@if ($product->type == 'configurable')
<span class="price-label">{{ __('shop::app.products.price-label') }}</span>
<span class="final-price">{{ core()->currency($priceHelper->getMinimalPrice($product)) }}</span>
@else
@if ($priceHelper->haveSpecialPrice($product))
<div class="sticker sale">
{{ __('shop::app.products.sale') }}
</div>
<span class="regular-price">{{ core()->currency($product->price) }}</span>
<span class="special-price">{{ core()->currency($priceHelper->getSpecialPrice($product)) }}</span>
@else
<span>{{ core()->currency($product->price) }}</span>
@endif
@endif
{!! $product->getTypeInstance()->getPriceHtml() !!}
</div>
{!! view_render_event('bagisto.shop.products.price.after', ['product' => $product]) !!}

View File

@ -1,13 +0,0 @@
<div class="product-price mt-10">
@inject ('priceHelper', 'Webkul\Product\Helpers\Price')
@if ($product->type == 'configurable')
<span class="pro-price">{{ core()->currency($priceHelper->getMinimalPrice($product)) }}</span>
@else
@if ($priceHelper->haveSpecialPrice($product))
<span class="pro-price">{{ core()->currency($priceHelper->getSpecialPrice($product)) }}</span>
@else
<span class="pro-price">{{ core()->currency($product->price) }}</span>
@endif
@endif
</div>

View File

@ -26,7 +26,7 @@
</a>
</div>
@include('shop::products.review-price')
@include('shop::products.price')
</div>

View File

@ -10,8 +10,8 @@
<div class="review-layouter">
@inject ('productImageHelper', 'Webkul\Product\Helpers\ProductImage')
@inject ('reviewHelper', 'Webkul\Product\Helpers\Review')
@inject ('priceHelper', 'Webkul\Product\Helpers\Price')
<?php $productBaseImage = $productImageHelper->getProductBaseImage($product); ?>
@ -29,15 +29,10 @@
</div>
<div class="product-price mt-10">
@inject ('priceHelper', 'Webkul\Product\Helpers\Price')
@if ($product->type == 'configurable')
<span class="pro-price">{{ core()->currency($priceHelper->getMinimalPrice($product)) }}</span>
@if ($product->getTypeInstance()->haveSpecialPrice())
<span class="pro-price">{{ core()->currency($product->getTypeInstance()->getSpecialPrice()) }}</span>
@else
@if ($priceHelper->haveSpecialPrice($product))
<span class="pro-price">{{ core()->currency($priceHelper->getSpecialPrice($product)) }}</span>
@else
<span class="pro-price">{{ core()->currency($product->price) }}</span>
@endif
<span class="pro-price">{{ core()->currency($product->price) }}</span>
@endif
</div>
</div>

View File

@ -47,7 +47,7 @@
{!! view_render_event('bagisto.shop.products.view.quantity.before', ['product' => $product]) !!}
@if ($product->type == 'configurable' || $product->isStockable())
@if ($product->getTypeInstance()->showQuantityBox())
<div class="quantity control-group" :class="[errors.has('quantity') ? 'has-error' : '']">
<label class="required">{{ __('shop::app.products.quantity') }}</label>