add the ability to copy an configurable product
This commit is contained in:
parent
a9b79d7f4d
commit
f91533f47b
|
|
@ -4,12 +4,15 @@
|
|||
.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;
|
||||
}
|
||||
|
|
@ -48,17 +51,31 @@
|
|||
@parent
|
||||
|
||||
<script type="text/x-template" id="variant-form-template">
|
||||
<form method="POST" action="{{ route('admin.catalog.products.store') }}" data-vv-scope="add-variant-form" @submit.prevent="addVariant('add-variant-form')">
|
||||
<form method="POST" action="{{ route('admin.catalog.products.store') }}"
|
||||
data-vv-scope="add-variant-form" @submit.prevent="addVariant('add-variant-form')">
|
||||
|
||||
<div class="page-content">
|
||||
<div class="form-container">
|
||||
|
||||
<div v-for='(attribute, index) in super_attributes' class="control-group" :class="[errors.has('add-variant-form.' + attribute.code) ? 'has-error' : '']">
|
||||
<label :for="attribute.code" class="required">@{{ attribute.admin_name }}</label>
|
||||
<select v-validate="'required'" v-model="variant[attribute.code]" class="control" :id="attribute.code" :name="attribute.code" :data-vv-as="'"' + attribute.admin_name + '"'">
|
||||
<option v-for='(option, index) in attribute.options' :value="option.id">@{{ option.admin_name }}</option>
|
||||
<div v-for='(attribute, index) in super_attributes' class="control-group"
|
||||
:class="[errors.has('add-variant-form.' + attribute.code) ? 'has-error' : '']"
|
||||
>
|
||||
<label :for="attribute.code" class="required">@{{ attribute.admin_name
|
||||
}}</label>
|
||||
<select
|
||||
v-validate="'required'"
|
||||
v-model="variant[attribute.code]"
|
||||
class="control"
|
||||
:id="attribute.code"
|
||||
:name="attribute.code"
|
||||
:data-vv-as="'"' + attribute.admin_name + '"'"
|
||||
>
|
||||
<option v-for='(option, index) in attribute.options' :value="option.id">
|
||||
@{{ option.admin_name }}
|
||||
</option>
|
||||
</select>
|
||||
<span class="control-error" v-if="errors.has('add-variant-form.' + attribute.code)">@{{ errors.first('add-variant-form.' + attribute.code) }}</span>
|
||||
<span class="control-error"
|
||||
v-if="errors.has('add-variant-form.' + attribute.code)">@{{ errors.first('add-variant-form.' + attribute.code) }}</span>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-lg btn-primary">
|
||||
|
|
@ -76,25 +93,28 @@
|
|||
<table>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sku">{{ __('admin::app.catalog.products.sku') }}</th>
|
||||
<th>{{ __('admin::app.catalog.products.name') }}</th>
|
||||
<tr>
|
||||
<th class="sku">{{ __('admin::app.catalog.products.sku') }}</th>
|
||||
<th>{{ __('admin::app.catalog.products.name') }}</th>
|
||||
|
||||
@foreach ($product->super_attributes as $attribute)
|
||||
<th class="{{ $attribute->code }}" style="width: 150px">{{ $attribute->admin_name }}</th>
|
||||
@endforeach
|
||||
@foreach ($product->super_attributes as $attribute)
|
||||
<th class="{{ $attribute->code }}"
|
||||
style="width: 150px">{{ $attribute->admin_name }}</th>
|
||||
@endforeach
|
||||
|
||||
<th class="qty">{{ __('admin::app.catalog.products.qty') }}</th>
|
||||
<th class="price">{{ __('admin::app.catalog.products.price') }}</th>
|
||||
<th class="weight">{{ __('admin::app.catalog.products.weight') }}</th>
|
||||
<th class="status">{{ __('admin::app.catalog.products.status') }}</th>
|
||||
<th class="actions"></th>
|
||||
</tr>
|
||||
<th class="qty">{{ __('admin::app.catalog.products.qty') }}</th>
|
||||
<th class="price">{{ __('admin::app.catalog.products.price') }}</th>
|
||||
<th class="weight">{{ __('admin::app.catalog.products.weight') }}</th>
|
||||
<th class="status">{{ __('admin::app.catalog.products.status') }}</th>
|
||||
<th class="actions"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
<variant-item v-for='(variant, index) in variants' :variant="variant" :key="index" :index="variant.id" @onRemoveVariant="removeVariant($event)"></variant-item>
|
||||
<variant-item v-for='(variant, index) in variants' :variant="variant" :key="index"
|
||||
:index="variant.id"
|
||||
@onRemoveVariant="removeVariant($event)"></variant-item>
|
||||
|
||||
</tbody>
|
||||
|
||||
|
|
@ -105,23 +125,32 @@
|
|||
<script type="text/x-template" id="variant-item-template">
|
||||
<tr>
|
||||
<td>
|
||||
<div class="control-group" :class="[errors.has(variantInputName + '[sku]') ? 'has-error' : '']">
|
||||
<input type="text" v-validate="'required'" v-model="variant.sku" :name="[variantInputName + '[sku]']" class="control" data-vv-as=""{{ __('admin::app.catalog.products.sku') }}"" v-slugify/>
|
||||
<div class="control-group"
|
||||
:class="[errors.has(variantInputName + '[sku]') ? 'has-error' : '']">
|
||||
<input type="text" v-validate="'required'" v-model="variant.sku"
|
||||
:name="[variantInputName + '[sku]']" class="control"
|
||||
data-vv-as=""{{ __('admin::app.catalog.products.sku') }}""
|
||||
v-slugify/>
|
||||
<span class="control-error" v-if="errors.has(variantInputName + '[sku]')">@{{ errors.first(variantInputName + '[sku]') }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="control-group" :class="[errors.has(variantInputName + '[name]') ? 'has-error' : '']">
|
||||
<input type="text" v-validate="'required'" v-model="variant.name" :name="[variantInputName + '[name]']" class="control" data-vv-as=""{{ __('admin::app.catalog.products.name') }}""/>
|
||||
<div class="control-group"
|
||||
:class="[errors.has(variantInputName + '[name]') ? 'has-error' : '']">
|
||||
<input type="text" v-validate="'required'" v-model="variant.name"
|
||||
:name="[variantInputName + '[name]']" class="control"
|
||||
data-vv-as=""{{ __('admin::app.catalog.products.name') }}""/>
|
||||
<span class="control-error" v-if="errors.has(variantInputName + '[name]')">@{{ errors.first(variantInputName + '[name]') }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td v-for='(attribute, index) in superAttributes'>
|
||||
<div class="control-group">
|
||||
<input type="hidden" :name="[variantInputName + '[' + attribute.code + ']']" :value="variant[attribute.code]"/>
|
||||
<input type="text" class="control" :value="optionName(variant[attribute.code])" readonly/>
|
||||
<input type="hidden" :name="[variantInputName + '[' + attribute.code + ']']"
|
||||
:value="variant[attribute.code]"/>
|
||||
<input type="text" class="control" :value="optionName(variant[attribute.code])"
|
||||
readonly/>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
|
@ -135,10 +164,16 @@
|
|||
<div class="dropdown-container">
|
||||
<ul>
|
||||
<li v-for='(inventorySource, index) in inventorySources'>
|
||||
<div class="control-group" :class="[errors.has(variantInputName + '[inventories][' + inventorySource.id + ']') ? 'has-error' : '']">
|
||||
<div class="control-group"
|
||||
:class="[errors.has(variantInputName + '[inventories][' + inventorySource.id + ']') ? 'has-error' : '']">
|
||||
<label>@{{ inventorySource.name }}</label>
|
||||
<input type="text" v-validate="'numeric|min:0'" :name="[variantInputName + '[inventories][' + inventorySource.id + ']']" v-model="inventories[inventorySource.id]" class="control" v-on:keyup="updateTotalQty()" :data-vv-as="'"' + inventorySource.name + '"'"/>
|
||||
<span class="control-error" v-if="errors.has(variantInputName + '[inventories][' + inventorySource.id + ']')">@{{ errors.first(variantInputName + '[inventories][' + inventorySource.id + ']') }}</span>
|
||||
<input type="text" v-validate="'numeric|min:0'"
|
||||
:name="[variantInputName + '[inventories][' + inventorySource.id + ']']"
|
||||
v-model="inventories[inventorySource.id]" class="control"
|
||||
v-on:keyup="updateTotalQty()"
|
||||
:data-vv-as="'"' + inventorySource.name + '"'"/>
|
||||
<span class="control-error"
|
||||
v-if="errors.has(variantInputName + '[inventories][' + inventorySource.id + ']')">@{{ errors.first(variantInputName + '[inventories][' + inventorySource.id + ']') }}</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -147,30 +182,42 @@
|
|||
</td>
|
||||
|
||||
<td>
|
||||
<div class="control-group" :class="[errors.has(variantInputName + '[price]') ? 'has-error' : '']">
|
||||
<input type="number" v-validate="'required'" v-model="variant.price" :name="[variantInputName + '[price]']" class="control" data-vv-as=""{{ __('admin::app.catalog.products.price') }}"" step="any"/>
|
||||
<div class="control-group"
|
||||
:class="[errors.has(variantInputName + '[price]') ? 'has-error' : '']">
|
||||
<input type="number" v-validate="'required'" v-model="variant.price"
|
||||
:name="[variantInputName + '[price]']" class="control"
|
||||
data-vv-as=""{{ __('admin::app.catalog.products.price') }}""
|
||||
step="any"/>
|
||||
<span class="control-error" v-if="errors.has(variantInputName + '[price]')">@{{ errors.first(variantInputName + '[price]') }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="control-group" :class="[errors.has(variantInputName + '[weight]') ? 'has-error' : '']">
|
||||
<input type="number" v-validate="'required'" v-model="variant.weight" :name="[variantInputName + '[weight]']" class="control" data-vv-as=""{{ __('admin::app.catalog.products.weight') }}"" step="any"/>
|
||||
<div class="control-group"
|
||||
:class="[errors.has(variantInputName + '[weight]') ? 'has-error' : '']">
|
||||
<input type="number" v-validate="'required'" v-model="variant.weight"
|
||||
:name="[variantInputName + '[weight]']" class="control"
|
||||
data-vv-as=""{{ __('admin::app.catalog.products.weight') }}""
|
||||
step="any"/>
|
||||
<span class="control-error" v-if="errors.has(variantInputName + '[weight]')">@{{ errors.first(variantInputName + '[weight]') }}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<div class="control-group">
|
||||
<select type="text" v-model="variant.status" :name="[variantInputName + '[status]']" class="control">
|
||||
<option value="1" :selected="variant.status">{{ __('admin::app.catalog.products.enabled') }}</option>
|
||||
<option value="0" :selected="!variant.status">{{ __('admin::app.catalog.products.disabled') }}</option>
|
||||
<select type="text" v-model="variant.status"
|
||||
:name="[variantInputName + '[status]']" class="control">
|
||||
<option value="1"
|
||||
:selected="variant.status">{{ __('admin::app.catalog.products.enabled') }}</option>
|
||||
<option value="0"
|
||||
:selected="!variant.status">{{ __('admin::app.catalog.products.disabled') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td class="actions">
|
||||
<a :href="['{{ route('admin.catalog.products.index') }}/edit/' + variant.id]"><i class="icon pencil-lg-icon"></i></a>
|
||||
<a :href="['{{ route('admin.catalog.products.index') }}/edit/' + variant.id]"><i
|
||||
class="icon pencil-lg-icon"></i></a>
|
||||
<i class="icon remove-icon" @click="removeVariant()"></i>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -190,7 +237,7 @@
|
|||
|
||||
Vue.component('variant-form', {
|
||||
|
||||
data: function() {
|
||||
data: function () {
|
||||
return {
|
||||
variant: {},
|
||||
super_attributes: super_attributes
|
||||
|
|
@ -209,7 +256,7 @@
|
|||
if (result) {
|
||||
var this_this = this;
|
||||
|
||||
var filteredVariants = variants.filter(function(variant) {
|
||||
var filteredVariants = variants.filter(function (variant) {
|
||||
var matchCount = 0;
|
||||
|
||||
for (var key in this_this.variant) {
|
||||
|
|
@ -224,7 +271,10 @@
|
|||
if (filteredVariants.length) {
|
||||
this.$parent.closeModal();
|
||||
|
||||
window.flashMessages = [{'type': 'alert-error', 'message': "{{ __('admin::app.catalog.products.variant-already-exist-message') }}" }];
|
||||
window.flashMessages = [{
|
||||
'type': 'alert-error',
|
||||
'message': "{{ __('admin::app.catalog.products.variant-already-exist-message') }}"
|
||||
}];
|
||||
|
||||
this.$root.addFlashMessages()
|
||||
} else {
|
||||
|
|
@ -234,12 +284,12 @@
|
|||
}
|
||||
|
||||
variants.push(Object.assign({
|
||||
sku: '{{ $product->sku }}' + '-variant-' + optionIds.join('-'),
|
||||
name: '',
|
||||
price: 0,
|
||||
weight: 0,
|
||||
status: 1
|
||||
}, this.variant));
|
||||
sku: '{{ $product->sku }}' + '-variant-' + optionIds.join('-'),
|
||||
name: '',
|
||||
price: 0,
|
||||
weight: 0,
|
||||
status: 1
|
||||
}, this.variant));
|
||||
|
||||
this.resetModel();
|
||||
|
||||
|
|
@ -252,7 +302,7 @@
|
|||
resetModel: function () {
|
||||
var this_this = this;
|
||||
|
||||
this.super_attributes.forEach(function(attribute) {
|
||||
this.super_attributes.forEach(function (attribute) {
|
||||
this_this.variant[attribute.code] = '';
|
||||
})
|
||||
}
|
||||
|
|
@ -265,7 +315,7 @@
|
|||
|
||||
inject: ['$validator'],
|
||||
|
||||
data: function() {
|
||||
data: function () {
|
||||
return {
|
||||
variants: variants,
|
||||
|
||||
|
|
@ -285,7 +335,10 @@
|
|||
var inventories = [];
|
||||
|
||||
for (var inventorySourceId in variant['inventories']) {
|
||||
inventories.push({'qty': variant['inventories'][inventorySourceId], 'inventory_source_id': inventorySourceId})
|
||||
inventories.push({
|
||||
'qty': variant['inventories'][inventorySourceId],
|
||||
'inventory_source_id': inventorySourceId
|
||||
})
|
||||
}
|
||||
|
||||
variant['inventories'] = inventories;
|
||||
|
|
@ -299,7 +352,10 @@
|
|||
variants[index][code] = [];
|
||||
|
||||
for (var inventorySourceId in variant[code]) {
|
||||
variants[index][code].push({'qty': variant[code][inventorySourceId], 'inventory_source_id': inventorySourceId})
|
||||
variants[index][code].push({
|
||||
'qty': variant[code][inventorySourceId],
|
||||
'inventory_source_id': inventorySourceId
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -310,7 +366,7 @@
|
|||
},
|
||||
|
||||
methods: {
|
||||
removeVariant: function(variant) {
|
||||
removeVariant: function (variant) {
|
||||
let index = this.variants.indexOf(variant)
|
||||
|
||||
this.variants.splice(index, 1)
|
||||
|
|
@ -327,7 +383,7 @@
|
|||
|
||||
inject: ['$validator'],
|
||||
|
||||
data: function() {
|
||||
data: function () {
|
||||
return {
|
||||
inventorySources: @json($inventorySources),
|
||||
inventories: {},
|
||||
|
|
@ -339,7 +395,7 @@
|
|||
created: function () {
|
||||
var this_this = this;
|
||||
|
||||
this.inventorySources.forEach(function(inventorySource) {
|
||||
this.inventorySources.forEach(function (inventorySource) {
|
||||
this_this.inventories[inventorySource.id] = this_this.sourceInventoryQty(inventorySource.id)
|
||||
this_this.totalQty += parseInt(this_this.inventories[inventorySource.id]);
|
||||
})
|
||||
|
|
@ -362,8 +418,8 @@
|
|||
optionName: function (optionId) {
|
||||
var optionName = '';
|
||||
|
||||
this.superAttributes.forEach(function(attribute) {
|
||||
attribute.options.forEach(function(option) {
|
||||
this.superAttributes.forEach(function (attribute) {
|
||||
attribute.options.forEach(function (option) {
|
||||
if (optionId == option.id) {
|
||||
optionName = option.admin_name;
|
||||
}
|
||||
|
|
@ -374,10 +430,10 @@
|
|||
},
|
||||
|
||||
sourceInventoryQty: function (inventorySourceId) {
|
||||
if (! Array.isArray(this.variant.inventories))
|
||||
if (!Array.isArray(this.variant.inventories))
|
||||
return 0;
|
||||
|
||||
var inventories = this.variant.inventories.filter(function(inventory) {
|
||||
var inventories = this.variant.inventories.filter(function (inventory) {
|
||||
return inventorySourceId === parseInt(inventory.inventory_source_id);
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Webkul\Product\Models;
|
||||
|
||||
use Exception;
|
||||
use Webkul\Product\Type\AbstractType;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Webkul\Category\Models\CategoryProxy;
|
||||
use Webkul\Attribute\Models\AttributeProxy;
|
||||
|
|
@ -240,6 +242,12 @@ class Product extends Model implements ProductContract
|
|||
|
||||
$this->typeInstance = app(config('product_types.' . $this->type . '.class'));
|
||||
|
||||
if (! $this->typeInstance instanceof AbstractType) {
|
||||
throw new Exception(
|
||||
"Please ensure the product type '{$this->type}' is configured in your application."
|
||||
);
|
||||
}
|
||||
|
||||
$this->typeInstance->setProduct($this);
|
||||
|
||||
return $this->typeInstance;
|
||||
|
|
|
|||
|
|
@ -657,9 +657,23 @@ class ProductRepository extends Repository
|
|||
}
|
||||
}
|
||||
|
||||
if (! isset($attributesToSkip['super_attributes'])) {
|
||||
foreach ($originalProduct->super_attributes as $super_attribute) {
|
||||
$copiedProduct->super_attributes()->save($super_attribute);
|
||||
}
|
||||
}
|
||||
|
||||
if (! isset($attributesToSkip['bundle_options'])) {
|
||||
foreach ($originalProduct->bundle_options as $bundle_option) {
|
||||
$copiedProduct->bundle_options()->save($bundle_option);
|
||||
}
|
||||
}
|
||||
|
||||
if (! isset($attributesToSkip['variants'])) {
|
||||
foreach ($originalProduct->variants as $variant) {
|
||||
$this->copy($variant);
|
||||
$variant = $this->copy($variant);
|
||||
$variant->parent_id = $copiedProduct->id;
|
||||
$variant->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue