Cart rule inside promotions, various improvements and fixes. (#964)

* Some syntax issue fixes

* Adding discount things inside promotions in admin

* routes updated for creating discount rules

* fixed customer profile redirect parameter issue

* added cart and catalog rule routes to admin section

* migrations for discount almost to be finished, final revision left

* migrations for discounts complete

* catalog rule form underway

* catalog rule

* Made some dynamic form fields in catalog rule create form

* made some changes for custom validations used in ProductForm request class

* working on prepopulating the values fields in conditions

* need another revisit on discount rules tables and then prepare for actions

* removed discounts table

* catalog rule form changed to muliti attribute set for single condition

* new icons added for promotion and customer note. new action for customer added to take notes on the customers

* catalog and cart rule designs stable, now moving towards validations on client side and backend

* catalog rules migrations added

* catalog rule models added with contracts and proxies

* fixed customer group bug in customer registration controller

* fixed customer registration bug due to last fix

* fixed product card image not found issue

* catalog rule translations added

* Added migrations for cart rules and catalog rule products their contracts and remaining necessary files related to them.

* making cart attributes for cart rules

* Added more fields for cart rules

* working on conditions for cart rule form

* minor changes in migrations related to price rules

* currency and locale switcher now only available for a channel on storefront when more than on locale and currency are assigned to that channel

* part of conditions on the cart rule form added

* daily sync

* cart rate migrations updated

* Added select and multi select attributes options fetching with ajax on catalog rule form

* changed some migrations and data being populated at runtime inside catalog rule form

* catalog rule create complete, migrations changes, translations added

* catalog rule edit form complete

* catalog rule form complete, now moving towards catalog rule products

* added delete functionality for catalog rules

* added cart rule preferences for coupon codes

* cart rule submission problem due to repository issue

* Cart rule form and migrations complete

* Models and Repositories updated for cart rule usage

* base sync with master

* designing process to get suitable discount rules

* cart rule form completed

* added helpers in cart and discount to apply rules on cart at checkout or not

* cart rule coupon implementation in progress for discount coupon in shop checkout pages

* cart rules working

* added coupon box on checkout screen

* removed the conditions empty bug

* Nearing to completion of coupon based rules

* made some changes in cart rule coupon application on checkout related to new designs

* some bug fixes

* calculation for automatic cart rules complete

* non couponable cart rules implemented, now moving on to binding them on frontend

* some conditions improvements in couponable cart rules

* some bug fixes

* removed some bugs from summary blade for cart rule

* added the table for cart rule cart for managing rules with cart and removed various bug fixes

* some bug fixes

* Removed bugs and added coupon based cart rule removal functionality

* some bug fixes

* cart rule labels refactoring in midway

* Cart rule labels bug fixed

* removed margin classes from shop to UI

* Refactoring cart rule on front end

* added checks

* Discount rule implemented.

* cart rule bug fixes

* provision to remove the couponable and non couponable rule had been added

* Cart rule frontend work done

* altered some frontend variables on onepage checkout

* Altered cart rules to some extent
This commit is contained in:
Prashant Singh 2019-06-10 13:19:05 +05:30 committed by GitHub
parent b37ac65e13
commit 6da603530e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
139 changed files with 6641 additions and 555 deletions

View File

@ -1,6 +1,6 @@
APP_NAME=Laravel
APP_ENV=local
APP_VERSION=0.1.5
APP_VERSION=0.1.6
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost

View File

@ -27,6 +27,8 @@ a progressive Javascript framework.
**Bagisto is viable attempt to cut down your time, cost and workforce for building online stores or migrating from physical stores
to the ever demanding online world. Your business whether small or huge it suits all and very simple to set it up.**
**Read our documentation: [Bagisto Docs](https://devdocs.bagisto.com/)**
**We are also having a forum for any type of your concern, feature request discussions. Please visit: [Bagisto Forums](https://forums.bagisto.com/)**
# Visit our live [Demo](https://demo.bagisto.com)
@ -45,6 +47,7 @@ It packs in lots of demanding features that allows your business to scale in no
* Orders Management System.
* Customer Cart, Wishlist, Product Reviews.
* Simple and Configurable Products.
* Price rules (Discount) inbuilt.
* Check out [click here](https://bagisto.com/features/).
**For Developers**:
@ -137,6 +140,11 @@ Although, mailer environment variables are also required to be set up as **Bagis
##### On server:
Warning: Before going full on production mode we recommend you to install developer dependencies.
In order to do that, run the command below:
> composer install --no-dev
~~~
Open the specified entry point in your hosts file in browser or make entry in hosts file if not done.
~~~

View File

@ -39,6 +39,7 @@
"laravel/dusk": "^4.0",
"mockery/mockery": "^1.0",
"nunomaduro/collision": "^2.0",
"phploc/phploc": "^5.0@dev",
"phpunit/phpunit": "^7.0"
},
"replace": {
@ -57,7 +58,10 @@
"bagisto/laravel-shipping": "v0.1.0",
"bagisto/laravel-payment": "v0.1.0",
"bagisto/laravel-sales": "v0.1.0",
"bagisto/laravel-tax": "v0.1.0"
"bagisto/laravel-tax": "v0.1.0",
"bagisto/laravel-api": "v0.1.0",
"bagisto/laravel-paypal": "v0.1.0",
"bagisto/laravel-discount": "v0.1.0"
},
"autoload": {
"classmap": [
@ -84,7 +88,8 @@
"Webkul\\Paypal\\": "packages/Webkul/Paypal/src",
"Webkul\\Sales\\": "packages/Webkul/Sales/src",
"Webkul\\Tax\\": "packages/Webkul/Tax/src",
"Webkul\\API\\": "packages/Webkul/API"
"Webkul\\API\\": "packages/Webkul/API",
"Webkul\\Discount\\": "packages/Webkul/Discount/src"
}
},
"autoload-dev": {

View File

@ -138,7 +138,7 @@ return [
/*
Application Version
*/
'version' => env('APP_VERSION', '0.1.5'),
'version' => env('APP_VERSION', '0.1.6'),
/**
* Blacklisting attributes while debugging
@ -245,6 +245,7 @@ return [
Webkul\Sales\Providers\SalesServiceProvider::class,
Webkul\Tax\Providers\TaxServiceProvider::class,
Webkul\API\Providers\APIServiceProvider::class,
Webkul\Discount\Providers\DiscountServiceProvider::class
],
/*

View File

@ -17,6 +17,6 @@ return [
\Webkul\Inventory\Providers\ModuleServiceProvider::class,
\Webkul\Product\Providers\ModuleServiceProvider::class,\Webkul\Sales\Providers\ModuleServiceProvider::class,
\Webkul\Tax\Providers\ModuleServiceProvider::class,
\Webkul\User\Providers\ModuleServiceProvider::class
\Webkul\User\Providers\ModuleServiceProvider::class,\Webkul\Discount\Providers\ModuleServiceProvider::class
]
];

View File

@ -1,6 +1,4 @@
<?php
return [
];
?>
];

5
config/pricerules.php Normal file
View File

@ -0,0 +1,5 @@
<?php
return [
];

View File

@ -2,6 +2,4 @@
return [
];
?>
];

View File

@ -29,7 +29,7 @@ return [
|
*/
'lifetime' => env('SESSION_LIFETIME', 10),
'lifetime' => env('SESSION_LIFETIME', 30),
'expire_on_close' => true,

View File

@ -1,31 +0,0 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateDiscountsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('discounts', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('discounts');
}
}

View File

@ -1,31 +0,0 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateDiscountRulesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('discount_rules', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('discount_rules');
}
}

View File

@ -1,31 +0,0 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateDiscountChannelsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('discount_channels', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('discount_channels');
}
}

View File

@ -1,31 +0,0 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateDiscountCustomerGroupsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('discount_customer_group', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('discount_customer_group');
}
}

View File

@ -26,4 +26,4 @@
}
},
"minimum-stability": "dev"
}
}

View File

@ -176,5 +176,24 @@ return [
'route' => 'admin.tax-rates.index',
'sort' => 2,
'icon-class' => '',
], [
'key' => 'promotion',
'name' => 'admin::app.layouts.promotion',
'route' => 'admin.cart-rule.index',
'sort' => 7,
'icon-class' => 'promotion-icon',
], [
'key' => 'promotion.cart-rule',
'name' => 'admin::app.promotion.cart-rule',
'route' => 'admin.cart-rule.index',
'sort' => 1,
'icon-class' => '',
]
// , [
// 'key' => 'promotion.catalog-rule',
// 'name' => 'admin::app.promotion.catalog-rule',
// 'route' => 'admin.catalog-rule.index',
// 'sort' => 1,
// 'icon-class' => '',
// ]
];

View File

@ -0,0 +1,139 @@
<?php
namespace Webkul\Admin\DataGrids;
use Webkul\Ui\DataGrid\DataGrid;
use DB;
/**
* Cart Rule DataGrid class
*
* @author Prashant Singh <prashant.singh852@webkul.com> @prashant-webkul
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CartRuleDataGrid extends DataGrid
{
protected $index = 'id'; //the column that needs to be treated as index column
protected $sortOrder = 'desc'; //asc or desc
public function prepareQueryBuilder()
{
$queryBuilder = DB::table('cart_rules')
->select('id')
->addSelect('id', 'name', 'starts_from', 'ends_till', 'priority', 'usage_limit', 'per_customer', 'status', 'end_other_rules', 'is_guest', 'action_type');
$this->setQueryBuilder($queryBuilder);
}
public function addColumns()
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.datagrid.id'),
'type' => 'number',
'searchable' => false,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.datagrid.name'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'priority',
'label' => trans('admin::app.datagrid.priority'),
'type' => 'number',
'searchable' => false,
'sortable' => true,
'filterable' => true,
]);
$this->addColumn([
'index' => 'status',
'label' => trans('admin::app.datagrid.status'),
'type' => 'boolean',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'wrapper' => function($value) {
if ($value->status == 1)
return 'True';
else
return 'False';
}
]);
$this->addColumn([
'index' => 'is_guest',
'label' => trans('admin::app.datagrid.for-guest'),
'type' => 'boolean',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'wrapper' => function($value) {
if ($value->is_guest == 1)
return 'True';
else
return 'False';
}
]);
$this->addColumn([
'index' => 'end_other_rules',
'label' => 'End Other Rules',
'type' => 'boolean',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'wrapper' => function($value) {
if ($value->end_other_rules == 1)
return 'True';
else
return 'False';
}
]);
$this->addColumn([
'index' => 'action_type',
'label' => 'Action Type',
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true
]);
}
public function prepareActions()
{
$this->addAction([
'type' => 'Edit',
'method' => 'GET', //use post only for redirects only
'route' => 'admin.cart-rule.edit',
'icon' => 'icon pencil-lg-icon'
]);
$this->addAction([
'type' => 'Delete',
'method' => 'POST', //use post only for requests other than redirects
'route' => 'admin.cart-rule.delete',
'icon' => 'icon trash-icon'
]);
}
public function prepareMassActions()
{
// $this->addMassAction([
// 'type' => 'delete',
// 'action' => route('admin.catalog.attributes.massdelete'),
// 'label' => 'Delete',
// 'method' => 'DELETE'
// ]);
}
}

View File

@ -0,0 +1,149 @@
<?php
namespace Webkul\Admin\DataGrids;
use Webkul\Ui\DataGrid\DataGrid;
use DB;
/**
* Catalog Rule DataGrid class
*
* @author Prashant Singh <prashant.singh852@webkul.com> @prashant-webkul
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CatalogRuleDataGrid extends DataGrid
{
protected $index = 'id'; //the column that needs to be treated as index column
protected $sortOrder = 'desc'; //asc or desc
public function prepareQueryBuilder()
{
$queryBuilder = DB::table('catalog_rules')
->select('id')
->addSelect('id', 'name', 'starts_from', 'ends_till', 'priority', 'status', 'end_other_rules', 'action_type');
$this->setQueryBuilder($queryBuilder);
}
public function addColumns()
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.datagrid.id'),
'type' => 'number',
'searchable' => false,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.datagrid.name'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'starts_from',
'label' => trans('admin::app.datagrid.starts-from'),
'type' => 'date',
'searchable' => false,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'ends_till',
'label' => trans('admin::app.datagrid.ends-till'),
'type' => 'date',
'searchable' => false,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'priority',
'label' => trans('admin::app.datagrid.priority'),
'type' => 'number',
'searchable' => false,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'status',
'label' => trans('admin::app.datagrid.status'),
'type' => 'boolean',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'wrapper' => function ($value) {
if ($value->status == 1)
return 'True';
else
return 'False';
}
]);
$this->addColumn([
'index' => 'end_other_rules',
'label' => 'End Other Rules',
'type' => 'boolean',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'wrapper' => function ($value) {
if ($value->end_other_rules == 1)
return 'True';
else
return 'False';
}
]);
$this->addColumn([
'index' => 'action_type',
'label' => 'Action Type',
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'wrapper' => function ($value) {
foreach(config('pricerules.catalog.actions') as $key => $action) {
if ($value->action_type == $key) {
return trans($action);
}
}
}
]);
}
public function prepareActions()
{
$this->addAction([
'type' => 'Edit',
'method' => 'GET', //use post only for redirects only
'route' => 'admin.catalog-rule.edit',
'icon' => 'icon pencil-lg-icon'
]);
$this->addAction([
'type' => 'Delete',
'method' => 'POST', //use post only for requests other than redirects
'route' => 'admin.catalog-rule.delete',
'icon' => 'icon trash-icon'
]);
}
public function prepareMassActions()
{
// $this->addMassAction([
// 'type' => 'delete',
// 'action' => route('admin.catalog.attributes.massdelete'),
// 'label' => 'Delete',
// 'method' => 'DELETE'
// ]);
}
}

View File

@ -0,0 +1,96 @@
<?php
namespace Webkul\Admin\DataGrids;
use Webkul\Ui\DataGrid\DataGrid;
use DB;
/**
* Cart Rule DataGrid class
*
* @author Prashant Singh <prashant.singh852@webkul.com> @prashant-webkul
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CartRuleCouponsDataGrid extends DataGrid
{
protected $index = 'id'; //the column that needs to be treated as index column
protected $sortOrder = 'desc'; //asc or desc
public function prepareQueryBuilder()
{
$queryBuilder = DB::table('cart_rules')
->select('id')
->addSelect('id', 'code', 'limit', 'usage_per_customer', 'usage_throttle');
$this->setQueryBuilder($queryBuilder);
}
public function addColumns()
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.datagrid.id'),
'type' => 'number',
'searchable' => false,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'code',
'label' => trans('admin::app.datagrid.code'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'limit',
'label' => trans('admin::app.datagrid.limit'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'limit',
'label' => trans('admin::app.datagrid.limit'),
'type' => 'string',
'searchable' => false,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'usage_per_customer',
'label' => trans('admin::app.datagrid.usage-per-customer'),
'type' => 'boolean',
'searchable' => false,
'sortable' => true,
'filterable' => true,
'wrapper' => function($value) {
if ($value->end_other_rules == 1)
return 'true';
else
return 'false';
}
]);
}
public function prepareActions()
{
}
public function prepareMassActions()
{
// $this->addMassAction([
// 'type' => 'delete',
// 'action' => route('admin.catalog.attributes.massdelete'),
// 'label' => 'Delete',
// 'method' => 'DELETE'
// ]);
}
}

View File

@ -76,14 +76,24 @@ class CustomerDataGrid extends DataGrid
'type' => 'Edit',
'method' => 'GET', // use GET request only for redirect purposes
'route' => 'admin.customer.edit',
'icon' => 'icon pencil-lg-icon'
'icon' => 'icon pencil-lg-icon',
'title' => trans('admin::app.customers.customers.edit-help-title')
]);
$this->addAction([
'type' => 'Delete',
'method' => 'POST', // use GET request only for redirect purposes
'route' => 'admin.customer.delete',
'icon' => 'icon trash-icon'
'icon' => 'icon trash-icon',
'title' => trans('admin::app.customers.customers.delete-help-title')
]);
$this->addAction([
'type' => 'Add Note',
'method' => 'GET',
'route' => 'admin.customer.note.create',
'icon' => 'icon note-icon',
'title' => trans('admin::app.customers.note.help-title')
]);
}
}

View File

@ -186,4 +186,42 @@ class CustomerController extends Controller
return response()->json(['message' => false], 400);
}
/**
* To load the note taking screen for the customers
*
* @return View
*/
public function createNote($id)
{
$customer = $this->customer->find($id);
return view($this->_config['view'])->with('customer', $customer);
}
/**
* To store the response of the note in storage
*
* @return Redirect
*/
public function storeNote()
{
$this->validate(request(), [
'notes' => 'required|string'
]);
$customer = $this->customer->find(request()->input('_customer'));
$noteTaken = $customer->update([
'notes' => request()->input('notes')
]);
if ($noteTaken) {
session()->flash('success', 'Note taken');
} else {
session()->flash('error', 'Note cannot be taken');
}
return redirect()->route($this->_config['redirect']);
}
}

View File

@ -60,6 +60,14 @@ Route::group(['middleware' => ['web']], function () {
'view' => 'admin::customers.edit'
])->name('admin.customer.edit');
Route::get('customers/note/{id}', 'Webkul\Admin\Http\Controllers\Customer\CustomerController@createNote')->defaults('_config',[
'view' => 'admin::customers.note'
])->name('admin.customer.note.create');
Route::put('customers/note/{id}', 'Webkul\Admin\Http\Controllers\Customer\CustomerController@storeNote')->defaults('_config',[
'redirect' => 'admin.customer.index'
])->name('admin.customer.note.store');
Route::put('customers/edit/{id}', 'Webkul\Admin\Http\Controllers\Customer\CustomerController@update')->defaults('_config', [
'redirect' => 'admin.customer.index'
])->name('admin.customer.update');
@ -186,7 +194,6 @@ Route::group(['middleware' => ['web']], function () {
])->name('admin.sales.shipments.view');
});
// Catalog Routes
Route::prefix('catalog')->group(function () {
Route::get('/sync', 'Webkul\Product\Http\Controllers\ProductController@sync');
@ -609,6 +616,57 @@ Route::group(['middleware' => ['web']], function () {
//DataGrid Export
Route::post('admin/export', 'Webkul\Admin\Http\Controllers\ExportController@export')->name('admin.datagrid.export');
Route::prefix('promotion')->group(function () {
// Route::get('/catalog-rule', 'Webkul\Discount\Http\Controllers\CatalogRuleController@index')->defaults('_config', [
// 'view' => 'admin::promotions.catalog-rule.index'
// ])->name('admin.catalog-rule.index');
// Route::get('/catalog-rule/create', 'Webkul\Discount\Http\Controllers\CatalogRuleController@create')->defaults('_config', [
// 'view' => 'admin::promotions.catalog-rule.create'
// ])->name('admin.catalog-rule.create');
// Route::post('/catalog-rule/create', 'Webkul\Discount\Http\Controllers\CatalogRuleController@store')->defaults('_config', [
// 'redirect' => 'admin.catalog-rule.index'
// ])->name('admin.catalog-rule.store');
// Route::get('/catalog-rule/edit/{id}', 'Webkul\Discount\Http\Controllers\CatalogRuleController@edit')->defaults('_config', [
// 'view' => 'admin::promotions.catalog-rule.edit'
// ])->name('admin.catalog-rule.edit');
// Route::post('/catalog-rule/edit/{id}', 'Webkul\Discount\Http\Controllers\CatalogRuleController@update')->defaults('_config', [
// 'redirect' => 'admin.catalog-rule.index'
// ])->name('admin.catalog-rule.update');
// Route::get('/catalog-rule/apply', 'Webkul\Discount\Http\Controllers\CatalogRuleController@applyRules')->defaults('_config', [
// 'view' => 'admin::promotions.catalog-rule.index'
// ])->name('admin.catalog-rule.apply');
// Route::post('/catalog-rule/delete/{id}', 'Webkul\Discount\Http\Controllers\CatalogRuleController@destroy')->name('admin.catalog-rule.delete');
// Route::post('fetch/options', 'Webkul\Discount\Http\Controllers\CatalogRuleController@fetchAttributeOptions')->name('admin.catalog-rule.options');
Route::get('/cart-rule', 'Webkul\Discount\Http\Controllers\CartRuleController@index')->defaults('_config', [
'view' => 'admin::promotions.cart-rule.index'
])->name('admin.cart-rule.index');
Route::get('/cart-rule/create', 'Webkul\Discount\Http\Controllers\CartRuleController@create')->defaults('_config', [
'view' => 'admin::promotions.cart-rule.create'
])->name('admin.cart-rule.create');
Route::post('/cart-rule/store', 'Webkul\Discount\Http\Controllers\CartRuleController@store')->defaults('_config', [
'redirect' => 'admin.cart-rule.index'
])->name('admin.cart-rule.store');
Route::get('/cart-rule/edit/{id}', 'Webkul\Discount\Http\Controllers\CartRuleController@edit')->defaults('_config', [
'view' => 'admin::promotions.cart-rule.edit'
])->name('admin.cart-rule.edit');
Route::post('/cart-rule/update/{id}', 'Webkul\Discount\Http\Controllers\CartRuleController@update')->defaults('_config', [
'redirect' => 'admin.cart-rule.index'
])->name('admin.cart-rule.update');
Route::post('/cart-rule/delete/{id}', 'Webkul\Discount\Http\Controllers\CartRuleController@destroy')->name('admin.cart-rule.delete');
});
});
});
});
});

View File

@ -173,23 +173,6 @@ body {
height: 90%;
}
// .close-nav-aside {
// width: 100%;
// padding: 20px;
// text-align: center;
// border: 1px solid $border-color;
// .icon {
// height: 24px;
// width: 24px;
// }
// }
// .close-nav-aside:hover {
// background: white;
// cursor: pointer;
// }
a {
padding: 15px;
display: block;
@ -235,7 +218,7 @@ body {
width: 24px;
height: 24px;
cursor: pointer;
margin-top: 5px;
margin-top: -2px;
}
h1 {
@ -278,6 +261,54 @@ body {
}
}
.control-container {
border: 1px solid #c7c7c7;
padding: 10px;
width: 63%;
.title-bar {
display: inline-flex;
justify-content: space-between;
width: 100%;
.icon {
cursor: pointer;
}
.icon.cross-icon {
height: 24px;
width: 24px;
}
}
.control-group {
display: flex;
flex-direction: row;
align-content: center;
justify-content: flex-start;
padding: 10px;
border: 1px solid #c7c7c7;
border-radius: 2px;
margin-right: 15px;
margin-bottom: 0px;
}
.control-group::last-child {
margin-right: 0px;
}
}
.boolean-control-container {
display: flex;
flex-direction: row;
justify-content: flex-start;
width: 750px;
.control-group {
width: 200px;
}
}
.control-group {
label {
width: 70%;

View File

@ -39,7 +39,9 @@ return [
'sliders' => 'Sliders',
'taxes' => 'Taxes',
'tax-categories' => 'Tax Categories',
'tax-rates' => 'Tax Rates'
'tax-rates' => 'Tax Rates',
'promotion' => 'Promotions',
'discount' => 'Discount'
],
'acl' => [
@ -151,7 +153,12 @@ return [
'role' => 'Role',
'sub-total' => 'Sub Total',
'no-of-products' => 'Number of Products',
'attribute-family' => 'Attribute Family'
'attribute-family' => 'Attribute Family',
'starts-from' => 'Starts From',
'ends-till' => 'Ends Till',
'per-cust' => 'Per Customer',
'usage-throttle' => 'Usage Times',
'for-guest' => 'For Guest'
],
'account' => [
@ -732,6 +739,14 @@ return [
'is_user_defined' => 'User Defined',
'yes' => 'Yes'
],
'note' => [
'title' => 'Add Note',
'save-note' => 'Save Note',
'enter-note' => 'Enter Note',
'help-title' => 'Add Note On This Customer'
],
'customers' => [
'add-title' => 'Add Customer',
'edit-title' => 'Edit Customer',
@ -752,7 +767,10 @@ return [
'female' => 'Female',
'phone' => 'Phone',
'group-default' => 'Cannot delete the default group.',
'edit-help-title' => 'Edit Customer',
'delete-help-title' => 'Delete Customer'
],
'reviews' => [
'title' => 'Reviews',
'edit-title' => 'Edit Review',
@ -777,6 +795,85 @@ return [
]
],
'promotion' => [
'catalog-rule' => 'Catalog Rules',
'cart-rule' => 'Cart Rule',
'add-catalog-rule' => 'Add Catalog Rule',
'add-cart-rule' => 'Add Cart Rule',
'edit-cart-rule' => 'Edit Cart Rule',
'edit-catalog-rule' => 'Edit Catalog Rule',
'create-catalog-rule' => 'Create Catalog Rule',
'create-cart-rule' => 'Create Cart Rule',
'save-btn-title' => 'Create',
'edit-btn-title' => 'Edit',
'select-attr' => 'Select Attribute',
'select-attr-fam' => 'Select Attribute Family',
'select-cart-attr' => 'Select Cart Attribute',
'general-info' => [
'name' => 'Rule Name',
'description' => 'Description',
'starts-from' => 'Start',
'ends-till' => 'End',
'channels' => 'Channels',
'cust-groups' => 'Customer Groups',
'priority' => 'Priority',
'add-condition' => 'Add Conditions',
'disc_amt' => 'Discount Amount',
'disc_percent' => 'Discount Percentage',
'is-coupon' => 'Use Coupon',
'is-coupon-yes' => 'Yes',
'is-coupon-no' => 'No',
'uses-per-cust' => 'Uses per customer',
'all' => 'All',
'any' => 'Any',
'end-other-rules' => 'End other rules',
'status' => 'Is Active',
'all-conditions-true' => 'Assuming all conditions are true',
'assuming' => 'Assuming',
'any' => 'Any',
'all' => 'All',
'conditions' => 'Conditions',
'are' => 'are',
'true' => 'true',
'false' => 'false',
'limit' => 'Usage Limit',
'specific-coupon' => 'Specific Coupon(Check) / Auto Generated(Unheck)',
'free-shipping' => 'Free Shipping',
'is-guest' => 'For Guests',
'disc_qty' => 'Max. Quantity Allowed To Be Discounted',
],
'status' => [
'success' => 'Success! rule created',
'success-coupon' => 'Success! rule created along with coupon',
'failed' => 'Error! failed to create rule',
'update-success' => 'Success! rule updated',
'update-coupon-success' => 'Success! rule updated along with coupon',
'update-failed' => 'Error! cannot update',
'delete-success' => 'Sucess! rule deleted',
'delete-failed' => 'Error! cannot delete',
'coupon-applied' => 'Coupon used',
'coupon-failed' => 'Coupon failed to apply',
'no-coupon' => '* Coupon not applicable',
'coupon-removed' => 'Coupon removed successfully',
'coupon-remove-failed' => 'Coupon removal failed'
],
'catalog' => [
'name' => 'Name',
'description' => 'Description',
'apply-percent' => 'Apply as percentage',
'apply-fixed' => 'Apply as fixed amount',
'adjust-to-percent' => 'Adjust to percentage',
'adjust-to-value' => 'Adjust to discount value',
'condition-missing' => 'Please check conditons, some values might be missing'
],
'cart' => [
'buy-atleast' => 'Buy Atleast (N)',
'apply-to-shipping' => 'Apply to shipping'
]
],
'error' => [
'go-to-home' => 'GO TO HOME',
'in-maitainace' => 'In Maintenance',

View File

@ -677,6 +677,14 @@ return [
'is_user_defined' => 'Usuário Definido',
'yes' => 'Sim'
],
'note' => [
'title' => 'Add Note',
'save-note' => 'Save Note',
'enter-note' => 'Enter Note',
'help-title' => 'Add Note On This Customer'
],
'customers' => [
'add-title' => 'Add Cliente',
'edit-title' => 'Editar Cliente',

View File

@ -1,24 +0,0 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.customers.customers.title') }}
@stop
@section('content')
<div class="content">
<div class="page-header">
<div class="page-title">
<h1>{{ __('admin::app.customers.customers.title') }}</h1>
</div>
<div class="page-action">
<a href="{{ route('admin.customer.create') }}" class="btn btn-lg btn-primary">
{{ __('admin::app.customers.customers.add-title') }}
</a>
</div>
</div>
<div class="page-content">
<h1>Test the grid here</h1>
</div>
</div>
@endsection

View File

@ -22,7 +22,7 @@
{{ __('admin::app.catalog.products.related-products') }}
</label>
<input type="text" class="control" autocomplete="off" v-model="search_term[key]" placeholder="{{ __('admin::app.catalog.products.product-search-hint') }}" v-on:keyup="search(key)">
<input type="text" class="control" autocomplete="off" v-model="search_term[key]" placeholder="{{ __('admin::app.catalog.products.product-search-hint') }}" v-on:keyup="search(key)">
<div class="linked-product-search-result">
<ul>

View File

@ -21,9 +21,6 @@
{!! view_render_event('bagisto.admin.catalog.products.list.before') !!}
<div class="page-content">
{{-- @inject('product','Webkul\Admin\DataGrids\ProductDataGrid')
{!! $product->render() !!} --}}
@inject('products', 'Webkul\Admin\DataGrids\ProductDataGrid')
{!! $products->render() !!}
</div>

View File

@ -27,7 +27,7 @@
<div class="page-content">
@inject('customerGrid','Webkul\Admin\DataGrids\CustomerDataGrid')
{!! $customerGrid->render() !!}
</div>
</div>

View File

@ -0,0 +1,45 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.customers.note.title') }}
@stop
@section('content')
<div class="content">
<form method="POST" action="{{ route('admin.customer.note.store', $customer->id) }}">
<div class="page-header">
<div class="page-title">
<h1>
<i class="icon angle-left-icon back-link" onclick="history.length > 1 ? history.go(-1) : window.location = '{{ url('/admin/dashboard') }}';"></i>
{{ __('admin::app.customers.note.title') }}
</h1>
</div>
<div class="page-action">
<button type="submit" class="btn btn-lg btn-primary">
{{ __('admin::app.customers.note.save-note') }}
</button>
</div>
</div>
<div class="page-content">
<div class="form-container">
@csrf()
<input name="_method" type="hidden" value="PUT">
<input name="_customer" type="hidden" value="{{ $customer->id }}">
<div class="control-group" :class="[errors.has('channel_id') ? 'has-error' : '']">
<label for="notes">{{ __('admin::app.customers.note.enter-note') }} for {{ $customer->name }}</label>
<textarea class="control" name="notes" v-validate="'required'">{{ $customer->notes }}</textarea>
<span class="control-error" v-if="errors.has('notes')">@{{ errors.first('notes') }}</span>
</div>
</div>
</div>
</form>
</div>
@stop

View File

@ -62,7 +62,7 @@
@elseif ($error = session('error'))
window.flashMessages = [{'type': 'alert-error', 'message': "{{ $error }}" }];
@elseif ($info = session('info'))
window.flashMessages = [{'type': 'alert-error', 'message': "{{ $info }}" }];
window.flashMessages = [{'type': 'alert-info', 'message': "{{ $info }}" }];
@endif
window.serverErrors = [];

View File

@ -0,0 +1,539 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.promotion.add-cart-rule') }}
@stop
@section('content')
<div class="content">
<cart-rule></cart-rule>
</div>
@push('scripts')
<script type="text/x-template" id="cart-rule-form-template">
<form method="POST" action="{{ route('admin.cart-rule.store') }}" @submit.prevent="onSubmit">
@csrf
<div class="page-header">
<div class="page-title">
<h1>
<i class="icon angle-left-icon back-link" onclick="history.length > 1 ? history.go(-1) : window.location = '{{ url('/admin/dashboard') }}';"></i>
{{ __('admin::app.promotion.add-cart-rule') }}
</h1>
</div>
<div class="page-action">
<button type="submit" class="btn btn-lg btn-primary">
{{ __('admin::app.promotion.save-btn-title') }}
</button>
</div>
</div>
<div class="page-content">
<div class="form-container">
<div>
@csrf()
<accordian :active="true" title="Information">
<div slot="body">
<div class="control-group" :class="[errors.has('name') ? 'has-error' : '']">
<label for="name" class="required">{{ __('admin::app.promotion.general-info.name') }}</label>
<input type="text" class="control" name="name" v-model="name" v-validate="'required|alpha_spaces'" value="{{ old('name') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.name') }}&quot;">
<span class="control-error" v-if="errors.has('name')">@{{ errors.first('name') }}</span>
</div>
<div class="control-group" :class="[errors.has('description') ? 'has-error' : '']">
<label for="description">{{ __('admin::app.promotion.general-info.description') }}</label>
<textarea class="control" name="description" v-model="description" v-validate="'alpha_spaces'" value="{{ old('description') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.description') }}&quot;"></textarea>
<span class="control-error" v-if="errors.has('description')">@{{ errors.first('description') }}</span>
</div>
<datetime :name="starts_from">
<div class="control-group" :class="[errors.has('starts_from') ? 'has-error' : '']">
<label for="starts_from">{{ __('admin::app.promotion.general-info.starts-from') }}</label>
<input type="text" class="control" v-model="starts_from" name="starts_from" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.starts-from') }}&quot;">
<span class="control-error" v-if="errors.has('starts_from')">@{{ errors.first('starts_from') }}</span>
</div>
</datetime>
<datetime :name="starts_from">
<div class="control-group" :class="[errors.has('ends_till') ? 'has-error' : '']">
<label for="ends_till">{{ __('admin::app.promotion.general-info.ends-till') }}</label>
<input type="text" class="control" v-model="ends_till" name="ends_till" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.ends-till') }}&quot;">
<span class="control-error" v-if="errors.has('ends_till')">@{{ errors.first('ends_till') }}</span>
</div>
</datetime>
{{-- <div class="control-group" :class="[errors.has('customer_groups[]') ? 'has-error' : '']">
<label for="customer_groups" class="required">{{ __('admin::app.promotion.general-info.cust-groups') }}</label>
<select type="text" class="control" name="customer_groups[]" v-model="customer_groups" v-validate="'required'" value="{{ old('customer_groups[]') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.cust-groups') }}&quot;" multiple="multiple">
<option disabled="disabled">Select Customer Groups</option>
@foreach(app('Webkul\Customer\Repositories\CustomerGroupRepository')->all() as $channel)
<option value="{{ $channel->id }}">{{ $channel->name }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('customer_groups')">@{{ errors.first('customer_groups') }}</span>
</div> --}}
<div class="control-group" :class="[errors.has('channels[]') ? 'has-error' : '']">
<label for="channels" class="required">{{ __('admin::app.promotion.general-info.channels') }}</label>
<select type="text" class="control" name="channels[]" v-model="channels" v-validate="'required'" value="{{ old('channels[]') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.channels') }}&quot;" multiple="multiple">
<option disabled="disabled">Select Channels</option>
@foreach(app('Webkul\Core\Repositories\ChannelRepository')->all() as $channel)
<option value="{{ $channel->id }}">{{ $channel->name }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('channels')">@{{ errors.first('channels') }}</span>
</div>
<div class="control-group" :class="[errors.has('status') ? 'has-error' : '']">
<label for="status" class="required">{{ __('admin::app.promotion.general-info.status') }}</label>
<select type="text" class="control" name="status" v-model="status" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.status') }}&quot;">
<option disabled="disabled">Select status</option>
<option value="1">Yes</option>
<option value="0">No</option>
</select>
<span class="control-error" v-if="errors.has('status')">@{{ errors.first('status') }}</span>
</div>
<div class="control-group" :class="[errors.has('use_coupon') ? 'has-error' :
'']">
<label for="customer_groups" class="required">{{ __('admin::app.promotion.general-info.is-coupon') }}</label>
<select type="text" class="control" name="use_coupon" v-model="use_coupon" v-validate="'required'" value="{{ old('use_coupon')}}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.is-coupon') }}&quot;">
<option value="1" :selected="use_coupon == 1">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="0" :selected="use_coupon == 0">{{ __('admin::app.promotion.general-info.is-coupon-no') }}</option>
</select>
<span class="control-error" v-if="errors.has('use_coupon')">@{{ errors.first('use_coupon') }}</span>
</div>
{{-- <div class="control-group" :class="[errors.has('auto_generation') ? 'has-error' : '']" v-if="use_coupon == 1">
<label for="auto_generation" class="required">{{ __('admin::app.promotion.general-info.specific-coupon') }}</label>
<input type="checkbox" class="control" name="auto_generation" v-model="auto_generation" value="{{ old('auto_generation') }}" data-vv-as="&quot;Specific Coupon&quot;" v-on:change="checkAutogen">
<span class="control-error" v-if="errors.has('auto_generation')">@{{ errors.first('auto_generation') }}</span>
</div> --}}
{{-- <input type="hidden" name="auto_generation" v-model="auto_generation"> --}}
{{-- <div class="control-group" :class="[errors.has('per_customer') ? 'has-error' : '']">
<label for="per_customer" class="required">{{ __('admin::app.promotion.general-info.uses-per-cust') }}</label>
<input type="number" step="1" class="control" name="per_customer" v-model="per_customer" v-validate="'required|numeric|min_value:0'" value="{{ old('per_customer') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.uses-per-cust') }}&quot;">
<span class="control-error" v-if="errors.has('per_customer')">@{{ errors.first('per_customer') }}</span>
</div> --}}
<div class="control-group" :class="[errors.has('is_guest') ? 'has-error' : '']">
<label for="is_guest" class="required">{{ __('admin::app.promotion.general-info.is-guest') }}</label>
<select type="text" class="control" name="is_guest" v-model="is_guest" v-validate="'required'" value="{{ old('is_guest')}}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.is-guest') }}&quot;">
<option value="1" :selected="is_guest == 1">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="0" :selected="is_guest == 0">{{ __('admin::app.promotion.general-info.is-coupon-no') }}</option>
</select>
<span class="control-error" v-if="errors.has('is_guest')">@{{ errors.first('is_guest') }}</span>
</div>
{{-- <div class="control-group" :class="[errors.has('usage_limit') ? 'has-error' : '']">
<label for="usage_limit" class="required">{{ __('admin::app.promotion.general-info.limit') }}</label>
<input type="number" step="1" class="control" name="usage_limit" v-model="usage_limit" v-validate="'required|numeric|min_value:0'" value="{{ old('usage_limit') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.uses-per-cust') }}&quot;">
<span class="control-error" v-if="errors.has('usage_limit')">@{{ errors.first('usage_limit') }}</span>
</div> --}}
<div class="control-group" :class="[errors.has('priority') ? 'has-error' : '']">
<label for="priority" class="required">{{ __('admin::app.promotion.general-info.priority') }}</label>
<input type="number" class="control" step="1" name="priority" v-model="priority" v-validate="'required|numeric|min_value:1'" value="{{ old('priority') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.priority') }}&quot;">
<span class="control-error" v-if="errors.has('priority')">@{{ errors.first('priority') }}</span>
</div>
</div>
</accordian>
<accordian :active="false" title="Conditions">
<div slot="body">
<input type="hidden" name="all_conditions" v-model="all_conditions">
<div class="add-condition">
<div class="control-group">
<label for="criteria" class="required">{{ __('admin::app.promotion.general-info.add-condition') }}</label>
<select type="text" class="control" name="criteria" v-model="criteria">
<option value="cart">Cart Properties</option>
</select>
</div>
<span class="btn btn-primary btn-lg" v-on:click="addCondition">Add Condition</span>
</div>
<div class="mt-10 mb-10"><b>Any of the condition is true</b></div>
<div class="condition-set">
<!-- Cart Attributes -->
<div v-for="(condition, index) in conditions_list" :key="index">
<div class="control-container mt-20">
<div class="title-bar">
<span>Cart Attribute is </span>
<span class="icon cross-icon" v-on:click="removeCartAttr(index)"></span>
</div>
<div class="control-group mt-10" :key="index">
<select class="control" name="cart_attributes[]" v-model="conditions_list[index].attribute" title="You Can Make Multiple Selections Here" style="margin-right: 15px;" v-on:change="enableCondition($event, index)">
<option disabled="disabled">Select Option</option>
<option v-for="(cart_ip, index1) in cart_input" :value="cart_ip.code" :key="index1">@{{ cart_ip.name }}</option>
</select>
<div v-if='conditions_list[index].type == "string"'>
<select class="control" name="cart_attributes[]" v-model="conditions_list[index].condition" style="margin-right: 15px;">
<option v-for="(condition, index) in conditions.numeric" :value="index" :key="index">@{{ condition }}</option>
</select>
<div v-if='conditions_list[index].attribute == "shipping_state"'>
<select class="control" v-model="conditions_list[index].value">
<option disabled="disabled">Select State</option>
<optgroup v-for='(state, code) in country_and_states.states' :label="code">
<option v-for="(stateObj, index) in state" :value="stateObj.code">@{{ stateObj.default_name }}</option>
</optgroup>
</select>
</div>
<div v-if='conditions_list[index].attribute == "shipping_country"'>
<select class="control" v-model="conditions_list[index].value">
<option disabled="disabled">Select Country</option>
<option v-for="(country, index) in country_and_states.countries" :value="country.code">@{{ country.name }}</option>
</select>
</div>
<input type="text" class="control" name="cart_attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value" v-if='conditions_list[index].attribute != "shipping_state" && conditions_list[index].attribute != "shipping_country"'>
</div>
<div v-if='conditions_list[index].type == "numeric"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" style="margin-right: 15px;">
<option v-for="(condition, index) in conditions.numeric" :value="index" :key="index">@{{ condition }}</option>
</select>
<input type="number" step="0.1000" class="control" name="cart_attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value">
</div>
</div>
</div>
</div>
</div>
</div>
</accordian>
<accordian :active="false" title="Actions">
<div slot="body">
<div class="control-group" :class="[errors.has('action_type') ? 'has-error' : '']">
<label for="action_type" class="required">Apply</label>
<select class="control" name="action_type" v-model="action_type" v-validate="'required'" value="{{ old('action_type') }}" data-vv-as="&quot;Apply As&quot;" v-on:change="detectApply">
<option v-for="(action, index) in actions" :value="index">@{{ action }}</option>
</select>
<span class="control-error" v-if="errors.has('action_type')">@{{ errors.first('action_type') }}</span>
</div>
<div class="control-group" :class="[errors.has('disc_amount') ? 'has-error' : '']">
<label for="disc_amount" class="required">{{ __('admin::app.promotion.general-info.disc_amt') }}</label>
<input type="number" step="0.5000" class="control" name="disc_amount" v-model="disc_amount" v-validate="'required|decimal|min_value:0.0001'" value="{{ old('disc_amount') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.disc_amt') }}&quot;">
<span class="control-error" v-if="errors.has('disc_amount')">@{{ errors.first('disc_amount') }}</span>
</div>
<div class="control-group" :class="[errors.has('disc_threshold') ? 'has-error' : '']">
<label for="disc_threshold" class="required">{{ __('admin::app.promotion.cart.buy-atleast') }}</label>
<input type="number" step="1" class="control" name="disc_threshold" v-model="disc_threshold" v-validate="'required|numeric|min_value:1'" value="{{ old('disc_threshold') }}" data-vv-as="&quot;{{ __('admin::app.promotion.cart.buy-atleast') }}&quot;">
<span class="control-error" v-if="errors.has('disc_threshold')">@{{ errors.first('disc_threshold') }}</span>
</div>
<div class="control-group" :class="[errors.has('disc_quantity') ? 'has-error' : '']">
<label for="disc_quantity" class="required">{{ __('admin::app.promotion.general-info.disc_qty') }}</label>
<input type="number" step="1" class="control" name="disc_quantity" v-model="disc_quantity" v-validate="'required|decimal|min_value:0'" value="{{ old('disc_quantity') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.disc_qty') }}&quot;">
<span class="control-error" v-if="errors.has('disc_quantity')">@{{ errors.first('disc_quantity') }}</span>
</div>
<div class="boolean-control-container">
<div class="control-group" :class="[errors.has('free_shipping') ? 'has-error' : '']">
<label for="free_shipping" class="required">{{ __('admin::app.promotion.general-info.free-shipping') }}</label>
<select type="text" class="control" name="free_shipping" v-model="free_shipping" v-validate="'required'" value="{{ old('free_shipping') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.free-shipping') }}&quot;">
<option value="0" :selected="free_shipping == 0">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="1" :selected="free_shipping == 1">{{ __('admin::app.promotion.general-info.is-coupon-no') }}</option>
</select>
<span class="control-error" v-if="errors.has('free_shipping')">@{{ errors.first('free_shipping') }}</span>
</div>
<div class="control-group" :class="[errors.has('apply_to_shipping') ? 'has-error' : '']">
<label for="customer_groups" class="required">{{ __('admin::app.promotion.cart.apply-to-shipping') }}</label>
<select type="text" class="control" name="apply_to_shipping" v-model="apply_to_shipping" v-validate="'required'" value="{{ old('apply_to_shipping') }}" data-vv-as="&quot;{{ __('admin::app.promotion.cart.apply-to-shipping') }}&quot;">
<option value="0" :selected="apply_to_shipping == 0">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="1" :selected="apply_to_shipping == 1">{{ __('admin::app.promotion.general-info.is-coupon-no') }}</option>
</select>
<span class="control-error" v-if="errors.has('apply_to_shipping')">@{{ errors.first('apply_to_shipping') }}</span>
</div>
<div class="control-group" :class="[errors.has('end_other_rules') ? 'has-error' : '']">
<label for="end_other_rules" class="required">{{ __('admin::app.promotion.general-info.end-other-rules') }}</label>
<select type="text" class="control" name="end_other_rules" v-model="end_other_rules" v-validate="'required'" value="{{ old('end_other_rules')}}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.end-other-rules') }}&quot;">
<option value="1" :selected="end_other_rules == 1">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="0" :selected="end_other_rules == 0">{{ __('admin::app.promotion.general-info.is-coupon-no') }}</option>
</select>
<span class="control-error" v-if="errors.has('end_other_rules')">@{{ errors.first('end_other_rules') }}</span>
</div>
</div>
</div>
</accordian>
<accordian :active="false" title="Coupons" v-if="use_coupon == 1">
<div slot="body">
{{-- <div v-if="!auto_generation">
<div class="control-group" :class="[errors.has('prefix') ? 'has-error' : '']">
<label for="prefix" class="required">Prefix</label>
<input type="text" class="control" name="prefix" v-model="prefix" v-validate="'alpha'" value="{{ old('prefix') }}" data-vv-as="&quot;Prefix&quot;">
<span class="control-error" v-if="errors.has('prefix')">@{{ errors.first('prefix') }}</span>
</div>
<div class="control-group" :class="[errors.has('suffix') ? 'has-error' : '']"">
<label for="suffix" class="required">Suffix</label>
<input type="text" class="control" name="suffix" v-model="suffix" v-validate="'alpha'" value="{{ old('suffix') }}" data-vv-as="&quot;suffix&quot;">
<span class="control-error" v-if="errors.has('suffix')">@{{ errors.first('suffix') }}</span>
</div>
</div> --}}
<div>
<div class="control-group" :class="[errors.has('code') ? 'has-error' : '']">
<label for="code" class="required">Code</label>
<input type="text" class="control" name="code" v-model="code" v-validate="'required'" value="{{ old('code') }}" data-vv-as="&quot;Code&quot;">
<span class="control-error" v-if="errors.has('code')">@{{ errors.first('code') }}</span>
</div>
</div>
</div>
</accordian>
<accordian :active="false" title="labels">
<div slot="body">
<div class="control-group" :class="[errors.has('label') ? 'has-error' : '']" v-if="dedicated_label">
<label for="label" class="required">Global Label</label>
<input type="text" class="control" name="label[global]" v-model="label.global" v-validate="'required'" data-vv-as="&quot;label&quot;">
<span class="control-error" v-if="errors.has('label')">@{{ errors.first('label') }}</span>
</div>
<div v-if="label.global == null || label.global == ''">
@foreach(core()->getAllChannels() as $channel)
<span>[{{ $channel->code }}]</span>
@foreach($channel->locales as $locale)
<div class="control-group" :class="[errors.has('label') ? 'has-error' : '']">
<label for="code">{{ $locale->code }}</label>
<input type="text" class="control" name="label[{{ $channel->code }}][{{ $locale->code }}]" v-model="label.{{ $channel->code }}.{{ $locale->code }}" v-validate="'alpha_spaces'" data-vv-as="&quot;Label&quot;">
<span class="control-error" v-if="errors.has('label')">@{{ errors.first('label') }}</span>
</div>
@endforeach
@endforeach
</div>
</div>
</accordian>
</div>
</div>
</div>
</form>
</script>
<script>
Vue.component('cart-rule', {
template: '#cart-rule-form-template',
inject: ['$validator'],
data () {
return {
name: null,
description: null,
conditions_list: [],
channels: [],
customer_groups: [],
ends_till: null,
starts_from: null,
priority: 0,
per_customer: 0,
status: null,
use_coupon: null,
auto_generation: false,
usage_limit: 0,
is_guest: 0,
action_type: null,
apply: null,
apply_amt: false,
apply_prct: false,
apply_to_shipping: null,
disc_amount: null,
disc_threshold: null,
disc_quantity: null,
end_other_rules: null,
coupon_type: null,
free_shipping: null,
all_conditions: [],
code: null,
suffix: null,
prefix: null,
dedicated_label: true,
label: {
global: null,
@foreach(core()->getAllChannels() as $channel)
@foreach($channel->locales as $locale)
{{ trim($channel->code) }} : {
{{ trim($locale->code) }}: ''
},
@endforeach
@endforeach
},
criteria: null,
conditions: @json($cart_rule[0]).conditions,
cart_input: @json($cart_rule[0]).attributes,
actions: @json($cart_rule[0]).actions,
conditions_list:[],
cart_object: {
criteria: null,
attribute: null,
condition: null,
value: []
},
country_and_states: @json($cart_rule[2])
}
},
mounted () {
},
methods: {
addCondition () {
if (this.criteria == 'product_subselection' || this.criteria == 'cart') {
this.condition_on = this.criteria;
} else {
alert('please try again');
return false;
}
if (this.condition_on == 'cart') {
this.conditions_list.push(this.cart_object);
this.cart_object = {
criteria: null,
attribute: null,
condition: null,
value: []
};
}
},
checkAutogen() {
},
detectApply() {
if (this.apply == 'percent_of_product' || this.apply == 'buy_a_get_b') {
this.apply_prct = true;
this.apply_amt = false;
} else if (this.apply == 'fixed_amount' || this.apply == 'fixed_amount_cart') {
this.apply_prct = false;
this.apply_amt = true;
}
},
enableCondition(event, index) {
selectedIndex = event.target.selectedIndex - 1;
for (i in this.cart_input) {
if (i == selectedIndex) {
this.conditions_list[index].type = this.cart_input[i].type;
console.log(this.conditions_list[index]);
}
}
},
// useCoupon() {
// if (this.use_coupon == 0) {
// this.auto_generation = null;
// } else {
// this.auto_generation = true;
// }
// },
removeCartAttr(index) {
this.conditions_list.splice(index, 1);
},
removeCat(index) {
this.cats.splice(index, 1);
},
onSubmit: function (e) {
this.$validator.validateAll().then(result => {
if (result) {
e.target.submit();
}
});
this.all_conditions = JSON.stringify(this.conditions_list);
},
addFlashMessages() {
const flashes = this.$refs.flashes;
flashMessages.forEach(function(flash) {
flashes.addFlash(flash);
}, this);
}
}
});
</script>
@endpush
@stop

View File

@ -0,0 +1,582 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.promotion.edit-cart-rule') }}
@stop
@section('content')
<div class="content">
<cart-rule></cart-rule>
</div>
@push('scripts')
<script type="text/x-template" id="cart-rule-form-template">
<form method="POST" action="{{ route('admin.cart-rule.update', $cart_rule[3]->id) }}" @submit.prevent="onSubmit">
@csrf
<div class="page-header">
<div class="page-title">
<h1>
<i class="icon angle-left-icon back-link" onclick="history.length > 1 ? history.go(-1) : window.location = '{{ url('/admin/dashboard') }}';"></i>
{{ __('admin::app.promotion.edit-cart-rule') }}
</h1>
</div>
<div class="page-action">
<button type="submit" class="btn btn-lg btn-primary">
{{ __('admin::app.promotion.edit-btn-title') }}
</button>
</div>
</div>
<div class="page-content">
<div class="form-container">
<div>
@csrf()
<accordian :active="true" title="Information">
<div slot="body">
<input type="hidden" name="all_conditions" v-model="all_conditions">
<div class="control-group" :class="[errors.has('name') ? 'has-error' : '']">
<label for="name" class="required">{{ __('admin::app.promotion.general-info.name') }}</label>
<input type="text" class="control" name="name" v-model="name" v-validate="'required|alpha_spaces'" value="{{ old('name') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.name') }}&quot;">
<span class="control-error" v-if="errors.has('name')">@{{ errors.first('name') }}</span>
</div>
<div class="control-group" :class="[errors.has('description') ? 'has-error' : '']">
<label for="description">{{ __('admin::app.promotion.general-info.description') }}</label>
<textarea class="control" name="description" v-model="description" v-validate="'alpha_spaces'" value="{{ old('description') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.description') }}&quot;"></textarea>
<span class="control-error" v-if="errors.has('description')">@{{ errors.first('description') }}</span>
</div>
<datetime :name="starts_from">
<div class="control-group" :class="[errors.has('starts_from') ? 'has-error' : '']">
<label for="starts_from">{{ __('admin::app.promotion.general-info.starts-from') }}</label>
<input type="text" class="control" v-model="starts_from" name="starts_from" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.starts-from') }}&quot;">
<span class="control-error" v-if="errors.has('starts_from')">@{{ errors.first('starts_from') }}</span>
</div>
</datetime>
<datetime :name="starts_from">
<div class="control-group" :class="[errors.has('ends_till') ? 'has-error' : '']">
<label for="ends_till">{{ __('admin::app.promotion.general-info.ends-till') }}</label>
<input type="text" class="control" v-model="ends_till" name="ends_till" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.ends-till') }}&quot;">
<span class="control-error" v-if="errors.has('ends_till')">@{{ errors.first('ends_till') }}</span>
</div>
</datetime>
{{-- <div class="control-group" :class="[errors.has('customer_groups[]') ? 'has-error' : '']">
<label for="customer_groups" class="required">{{ __('admin::app.promotion.general-info.cust-groups') }}</label>
<select type="text" class="control" name="customer_groups[]" v-model="customer_groups" v-validate="'required'" value="{{ old('customer_groups[]') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.cust-groups') }}&quot;" multiple="multiple">
<option disabled="disabled">Select Customer Groups</option>
@foreach(app('Webkul\Customer\Repositories\CustomerGroupRepository')->all() as $customerGroup)
<option value="{{ $customerGroup->id }}">{{ $customerGroup->name }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('customer_groups')">@{{ errors.first('customer_groups') }}</span>
</div> --}}
<div class="control-group" :class="[errors.has('channels[]') ? 'has-error' : '']">
<label for="channels" class="required">{{ __('admin::app.promotion.general-info.channels') }}</label>
<select type="text" class="control" name="channels[]" v-model="channels" v-validate="'required'" value="{{ old('channels[]') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.channels') }}&quot;" multiple="multiple">
<option disabled="disabled">Select Channels</option>
@foreach(app('Webkul\Core\Repositories\ChannelRepository')->all() as $channel)
<option value="{{ $channel->id }}">{{ $channel->name }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('channels')">@{{ errors.first('channels') }}</span>
</div>
<div class="control-group" :class="[errors.has('status') ? 'has-error' : '']">
<label for="status" class="required">{{ __('admin::app.promotion.general-info.status') }}</label>
<select type="text" class="control" name="status" v-model="status" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.status') }}&quot;">
<option disabled="disabled">Select status</option>
<option value="1">Yes</option>
<option value="0">No</option>
</select>
<span class="control-error" v-if="errors.has('status')">@{{ errors.first('status') }}</span>
</div>
<div class="control-group" :class="[errors.has('use_coupon') ? 'has-error' :
'']">
<label for="customer_groups" class="required">{{ __('admin::app.promotion.general-info.is-coupon') }}</label>
<select type="text" class="control" name="use_coupon" v-model="use_coupon" v-validate="'required'" value="{{ old('use_coupon')}}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.is-coupon') }}&quot;" v-on:change="useCoupon">
<option value="1" :selected="use_coupon == 1">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="0" :selected="use_coupon == 0">{{ __('admin::app.promotion.general-info.is-coupon-no') }}</option>
</select>
<span class="control-error" v-if="errors.has('use_coupon')">@{{ errors.first('use_coupon') }}</span>
</div>
{{-- <div class="control-group" :class="[errors.has('auto_generation') ? 'has-error' : '']" v-if="use_coupon == 1">
<label for="auto_generation" class="required">{{ __('admin::app.promotion.general-info.specific-coupon') }}</label>
<input type="checkbox" class="control" name="auto_generation" v-model="auto_generation" value="{{ old('auto_generation') }}" data-vv-as="&quot;Specific Coupon&quot;" v-on:change="checkAutogen">
<span class="control-error" v-if="errors.has('auto_generation')">@{{ errors.first('auto_generation') }}</span>
</div> --}}
{{-- <input type="hidden" name="auto_generation" v-model="auto_generation"> --}}
{{-- <div class="control-group" :class="[errors.has('per_customer') ? 'has-error' : '']">
<label for="per_customer" class="required">{{ __('admin::app.promotion.general-info.uses-per-cust') }}</label>
<input type="number" step="1" class="control" name="per_customer" v-model="per_customer" v-validate="'required|numeric|min_value:0'" value="{{ old('per_customer') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.uses-per-cust') }}&quot;">
<span class="control-error" v-if="errors.has('per_customer')">@{{ errors.first('per_customer') }}</span>
</div> --}}
<div class="control-group" :class="[errors.has('is_guest') ? 'has-error' : '']">
<label for="is_guest" class="required">{{ __('admin::app.promotion.general-info.is-guest') }}</label>
<select type="text" class="control" name="is_guest" v-model="is_guest" v-validate="'required'" value="{{ old('is_guest')}}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.is-guest') }}&quot;">
<option value="1" :selected="is_guest == 1">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="0" :selected="is_guest == 0">{{ __('admin::app.promotion.general-info.is-coupon-no') }}</option>
</select>
<span class="control-error" v-if="errors.has('is_guest')">@{{ errors.first('is_guest') }}</span>
</div>
{{-- <div class="control-group" :class="[errors.has('usage_limit') ? 'has-error' : '']">
<label for="usage_limit" class="required">{{ __('admin::app.promotion.general-info.limit') }}</label>
<input type="number" step="1" class="control" name="usage_limit" v-model="usage_limit" v-validate="'required|numeric|min_value:0'" value="{{ old('usage_limit') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.uses-per-cust') }}&quot;">
<span class="control-error" v-if="errors.has('usage_limit')">@{{ errors.first('usage_limit') }}</span>
</div> --}}
<div class="control-group" :class="[errors.has('priority') ? 'has-error' : '']">
<label for="priority" class="required">{{ __('admin::app.promotion.general-info.priority') }}</label>
<input type="number" class="control" step="1" name="priority" v-model="priority" v-validate="'required|numeric|min_value:1'" value="{{ old('priority') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.priority') }}&quot;">
<span class="control-error" v-if="errors.has('priority')">@{{ errors.first('priority') }}</span>
</div>
</div>
</accordian>
<accordian :active="false" title="Conditions">
<div slot="body">
<div class="add-condition">
<div class="control-group">
<label for="criteria" class="required">{{ __('admin::app.promotion.general-info.add-condition') }}</label>
<select type="text" class="control" name="criteria" v-model="criteria">
<option value="cart">Cart Properties</option>
</select>
</div>
<span class="btn btn-primary btn-lg" v-on:click="addCondition">Add Condition</span>
</div>
<div class="mt-10 mb-10"><b>Any of the condition is true</b></div>
<div class="condition-set">
<!-- Cart Attributes -->
<div v-for="(condition, index) in conditions_list" :key="index">
<div class="control-container mt-20">
<div class="title-bar">
<span>Cart Attribute is </span>
<span class="icon cross-icon" v-on:click="removeCartAttr(index)"></span>
</div>
<div class="control-group mt-10" :key="index">
<select class="control" name="cart_attributes[]" v-model="conditions_list[index].attribute" title="You Can Make Multiple Selections Here" style="margin-right: 15px;" v-on:change="enableCondition($event, index)">
<option disabled="disabled">Select Option</option>
<option v-for="(cart_ip, index1) in cart_input" :value="cart_ip.code" :key="index1">@{{ cart_ip.name }}</option>
</select>
<div v-if='conditions_list[index].type == "string"'>
<select class="control" name="cart_attributes[]" v-model="conditions_list[index].condition" style="margin-right: 15px;">
<option v-for="(condition, index) in conditions.numeric" :value="index" :key="index">@{{ condition }}</option>
</select>
<div v-if='conditions_list[index].attribute == "shipping_state"'>
<select class="control" v-model="conditions_list[index].value">
<option disabled="disabled">Select State</option>
<optgroup v-for='(state, code) in country_and_states.states' :label="code">
<option v-for="(stateObj, index) in state" :value="stateObj.code">@{{ stateObj.default_name }}</option>
</optgroup>
</select>
</div>
<div v-if='conditions_list[index].attribute == "shipping_country"'>
<select class="control" v-model="conditions_list[index].value">
<option disabled="disabled">Select Country</option>
<option v-for="(country, index) in country_and_states.countries" :value="country.code">@{{ country.name }}</option>
</select>
</div>
<input type="text" class="control" name="cart_attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value" v-if='conditions_list[index].attribute != "shipping_state" && conditions_list[index].attribute != "shipping_country"'>
</div>
<div v-if='conditions_list[index].type == "numeric"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" style="margin-right: 15px;">
<option v-for="(condition, index) in conditions.numeric" :value="index" :key="index">@{{ condition }}</option>
</select>
<input type="number" step="0.1000" class="control" name="cart_attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value">
</div>
</div>
</div>
</div>
</div>
</div>
</accordian>
<accordian :active="false" title="Actions">
<div slot="body">
<div class="control-group" :class="[errors.has('action_type') ? 'has-error' : '']">
<label for="action_type" class="required">Apply</label>
<select class="control" name="action_type" v-model="action_type" v-validate="'required'" value="{{ old('action_type') }}" data-vv-as="&quot;Apply As&quot;" v-on:change="detectApply">
<option v-for="(action, index) in actions" :value="index">@{{ action }}</option>
</select>
<span class="control-error" v-if="errors.has('action_type')">@{{ errors.first('action_type') }}</span>
</div>
<div class="control-group" :class="[errors.has('disc_amount') ? 'has-error' : '']">
<label for="disc_amount" class="required">{{ __('admin::app.promotion.general-info.disc_amt') }}</label>
<input type="number" step="0.5000" class="control" name="disc_amount" v-model="disc_amount" v-validate="'required|decimal|min_value:0.0001'" value="{{ old('disc_amount') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.disc_amt') }}&quot;">
<span class="control-error" v-if="errors.has('disc_amount')">@{{ errors.first('disc_amount') }}</span>
</div>
<div class="control-group" :class="[errors.has('disc_threshold') ? 'has-error' : '']">
<label for="disc_threshold" class="required">{{ __('admin::app.promotion.cart.buy-atleast') }}</label>
<input type="number" step="1" class="control" name="disc_threshold" v-model="disc_threshold" v-validate="'required|numeric|min_value:1'" value="{{ old('disc_threshold') }}" data-vv-as="&quot;{{ __('admin::app.promotion.cart.buy-atleast') }}&quot;">
<span class="control-error" v-if="errors.has('disc_threshold')">@{{ errors.first('disc_threshold') }}</span>
</div>
<div class="control-group" :class="[errors.has('disc_quantity') ? 'has-error' : '']">
<label for="disc_amount" class="required">{{ __('admin::app.promotion.general-info.disc_qty') }}</label>
<input type="number" step="1" class="control" name="disc_quantity" v-model="disc_quantity" v-validate="'required|decimal|min_value:1'" value="{{ old('disc_quantity') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.disc_qty') }}&quot;">
<span class="control-error" v-if="errors.has('disc_quantity')">@{{ errors.first('disc_quantity') }}</span>
</div>
<div class="boolean-control-container">
<div class="control-group" :class="[errors.has('free_shipping') ? 'has-error' : '']">
<label for="free_shipping" class="required">{{ __('admin::app.promotion.general-info.free-shipping') }}</label>
<select type="text" class="control" name="free_shipping" v-model="free_shipping" v-validate="'required'" value="{{ old('free_shipping') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.free-shipping') }}&quot;">
<option value="0" :selected="free_shipping == 0">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="1" :selected="free_shipping == 1">{{ __('admin::app.promotion.general-info.is-coupon-no') }}</option>
</select>
<span class="control-error" v-if="errors.has('free_shipping')">@{{ errors.first('free_shipping') }}</span>
</div>
<div class="control-group" :class="[errors.has('apply_to_shipping') ? 'has-error' : '']">
<label for="customer_groups" class="required">{{ __('admin::app.promotion.cart.apply-to-shipping') }}</label>
<select type="text" class="control" name="apply_to_shipping" v-model="apply_to_shipping" v-validate="'required'" value="{{ old('apply_to_shipping') }}" data-vv-as="&quot;{{ __('admin::app.promotion.cart.apply-to-shipping') }}&quot;">
<option value="0" :selected="apply_to_shipping == 0">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="1" :selected="apply_to_shipping == 1">{{ __('admin::app.promotion.general-info.is-coupon-no') }}</option>
</select>
<span class="control-error" v-if="errors.has('apply_to_shipping')">@{{ errors.first('apply_to_shipping') }}</span>
</div>
<div class="control-group" :class="[errors.has('end_other_rules') ? 'has-error' : '']">
<label for="end_other_rules" class="required">{{ __('admin::app.promotion.general-info.end-other-rules') }}</label>
<select type="text" class="control" name="end_other_rules" v-model="end_other_rules" v-validate="'required'" value="{{ old('end_other_rules')}}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.end-other-rules') }}&quot;">
<option value="1" :selected="end_other_rules == 1">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="0" :selected="end_other_rules == 0">{{ __('admin::app.promotion.general-info.is-coupon-no') }}</option>
</select>
<span class="control-error" v-if="errors.has('end_other_rules')">@{{ errors.first('end_other_rules') }}</span>
</div>
</div>
</div>
</accordian>
<accordian :active="false" title="Coupons" v-if="use_coupon == 1">
<div slot="body">
{{-- <div v-if="!auto_generation">
<div class="control-group" :class="[errors.has('prefix') ? 'has-error' : '']">
<label for="prefix" class="required">Prefix</label>
<input type="text" class="control" name="prefix" v-model="prefix" value="{{ old('prefix') }}" data-vv-as="&quot;Prefix&quot;">
<span class="control-error" v-if="errors.has('prefix')">@{{ errors.first('prefix') }}</span>
</div>
<div class="control-group" :class="[errors.has('suffix') ? 'has-error' : '']"">
<label for="suffix" class="required">Suffix</label>
<input type="text" class="control" name="suffix" v-model="suffix" value="{{ old('suffix') }}" data-vv-as="&quot;suffix&quot;">
<span class="control-error" v-if="errors.has('suffix')">@{{ errors.first('suffix') }}</span>
</div>
</div> --}}
{{-- <div v-if="auto_generation != 0"> --}}
<div class="control-group" :class="[errors.has('code') ? 'has-error' : '']">
<label for="code" class="required">Code</label>
<input type="text" class="control" name="code" v-model="code" v-validate="'required'" value="{{ old('code') }}" data-vv-as="&quot;Code&quot;">
<span class="control-error" v-if="errors.has('code')">@{{ errors.first('code') }}</span>
</div>
{{-- </div> --}}
</div>
</accordian>
<accordian :active="false" title="labels">
<div slot="body">
@foreach($cart_rule[3]->labels as $label)
<span>[{{ $label->channel->code }}]</span>
<div class="control-group" :class="[errors.has('label') ? 'has-error' : '']">
<label for="code">{{ $label->locale->code }}</label>
<input type="text" class="control" name="label[{{ $label->channel->code }}][{{ $label->locale->code }}]" value="{{ $label->label }}" v-validate="'required'" data-vv-as="&quot;Label&quot;">
<span class="control-error" v-if="errors.has('label')">@{{ errors.first('label') }}</span>
</div>
@endforeach
</div>
</accordian>
</div>
</div>
</div>
</form>
</script>
<script>
Vue.component('cart-rule', {
template: '#cart-rule-form-template',
inject: ['$validator'],
data () {
return {
name: 'Name of rule',
description: 'Enter Some Description',
channels: [],
customer_groups: [],
ends_till: null,
starts_from: null,
priority: 0,
per_customer: 0,
status: null,
use_coupon: null,
// auto_generation: 0,
usage_limit: 0,
is_guest: 0,
action_type: null,
apply: null,
apply_amt: false,
apply_prct: false,
apply_to_shipping: null,
buy_atleast: null,
disc_amount: null,
disc_threshold: null,
disc_quantity: null,
end_other_rules: null,
coupon_type: null,
free_shipping: null,
all_conditions: null,
code: null,
suffix: null,
prefix: null,
dedicated_label: true,
global_label: null,
labels: [],
// label: {
// @foreach(core()->getAllChannels() as $channel)
// @foreach($channel->locales as $locale)
// {{ trim($channel->code) }} : null,
// @endforeach
// @endforeach
// },
criteria: null,
conditions: @json($cart_rule[0]).conditions,
cart_input: @json($cart_rule[0]).attributes,
actions: @json($cart_rule[0]).actions,
conditions_list:[],
cart_object: {
criteria: null,
attribute: null,
condition: null,
value: []
},
country_and_states: @json($cart_rule[2])
}
},
mounted () {
data = @json($cart_rule[3]);
this.name = data.name;
this.description = data.description;
this.conditions_list = [];
this.channels = [];
for (i in data.channels) {
this.channels.push(data.channels[i].channel_id);
}
this.customer_groups = data.customer_groups;
for (i in data.customer_groups) {
this.customer_groups.push(data.customer_groups[i].customer_group_id);
}
this.ends_till = data.ends_till;
this.starts_from = data.starts_from;
this.priority = data.priority;
this.per_customer = data.per_customer;
this.status = data.status;
if (data.use_coupon == 0) {
// this.auto_generation = null;
this.use_coupon = 0;
} else {
this.use_coupon = 1;
// this.auto_generation = data.auto_generation;
this.code = data.coupons.code;
// this.suffix = data.coupons.suffix;
// this.prefix = data.coupons.prefix;
// if (data.auto_generation == 0)
// this.auto_generation = true;
// else
// this.auto_generation = false;
}
this.usage_limit = data.usage_limit;
this.is_guest = data.is_guest;
this.action_type = data.action_type;
this.apply = null;
this.apply_amt = false;
this.apply_prct = false;
this.apply_to_shipping = data.apply_to_shipping;
this.buy_atleast = data.disc_threshold;
this.disc_amount = data.disc_amount;
this.disc_threshold = data.disc_threshold;
this.disc_quantity = data.disc_quantity;
this.end_other_rules = data.end_other_rules;
this.coupon_type = data.coupon_type;
this.free_shipping = data.free_shipping;
this.all_conditions = null;
if (data.conditions != null) {
this.conditions_list = JSON.parse(JSON.parse(data.conditions));
}
criteria = null;
},
methods: {
addCondition () {
if (this.criteria == 'product_subselection' || this.criteria == 'cart') {
this.condition_on = this.criteria;
} else {
alert('please try again');
return false;
}
if (this.condition_on == 'cart') {
this.conditions_list.push(this.cart_object);
this.cart_object = {
criteria: null,
attribute: null,
condition: null,
value: []
};
}
},
checkAutogen() {
},
detectApply() {
if (this.apply == 'percent_of_product' || this.apply == 'buy_a_get_b') {
this.apply_prct = true;
this.apply_amt = false;
} else if (this.apply == 'fixed_amount' || this.apply == 'fixed_amount_cart') {
this.apply_prct = false;
this.apply_amt = true;
}
},
enableCondition(event, index) {
selectedIndex = event.target.selectedIndex - 1;
for (i in this.cart_input) {
if (i == selectedIndex) {
this.conditions_list[index].type = this.cart_input[i].type;
}
}
},
useCoupon() {
if (this.use_coupon == 0) {
this.auto_generation = null;
} else {
this.auto_generation = true;
}
},
removeCartAttr(index) {
this.conditions_list.splice(index, 1);
},
removeCat(index) {
this.cats.splice(index, 1);
},
onSubmit: function (e) {
this.$validator.validateAll().then(result => {
if (result) {
e.target.submit();
}
});
this.all_conditions = JSON.stringify(this.conditions_list);
},
addFlashMessages() {
const flashes = this.$refs.flashes;
flashMessages.forEach(function(flash) {
flashes.addFlash(flash);
}, this);
}
}
});
</script>
@endpush
@stop

View File

@ -0,0 +1,27 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.promotion.cart-rule') }}
@stop
@section('content')
<div class="content">
<div class="page-header">
<div class="page-title">
<h1>{{ __('admin::app.promotion.cart-rule') }}</h1>
</div>
<div class="page-action">
<a href="{{ route('admin.cart-rule.create') }}" class="btn btn-lg btn-primary">
{{ __('admin::app.promotion.add-cart-rule') }}
</a>
</div>
</div>
<div class="page-content">
@inject('cartRuleGrid','Webkul\Admin\DataGrids\CartRuleDataGrid')
{!! $cartRuleGrid->render() !!}
</div>
</div>
@endsection

View File

@ -0,0 +1,539 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.promotion.add-catalog-rule') }}
@stop
@section('content')
<div class="content">
<catalog-rule></catalog-rule>
</div>
@push('scripts')
<script type="text/x-template" id="catalog-rule-form-template">
<div>
<form method="POST" action="{{ route('admin.catalog-rule.store') }}" @submit.prevent="onSubmit">
@csrf
<div class="page-header">
<div class="page-title">
<h1>
<i class="icon angle-left-icon back-link" onclick="history.length > 1 ? history.go(-1) : window.location = '{{ url('/admin/dashboard') }}';"></i>
{{ __('admin::app.promotion.add-catalog-rule') }}
</h1>
</div>
<div class="page-action">
<button type="submit" class="btn btn-lg btn-primary">
{{ __('admin::app.promotion.save-btn-title') }}
</button>
</div>
</div>
<div class="page-content">
<div class="form-container">
@csrf()
<accordian :active="true" title="Information">
<div slot="body">
<div class="control-group" :class="[errors.has('name') ? 'has-error' : '']">
<label for="name" class="required">{{ __('admin::app.promotion.general-info.name') }}</label>
<input type="text" class="control" name="name" v-model="name" v-validate="'required'" value="{{ old('name') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.name') }}&quot;">
<span class="control-error" v-if="errors.has('name')">@{{ errors.first('name') }}</span>
</div>
<div class="control-group" :class="[errors.has('description') ? 'has-error' : '']">
<label for="description">{{ __('admin::app.promotion.general-info.description') }}</label>
<textarea class="control" name="description" v-model="description" v-validate="'required'" value="{{ old('description') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.description') }}&quot;"></textarea>
<span class="control-error" v-if="errors.has('description')">@{{ errors.first('description') }}</span>
</div>
<div class="control-group" :class="[errors.has('customer_groups[]') ? 'has-error' : '']">
<label for="customer_groups" class="required">{{ __('admin::app.promotion.general-info.cust-groups') }}</label>
<select type="text" class="control" name="customer_groups[]" v-model="customer_groups" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.cust-groups') }}&quot;" multiple="multiple">
<option disabled="disabled">Select Customer Groups</option>
@foreach(app('Webkul\Customer\Repositories\CustomerGroupRepository')->all() as $channel)
<option value="{{ $channel->id }}">{{ $channel->name }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('customer_groups[]')">@{{ errors.first('customer_groups[]') }}</span>
</div>
<div class="control-group" :class="[errors.has('channels[]') ? 'has-error' : '']">
<label for="channels" class="required">{{ __('admin::app.promotion.general-info.channels') }}</label>
<select type="text" class="control" name="channels[]" v-model="channels" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.channels') }}&quot;" multiple="multiple">
<option disabled="disabled">Select Channels</option>
@foreach(app('Webkul\Core\Repositories\ChannelRepository')->all() as $channel)
<option value="{{ $channel->id }}">{{ $channel->name }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('channels[]')">@{{ errors.first('status') }}</span>
</div>
<div class="control-group" :class="[errors.has('status') ? 'has-error' : '']">
<label for="status" class="required">{{ __('admin::app.promotion.general-info.status') }}</label>
<select type="text" class="control" name="status" v-model="status" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.status') }}&quot;">
<option disabled="disabled">Select status</option>
<option value="1">Yes</option>
<option value="0">No</option>
</select>
<span class="control-error" v-if="errors.has('status')">@{{ errors.first('status') }}</span>
</div>
<div class="control-group" :class="[errors.has('end_other_rules') ? 'has-error' : '']">
<label for="end_other_rules" class="required">{{ __('admin::app.promotion.general-info.end_other_rules') }}</label>
<select type="text" class="control" name="end_other_rules" v-model="end_other_rules" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.end_other_rules') }}&quot;">
<option disabled="disabled">Select option</option>
<option value="1">Yes</option>
<option value="0">No</option>
</select>
<span class="control-error" v-if="errors.has('end_other_rules')">@{{ errors.first('end_other_rules') }}</span>
</div>
<datetime :name="starts_from">
<div class="control-group" :class="[errors.has('starts_from') ? 'has-error' : '']">
<label for="starts_from" class="required">{{ __('admin::app.promotion.general-info.starts-from') }}</label>
<input type="text" class="control" v-model="starts_from" name="starts_from" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.starts-from') }}&quot;">
<span class="control-error" v-if="errors.has('starts_from')">@{{ errors.first('starts_from') }}</span>
</div>
</datetime>
<datetime :name="starts_from">
<div class="control-group" :class="[errors.has('ends_till') ? 'has-error' : '']">
<label for="ends_till" class="required">{{ __('admin::app.promotion.general-info.ends-till') }}</label>
<input type="text" class="control" v-model="ends_till" name="ends_till" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.ends-till') }}&quot;">
<span class="control-error" v-if="errors.has('ends_till')">@{{ errors.first('ends_till') }}</span>
</div>
</datetime>
<div class="control-group" :class="[errors.has('priority') ? 'has-error' : '']">
<label for="priority" class="required">{{ __('admin::app.promotion.general-info.priority') }}</label>
<input type="number" class="control" step="1" name="priority" v-model="priority" v-validate="'required|numeric|min_value:1'" value="{{ old('priority') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.priority') }}&quot;">
<span class="control-error" v-if="errors.has('priority')">@{{ errors.first('priority') }}</span>
</div>
</div>
</accordian>
<accordian :active="false" title="Conditions">
<div slot="body">
<div class="add-condition">
<input type="hidden" v-model="global_condition.allorany">
<input type="hidden" v-model="global_condition.alltrueorfalse">
<div class="control-group" :class="[errors.has('criteria') ? 'has-error' : '']">
<label for="criteria" class="required">{{ __('admin::app.promotion.general-info.add-condition') }}</label>
<select type="text" class="control" name="criteria" v-model="criteria" v-validate="'required'" value="" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.cust-groups') }}&quot;">
<option value="attribute">Attribute</option>
<option value="category">Category</option>
<option value="attribute_family">Attribute Family</option>
</select>
<span class="control-error" v-if="errors.has('criteria')">@{{ errors.first('criteria') }}</span>
</div>
<span class="btn btn-primary btn-lg" v-on:click="addCondition">Add Condition</span>
</div>
<div class="condition-set" v-if="conditions_list.length">
{{-- <span class="control-group" v-on:click="genericGroupCondition" v-if="generic_condition">
{{ __('admin::app.promotion.general-info.all-conditions-true') }}
</span>
<span class="control-group" v-on:click="genericGroupCondition" v-if="! generic_condition">
{{ __('admin::app.promotion.general-info.assuming') }}
<select>
<option selected="selected">All</option>
<option>Any</option>
</select>
{{ __('admin::app.promotion.general-info.are') }}
<select>
<option selected="selected">True</option>
<option>False</option>
</select>
</span> --}}
<!-- Conditions -->
<div v-for="(condition, index) in conditions_list" :key="index">
<div class="control-container mt-20" v-if='conditions_list[index].criteria == "attribute"'>
<div class="title-bar">
{{-- <span>Group </span>
<input type="checkbox" v-model="condition_groups" v-on:click="groupSelected(index)" /> --}}
<span>Attribute is </span>
<span class="icon cross-icon" v-on:click="removeAttr(index)"></span>
</div>
<div class="control-group mt-10" :key="index">
<select class="control" name="attributes[]" v-model="conditions_list[index].attribute" v-validate="'required'" title="You Can Make Multiple Selections Here" style="margin-right: 15px;" v-on:change="enableCondition($event, index)">
<option disabled="disabled">Select attribute</option>
<option v-for="(attr_ip, i) in attrs_input" :value="attr_ip.code">@{{ attr_ip.name }}</option>
</select>
<div v-if='conditions_list[index].type == "text" || conditions_list[index].type == "textarea"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['text'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<input type="text" class="control" name="attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value">
</div>
<div v-if='conditions_list[index].type == "price"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['numeric'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<input type="number" step="0.1000" class="control" name="attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value">
</div>
<div v-else-if='conditions_list[index].type == "boolean"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
<option selected="selected">is</option>
</select>
<select class="control" name="attributes[]" v-model="conditions_list[index].value">
@foreach($catalog_rule[4]['boolean'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
</div>
<div v-else-if='conditions_list[index].type == "date"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['numeric'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<date>
<input type="text" class="control" v-model="conditions_list[index].value" name="attributes[]" v-validate="'required'" value="Enter Value">
</date>
</div>
<div v-else-if='conditions_list[index].type == "datetime"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['numeric'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<datetime>
<input type="text" class="control" v-model="conditions_list[index].value" name="attributes[]" v-validate="'required'" value="Enter Value">
</datetime>
</div>
<div v-else-if='conditions_list[index].type == "select" || conditions_list[index].type == "multiselect"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['text'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<select class="control" v-model="conditions_list[index].value" name="attributes[]" v-validate="'required'" multiple>
<option v-for="option in conditions_list[index].options" :value="option.id">@{{ option.admin_name }}</option>
</select>
</div>
</div>
</div>
<div class="control-container mt-20" v-if='conditions_list[index].criteria == "category"'>
<div class="title-bar">
<span>Category </span>
{{-- <span>Group </span>
<input type="checkbox" v-model="condition_groups" v-on:click="groupSelected(index)" /> --}}
<span class="icon cross-icon" v-on:click="removeAttr(index)"></span>
</div>
<div class="control-group mt-10" :key="index">
<input type="hidden" class="control" name="attributes[]" v-model="conditions_list[index].category" v-validate="'required'" style="margin-right: 15px;" v-on:change="enableCondition($event, index)">
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['text'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<select class="control" name="attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value" multiple>
<option v-for="(category, index) in categories" :value="category.id">@{{ category.name }}</option>
</select>
</div>
</div>
<div class="control-container mt-20" v-if='conditions_list[index].criteria == "attribute_family"'>
<div class="title-bar">
<span>Attribute Family </span>
{{-- <span>Group </span>
<input type="checkbox" v-model="condition_groups" v-on:click="groupSelected(index)" /> --}}
<span class="icon cross-icon" v-on:click="removeAttr(index)"></span>
</div>
<div class="control-group mt-10" :key="index">
<input type="hidden" class="control" name="attributes[]" v-model="conditions_list[index].family" v-validate="'required'" style="margin-right: 15px;" v-on:change="enableCondition($event, index)">
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['boolean'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<select class="control" name="attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value">
<option v-for="(attr_family, index) in attr_families" :value="attr_family.id">@{{ attr_family.name }}</option>
</select>
</div>
</div>
</div>
<input type="hidden" name="all_conditions[]" v-model="all_conditions">
</div>
</div>
</accordian>
<accordian :active="false" title="Actions">
<div slot="body">
<div class="control-group" :class="[errors.has('apply') ? 'has-error' : '']">
<label for="apply" class="required">Apply</label>
<select class="control" name="apply" v-model="apply" v-validate="'required'" value="{{ old('apply') }}" data-vv-as="&quot;Apply As&quot;" v-on:change="detectApply">
@foreach($catalog_rule[3]['actions'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('apply')">@{{ errors.first('apply') }}</span>
</div>
<div class="control-group" :class="[errors.has('disc_amount') ? 'has-error' : '']" v-if="apply_amt">
<label for="disc_amount" class="required">{{ __('admin::app.promotion.general-info.disc_amt') }}</label>
<input type="number" step="1.0000" class="control" name="disc_amount" v-model="disc_amount" v-validate="'required|decimal|min_value:0.0001'" value="{{ old('disc_amount') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.disc_amt') }}&quot;">
<span class="control-error" v-if="errors.has('disc_amount')">@{{ errors.first('disc_amount') }}</span>
</div>
<div class="control-group" :class="[errors.has('disc_percent') ? 'has-error' : '']" v-if="apply_prct">
<label for="disc_percent" class="required">{{ __('admin::app.promotion.general-info.disc_percent') }}</label>
<input type="number" step="0.5000" class="control" name="disc_percent" v-model="disc_percent" v-validate="'required|decimal|min_value:0.0001'" value="{{ old('disc_percent') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.disc_percent') }}&quot;">
<span class="control-error" v-if="errors.has('disc_percent')">@{{ errors.first('disc_percent') }}</span>
</div>
</div>
</accordian>
</div>
</div>
</form>
</div>
</script>
<script>
Vue.component('catalog-rule', {
template: '#catalog-rule-form-template',
inject: ['$validator'],
data () {
return {
all_conditions: [],
apply: null,
apply_amt: false,
apply_prct: false,
applied_config: @json($catalog_rule[3]),
conditions_list: [],
attr_families: @json($catalog_rule[5]),
attrs_input: @json($catalog_rule[0]),
attrs_options: @json($catalog_rule[2]),
global_condition: {
allorany: false,
alltrueorfalse: true
},
attr_object: {
criteria: 'attribute',
attribute: null,
condition: null,
type: null,
value: null
},
cat_object: {
criteria: 'category',
category: 'category',
condition: null,
value: []
},
fam_object: {
criteria: 'attribute_family',
family: 'attribute_family',
condition: null,
value: null
},
categories: @json($catalog_rule[1]),
cats_count: 0,
channels: [],
conditions: [],
condition_groups: [],
criteria: null,
customer_groups: [],
description: null,
disc_amount: 0.0,
disc_percent: 0.0,
ends_till: null,
end_other_rules: null,
generic_condition: true,
name: null,
priority: 0,
starts_from: null,
status: null
}
},
mounted () {
},
methods: {
addCondition () {
if (this.criteria == 'attribute' || this.criteria == 'attribute_family' || this.criteria == 'category') {
this.condition_on = this.criteria;
} else {
alert('please try again');
return false;
}
if (this.condition_on == 'attribute') {
this.conditions_list.push(this.attr_object);
this.attr_object = {
criteria: this.condition_on,
attribute: null,
condition: null,
value: null,
type: null,
options: null
};
} else if (this.condition_on == 'category') {
this.conditions_list.push(this.cat_object);
this.cat_object = {
criteria: this.condition_on,
category: 'category',
condition: null,
value: []
};
} else if (this.condition_on == 'attribute_family') {
this.conditions_list.push(this.fam_object);
this.fam_object = {
criteria: this.condition_on,
family: 'attribute_family',
condition: null,
value: null
};
}
},
enableCondition(event, index) {
this.conditions_list[index].type = this.attrs_input[event.target.selectedIndex - 1].type;
var this_this = this;
if (this.attrs_input[event.target.selectedIndex - 1].type == 'select' || this.attrs_input[event.target.selectedIndex - 1].type == 'multiselect') {
this.conditions_list[index].options = this.attrs_options[this.attrs_input[event.target.selectedIndex - 1].code];
this.conditions_list[index].value = [];
}
},
detectApply() {
if (this.apply == 0 || this.apply == 2) {
this.apply_prct = true;
this.apply_amt = false;
} else if (this.apply == 1 || this.apply == 3) {
this.apply_prct = false;
this.apply_amt = true;
}
},
removeAttr(index) {
this.conditions_list.splice(index, 1);
},
removeCat(index) {
this.cats.splice(index, 1);
},
onSubmit: function (e) {
this.$validator.validateAll().then(result => {
if (result) {
e.target.submit();
}
});
for (index in this.conditions_list) {
if (this.conditions_list[index].condition == null || this.conditions_list[index].condition == "" || this.conditions_list[index].condition == undefined) {
window.flashMessages = [{'type': 'alert-error', 'message': "{{ __('admin::app.promotion.catalog.condition-missing') }}" }];
this.$root.addFlashMessages();
return false;
} else if (this.conditions_list[index].value == null || this.conditions_list[index].value == "" || this.conditions_list[index].value == undefined) {
window.flashMessages = [{'type': 'alert-error', 'message': "{{ __('admin::app.promotion.catalog.condition-missing') }}" }];
this.$root.addFlashMessages();
return false;
}
}
if (this.conditions_list.length == 0) {
window.flashMessages = [{'type': 'alert-error', 'message': "{{ __('admin::app.promotion.catalog.condition-missing') }}" }];
this.$root.addFlashMessages();
return false;
}
this.all_conditions = JSON.stringify(this.conditions_list);
},
genericGroupCondition() {
this.generic_condition = false;
},
addFlashMessages() {
const flashes = this.$refs.flashes;
flashMessages.forEach(function(flash) {
flashes.addFlash(flash);
}, this);
}
}
});
</script>
@endpush
@stop

View File

@ -0,0 +1,542 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.promotion.edit-catalog-rule') }}
@stop
@section('content')
<div class="content">
<catalog-rule></catalog-rule>
</div>
@push('scripts')
<script type="text/x-template" id="catalog-rule-form-template">
<div>
<form method="POST" action="{{ route('admin.catalog-rule.update', $catalog_rule[5]->id) }}" @submit.prevent="onSubmit">
@csrf
<div class="page-header">
<div class="page-title">
<h1>
<i class="icon angle-left-icon back-link" onclick="history.length > 1 ? history.go(-1) : window.location = '{{ url('/admin/dashboard') }}';"></i>
{{ __('admin::app.promotion.edit-catalog-rule') }}
</h1>
</div>
<div class="page-action">
<button type="submit" class="btn btn-lg btn-primary">
{{ __('admin::app.promotion.edit-btn-title') }}
</button>
</div>
</div>
<div class="page-content">
<div class="form-container">
@csrf()
<accordian :active="true" title="Information">
<div slot="body">
<div class="control-group" :class="[errors.has('name') ? 'has-error' : '']">
<label for="name" class="required">{{ __('admin::app.promotion.general-info.name') }}</label>
<input type="text" class="control" name="name" v-model="name" v-validate="'required'" value="{{ old('name') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.name') }}&quot;">
<span class="control-error" v-if="errors.has('name')">@{{ errors.first('name') }}</span>
</div>
<div class="control-group" :class="[errors.has('description') ? 'has-error' : '']">
<label for="description">{{ __('admin::app.promotion.general-info.description') }}</label>
<textarea class="control" name="description" v-model="description" v-validate="'required'" value="{{ old('description') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.description') }}&quot;"></textarea>
<span class="control-error" v-if="errors.has('description')">@{{ errors.first('description') }}</span>
</div>
<div class="control-group" :class="[errors.has('customer_groups[]') ? 'has-error' : '']">
<label for="customer_groups" class="required">{{ __('admin::app.promotion.general-info.cust-groups') }}</label>
<select type="text" class="control" name="customer_groups[]" v-model="customer_groups" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.cust-groups') }}&quot;" multiple="multiple">
<option disabled="disabled">Select Customer Groups</option>
@foreach(app('Webkul\Customer\Repositories\CustomerGroupRepository')->all() as $channel)
<option value="{{ $channel->id }}">{{ $channel->name }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('customer_groups[]')">@{{ errors.first('customer_groups[]') }}</span>
</div>
<div class="control-group" :class="[errors.has('channels[]') ? 'has-error' : '']">
<label for="channels" class="required">{{ __('admin::app.promotion.general-info.channels') }}</label>
<select type="text" class="control" name="channels[]" v-model="channels" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.channels') }}&quot;" multiple="multiple">
<option disabled="disabled">Select Channels</option>
@foreach(app('Webkul\Core\Repositories\ChannelRepository')->all() as $channel)
<option value="{{ $channel->id }}">{{ $channel->name }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('channels[]')">@{{ errors.first('channels[]') }}</span>
</div>
<div class="control-group" :class="[errors.has('status') ? 'has-error' : '']">
<label for="status" class="required">{{ __('admin::app.promotion.general-info.status') }}</label>
<select type="text" class="control" name="status" v-model="status" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.status') }}&quot;">
<option disabled="disabled">Select status</option>
<option value="1">Yes</option>
<option value="0">No</option>
</select>
<span class="control-error" v-if="errors.has('status')">@{{ errors.first('status') }}</span>
</div>
<div class="control-group" :class="[errors.has('end_other_rules') ? 'has-error' : '']">
<label for="end_other_rules" class="required">{{ __('admin::app.promotion.general-info.end_other_rules') }}</label>
<select type="text" class="control" name="end_other_rules" v-model="end_other_rules" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.end_other_rules') }}&quot;">
<option disabled="disabled">Select option</option>
<option value="1">Yes</option>
<option value="0">No</option>
</select>
<span class="control-error" v-if="errors.has('end_other_rules')">@{{ errors.first('end_other_rules') }}</span>
</div>
<datetime :name="starts_from">
<div class="control-group" :class="[errors.has('starts_from') ? 'has-error' : '']">
<label for="starts_from" class="required">{{ __('admin::app.promotion.general-info.starts-from') }}</label>
<input type="text" class="control" v-model="starts_from" name="starts_from" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.starts-from') }}&quot;">
<span class="control-error" v-if="errors.has('starts_from')">@{{ errors.first('starts_from') }}</span>
</div>
</datetime>
<datetime :name="starts_from">
<div class="control-group" :class="[errors.has('ends_till') ? 'has-error' : '']">
<label for="ends_till" class="required">{{ __('admin::app.promotion.general-info.ends-till') }}</label>
<input type="text" class="control" v-model="ends_till" name="ends_till" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.ends-till') }}&quot;">
<span class="control-error" v-if="errors.has('ends_till')">@{{ errors.first('ends_till') }}</span>
</div>
</datetime>
<div class="control-group" :class="[errors.has('priority') ? 'has-error' : '']">
<label for="priority" class="required">{{ __('admin::app.promotion.general-info.priority') }}</label>
<input type="number" class="control" step="1" name="priority" v-model="priority" v-validate="'required|numeric|min_value:1'" value="{{ old('priority') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.priority') }}&quot;">
<span class="control-error" v-if="errors.has('priority')">@{{ errors.first('priority') }}</span>
</div>
</div>
</accordian>
<accordian :active="true" title="Conditions">
<div slot="body">
<div class="add-condition">
<div class="control-group" :class="[errors.has('criteria') ? 'has-error' : '']">
<label for="criteria" class="required">{{ __('admin::app.promotion.general-info.add-condition') }}</label>
<select type="text" class="control" name="criteria" v-model="criteria" v-validate="'required'" value="" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.cust-groups') }}&quot;">
<option value="attribute">Attribute</option>
<option value="category">Category</option>
<option value="attribute_family">Attribute Family</option>
</select>
<span class="control-error" v-if="errors.has('criteria')">@{{ errors.first('criteria') }}</span>
</div>
<span class="btn btn-primary btn-lg" v-on:click="addCondition">Add Condition</span>
</div>
<div class="condition-set" v-if="conditions_list.length">
<!-- Attribute -->
<div v-for="(condition, index) in conditions_list" :key="index">
<div class="control-container mt-20" v-if='conditions_list[index].criteria == "attribute"'>
<div class="title-bar">
{{-- <span>Group </span>
<input type="checkbox" v-model="condition_groups" v-on:click="groupSelected(index)" /> --}}
<span>Attribute is </span>
<span class="icon cross-icon" v-on:click="removeAttr(index)"></span>
</div>
<div class="control-group mt-10" :key="index">
<select class="control" name="attributes[]" v-model="conditions_list[index].attribute" v-validate="'required'" title="You Can Make Multiple Selections Here" style="margin-right: 15px;" v-on:change="enableCondition($event, index)">
<option disabled="disabled">Select attribute</option>
<option v-for="(attr_ip, i) in attrs_input" :value="attr_ip.code">@{{ attr_ip.name }}</option>
</select>
<div v-if='conditions_list[index].type == "text" || conditions_list[index].type == "textarea"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['text'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<input type="text" class="control" name="attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value">
</div>
<div v-if='conditions_list[index].type == "price"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['numeric'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<input type="number" step="0.1000" class="control" name="attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value">
</div>
<div v-else-if='conditions_list[index].type == "boolean"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
<option selected="selected">is</option>
</select>
<select class="control" name="attributes[]" v-model="conditions_list[index].value">
@foreach($catalog_rule[4]['boolean'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
</div>
<div v-else-if='conditions_list[index].type == "date"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['numeric'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<date>
<input type="text" class="control" v-model="conditions_list[index].value" name="attributes[]" v-validate="'required'" value="Enter Value">
</date>
</div>
<div v-else-if='conditions_list[index].type == "datetime"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['numeric'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<datetime>
<input type="text" class="control" v-model="conditions_list[index].value" name="attributes[]" v-validate="'required'" value="Enter Value">
</datetime>
</div>
<div v-else-if='conditions_list[index].type == "select" || conditions_list[index].type == "multiselect"'>
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['text'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<select class="control" v-model="conditions_list[index].value" name="attributes[]" v-validate="'required'" multiple>
<option v-for="option in conditions_list[index].options" :value="option.id">@{{ option.admin_name }}</option>
</select>
</div>
</div>
</div>
<div class="control-container mt-20" v-if='conditions_list[index].criteria == "category"'>
<div class="title-bar">
<span>Category </span>
{{-- <span>Group </span>
<input type="checkbox" v-model="condition_groups" v-on:click="groupSelected(index)" /> --}}
<span class="icon cross-icon" v-on:click="removeAttr(index)"></span>
</div>
<div class="control-group mt-10" :key="index">
<input type="hidden" class="control" name="attributes[]" v-model="conditions_list[index].category" v-validate="'required'" style="margin-right: 15px;" v-on:change="enableCondition($event, index)">
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['text'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<select class="control" name="attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value" multiple>
<option v-for="(category, index) in categories" :value="category.id">@{{ category.name }}</option>
</select>
</div>
</div>
<div class="control-container mt-20" v-if='conditions_list[index].criteria == "attribute_family"'>
<div class="title-bar">
<span>Attribute Family </span>
{{-- <span>Group </span>
<input type="checkbox" v-model="condition_groups" v-on:click="groupSelected(index)" /> --}}
<span class="icon cross-icon" v-on:click="removeAttr(index)"></span>
</div>
<div class="control-group mt-10" :key="index">
<input type="hidden" class="control" name="attributes[]" v-model="conditions_list[index].family" v-validate="'required'" style="margin-right: 15px;" v-on:change="enableCondition($event, index)">
<select class="control" name="attributes[]" v-model="conditions_list[index].condition" v-validate="'required'" style="margin-right: 15px;">
@foreach($catalog_rule[4]['boolean'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<select class="control" name="attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value">
<option v-for="(attr_family, index) in attr_families" :value="attr_family.id">@{{ attr_family.name }}</option>
</select>
</div>
</div>
</div>
<input type="hidden" name="all_conditions[]" v-model="all_conditions">
</div>
</div>
</accordian>
<accordian :active="true" title="Actions">
<div slot="body">
<div class="control-group" :class="[errors.has('apply') ? 'has-error' : '']">
<label for="apply" class="required">Apply</label>
<select class="control" name="apply" v-model="apply" v-validate="'required'" value="{{ old('apply') }}" data-vv-as="&quot;Apply As&quot;" v-on:change="detectApply">
@foreach($catalog_rule[3]['actions'] as $key => $value)
<option value="{{ $key }}">{{ __($value) }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('apply')">@{{ errors.first('apply') }}</span>
</div>
<div class="control-group" :class="[errors.has('disc_amount') ? 'has-error' : '']" v-if="apply_amt">
<label for="disc_amount" class="required">{{ __('admin::app.promotion.general-info.disc_amt') }}</label>
<input type="number" step="1.0000" class="control" name="disc_amount" v-model="disc_amount" v-validate="'required|decimal|min_value:0.0001'" value="{{ old('disc_amount') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.disc_amt') }}&quot;">
<span class="control-error" v-if="errors.has('disc_amount')">@{{ errors.first('disc_amount') }}</span>
</div>
<div class="control-group" :class="[errors.has('disc_percent') ? 'has-error' : '']" v-if="apply_prct">
<label for="disc_percent" class="required">{{ __('admin::app.promotion.general-info.disc_percent') }}</label>
<input type="number" step="0.5000" class="control" name="disc_percent" v-model="disc_percent" v-validate="'required|decimal|min_value:0.0001'" value="{{ old('disc_percent') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.disc_percent') }}&quot;">
<span class="control-error" v-if="errors.has('disc_percent')">@{{ errors.first('disc_percent') }}</span>
</div>
</div>
</accordian>
</div>
</div>
</form>
</div>
</script>
<script>
Vue.component('catalog-rule', {
template: '#catalog-rule-form-template',
inject: ['$validator'],
data () {
return {
all_conditions: [],
apply: null,
apply_amt: false,
apply_prct: false,
applied_config: @json($catalog_rule[3]),
conditions_list: [],
attr_families: @json($catalog_rule[8]),
attrs_input: @json($catalog_rule[0]),
attrs_options: @json($catalog_rule[2]),
global_condition: {
all_conditions: false,
all_condition: true
},
attr_object: {
criteria: 'attribute',
attribute: null,
condition: null,
type: null,
value: null
},
cat_object: {
criteria: 'category',
category: 'category',
condition: null,
value: []
},
fam_object: {
criteria: 'attribute_family',
family: 'attribute_family',
condition: null,
value: null
},
categories: @json($catalog_rule[1]),
cats: [],
cats_count: 0,
channels: [],
conditions: [],
criteria: null,
customer_groups: [],
description: null,
disc_amount: 0.0,
disc_percent: 0.0,
ends_till: null,
end_other_rules: null,
name: null,
priority: 0,
starts_from: null,
status: null,
}
},
mounted () {
all = @json($catalog_rule[5]);
this.name = all.name;
this.description = all.description;
this.apply = all.action_type;
this.end_other_rules = all.end_other_rules;
this.criteria = 'attribute';
channels = @json($catalog_rule[6]);
for (i in channels) {
this.channels.push(channels[i].channel_id);
}
customer_groups = @json($catalog_rule[7]);
for (i in customer_groups) {
this.customer_groups.push(customer_groups[i].customer_group_id);
}
this.conditions_list = JSON.parse(JSON.parse(all.conditions)[0]);
this.status = @json($catalog_rule[5]).status;
this.starts_from = @json($catalog_rule[5]).starts_from;
this.ends_till = @json($catalog_rule[5]).ends_till;
this.priority = @json($catalog_rule[5]).priority;
if (this.apply == 0 || this.apply == 2) {
this.apply_prct = true;
this.disc_percent = @json($catalog_rule[5]).disc_percent;
} else if (this.apply == 1 || this.apply == 3) {
this.apply_amt = true;
this.disc_amount = @json($catalog_rule[5]).disc_amount;
}
},
methods: {
addCondition () {
if (this.criteria == 'attribute' || this.criteria == 'attribute_family' || this.criteria == 'category') {
this.condition_on = this.criteria;
} else {
alert('please try again');
return false;
}
if (this.condition_on == 'attribute') {
this.conditions_list.push(this.attr_object);
this.attr_object = {
criteria: this.condition_on,
attribute: null,
condition: null,
value: null,
type: null,
options: null
};
} else if (this.condition_on == 'category') {
this.conditions_list.push(this.cat_object);
this.cat_object = {
criteria: this.condition_on,
category: 'category',
condition: null,
value: []
};
} else if (this.condition_on == 'attribute_family') {
this.conditions_list.push(this.fam_object);
this.fam_object = {
criteria: this.condition_on,
family: 'attribute_family',
condition: null,
value: null
};
}
},
enableCondition(event, index) {
this.conditions_list[index].type = this.attrs_input[event.target.selectedIndex - 1].type;
var this_this = this;
if (this.attrs_input[event.target.selectedIndex - 1].type == 'select' || this.attrs_input[event.target.selectedIndex - 1].type == 'multiselect') {
this.conditions_list[index].options = this.attrs_options[this.attrs_input[event.target.selectedIndex - 1].name];
this.conditions_list[index].value = [];
}
},
detectApply() {
if (this.apply == 0 || this.apply == 2) {
this.apply_prct = true;
this.apply_amt = false;
} else if (this.apply == 1 || this.apply == 3) {
this.apply_prct = false;
this.apply_amt = true;
}
},
removeAttr(index) {
this.conditions_list.splice(index, 1);
},
onSubmit: function (e) {
this.$validator.validateAll().then(result => {
if (result) {
e.target.submit();
}
});
for (index in this.conditions_list) {
if (this.conditions_list[index].condition == null || this.conditions_list[index].condition == "" || this.conditions_list[index].condition == undefined) {
window.flashMessages = [{'type': 'alert-error', 'message': "{{ __('admin::app.promotion.catalog.condition-missing') }}" }];
this.$root.addFlashMessages();
return false;
} else if (this.conditions_list[index].value == null || this.conditions_list[index].value == "" || this.conditions_list[index].value == undefined) {
window.flashMessages = [{'type': 'alert-error', 'message': "{{ __('admin::app.promotion.catalog.condition-missing') }}" }];
this.$root.addFlashMessages();
return false;
}
}
if (this.conditions_list.length == 0) {
window.flashMessages = [{'type': 'alert-error', 'message': "{{ __('admin::app.promotion.catalog.condition-missing') }}" }];
this.$root.addFlashMessages();
return false;
}
this.all_conditions = JSON.stringify(this.conditions_list);
},
addFlashMessages() {
const flashes = this.$refs.flashes;
flashMessages.forEach(function(flash) {
flashes.addFlash(flash);
}, this);
}
}
});
</script>
@endpush
@stop

View File

@ -0,0 +1,31 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.promotion.catalog-rule') }}
@stop
@section('content')
<div class="content">
<div class="page-header">
<div class="page-title">
<h1>{{ __('admin::app.promotion.catalog-rule') }}</h1>
</div>
<div class="page-action">
<a href="{{ route('admin.catalog-rule.apply') }}" class="btn btn-lg btn-primary">
{{ __('Apply Rules') }}
</a>
<a href="{{ route('admin.catalog-rule.create') }}" class="btn btn-lg btn-primary">
{{ __('admin::app.promotion.add-catalog-rule') }}
</a>
</div>
</div>
<div class="page-content">
@inject('catalogRuleGrid','Webkul\Admin\DataGrids\CatalogRuleDataGrid')
{!! $catalogRuleGrid->render() !!}
</div>
</div>
@endsection

View File

@ -13,27 +13,27 @@ class AttributeOptionTableSeeder extends Seeder
DB::table('attribute_options')->delete();
DB::table('attribute_options')->insert([
['id' => '1','admin_name' => 'Red','sort_order' => '1','attribute_id' => '23'],
['id' => '2','admin_name' => 'Green','sort_order' => '2','attribute_id' => '23'],
['id' => '3','admin_name' => 'Yellow','sort_order' => '3','attribute_id' => '23'],
['id' => '4','admin_name' => 'Black','sort_order' => '4','attribute_id' => '23'],
['id' => '5','admin_name' => 'White','sort_order' => '5','attribute_id' => '23'],
['id' => '6','admin_name' => 'S','sort_order' => '1','attribute_id' => '24'],
['id' => '7','admin_name' => 'M','sort_order' => '2','attribute_id' => '24'],
['id' => '8','admin_name' => 'L','sort_order' => '3','attribute_id' => '24'],
['id' => '9','admin_name' => 'XL','sort_order' => '4','attribute_id' => '24']
['id' => '1', 'admin_name' => 'Red', 'sort_order' => '1', 'attribute_id' => '23'],
['id' => '2', 'admin_name' => 'Green', 'sort_order' => '2', 'attribute_id' => '23'],
['id' => '3', 'admin_name' => 'Yellow', 'sort_order' => '3', 'attribute_id' => '23'],
['id' => '4', 'admin_name' => 'Black', 'sort_order' => '4', 'attribute_id' => '23'],
['id' => '5', 'admin_name' => 'White', 'sort_order' => '5', 'attribute_id' => '23'],
['id' => '6', 'admin_name' => 'S', 'sort_order' => '1', 'attribute_id' => '24'],
['id' => '7', 'admin_name' => 'M', 'sort_order' => '2', 'attribute_id' => '24'],
['id' => '8', 'admin_name' => 'L', 'sort_order' => '3', 'attribute_id' => '24'],
['id' => '9', 'admin_name' => 'XL', 'sort_order' => '4', 'attribute_id' => '24']
]);
DB::table('attribute_option_translations')->insert([
['id' => '1','locale' => 'en','label' => 'Red','attribute_option_id' => '1'],
['id' => '2','locale' => 'en','label' => 'Green','attribute_option_id' => '2'],
['id' => '3','locale' => 'en','label' => 'Yellow','attribute_option_id' => '3'],
['id' => '4','locale' => 'en','label' => 'Black','attribute_option_id' => '4'],
['id' => '5','locale' => 'en','label' => 'White','attribute_option_id' => '5'],
['id' => '6','locale' => 'en','label' => 'S','attribute_option_id' => '6'],
['id' => '7','locale' => 'en','label' => 'M','attribute_option_id' => '7'],
['id' => '8','locale' => 'en','label' => 'L','attribute_option_id' => '8'],
['id' => '9','locale' => 'en','label' => 'XL','attribute_option_id' => '9']
['id' => '1', 'locale' => 'en', 'label' => 'Red', 'attribute_option_id' => '1'],
['id' => '2', 'locale' => 'en', 'label' => 'Green', 'attribute_option_id' => '2'],
['id' => '3', 'locale' => 'en', 'label' => 'Yellow', 'attribute_option_id' => '3'],
['id' => '4', 'locale' => 'en', 'label' => 'Black', 'attribute_option_id' => '4'],
['id' => '5', 'locale' => 'en', 'label' => 'White', 'attribute_option_id' => '5'],
['id' => '6', 'locale' => 'en', 'label' => 'S', 'attribute_option_id' => '6'],
['id' => '7', 'locale' => 'en', 'label' => 'M', 'attribute_option_id' => '7'],
['id' => '8', 'locale' => 'en', 'label' => 'L', 'attribute_option_id' => '8'],
['id' => '9', 'locale' => 'en', 'label' => 'XL', 'attribute_option_id' => '9']
]);
}
}

View File

@ -153,6 +153,24 @@ class AttributeFamilyRepository extends Repository
return $family;
}
public function getPartial()
{
$attributeFamilies = $this->model->all();
$trimmed = array();
foreach($attributeFamilies as $key => $attributeFamily) {
if ($attributeFamily->name != null || $attributeFamily->name != "") {
$trimmed[$key] = [
'id' => $attributeFamily->id,
'code' => $attributeFamily->code,
'name' => $attributeFamily->name
];
}
}
return $trimmed;
}
/**
* @param $id
* @return void

View File

@ -210,4 +210,26 @@ class AttributeRepository extends Repository
return $attributes[$attributeFamily->id] = $attributeFamily->custom_attributes;
}
/**
* @return Object
*/
public function getPartial()
{
$attributes = $this->model->all();
$trimmed = array();
foreach($attributes as $key => $attribute) {
if ($attribute->code != 'tax_category_id' && ($attribute->type == 'select' || $attribute->type == 'multiselect' || $attribute->code == 'sku')) {
array_push($trimmed, [
'id' => $attribute->id,
'name' => $attribute->name,
'type' => $attribute->type,
'code' => $attribute->code
]);
}
}
return $trimmed;
}
}

View File

@ -212,4 +212,21 @@ class CategoryRepository extends Repository
$category->save();
}
}
public function getPartial()
{
$categories = $this->model->all();
$trimmed = array();
foreach ($categories as $key => $category) {
if ($category->name != null || $category->name != "") {
$trimmed[$key] = [
'id' => $category->id,
'name' => $category->name
];
}
}
return $trimmed;
}
}

View File

@ -12,6 +12,8 @@ use Webkul\Tax\Repositories\TaxCategoryRepository;
use Webkul\Checkout\Models\CartPayment;
use Webkul\Customer\Repositories\WishlistRepository;
use Webkul\Customer\Repositories\CustomerAddressRepository;
use Webkul\Discount\Repositories\CartRuleRepository as CartRule;
use Webkul\Discount\Helpers\Discount;
use Webkul\Product\Helpers\Price;
/**
@ -24,81 +26,94 @@ use Webkul\Product\Helpers\Price;
class Cart {
/**
* CartRepository model
* CartRepository instance
*
* @var mixed
*/
protected $cart;
/**
* CartItemRepository model
* CartItemRepository instance
*
* @var mixed
*/
protected $cartItem;
/**
* CustomerRepository model
* CustomerRepository instance
*
* @var mixed
*/
protected $customer;
/**
* CartAddressRepository model
* CartAddressRepository instance
*
* @var mixed
*/
protected $cartAddress;
/**
* ProductRepository model
* ProductRepository instance
*
* @var mixed
*/
protected $product;
/**
* TaxCategoryRepository model
* TaxCategoryRepository instance
*
* @var mixed
*/
protected $taxCategory;
/**
* WishlistRepository model
* WishlistRepository instance
*
* @var mixed
*/
protected $wishlist;
/**
* CustomerAddressRepository model
* CustomerAddressRepository instance
*
* @var mixed
*/
protected $customerAddress;
protected $customerAddress;
/**
* CartRule Repository instance
*/
protected $cartRule;
/**
* Discount helper instance
*/
protected $discount;
/**
* Suppress the session flash messages
*/
*/
protected $suppressFlash;
/**
* Product price helper instance
*/
*/
protected $price;
/**
* Create a new controller instance.
*
* @param Webkul\Checkout\Repositories\CartRepository $cart
* @param Webkul\Checkout\Repositories\CartItemRepository $cartItem
* @param Webkul\Checkout\Repositories\CartAddressRepository $cartAddress
* @param Webkul\Customer\Repositories\CustomerRepository $customer
* @param Webkul\Product\Repositories\ProductRepository $product
* @param Webkul\Product\Repositories\TaxCategoryRepository $taxCategory
* @param Webkul\Checkout\Repositories\CartRepository $cart
* @param Webkul\Checkout\Repositories\CartItemRepository $cartItem
* @param Webkul\Checkout\Repositories\CartAddressRepository $cartAddress
* @param Webkul\Customer\Repositories\CustomerRepository $customer
* @param Webkul\Product\Repositories\ProductRepository $product
* @param Webkul\Product\Repositories\TaxCategoryRepository $taxCategory
* @param Webkul\Product\Repositories\CustomerAddressRepository $customerAddress
* @param Webkul\Product\Repositories\CustomerAddressRepository $customerAddress
* @param Webkul\Discount\Repositories\CartRuleRepository $cartRule
* @param Webkul\Helpers\Discount $discount
* @return void
*/
public function __construct(
@ -110,6 +125,8 @@ class Cart {
TaxCategoryRepository $taxCategory,
WishlistRepository $wishlist,
CustomerAddressRepository $customerAddress,
CartRule $cartRule,
Discount $discount,
Price $price
)
{
@ -129,12 +146,15 @@ class Cart {
$this->customerAddress = $customerAddress;
$this->cartRule = $cartRule;
$this->discount = $discount;
$this->price = $price;
$this->suppressFlash = false;
}
/**
* Return current logged in customer
*
@ -1072,6 +1092,59 @@ class Cart {
return $finalData;
}
/**
* Save discount data for cart
*/
public function saveDiscount()
{
$rule = $impact['rule'];
$cart = $this->getCart();
//update the cart items
foreach($cart->items as $item) {
if ($rule->use_coupon) {
if ($rule->action_type != config('pricerules.cart.validation.0')) {
$item->update([
'coupon_code' => $rule->coupon->code,
'discount_amount' => core()->convertPrice($impact['amount'], $cart->channel_currency_code),
'base_discount_amount' => $impact['amount']
]);
} else {
$item->update([
'coupon_code' => $rule->coupon->code,
'discount_percent' => $rule->disc_amount
]);
}
} else {
if ($rule->action_type != config('pricerules.cart.validation.0')) {
$item->update([
'discount_amount' => core()->convertPrice($impact['amount'], $cart->channel_currency_code),
'base_discount_amount' => $impact['amount']
]);
} else {
$item->update([
'discount_percent' => $rule->disc_amount
]);
}
}
}
// update the cart
if ($rule->use_coupon) {
$cart->update([
'coupon_code' => $rule->coupons->code,
'discount_amount' => core()->convertPrice($impact['amount'], $cart->channel_currency_code),
'base_discount_amount' => $impact['amount']
]);
} else {
$cart->update([
'discount_amount' => core()->convertPrice($impact['amount'], $cart->channel_currency_code),
'base_discount_amount' => $impact['amount']
]);
}
}
/**
* Prepares data for order item
*
@ -1212,4 +1285,67 @@ class Cart {
}
}
}
public function applyCoupon($code)
{
$result = $this->discount->applyCouponAbleRule($code);
return $result;
}
public function applyNonCoupon()
{
$result = $this->discount->applyNonCouponAbleRule();
return $result;
}
public function removeCoupon()
{
$result = $this->discount->removeCoupon();
return $result;
}
public function leastWorthItem()
{
$cart = $this->getCart();
$leastValue = 999999999999;
$leastSubTotal = [];
foreach ($cart->items as $item) {
if ($item->price < $leastValue) {
$leastValue = $item->price;
$leastSubTotal = [
'id' => $item->id,
'total' => $item->total,
'base_total' => $leastValue,
'quantity' => $item->quantity
];
}
}
return $leastSubTotal;
}
public function maxWorthItem()
{
$cart = $this->getCart();
$maxValue = 0;
$maxSubTotal = [];
foreach ($cart->items as $item) {
if ($item->base_total > $maxValue) {
$maxValue = $item->total;
$maxSubTotal = [
'id' => $item->id,
'total' => $item->total,
'base_total' => $maxValue,
'quantity' => $item->quantity
];
}
}
return $maxSubTotal;
}
}

View File

@ -17,6 +17,6 @@ return [
\Webkul\Inventory\Providers\ModuleServiceProvider::class,
\Webkul\Product\Providers\ModuleServiceProvider::class,\Webkul\Sales\Providers\ModuleServiceProvider::class,
\Webkul\Tax\Providers\ModuleServiceProvider::class,
\Webkul\User\Providers\ModuleServiceProvider::class
\Webkul\User\Providers\ModuleServiceProvider::class,\Webkul\Discount\Providers\ModuleServiceProvider::class,
]
];

View File

@ -19,6 +19,10 @@ class CustomerGroupTableSeeder extends Seeder
'id' => 2,
'name' => 'Wholesale',
'is_user_defined' => 0,
], [
'id' => 3,
'name' => 'Not Logged In',
'is_user_defined' => 0,
]);
}
}

View File

@ -0,0 +1,8 @@
Discount Condition:
Attribute Family is all the attributes
Cart Attribute such as
base_total,
grand_total,
shipping_free,
shipping_not_free,

View File

@ -0,0 +1,27 @@
{
"name": "bagisto/laravel-discount",
"license": "MIT",
"authors": [
{
"name": "Prashant Singh",
"email": "prashant.singh852@webkul.com"
}
],
"require": {
"propaganistas/laravel-intl": "^2.0"
},
"autoload": {
"psr-4": {
"Webkul\\Discount\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Webkul\\Discount\\DiscountServiceProvider"
],
"aliases": {}
}
},
"minimum-stability": "dev"
}

View File

@ -0,0 +1,179 @@
<?php
return [
'conditions' => [
'numeric' => [
0 => 'Equals',
1 => 'Equals or greater',
2 => 'Equals or lesser',
3 => 'Greater than',
4 => 'Lesser than'
],
'text' => [
0 => 'is',
1 => 'is not',
2 => 'contains',
3 => 'does not contains'
],
'boolean' => [
0 => 'True/Yes',
1 => 'False/No',
]
],
'catalog' => [
'actions' => [
0 => 'admin::app.promotion.catalog.apply-percent',
1 => 'admin::app.promotion.catalog.apply-fixed',
2 => 'admin::app.promotion.catalog.adjust-to-percent',
3 => 'admin::app.promotion.catalog.adjust-to-value'
],
'attributes' => [
0 => [
'name' => 'Sub-total',
'type' => 'numeric'
],
1 => [
'name' => 'Total Items Quantity',
'type' => 'numeric'
],
2 => [
'name' => 'Total Weight',
'type' => 'numeric'
],
3 => [
'name' => 'Payment Method',
'type' => 'string'
],
4 => [
'name' => 'Shipping Postcode',
'type' => 'string'
],
5 => [
'name' => 'Shipping State',
'type' => 'string'
],
6 => [
'name' => 'Shipping Country',
'type' => 'string'
]
]
],
'cart' => [
'actions' => [
'percent_of_product' => 'Percentage of product',
'fixed_amount' => 'Apply as fixed amount',
'buy_a_get_b' => 'Get B amount back',
// 'fixed_amount_cart' => 'Fixed amount for whole cart'
],
'validation' => [
0 => 'percent_of_product',
1 => 'fixed_amount',
2 => 'buy_a_get_b',
3 => 'fixed_amount_cart'
],
'conditions' => [
'numeric' => [
'=' => 'Equals',
'>=' => 'Greater or equals',
'<=' => 'Lesser or equals',
'>' => 'Greater than',
'<' => 'Lesser than',
'{}' => 'Contains'
// '!{}' => 'Does not contains',
// '()' => 'Is one of',
// '!()' => 'Not is one of'
],
'text' => [
'=' => 'Equals',
'>=' => 'Greater or equals',
'<=' => 'Lesser or equals',
'>' => 'Greater than',
'<' => 'Lesser than',
'{}' => 'Contains',
'!{}' => 'Does not contains'
// '()' => 'Is one of',
// '!()' => 'Not is one of'
],
'string' => [
'=' => 'Equals',
'>=' => 'Greater or equals',
'<=' => 'Lesser or equals',
'>' => 'Greater than',
'<' => 'Lesser than',
'{}' => 'Contains',
'!{}' => 'Does not contains'
// '()' => 'Is one of',
// '!()' => 'Not is one of'
],
'boolean' => [
0 => 'True/Yes',
1 => 'False/No'
]
],
'attributes' => [
0 => [
'code' => 'sub_total',
'name' => 'Sub Total',
'type' => 'numeric'
],
1 => [
'code' => 'total_items',
'name' => 'Total Items',
'type' => 'numeric'
],
2 => [
'code' => 'total_weight',
'name' => 'Total Weight',
'type' => 'numeric'
],
3 => [
'code' => 'shipping_method',
'name' => 'Shipping Method',
'type' => 'string'
],
4 => [
'code' => 'payment_method',
'name' => 'Payment Method',
'type' => 'string'
],
5 => [
'code' => 'shipping_postcode',
'name' => 'Shipping Postcode',
'type' => 'string'
],
6 => [
'code' => 'shipping_state',
'name' => 'Shipping State',
'type' => 'string'
],
7 => [
'code' => 'shipping_country',
'name' => 'Shipping Country',
'type' => 'string'
],
8 => [
'code' => 'shipping_city',
'name' => 'Shipping City',
'type' => 'string'
]
]
],
'test_mode' => [
0 => 'all_are_true',
1 => 'all_are_false',
2 => 'any_of_true',
3 => 'all_of_false'
]
];

View File

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

View File

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

View File

@ -0,0 +1,6 @@
<?php
namespace Webkul\Discount\Contracts;
interface CartRuleChannels
{ }

View File

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

View File

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

View File

@ -0,0 +1,8 @@
<?php
namespace Webkul\Discount\Contracts;
interface CartRuleCustomerGroups
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,8 @@
<?php
namespace Webkul\Discount\Contracts;
interface CatalogRuleProducts
{
}

View File

@ -0,0 +1,8 @@
<?php
namespace Webkul\Discount\Contracts;
interface CatalogRuleProductsPrice
{
}

View File

@ -0,0 +1,61 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCartRuleTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::dropIfExists('discount_customer_group');
Schema::dropIfExists('discount_channels');
Schema::dropIfExists('discount_rules');
Schema::dropIfExists('discounts');
Schema::dropIfExists('products_grid');
Schema::create('cart_rules', function (Blueprint $table) {
$table->increments('id');
$table->string('name')->nullable();
$table->string('description')->nullable();
$table->datetime('starts_from')->nullable();
$table->datetime('ends_till')->nullable();
$table->boolean('status')->default(0);
$table->integer('per_customer')->unsigned()->default(0);
$table->boolean('is_guest')->default(0);
$table->boolean('use_coupon')->default(0);
$table->integer('usage_limit')->unsigned()->default(0);
$table->json('conditions')->nullable();
$table->json('actions')->nullable();
$table->boolean('end_other_rules')->default(0);
$table->integer('priority')->unsigned()->default(0);
$table->boolean('uses_attribute_conditions')->default(0);
$table->longtext('product_ids')->nullable();
$table->integer('sort_order')->unsigned()->default(0);
$table->string('action_type')->nullable();
$table->decimal('disc_amount', 12, 4)->default(0);
$table->decimal('disc_quantity', 12, 4)->default(0);
$table->string('disc_threshold')->default(0);
$table->integer('coupon_type')->default(1);
$table->boolean('auto_generation')->default(0);
$table->boolean('apply_to_shipping')->default(0);
$table->boolean('free_shipping')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('cart_rule');
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCartRuleChannelsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cart_rule_channels', function (Blueprint $table) {
$table->increments('id');
$table->integer('cart_rule_id')->unsigned();
$table->foreign('cart_rule_id')->references('id')->on('cart_rules')->onDelete('cascade');
$table->integer('channel_id')->unsigned();
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('cart_rule_channels');
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCartRuleCustomerGroupsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cart_rule_customer_groups', function (Blueprint $table) {
$table->increments('id');
$table->integer('cart_rule_id')->unsigned();
$table->foreign('cart_rule_id')->references('id')->on('cart_rules')->onDelete('cascade');
$table->integer('customer_group_id')->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('cart_rule_customer_groups');
}
}

View File

@ -0,0 +1,38 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCartRuleLabelsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cart_rule_labels', function (Blueprint $table) {
$table->increments('id');
$table->integer('channel_id')->unsigned()->nullable();
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
$table->integer('locale_id')->unsigned()->nullable();
$table->foreign('locale_id')->references('id')->on('locales')->onDelete('cascade');
$table->integer('cart_rule_id')->unsigned()->nullable();
$table->foreign('cart_rule_id')->references('id')->on('cart_rules')->onDelete('cascade');
$table->text('label')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('cart_rule_labels');
}
}

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCartruleCustomersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cart_rule_customers', function (Blueprint $table) {
$table->increments('id');
$table->integer('cart_rule_id')->unsigned();
$table->foreign('cart_rule_id')->references('id')->on('cart_rules')->onDelete('cascade');
$table->integer('customer_id')->unsigned();
$table->foreign('customer_id')->references('id')->on('customers')->onDelete('cascade');
$table->bigInteger('usage_throttle')->unsigned()->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('cartrule_customers');
}
}

View File

@ -0,0 +1,40 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCartruleCouponsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cart_rule_coupons', function (Blueprint $table) {
$table->increments('id');
$table->integer('cart_rule_id')->unsigned();
$table->foreign('cart_rule_id')->references('id')->on('cart_rules')->onDelete('cascade');
$table->string('prefix')->nullable();
$table->string('suffix')->nullable();
$table->string('code')->nullable();
$table->integer('usage_limit')->unsigned()->default(0);
$table->integer('usage_per_customer')->unsigned()->default(0);
$table->integer('usage_throttle')->unsigned()->default(0);
$table->integer('type')->unsigned()->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('cartrule_coupons');
}
}

View File

@ -0,0 +1,39 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCartRuleCouponsUsageTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cart_rule_coupons_usage', function (Blueprint $table) {
$table->increments('id');
$table->integer('coupon_id')->unsigned();
$table->foreign('coupon_id')->references('id')->on('cart_rules')->onDelete('cascade');
$table->integer('channel_id')->unsigned();
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
$table->integer('customer_id')->unsigned();
$table->foreign('customer_id')->references('id')->on('customers')->onDelete('cascade');
$table->bigInteger('usage')->unsigned()->default(0);
$table->date('expired_on');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('cart_rule_coupons_usage');
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCartRuleCartTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cart_rule_cart', function (Blueprint $table) {
$table->increments('id');
$table->integer('cart_rule_id')->unsigned()->nullable();
$table->foreign('cart_rule_id')->references('id')->on('cart_rules')->onDelete('cascade');
$table->integer('cart_id')->unsigned()->nullable();
$table->foreign('cart_id')->references('id')->on('cart')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('cart_rule_order');
}
}

View File

@ -0,0 +1,440 @@
<?php
namespace Webkul\Discount\Helpers;
use Webkul\Discount\Repositories\CartRuleRepository as CartRule;
use Webkul\Discount\Repositories\CartRuleCartRepository as CartRuleCart;
use Carbon\Carbon;
class Discount
{
/**
* To hold the cart rule repository instance
*/
protected $cartRule;
/**
* To hold the cart rule cart repository instance
*/
protected $cartRuleCart;
/**
* To hold if end rules are present or not.
*/
protected $endRuleActive = false;
/**
* disable coupon
*/
protected $disableCoupon = false;
public function __construct(CartRule $cartRule, CartRuleCart $cartRuleCart)
{
$this->cartRule = $cartRule;
$this->endRuleActive = false;
$this->cartRuleCart = $cartRuleCart;
}
public function applyNonCouponAbleRule()
{
if (auth()->guard('customer')->check()) {
$nonCouponAbleRules = $this->cartRule->findWhere([
'use_coupon' => 0,
'status' => 1
]);
} else {
$nonCouponAbleRules = $this->cartRule->findWhere([
'use_coupon' => 0,
'is_guest' => 1,
'status' => 1
]);
}
$canBeApplied = array();
// time based filter
foreach($nonCouponAbleRules as $rule) {
$report = $this->checkApplicability($rule);
$report['rule'] = $rule;
$passed = 0;
if ($rule->starts_from != null && $rule->ends_till == null) {
if (Carbon::parse($rule->starts_from) < now()) {
$passed = 1;
}
} else if ($rule->starts_from == null && $rule->ends_till != null) {
if (Carbon::parse($rule->ends_till) > now()) {
$passed = 1;
}
} else if ($rule->starts_from != null && $rule->ends_till != null) {
if (Carbon::parse($rule->starts_from) < now() && now() < Carbon::parse($rule->ends_till)) {
$passed = 1;
}
} else {
$passed = 1;
}
if ($passed) {
$report['used_coupon'] = false;
array_push($canBeApplied, $report);
}
}
//min priority
$minPriority = collect($canBeApplied)->min('priority');
$canBeApplied = collect($canBeApplied)->where('priority', $minPriority);
if (count($canBeApplied) > 1) {
$maxDiscount = collect($canBeApplied)->max('discount');
$canBeApplied = collect($canBeApplied)->where('discount', $maxDiscount);
$leastId = 999999999999;
if (count($canBeApplied) > 1) {
foreach($canBeApplied as $rule) {
if ($rule['rule']->id < $leastId) {
$leastId = $rule['rule']->id;
}
}
// fighting the edge case for non couponable discount rule
foreach($canBeApplied as $rule) {
if ($rule['rule']->id == $leastId) {
$rule['used_coupon'] = false;
$this->save($rule['rule']);
return $rule;
}
}
}
}
$this->save($canBeApplied[0]['rule']);
return $canBeApplied[0];
}
public function applyCouponAbleRule($code)
{
if (auth()->guard('customer')->check()) {
$couponAbleRules = $this->cartRule->findWhere([
'use_coupon' => 1,
'status' => 1
]);
} else {
$couponAbleRules = $this->cartRule->findWhere([
'use_coupon' => 1,
'is_guest' => 1,
'status' => 1
]);
}
$rule;
foreach ($couponAbleRules as $couponAbleRule) {
if ($couponAbleRule->coupons->code == $code) {
$rule = $couponAbleRule;
}
}
$useCouponable = false;
if (isset($rule)) {
$canBeApplied = array();
// time based filter
$report = $this->checkApplicability($rule);
$report['rule'] = $rule;
$passed = 0;
if ($rule->starts_from != null && $rule->ends_till == null) {
if (Carbon::parse($rule->starts_from) < now()) {
$passed = 1;
}
} else if ($rule->starts_from == null && $rule->ends_till != null) {
if (Carbon::parse($rule->ends_till) > now()) {
$passed = 1;
}
} else if ($rule->starts_from != null && $rule->ends_till != null) {
if (Carbon::parse($rule->starts_from) < now() && now() < Carbon::parse($rule->ends_till)) {
$passed = 1;
}
} else {
$passed = 1;
}
if ($passed) {
array_push($canBeApplied, $report);
$useCouponable = true;
$alreadyAppliedRule = $this->cartRuleCart->findWhere([
'cart_id' => \Cart::getCart()->id
]);
if ($alreadyAppliedRule->count() && ! ($alreadyAppliedRule->first()->cart_rule->priority < $canBeApplied[0]['rule']->priority)) {
unset($report);
$alreadyAppliedRule = $alreadyAppliedRule->first()->cart_rule;
// analyze impact
$report = $this->checkApplicability($alreadyAppliedRule);
$report['rule'] = $alreadyAppliedRule;
array_push($canBeApplied, $report);
//min priority
$minPriority = collect($canBeApplied)->min('priority');
//min priority rule
$canBeApplied = collect($canBeApplied)->where('priority', $minPriority);
if (count($canBeApplied) > 1) {
$maxDiscount = collect($canBeApplied)->max('discount');
$canBeApplied = collect($canBeApplied)->where('discount', $maxDiscount);
$leastId = 999999999999;
if (count($canBeApplied) > 1) {
foreach($canBeApplied as $rule) {
if ($rule['rule']->id < $leastId) {
$leastId = $rule['rule']->id;
}
}
// fighting the edge case for couponable discount rule
foreach($canBeApplied as $rule) {
if ($rule['rule']->id == $leastId) {
if($rule['rule']->use_coupon) {
$useCouponable = true;
}
}
}
} else {
if ($canBeApplied[0]['rule']->use_coupon) {
$useCouponable = true;
}
}
} else if (count($canBeApplied)) {
if ($canBeApplied[0]['rule']->use_coupon) {
$useCouponable = true;
}
}
if ($alreadyAppliedRule->end_other_rules) {
$useCouponable = false;
}
}
}
}
$canBeApplied = array();
if ($useCouponable) {
$report = $this->checkApplicability($rule);
$report['rule'] = $rule;
$report['used_coupon'] = $useCouponable;
$this->save($rule);
return $report;
} else {
return false;
}
}
public function checkApplicability($rule = null)
{
$cart = \Cart::getCart();
$report = array();
$result = 0;
//check conditions
if ($rule->conditions != null) {
$conditions = json_decode(json_decode($rule->conditions));
$test_mode = config('pricerules.test_mode.0');
if ($test_mode == config('pricerules.test_mode.0')) {
$result = $this->testIfAllConditionAreTrue($conditions, $cart);
} else if ($test_mode == config('pricerules.test_mode.1')) {
$result = $this->testIfAllConditionAreFalse($conditions, $cart);
} else if ($test_mode == config('pricerules.test_mode.2')) {
$result = $this->testIfAnyConditionIsTrue($conditions, $cart);
} else if ($test_mode == config('pricerules.test_mode.3')) {
$result = $this->testIfAnyConditionIsFalse($conditions, $cart);
}
}
if ($result) {
$report['conditions'] = true;
} else {
if ($rule->conditions == null)
$report['conditions'] = true;
else
$report['conditions'] = false;
}
//check endrule
if ($rule->end_other_rules) {
$report['end_other_rules'] = true;
} else {
$report['end_other_rules'] = false;
}
//calculate discount amount
$action_type = $rule->action_type; // action type used
$disc_threshold = $rule->disc_threshold; // atleast quantity by default 1 --> may be omitted in near future
$disc_amount = $rule->disc_amount; // value of discount
$disc_quantity = $rule->disc_quantity; //max quantity allowed to be discounted
$amountDiscounted = 0;
$leastWorthItem = \Cart::leastWorthItem();
$realQty = $leastWorthItem['quantity'];
if ($cart->items_qty >= $disc_threshold && $realQty >= $disc_quantity) {
if ($action_type == config('pricerules.cart.validation.0')) {
$amountDiscounted = $leastWorthItem['total'] * ($disc_amount / 100);
} else if ($action_type == config('pricerules.cart.validation.1')) {
$amountDiscounted = $leastWorthItem['total'] - $disc_amount;
if ($amountDiscounted < 0) {
$amountDiscounted = $leastWorthItem['total'];
}
} else if ($action_type == config('pricerules.cart.validation.2')) {
$amountDiscounted = $disc_amount;
}
}
$report['discount'] = $amountDiscounted;
$report['formatted_discount'] = core()->formatPrice($amountDiscounted, $cart->cart_currency_code);
$report['new_grand_total'] = $cart->grand_total - $amountDiscounted;
$report['formatted_new_grand_total'] = core()->formatPrice($cart->grand_total - $amountDiscounted, $cart->cart_currency_code);
$report['priority'] = $rule->priority;
return $report;
}
public function save($rule)
{
$cart = \Cart::getCart();
// create or update
$existingRule = $this->cartRuleCart->findWhere([
'cart_id' => $cart->id
]);
if (count($existingRule)) {
if ($existingRule->first()->cart_rule_id != $rule->id) {
$existingRule->first()->update([
'cart_rule_id' => $rule->id
]);
return true;
}
} else {
$this->cartRuleCart->create([
'cart_id' => $cart->id,
'cart_rule_id' => $rule->id
]);
return true;
}
return false;
}
public function removeCoupon()
{
$cart = \Cart::getCart();
$existingRule = $this->cartRuleCart->findWhere([
'cart_id' => $cart->id
]);
if ($existingRule->count()) {
if ($existingRule->first()->delete()) {
return true;
} else {
return false;
}
} else {
return false;
}
}
protected function testIfAllConditionAreTrue($conditions, $cart) {
$shipping_address = $cart->getShippingAddressAttribute();
$shipping_method = $cart->shipping_method;
$shipping_country = $shipping_address->country;
$shipping_state = $shipping_address->state;
$shipping_postcode = $shipping_address->postcode;
$shipping_city = $shipping_address->city;
$payment_method = $cart->payment->method;
$sub_total = $cart->base_sub_total;
$total_items = $cart->items_qty;
$total_weight = 0;
foreach($cart->items as $item) {
$total_weight = $total_weight + $item->base_total_weight;
}
$test_mode = config('pricerules.test_mode.0');
$test_conditions = config('pricerules.cart.conditions');
$result = 1;
foreach ($conditions as $condition) {
$actual_value = ${$condition->attribute};
$test_value = $condition->value;
$test_condition = $condition->condition;
if ($condition->type == 'numeric' || $condition->type == 'string' || $condition->type == 'text') {
if ($test_condition == '=') {
if ($actual_value != $test_value) {
$result = 0;
break;
}
} else if ($test_condition == '>=') {
if (! ($actual_value >= $test_value)) {
$result = 0;
break;
}
} else if ($test_condition == '<=') {
if (! ($actual_value <= $test_value)) {
$result = 0;
break;
}
} else if ($test_condition == '>') {
if (! ($actual_value > $test_value)) {
$result = 0;
break;
}
} else if ($test_condition == '<') {
if (! ($actual_value < $test_value)) {
$result = 0;
break;
}
} else if ($test_condition == '{}') {
if (! str_contains($actual_value, $test_value)) {
$result = 0;
break;
}
} else if ($test_condition == '!{}') {
if (str_contains($actual_value, $test_value)) {
$result = 0;
break;
}
}
}
}
return $result;
}
}

View File

@ -0,0 +1,72 @@
<?php
namespace Webkul\Discount\Helpers;
use Webkul\Product\Repositories\ProductRepository as Product;
use Webkul\Product\Repositories\ProductFlatRepository as ProductFlat;
use Webkul\Attribute\Repositories\AttributeRepository as Attribute;
use Webkul\Core\Repositories\LocaleRepository as Locale;
class FindProducts
{
/**
* To hold the product repository instance
*/
protected $product;
/**
* To hold the attribute repository instance
*/
protected $attribute;
public function __construct(Product $product, Attribute $attribute, ProductFlat $productFlat, Locale $locale)
{
$this->product = $product;
$this->attribute = $attribute;
$this->productFlat = $productFlat;
$this->locale = $locale;
}
public function findByConditions($conditions)
{
dd($conditions);
/**
* Empty collection instance to hold all the products that satisfy the conditions
*/
$products = array();
foreach ($conditions as $condition) {
$attribute = $this->attribute->findOneByField('code', $condition->attribute);
dump($attribute->type);
if ($condition->type == 'select' || $condition->type == 'multiselect') {
$values = $condition->value;
$attributeValues = array();
foreach ($condition->options as $option) {
foreach ($values as $value) {
if ($value == $option->id) {
array_push($attributeValues, $option);
}
}
}
$defaultChannelCode = core()->getDefaultChannel()->code;
$defaultLocaleCode = $this->locale->find(core()->getDefaultChannel()->default_locale_id)->code;
foreach ($attributeValues as $attributeValue) {
$productFound = $this->productFlat->findByField([$attribute->code => $attributeValue->id, 'locale' => $defaultLocaleCode, 'channel' => $defaultChannelCode]);
if ($productFound->count()) {
array_push($products, $productFound->toArray());
}
}
} else {
}
}
dd(array_flatten($products, 1));
}
}

View File

@ -0,0 +1,426 @@
<?php
namespace Webkul\Discount\Http\Controllers;
use Webkul\Attribute\Repositories\AttributeRepository as Attribute;
use Webkul\Attribute\Repositories\AttributeFamilyRepository as AttributeFamily;
use Webkul\Category\Repositories\CategoryRepository as Category;
use Webkul\Product\Repositories\ProductFlatRepository as Product;
use Webkul\Discount\Repositories\CatalogRuleRepository as CatalogRule;
use Webkul\Discount\Repositories\CartRuleRepository as CartRule;
use Webkul\Checkout\Repositories\CartRepository as Cart;
use Webkul\Discount\Repositories\CartRuleLabelsRepository as CartRuleLabels;
use Webkul\Discount\Repositories\CartRuleCouponsRepository as CartRuleCoupons;
use Validator;
/**
* Cart Rule controller
*
* @author Prashant Singh <prashant.singh852@webkul.com> @prashant-webkul
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CartRuleController extends Controller
{
/**
* Initialize _config, a default request parameter with route
*/
protected $_config;
/**
* Attribute $attribute
*/
protected $attribute;
/**
* AttributeFamily $attributeFamily
*/
protected $attributeFamily;
/**
* Category $category
*/
protected $category;
/**
* Product $product
*/
protected $product;
/**
* Property for Cart rule application
*/
protected $appliedConfig;
/**
* To hold Cart repository instance
*/
protected $cartRule;
/**
* To hold Rule Label repository instance
*/
protected $cartRuleLabel;
/**
* To hold Coupons Repository instance
*/
protected $cartRuleCoupon;
/**
* To hold the cart repository instance
*/
protected $cart;
public function __construct(Attribute $attribute, AttributeFamily $attributeFamily, Category $category, Product $product, CatalogRule $catalogRule, CartRule $cartRule, CartRuleCoupons $cartRuleCoupon, CartRuleLabels $cartRuleLabel)
{
$this->_config = request('_config');
$this->attribute = $attribute;
$this->attributeFamily = $attributeFamily;
$this->category = $category;
$this->product = $product;
$this->cartRule = $cartRule;
$this->cartRuleCoupon = $cartRuleCoupon;
$this->cartRuleLabel = $cartRuleLabel;
$this->appliedConfig = config('pricerules.cart');
}
public function index()
{
return view($this->_config['view']);
}
public function create()
{
return view($this->_config['view'])->with('cart_rule', [$this->appliedConfig, $this->fetchOptionableAttributes(), $this->getStatesAndCountries()]);
}
public function store()
{
$data = request()->all();
$validated = Validator::make($data, [
'name' => 'required|string',
'description' => 'string',
// 'customer_groups' => 'required|array',
'channels' => 'required|array',
'status' => 'required|boolean',
'use_coupon' => 'boolean|required',
// 'usage_limit' => 'numeric|min:0',
// 'per_customer' => 'numeric|min:0',
'action_type' => 'required|string',
'disc_amount' => 'required|numeric',
'disc_quantity' => 'numeric',
'disc_threshold' => 'numeric',
'free_shipping' => 'required|boolean',
'apply_to_shipping' => 'required|boolean',
'code' => 'string|required_if:auto_generation,0',
'all_conditions' => 'sometimes|nullable',
'label' => 'array|nullable'
]);
$data['usage_limit'] = 0;
$data['per_customer'] = 0;
if ($validated->fails()) {
session()->flash('error', 'Validation failed');
return redirect()->route('admin.cart-rule.create')
->withErrors($validated)
->withInput();
}
if ($data['starts_from'] == "" || $data['ends_till'] == "") {
$data['starts_from'] = null;
$data['ends_till'] = null;
}
unset($data['_token']);
$channels = $data['channels'];
unset($data['channels']);
$customer_groups = $data['customer_groups'];
unset($data['customer_groups']);
unset($data['criteria']);
$labels = $data['label'];
unset($data['label']);
unset($data['cart_attributes']);
unset($data['attributes']);
if (isset($data['disc_amount']) && $data['action_type'] == config('pricerules.cart.validations.2')) {
$data['actions'] = [
'action_type' => $data['action_type'],
'disc_amount' => $data['disc_amount'],
'disc_threshold' => $data['disc_threshold']
];
$data['disc_quantity'] = $data['disc_amount'];
} else {
$data['actions'] = [
'action_type' => $data['action_type'],
'disc_amount' => $data['disc_amount'],
'disc_quantity' => $data['disc_quantity']
];
}
$data['actions'] = json_encode($data['actions']);
if (! isset($data['all_conditions']) || $data['all_conditions'] == "[]") {
$data['conditions'] = null;
} else {
$data['conditions'] = json_encode($data['all_conditions']);
}
unset($data['all_conditions']);
if ($data['use_coupon']) {
// if (isset($data['auto_generation']) && $data['auto_generation']) {
$data['auto_generation'] = 0;
$coupons['code'] = $data['code'];
unset($data['code']);
// } else {
// $data['auto_generation'] = 1;
// }
// if (isset($data['prefix'])) {
// $coupons['prefix'] = $data['prefix'];
// unset($data['prefix']);
// }
// if (isset($data['suffix'])) {
// $coupons['suffix'] = $data['suffix'];
// unset($data['suffix']);
// }
}
if(isset($data['usage_limit'])) {
$coupons['usage_limit'] = $data['usage_limit'];
}
$ruleCreated = $this->cartRule->create($data);
$ruleGroupCreated = $this->cartRule->CustomerGroupSync($customer_groups, $ruleCreated);
$ruleChannelCreated = $this->cartRule->ChannelSync($channels, $ruleCreated);
if (isset($labels['global'])) {
foreach (core()->getAllChannels() as $channel) {
$label1['channel_id'] = $channel->id;
foreach($channel->locales as $locale) {
$label1['locale_id'] = $locale->id;
$label1['label'] = $labels['global'];
$label1['cart_rule_id'] = $ruleCreated->id;
$ruleLabelCreated = $this->cartRuleLabel->create($label1);
}
}
} else {
$label2['label'] = $labels['global'];
$label2['cart_rule_id'] = $ruleCreated->id;
$ruleLabelCreated = $this->cartRuleLabel->create($label2);
}
if(isset($coupons)) {
$coupons['cart_rule_id'] = $ruleCreated->id;
$coupons['usage_per_customer'] = $data['per_customer']; //0 is for unlimited usage
$couponCreated = $this->cartRuleCoupon->create($coupons);
}
if ($ruleCreated && $ruleGroupCreated && $ruleChannelCreated) {
if (isset($couponCreated) && $couponCreated) {
session()->flash('success', trans('admin::app.promotion.status.success-coupon'));
}
session()->flash('success', trans('admin::app.promotion.status.success'));
} else {
session()->flash('success', trans('admin::app.promotion.status.success'));
return redirect()->back();
}
return redirect()->route($this->_config['redirect']);
}
public function edit($id)
{
$cart_rule = $this->cartRule->find($id);
return view($this->_config['view'])->with('cart_rule', [$this->appliedConfig, $this->fetchOptionableAttributes(), $this->getStatesAndCountries(), $cart_rule]);
}
public function update($id)
{
$types = config('price_rules.cart.validations');
$validated = Validator::make(request()->all(), [
'name' => 'required|string',
'description' => 'string',
// 'customer_groups' => 'required|array',
'channels' => 'required|array',
'status' => 'required|boolean',
'use_coupon' => 'boolean|required',
// 'usage_limit' => 'numeric|min:0',
// 'per_customer' => 'numeric|min:0',
'action_type' => 'required|string',
'disc_amount' => 'required|numeric',
'disc_quantity' => 'required|numeric',
'disc_threshold' => 'required|numeric',
'free_shipping' => 'required|boolean',
'apply_to_shipping' => 'required|boolean',
'code' => 'string|required_if:user_coupon,1',
'all_conditions' => 'present',
'label' => 'array|nullable'
]);
$data['usage_limit'] = 0;
$data['per_customer'] = 0;
if ($validated->fails()) {
session()->flash('error', 'Validation failed');
return redirect()->route('admin.cart-rule.create')
->withErrors($validated)
->withInput();
}
$data = request()->all();
if ($data['starts_from'] == "" || $data['ends_till'] == "") {
$data['starts_from'] = null;
$data['ends_till'] = null;
}
unset($data['_token']);
$channels = $data['channels'];
unset($data['channels']);
// $customer_groups = $data['customer_groups'];
// unset($data['customer_groups']);
// unset($data['criteria']);
if (isset($data['label'])) {
$labels = $data['label'];
unset($data['label']);
}
unset($data['cart_attributes']);
unset($data['attributes']);
if (isset($data['disc_amount']) && $data['action_type'] == config('pricerules.cart.validations.2')) {
$data['actions'] = [
'action_type' => $data['action_type'],
'disc_amount' => $data['disc_amount'],
'disc_threshold' => $data['disc_threshold']
];
$data['disc_quantity'] = $data['disc_amount'];
} else {
$data['actions'] = [
'action_type' => $data['action_type'],
'disc_amount' => $data['disc_amount'],
'disc_quantity' => $data['disc_quantity']
];
}
$data['actions'] = json_encode($data['actions']);
if (! isset($data['all_conditions']) || $data['all_conditions'] == "[]") {
$data['conditions'] = null;
} else {
$data['conditions'] = json_encode($data['all_conditions']);
}
unset($data['all_conditions']);
if ($data['use_coupon']) {
// if (isset($data['auto_generation']) && $data['auto_generation']) {
$data['auto_generation'] = 0;
$coupons['code'] = $data['code'];
unset($data['code']);
// } else {
// $data['auto_generation'] = 1;
// }
// if (isset($data['prefix'])) {
// $coupons['prefix'] = $data['prefix'];
// unset($data['prefix']);
// }
// if (isset($data['suffix'])) {
// $coupons['suffix'] = $data['suffix'];
// unset($data['suffix']);
// }
}
if (isset($data['usage_limit'])) {
$coupons['usage_limit'] = $data['usage_limit'];
}
$ruleUpdated = $this->cartRule->update($data, $id);
// $ruleGroupUpdated = $this->cartRule->CustomerGroupSync($customer_groups, $ruleUpdated);
$ruleChannelUpdated = $this->cartRule->ChannelSync($channels, $ruleUpdated);
$labelsUpdated = $this->cartRule->LabelSync($labels, $ruleUpdated);
if (isset($coupons)) {
$coupons['cart_rule_id'] = $ruleUpdated->id;
$coupons['usage_per_customer'] = $data['per_customer']; //0 is for unlimited usage
$couponUpdated = $ruleUpdated->coupons->update($coupons);
}
if ($ruleUpdated && $ruleChannelUpdated) {
if (isset($couponUpdated) && $couponUpdated) {
session()->flash('info', trans('admin::app.promotion.status.success-coupon'));
}
session()->flash('info', trans('admin::app.promotion.status.update-success'));
} else {
session()->flash('info', trans('admin::app.promotion.status.update-success'));
return redirect()->back();
}
return redirect()->route($this->_config['redirect']);
}
public function destroy($id)
{
$cartRule = $this->cartRule->findOrFail($id);
if ($cartRule->delete()) {
session()->flash('success', trans('admin::app.promotion.status.delete-success'));
return response()->json(['message' => true], 200);
} else {
session()->flash('success', trans('admin::app.promotion.status.delete-failed'));
return response()->json(['message' => false], 400);
}
}
public function getStatesAndCountries()
{
$countries = core()->countries()->toArray();
$states = core()->groupedStatesByCountries();
return [
'countries' => $countries,
'states' => $states
];
}
public function fetchOptionableAttributes()
{
$attributesWithOptions = array();
foreach($this->attribute->all() as $attribute) {
if (($attribute->type == 'select' || $attribute->type == 'multiselect') && $attribute->code != 'tax_category_id') {
$attributesWithOptions[$attribute->admin_name] = $attribute->options->toArray();
}
}
return $attributesWithOptions;
}
}

View File

@ -0,0 +1,307 @@
<?php
namespace Webkul\Discount\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Webkul\Attribute\Repositories\AttributeRepository as Attribute;
use Webkul\Attribute\Repositories\AttributeFamilyRepository as AttributeFamily;
use Webkul\Category\Repositories\CategoryRepository as Category;
use Webkul\Product\Repositories\ProductFlatRepository as Product;
use Webkul\Discount\Repositories\CatalogRuleRepository as CatalogRule;
use Webkul\Discount\Repositories\CatalogRuleChannelsRepository as CatalogRuleChannels;
use Webkul\Discount\Repositories\CatalogRuleCustomerGroupsRepository as CatalogRuleCustomerGroups;
use Webkul\Discount\Helpers\FindProducts;
/**
* Catalog Rule controller
*
* @author Prashant Singh <prashant.singh852@webkul.com> @prashant-webkul
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CatalogRuleController extends Controller
{
/**
* Initialize _config, a default request parameter with route
*/
protected $_config;
/**
* Attribute $attribute
*/
protected $attribute;
/**
* AttributeFamily $attributeFamily
*/
protected $attributeFamily;
/**
* Category $category
*/
protected $category;
/**
* Product $product
*/
protected $product;
/**
* Property for catalog rule application
*/
protected $appliedConfig;
/**
* Property for catalog rule application
*/
protected $appliedConditions;
/**
* Property to hold Catalog Rule Channels Repository
*/
protected $catalogRuleChannels;
/**
* Property to hold Catalog Rule Customer Groups Repository
*/
protected $catalogRuleCustomerGroups;
/**
* To hold the catalog repository instance
*/
protected $catalogRule;
/**
* Find products using conditions helper instance
*/
protected $findProducts;
public function __construct(Attribute $attribute, AttributeFamily $attributeFamily, Category $category, Product $product, CatalogRule $catalogRule, CatalogRuleChannels $catalogRuleChannels, CatalogRuleCustomerGroups $catalogRuleCustomerGroups, FindProducts $findProducts)
{
$this->_config = request('_config');
$this->attribute = $attribute;
$this->attributeFamily = $attributeFamily;
$this->category = $category;
$this->product = $product;
$this->catalogRule = $catalogRule;
$this->catalogRuleChannels = $catalogRuleChannels;
$this->catalogRuleCustomerGroups = $catalogRuleCustomerGroups;
$this->appliedConfig = config('pricerules.catalog');
$this->appliedConditions = config('pricerules.conditions');
$this->findProducts = $findProducts;
}
public function index()
{
return view($this->_config['view']);
}
public function create()
{
return view($this->_config['view'])->with('catalog_rule', [$this->attribute->getPartial(), $this->category->getPartial(), $this->fetchOptionableAttributes(), $this->appliedConfig, $this->appliedConditions, $this->attributeFamily->getPartial()]);
}
public function store()
{
$this->validate(request(), [
'name' => 'required|string',
'description' => 'string',
'customer_groups' => 'required',
'channels' => 'required',
'starts_from' => 'required|date',
'ends_till' => 'required|date',
'status' => 'required|boolean',
'end_other_rules' => 'required|boolean',
'priority' => 'required|numeric',
'criteria' => 'required',
'all_conditions' => 'required|array',
'apply' => 'required|numeric|min:0|max:3',
'disc_amount' => 'sometimes',
'disc_percent' => 'sometimes',
]);
$catalog_rule = request()->all();
$catalog_rule_channels = array();
$catalog_rule_customer_groups = array();
$catalog_rule_channels = $catalog_rule['channels'];
$catalog_rule_customer_groups = $catalog_rule['customer_groups'];
unset($catalog_rule['channels']); unset($catalog_rule['customer_groups']);
unset($catalog_rule['criteria']);
$catalog_rule['conditions'] = $catalog_rule['all_conditions'];
unset($catalog_rule['all_conditions']);
if (isset($catalog_rule['disc_amount'])) {
$catalog_rule['action_type'] = $catalog_rule['apply'];
$catalog_rule['actions'] = [
'action_type' => $catalog_rule['apply'],
'disc_amount' => $catalog_rule['disc_amount']
];
} else if (isset($catalog_rule['disc_percent'])) {
$catalog_rule['action_type'] = $catalog_rule['apply'];
$catalog_rule['actions'] = [
'action_type' => $catalog_rule['apply'],
'disc_percent' => $catalog_rule['disc_percent'],
];
}
unset($catalog_rule['apply']);
unset($catalog_rule['attributes']);
unset($catalog_rule['_token']);
unset($catalog_rule['all_actions']);
$catalog_rule['actions'] = json_encode($catalog_rule['actions']);
$catalog_rule['conditions'] = json_encode($catalog_rule['conditions']);
$catalogRule = $this->catalogRule->create($catalog_rule);
foreach($catalog_rule_channels as $catalog_rule_channel) {
$data['catalog_rule_id'] = $catalogRule->id;
$data['channel_id'] = $catalog_rule_channel;
$catalogRuleChannels = $this->catalogRuleChannels->create($data);
}
unset($data);
foreach ($catalog_rule_customer_groups as $catalog_rule_customer_group) {
$data['catalog_rule_id'] = $catalogRule->id;
$data['customer_group_id'] = $catalog_rule_customer_group;
$catalogRuleCustomerGroups = $this->catalogRuleCustomerGroups->create($data);
}
if($catalogRule && $catalogRuleChannels && $catalogRuleCustomerGroups) {
session()->flash('success', trans('admin::app.promotion.status.success'));
return redirect()->route('admin.catalog-rule.index');
} else {
session()->flash('error', trans('admin::app.promotion.status.failed'));
return redirect()->back();
}
}
public function edit($id)
{
$catalog_rule = $this->catalogRule->find($id);
$catalog_rule_channels = $this->catalogRuleChannels->findByField('catalog_rule_id', $id);
$catalog_rule_customer_groups = $this->catalogRuleCustomerGroups->findByField('catalog_rule_id', $id);
return view($this->_config['view'])->with('catalog_rule', [$this->attribute->getPartial(), $this->category->getPartial(), $this->fetchOptionableAttributes(), $this->appliedConfig, $this->appliedConditions, $catalog_rule, $catalog_rule_channels, $catalog_rule_customer_groups, $this->attributeFamily->getPartial()]);
}
public function update($id)
{
$this->validate(request(), [
'name' => 'required|string',
'description' => 'string',
'customer_groups' => 'required',
'channels' => 'required',
'starts_from' => 'required|date',
'ends_till' => 'required|date',
'status' => 'required|boolean',
'end_other_rules' => 'required|boolean',
'priority' => 'required|numeric',
'criteria' => 'required',
'all_conditions' => 'required|array',
'apply' => 'required|numeric|min:0|max:3',
'disc_amount' => 'sometimes',
'disc_percent' => 'sometimes',
]);
$catalog_rule = request()->all();
$catalog_rule_channels = array();
$catalog_rule_customer_groups = array();
$catalog_rule_channels = $catalog_rule['channels'];
$catalog_rule_customer_groups = $catalog_rule['customer_groups'];
unset($catalog_rule['channels']); unset($catalog_rule['customer_groups']);
unset($catalog_rule['criteria']);
$catalog_rule['conditions'] = $catalog_rule['all_conditions'];
unset($catalog_rule['all_conditions']);
if (isset($catalog_rule['disc_amount'])) {
$catalog_rule['action_type'] = $catalog_rule['apply'];
$catalog_rule['actions'] = [
'action_type' => $catalog_rule['apply'],
'disc_amount' => $catalog_rule['disc_amount']
];
} else if (isset($catalog_rule['disc_percent'])) {
$catalog_rule['action_type'] = $catalog_rule['apply'];
$catalog_rule['actions'] = [
'action_type' => $catalog_rule['apply'],
'disc_percent' => $catalog_rule['disc_percent'],
];
}
unset($catalog_rule['apply']);
unset($catalog_rule['attributes']);
unset($catalog_rule['_token']);
unset($catalog_rule['all_actions']);
$catalog_rule['actions'] = json_encode($catalog_rule['actions']);
$catalog_rule['conditions'] = json_encode($catalog_rule['conditions']);
$catalogRule = $this->catalogRule->update($catalog_rule, $id);
$catalogRuleChannels = $this->catalogRule->ChannelSync($catalog_rule_channels, $catalogRule);
$catalogRuleCustomerGroups = $this->catalogRule->CustomerGroupSync($catalog_rule_customer_groups, $catalogRule);
if($catalogRule && $catalogRuleChannels && $catalogRuleCustomerGroups) {
session()->flash('info', trans('admin::app.promotion.status.update-success'));
return redirect()->route($this->_config['redirect']);
} else {
session()->flash('error', trans('admin::app.promotion.status.update-failed'));
return redirect()->back();
}
}
public function applyRules()
{
$catalogRules = $this->catalogRule->all();
$decoded = json_decode($catalogRules->first()->conditions);
$conditions = json_decode($decoded[0]);
$optionableAttributes = $this->fetchOptionableAttributes();
$results = $this->findProducts->findByConditions($conditions);
dd($results);
}
public function fetchOptionableAttributes()
{
$attributesWithOptions = array();
foreach($this->attribute->all() as $attribute) {
if (($attribute->type == 'select' || $attribute->type == 'multiselect') && $attribute->code != 'tax_category_id') {
$attributesWithOptions[$attribute->code] = $attribute->options->toArray();
}
}
return $attributesWithOptions;
}
public function destroy($id)
{
$catalogRule = $this->catalogRule->findOrFail($id);
if ($catalogRule->delete()) {
session()->flash('success', trans('admin::app.promotion.delete-success'));
return response()->json(['message' => true], 200);
} else {
session()->flash('success', trans('admin::app.promotion.delete-failed'));
return response()->json(['message' => false], 400);
}
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Webkul\Discount\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

View File

@ -0,0 +1,39 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Contracts\CartRule as CartRuleContract;
use Webkul\Discount\Models\CartRuleChannelsProxy as CartRuleChannels;
use Webkul\Discount\Models\CartRuleCustomerGroupsProxy as CartRuleCustomerGroups;
use Webkul\Discount\Models\CartRuleCouponsProxy as CartRuleCoupons;
use Webkul\Discount\Models\CartRuleLabelsProxy as CartRuleLabels;
class CartRule extends Model implements CartRuleContract
{
protected $table = 'cart_rules';
protected $guarded = ['created_at', 'updated_at'];
protected $with = ['channels', 'customer_groups', 'coupons', 'labels'];
public function channels()
{
return $this->hasMany(CartRuleChannels::modelClass());
}
public function customer_groups()
{
return $this->hasMany(CartRuleCustomerGroups::modelClass());
}
public function coupons()
{
return $this->hasOne(CartRuleCoupons::modelClass());
}
public function labels()
{
return $this->hasMany(CartRuleLabels::modelClass());
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Models\CartRuleProxy as CartRule;
use Webkul\Discount\Contracts\CartRuleCart as CartRuleCartContract;
class CartRuleCart extends Model implements CartRuleCartContract
{
protected $table = 'cart_rule_cart';
protected $guarded = ['created_at', 'updated_at'];
protected $with = ['cart_rule'];
public function cart_rule()
{
return $this->hasOne(CartRule::modelClass(), 'id', 'cart_rule_id');
}
}

View File

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

View File

@ -0,0 +1,13 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Contracts\CartRuleChannels as CartRuleChannelsContract;
class CartRuleChannels extends Model implements CartRuleChannelsContract
{
protected $table = 'cart_rule_channels';
protected $guarded = ['created_at', 'updated_at'];
}

View File

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

View File

@ -0,0 +1,24 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Contracts\CartRuleCoupons as CartRuleCouponsContract;
use Webkul\Discount\Models\CartRuleProxy as CartRule;
class CartRuleCoupons extends Model implements CartRuleCouponsContract
{
protected $table = 'cart_rule_coupons';
protected $guarded = ['created_at', 'updated_at'];
public function cart_rule()
{
return $this->belongsTo(CartRule::modelClass(), 'cart_rule_id');
}
public function coupons_usage()
{
return $this->hasOne(CartRuleCouponsUsage::modelClass());
}
}

View File

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

View File

@ -0,0 +1,14 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Contracts\CartRuleCouponsUsage as CartRuleCouponsUsageContract;
use Webkul\Discount\Models\CartRuleProxy as CartRule;
class CartRuleCouponsUsage extends Model implements CartRuleCouponsUsageContract
{
protected $table = 'cart_rule_coupons_usage';
protected $guarded = ['created_at', 'updated_at'];
}

View File

@ -0,0 +1,9 @@
<?php
namespace Webkul\Discount\Models;
use Konekt\Concord\Proxies\ModelProxy;
class CartRuleCouponsUsageProxy extends ModelProxy
{
}

View File

@ -0,0 +1,13 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Contracts\CartRuleCustomerGroups as CartRuleCustomerGroupContract;
class CartRuleCustomerGroups extends Model implements CartRuleCustomerGroupContract
{
protected $table = 'cart_rule_customer_groups';
protected $guarded = ['created_at', 'updated_at'];
}

View File

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

View File

@ -0,0 +1,19 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Contracts\CartRuleCustomers as CartRuleCustomersContract;
use Webkul\Discount\Models\CartRuleProxy as CartRule;
class CartRuleCustomers extends Model implements CartRuleCustomersContract
{
protected $table = 'cart_rule_customers';
protected $guarded = ['created_at', 'updated_at'];
public function cart_rule()
{
return $this->belongsTo(CartRule::modelClass(), 'cart_rule_id');
}
}

View File

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

View File

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

View File

@ -0,0 +1,33 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Contracts\CartRuleLabels as CartRuleLabelsContract;
use Webkul\Discount\Models\CartRuleProxy as CartRule;
use Webkul\Core\Models\LocaleProxy as Locale;
use Webkul\Core\Models\ChannelProxy as Channel;
class CartRuleLabels extends Model implements CartRuleLabelsContract
{
protected $table = 'cart_rule_labels';
protected $guarded = ['created_at', 'updated_at'];
protected $with = ['locale', 'channel'];
public function cart_rule()
{
return $this->belongsTo(CartRule::modelClass(), 'cart_rule_id');
}
public function locale()
{
return $this->hasOne(Locale::modelClass(), 'id', 'locale_id');
}
public function channel()
{
return $this->hasOne(Channel::modelClass(), 'id', 'channel_id');
}
}

View File

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

View File

@ -0,0 +1,25 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Contracts\CatalogRule as CatalogRuleContract;
use Webkul\Discount\Models\CatalogRuleChannelsProxy as CatalogRuleChannels;
use Webkul\Discount\Models\CatalogRuleCustomerGroupsProxy as CatalogRuleCustomerGroups;
class CatalogRule extends Model implements CatalogRuleContract
{
protected $table = 'catalog_rules';
protected $guarded = ['created_at', 'updated_at'];
public function channels()
{
return $this->hasMany(CatalogRuleChannels::modelClass());
}
public function customer_groups()
{
return $this->hasMany(CatalogRuleCustomerGroups::modelClass());
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Contracts\CatalogRuleChannels as CatalogRuleChannelsContract;
use Webkul\Discount\Models\CatalogRuleProxy as CatalogRule;
class CatalogRuleChannels extends Model implements CatalogRuleChannelsContract
{
protected $table = 'catalog_rule_channels';
protected $guarded = ['created_at', 'updated_at'];
public function catalog_rule()
{
return $this->belongsTo(CatalogRule::modelClass(), 'catalog_rule_id');
}
}

View File

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

View File

@ -0,0 +1,19 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Contracts\CatalogRuleCustomerGroups as CatalogRuleCustomerGroupsContract;
use Webkul\Discount\Models\CatalogRuleProxy as CatalogRule;
class CatalogRuleCustomerGroups extends Model implements CatalogRuleCustomerGroupsContract
{
protected $table = 'catalog_rule_customer_groups';
protected $guarded = ['created_at', 'updated_at'];
public function catalog_rule()
{
return $this->belongsTo(CatalogRule::modelClass(), 'catalog_rule_id');
}
}

View File

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

View File

@ -0,0 +1,13 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Contracts\CatalogRuleProducts as CatalogRuleProductsContract;
class CatalogRuleProducts extends Model implements CatalogRuleProductsContract
{
protected $table = 'cart_rules_products';
protected $guarded = ['created_at', 'updated_at'];
}

View File

@ -0,0 +1,13 @@
<?php
namespace Webkul\Discount\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Discount\Contracts\CatalogRuleProductsPrice as CatalogRuleProductsPriceContract;
class CatalogRuleProductsPrice extends Model implements CatalogRuleProductsPriceContract
{
protected $table = 'cart_rules_products_price';
protected $guarded = ['created_at', 'updated_at'];
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,39 @@
<?php
namespace Webkul\Discount\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Routing\Router;
class DiscountServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* @return void
*/
public function boot(Router $router)
{
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
}
/**
* Register services.
*
* @return void
*/
public function register()
{
$this->registerConfig();
}
/**
* To merge the price rule configuration in price rule configuration
*/
protected function registerConfig()
{
$this->mergeConfigFrom(
dirname(__DIR__) . '/Config/rule-conditions.php', 'pricerules'
);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Webkul\Discount\Providers;
use Konekt\Concord\BaseModuleServiceProvider;
class ModuleServiceProvider extends BaseModuleServiceProvider
{
protected $models = [
\Webkul\Discount\Models\CatalogRule::class,
\Webkul\Discount\Models\CatalogRuleChannels::class,
\Webkul\Discount\Models\CatalogRuleCustomerGroups::class,
\Webkul\Discount\Models\CatalogRuleProducts::class,\Webkul\Discount\Models\CatalogRuleProductsPrice::class,
\Webkul\Discount\Models\CartRule::class,
\Webkul\Discount\Models\CartRuleChannels::class,
\Webkul\Discount\Models\CartRuleCustomerGroups::class,
\Webkul\Discount\Models\CartRuleCoupons::class,
\Webkul\Discount\Models\CartRuleLabels::class,
\Webkul\Discount\Models\CartRuleCouponsUsage::class,
\Webkul\Discount\Models\CartRuleCustomers::class,
\Webkul\Discount\Models\CartRuleCart::class,
];
}

Some files were not shown because too many files have changed in this diff Show More