Merge pull request #3162 from bagisto/1.0

Added customer group price
This commit is contained in:
Jitendra Singh 2020-06-02 18:14:42 +05:30 committed by GitHub
commit 0b4b09b670
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 444 additions and 21 deletions

View File

@ -528,7 +528,12 @@ return [
'checkbox' => 'Checkbox',
'multiselect' => 'Multiselect',
'new-option' => 'New Option',
'is-default' => 'Is Default'
'is-default' => 'Is Default',
'customer-group' => 'Customer Group',
'add-group-price' => 'Add Customer Group Price',
'all-group' => 'All Groups',
'fixed' => 'Fixed',
'discount' => 'Discount',
],
'attributes' => [

View File

@ -0,0 +1,162 @@
@section('css')
@parent
<style>
.table th.price, .table th.weight {
width: 100px;
}
.table th.actions {
width: 85px;
}
.table td.actions .icon {
margin-top: 8px;
}
.table td.actions .icon.pencil-lg-icon {
margin-right: 10px;
}
</style>
@stop
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.customer_group_prices.before', ['product' => $product]) !!}
<customer-group-price-list></customer-group-price-list>
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.customer_group_prices.after', ['product' => $product]) !!}
@push('scripts')
@parent
<script type="text/x-template" id="customer-group-price-list-template">
<div>
<div class="table" style="margin-top: 20px; overflow-x: unset;">
<table>
<thead>
<tr>
<th>{{ __('admin::app.catalog.products.customer-group') }}</th>
<th>{{ __('admin::app.catalog.products.qty') }}</th>
<th>{{ __('admin::app.catalog.products.price') }}</th>
<th class="actions"></th>
</tr>
</thead>
<tbody>
<customer-group-price-item v-for='(customerGroupPrice, index) in customer_group_prices' :customer-group-price="customerGroupPrice" :key="index" :index="index" @onRemoveCustomerGroupPrice="removeCustomerGroupPrice($event)"></customer-group-price-item>
</tbody>
</table>
<button type="button" class="btn btn-lg btn-primary" style="margin-top: 20px" @click="addCustomerGroupPrice()">
{{ __('admin::app.catalog.products.add-group-price') }}
</button>
</div>
</div>
</script>
<script type="text/x-template" id="customer-group-price-item-template">
<tr>
<td>
<div class="control-group">
<select :name="[inputName + '[customer_group_id]']" v-model="customerGroupPrice.customer_group_id" class="control">
<option value="">{{ __('admin::app.catalog.products.all-group') }}</option>
@foreach (app('Webkul\Customer\Repositories\CustomerGroupRepository')->all() as $customerGroup)
<option value="{{ $customerGroup->id }}">{{ $customerGroup->name }}</option>
@endforeach
</select>
</div>
</td>
<td>
<div class="control-group" :class="[errors.has(inputName + '[qty]') ? 'has-error' : '']">
<input type="number" v-validate="'required|min_value:0'" :name="[inputName + '[qty]']" v-model="customerGroupPrice.qty" class="control" data-vv-as="&quot;{{ __('admin::app.catalog.products.qty') }}&quot;"/>
<span class="control-error" v-if="errors.has(inputName + '[qty]')">@{{ errors.first(inputName + '[qty]') }}</span>
</div>
</td>
<td>
<div class="control-group">
<select :name="[inputName + '[value_type]']" v-model="customerGroupPrice.value_type" class="control">
<option value="fixed">{{ __('admin::app.catalog.products.fixed') }}</option>
<option value="discount">{{ __('admin::app.catalog.products.discount') }}</option>
</select>
</div>
<div class="control-group">
<div class="control-group" :class="[errors.has(inputName + '[value]') ? 'has-error' : '']">
<input type="number" v-validate="'required|min_value:0'" :name="[inputName + '[value]']" v-model="customerGroupPrice.value" class="control" data-vv-as="&quot;{{ __('admin::app.catalog.products.value') }}&quot;"/>
<span class="control-error" v-if="errors.has(inputName + '[value]')">@{{ errors.first(inputName + '[value]') }}</span>
</div>
</div>
</td>
<td class="actions">
<i class="icon remove-icon" @click="removeCustomerGroupPrice()"></i>
</td>
</tr>
</script>
<script>
Vue.component('customer-group-price-list', {
template: '#customer-group-price-list-template',
inject: ['$validator'],
data: function() {
return {
customer_group_prices: @json($product->customer_group_prices()->get())
}
},
methods: {
addCustomerGroupPrice: function(item, key) {
this.customer_group_prices.push({
customer_group_id: '',
qty: 0,
value_type: 'fixed',
amount: 0
});
},
removeCustomerGroupPrice: function(customerGroupPrice) {
let index = this.customer_group_prices.indexOf(customerGroupPrice)
this.customer_group_prices.splice(index, 1)
}
}
});
Vue.component('customer-group-price-item', {
template: '#customer-group-price-item-template',
props: ['index', 'customerGroupPrice'],
inject: ['$validator'],
mounted: function() {
if (! this.customerGroupPrice['customer_group_id']) {
this.customerGroupPrice['customer_group_id'] = '';
}
},
computed: {
inputName: function () {
if (this.customerGroupPrice.id) {
return 'customer_group_prices[' + this.customerGroupPrice.id + ']';
}
return 'customer_group_prices[customer_group_price_' + this.index + ']';
}
},
methods: {
removeCustomerGroupPrice: function () {
this.$emit('onRemoveCustomerGroupPrice', this.customerGroupPrice)
}
}
});
</script>
@endpush

View File

@ -144,6 +144,12 @@
@endforeach
@if ($attributeGroup->name == 'Price')
@include ('admin::catalog.products.accordians.customer-group-price')
@endif
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.' . $attributeGroup->name . '.controls.after', ['product' => $product]) !!}
</div>
</accordian>

View File

@ -531,7 +531,7 @@ class Booking
*/
public function validateCartItem($item)
{
$price = $item->product->getTypeInstance()->getFinalPrice();
$price = $item->product->getTypeInstance()->getFinalPrice($item->quantity);
if ($price == $item->base_price) {
return;

View File

@ -119,7 +119,7 @@ class EventTicket extends Booking
*/
public function validateCartItem($item)
{
$price = $item->product->getTypeInstance()->getFinalPrice();
$price = $item->product->getTypeInstance()->getFinalPrice($item->quantity);
$bookingProduct = $this->bookingProductRepository->findOneByField('product_id', $item->product_id);

View File

@ -224,7 +224,7 @@ class RentalSlot extends Booking
*/
public function validateCartItem($item)
{
$price = $item->product->getTypeInstance()->getFinalPrice();
$price = $item->product->getTypeInstance()->getFinalPrice($item->quantity);
$bookingProduct = $this->bookingProductRepository->findOneByField('product_id', $item->product_id);

View File

@ -140,9 +140,11 @@ class CartRule
public static $cartRules;
public static $cartID;
};
if ($staticCartRules::$cartID === cart()->getCart()->id && $staticCartRules::$cartRules) {
return $staticCartRules::$cartRules;
}
$staticCartRules::$cartID = cart()->getCart()->id;
$customerGroupId = null;

View File

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

View File

@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateProductCustomerGroupPricesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('product_customer_group_prices', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('qty')->default(0);
$table->string('value_type');
$table->decimal('value', 12, 4)->default(0);
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->integer('customer_group_id')->nullble()->unsigned();
$table->foreign('customer_group_id')->references('id')->on('customer_groups')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('product_customer_group_prices');
}
}

View File

@ -178,6 +178,14 @@ class Product extends Model implements ProductContract
return $this->hasMany(ProductBundleOptionProxy::modelClass());
}
/**
* Get the product customer group prices that owns the product.
*/
public function customer_group_prices()
{
return $this->hasMany(ProductCustomerGroupPriceProxy::modelClass());
}
/**
* @param integer $qty
*

View File

@ -0,0 +1,35 @@
<?php
namespace Webkul\Product\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use Webkul\Product\Contracts\ProductCustomerGroupPrice as ProductCustomerGroupPriceContract;
use Webkul\Product\Models\CustomerGroup;
class ProductCustomerGroupPrice extends Model implements ProductCustomerGroupPriceContract
{
protected $fillable = [
'qty',
'value_type',
'value',
'product_id',
'customer_group_id',
];
/**
* Get the product that owns the customer group price.
*/
public function product()
{
return $this->belongsTo(ProductProxy::modelClass());
}
/**
* Get the product that owns the customer group price.
*/
public function customer_group()
{
return $this->belongsTo(CustomerGroupProxy::modelClass());
}
}

View File

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

View File

@ -21,5 +21,6 @@ class ModuleServiceProvider extends BaseModuleServiceProvider
\Webkul\Product\Models\ProductBundleOption::class,
\Webkul\Product\Models\ProductBundleOptionTranslation::class,
\Webkul\Product\Models\ProductBundleOptionProduct::class,
\Webkul\Product\Models\ProductCustomerGroupPrice::class,
];
}

View File

@ -0,0 +1,53 @@
<?php
namespace Webkul\Product\Repositories;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Http\UploadedFile;
use Webkul\Core\Eloquent\Repository;
class ProductCustomerGroupPriceRepository extends Repository
{
/**
* Specify Model class name
*
* @return mixed
*/
function model()
{
return 'Webkul\Product\Contracts\ProductCustomerGroupPrice';
}
/**
* @param array $data
* @param \Webkul\Product\Contracts\Product $product
* @return void
*/
public function saveCustomerGroupPrices(array $data, $product)
{
$previousCustomerGroupPriceIds = $product->customer_group_prices()->pluck('id');
if (isset($data['customer_group_prices'])) {
foreach ($data['customer_group_prices'] as $customerGroupPriceId => $row) {
$row['customer_group_id'] = $row['customer_group_id'] == '' ? null : $row['customer_group_id'];
if (Str::contains($customerGroupPriceId, 'customer_group_price_')) {
$this->create(array_merge([
'product_id' => $product->id,
], $row));
} else {
if (is_numeric($index = $previousCustomerGroupPriceIds->search($customerGroupPriceId))) {
$previousCustomerGroupPriceIds->forget($index);
}
$this->update($row, $customerGroupPriceId);
}
}
}
foreach ($previousCustomerGroupPriceIds as $customerGroupPriceId) {
$this->delete($customerGroupPriceId);
}
}
}

View File

@ -11,7 +11,7 @@ use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Models\ProductAttributeValue;
use Webkul\Product\Helpers\ProductImage;
use Cart;
use Webkul\Checkout\Facades\Cart;
abstract class AbstractType
{
@ -246,6 +246,8 @@ abstract class AbstractType
$this->productInventoryRepository->saveInventories($data, $product);
$this->productImageRepository->uploadImages($data, $product);
app('Webkul\Product\Repositories\ProductCustomerGroupPriceRepository')->saveCustomerGroupPrices($data, $product);
}
return $product;
@ -449,11 +451,12 @@ abstract class AbstractType
/**
* Get product minimal price
*
* @param int $qty
* @return float
*/
public function getMinimalPrice()
public function getMinimalPrice($qty = null)
{
if ($this->haveSpecialPrice()) {
if ($this->haveSpecialPrice($qty)) {
return $this->product->special_price;
}
@ -473,57 +476,148 @@ abstract class AbstractType
/**
* Get product minimal price
*
* @param int $qty
* @return float
*/
public function getFinalPrice()
public function getFinalPrice($qty = null)
{
return $this->getMinimalPrice();
return $this->getMinimalPrice($qty);
}
/**
* Returns the product's minimal price
*
* @param int $qty
* @return float
*/
public function getSpecialPrice()
public function getSpecialPrice($qty = null)
{
return $this->haveSpecialPrice() ? $this->product->special_price : $this->product->price;
return $this->haveSpecialPrice($qty) ? $this->product->special_price : $this->product->price;
}
/**
* @param int $qty
* @return bool
*/
public function haveSpecialPrice()
public function haveSpecialPrice($qty = null)
{
$customerGroupPrice = $this->getCustomerGroupPrice($this->product, $qty);
$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;
}
$haveSpecialPrice = false;
if (! (float) $this->product->special_price) {
if ($rulePrice && $rulePrice->price < $this->product->price) {
$this->product->special_price = $rulePrice->price;
return true;
$haveSpecialPrice = true;
}
} else {
if ($rulePrice && $rulePrice->price <= $this->product->special_price) {
$this->product->special_price = $rulePrice->price;
return true;
$haveSpecialPrice = true;
} else {
if (core()->isChannelDateInInterval($this->product->special_price_from, $this->product->special_price_to)) {
return true;
$haveSpecialPrice = true;
} elseif ($rulePrice) {
$this->product->special_price = $rulePrice->price;
return true;
$haveSpecialPrice = true;
}
}
}
return false;
if ($haveSpecialPrice) {
$this->product->special_price = min($this->product->special_price, $customerGroupPrice);
} else {
$this->product->special_price = $customerGroupPrice;
}
return true;
}
/**
* Get product group price
*
* @return float
*/
public function getCustomerGroupPrice($product, $qty)
{
if (is_null($qty)) {
$qty = 1;
}
$customerGroupId = null;
if (Cart::getCurrentCustomer()->check()) {
$customerGroupId = Cart::getCurrentCustomer()->user()->customer_group_id;
} else {
$customerGroupRepository = app('Webkul\Customer\Repositories\CustomerGroupRepository');
if ($customerGuestGroup = $customerGroupRepository->findOneByField('code', 'guest')) {
$customerGroupId = $customerGuestGroup->id;
}
}
$customerGroupPrices = $product->customer_group_prices()->where(function ($query) use ($customerGroupId) {
$query->where('customer_group_id', $customerGroupId)
->orWhereNull('customer_group_id');
}
)->get();
if (! $customerGroupPrices->count()) {
return $product->price;
}
$lastQty = 1;
$lastPrice = $product->price;
$lastCustomerGroupId = null;
foreach ($customerGroupPrices as $price) {
if ($price->customer_group_id != $customerGroupId && $price->customer_group_id) {
continue;
}
if ($qty < $price->qty) {
continue;
}
if ($price->qty < $lastQty) {
continue;
}
if ($price->qty == $lastQty
&& $lastCustomerGroupId != null
&& $price->customer_group_id == null
) {
continue;
}
if ($price->value < $lastPrice) {
if ($price->value_type == 'percentage') {
$lastPrice = $product->price * ($price->value / 100);
} else {
$lastPrice = $price->value;
}
$lastQty = $price->qty;
$lastCustomerGroupId = $price->customer_group_id;
}
}
return $lastPrice;
}
/**
@ -685,7 +779,7 @@ abstract class AbstractType
*/
public function validateCartItem($item)
{
$price = $item->product->getTypeInstance()->getFinalPrice();
$price = $item->product->getTypeInstance()->getFinalPrice($item->quantity);
if ($price == $item->base_price) {
return;

View File

@ -542,7 +542,7 @@ class Configurable extends AbstractType
*/
public function validateCartItem($item)
{
$price = $item->child->product->getTypeInstance()->getFinalPrice();
$price = $item->child->product->getTypeInstance()->getFinalPrice($item->quantity);
if ($price == $item->base_price) {
return;

View File

@ -235,7 +235,7 @@ class Downloadable extends AbstractType
*/
public function validateCartItem($item)
{
$price = $item->product->getTypeInstance()->getFinalPrice();
$price = $item->product->getTypeInstance()->getFinalPrice($item->quantity);
foreach ($item->product->downloadable_links as $link) {
if (! in_array($link->id, $item->additional['links'])) {