Merge pull request #13 from Haendlerbund/merge-bagisto-master

Merge bagisto master
This commit is contained in:
hb-monah 2020-01-13 17:02:18 +01:00 committed by GitHub
commit 4d578b0b4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
263 changed files with 7847 additions and 8391 deletions

View File

@ -4,8 +4,9 @@ APP_VERSION=0.1.8
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
APP_TIMEZONE='Asia/Kolkata'
LOG_CHANNEL=stack
APP_CURRENCY=USD
DB_CONNECTION=mysql
DB_HOST=127.0.0.1

View File

@ -2,6 +2,70 @@
#### This changelog consists the bug & security fixes and new features being included in the releases listed below.
## **v0.1.9 (20th of December, 2019)** - *Release*
* #1875 [fixed] - Discount gets applied on cart if condition is "Visible Individually" is set to yes and product added in cart is not visible individually.
* #1868 [fixed] - Getting exception on checkout if action Buy X Get Y free is selected in cart rule.
* #1838 [fixed] - Cart rule not working for condition sku(children only) and sku(parent only).
* #1835 [fixed] - Getting exception if condition value remains blank.
* #1831 [fixed] - Negative sign is not required in discount amount.
* #1830 [fixed] - If Coupon type is selected as "No Coupon" then coupon code should not generate for that particular cart rule.
* #1904 [fixed] - Existing tax should display as list on creating catalog rule if in condition "Tax Category" is selected.
* #1903 [fixed] - UI issue in condition field
* #1895 [fixed] - Translation issue Action Type field while creating catalog rule.
* #1883 [fixed] - Free shipping not applied on cart, if from action "Free Shipping" is selected and discount amount is given as zero.
* #1882 [fixed] - If from Action "Apply on shipping " is selected as yes then the discounted amount is applying on both product and shipping charge.
* #1861 [fixed] - Getting exception on checkout if applying invalid coupon code and proceed for checkout.
* #1857 [fixed] - Getting exception when creating cart rule with condition URL KEY(children only).
* #1856 [fixed] - Getting exception on creating cart rule with condition Name(children only).
* #1847 [fixed] - If cart rule condition does not match the cart then on applying coupon it should display message "Coupon cannot be applied".
* #1839 [fixed] - Getting exception on filtering cart rule through id.
* #1836 [fixed] - Back button on Edit cart rule page doesn't work.
* #1834 [fixed] - On editing any cart rule, its priority updates to 1.
* #1833 [fixed] - Discount not applied if "Payment Method", "Shipping Method", "Shipping State" or "Shipping Country" is selected in condition.
* #1832 [fixed] - Able to use coupon irrespective of the number given in "Uses per coupon" field.
* #1828 [fixed] - Updated changes are not saved on editing cart rule.
* #1825 [fixed] - Not able to generate coupon code while creating cart rule.
* #1823 [fixed] - Getting exception after clicking on developement.
* #1929 [fixed] - Actual product amount and discounted amount both should display on product page for configurable product
* #1928 [fixed] - Catalog rule should not apply if any of the condition doesn't match, if condition type"All Condition are true"
* #1931 [fixed] - Catalog rule not applying if in condition type"Any Condition is true" is selected, and some of the condition doesn't match the product.
* #1935 [fixed] - Categories are not displaying if in condition Categories(children only) or Categories(Parent Only) is selected.
* #1938 [fixed] - multiple cart rules are getting applied by using only single coupon.
* #1939 [fixed] - Invalid coupon gets applied, if clicking on apply coupon multiple time, although there is no discount amount but its displaying message that coupon code applied successfully.
* #1942 [fixed] - When editing the cart rule, selected category is not checked.
## **v0.1.8 (4th of October, 2019)** - *Release*
* [feature] - Refund is added for orders.

17
CHANGELOG for v0.2.x.md Normal file
View File

@ -0,0 +1,17 @@
# CHANGELOG for v0.2.x
#### This changelog consists the bug & security fixes and new features being included in the releases listed below.
## **v0.2.0 (23th of December, 2019)** - *Release*
* #1955 [fixed] - Message need to be changed on mouse hover on cross symbol next to applied coupon.
* #1959 [fixed] - if admin has set the same condition twice, then catalog rule is not getting apply
* #1958 [fixed] - getting exception on front end, if action type is Buy x get y free in non couponable cart rule.
* #1957 [fixed] - if action type is Fixed Amount to Whole Cart, then apply to shipping option should get hide.
* #1954 [fixed] - If any different tax category has been assigned to variants in configurable product, then while using tax category condition in cart rule, rule is not working properly.
* #1950 [fixed] - multiple catalog rule should not get get applied, if 1st one has been created as End Other Rules = yes

View File

@ -21,20 +21,20 @@
### Introduction
[Bagisto](https://www.bagisto.com) is a hand tailored E-Commerce framework designed on some of the hottest opensource technologies
such as [Laravel](https://laravel.com) a [PHP](https://secure.php.net/) framework, [Vue.js](https://vuejs.org)
[Bagisto](https://www.bagisto.com) is a hand tailored E-Commerce framework built on some of the hottest opensource technologies
such as [Laravel](https://laravel.com) (a [PHP](https://secure.php.net/) framework) and [Vue.js](https://vuejs.org)
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.**
**Bagisto can help you 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 -- can benefit. And it's 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/)**
**We also have a forum for any type of concerns, feature requests, or discussions. Please visit: [Bagisto Forums](https://forums.bagisto.com/)**
# Visit our live [Demo](https://demo.bagisto.com)
It packs in lots of demanding features that allows your business to scale in no time:
It packs in lots of features that will allow your E-Commerce business to scale in no time:
* Multiple Channels, Locale, Currencies.
* Built-in Access Control Layer.
@ -50,11 +50,10 @@ It packs in lots of demanding features that allows your business to scale in no
* Simple and Configurable Products.
* Price rules (Discount) inbuilt.
* CMS Pages.
* Check out [click here](https://bagisto.com/features/).
* Check out [these features and more](https://bagisto.com/features/).
**For Developers**:
Dev guys can take advantage of two of the hottest frameworks used in this project Laravel and Vue.js, both of these frameworks have been used in Bagisto.
Bagisto is using power of both of these frameworks and making best out of it out of the box.
Take advantage of two of the hottest frameworks used in this project -- Laravel and Vue.js -- both of which have been used in Bagisto.
### Documentation
@ -80,7 +79,7 @@ Bagisto is using power of both of these frameworks and making best out of it out
[Download](https://github.com/bagisto/bagisto/archive/v0.1.6.zip)
##### b. Extract the contents of zip and execute the project in browser:
##### b. Extract the contents of zip and execute the project in your browser:
~~~
http(s)://localhost/bagisto/public
@ -94,15 +93,15 @@ http(s)://example.com/public
**2. Try our old fashioned way to install Bagisto:**
##### Execute these commands below as in their order
##### Execute these commands below, in order
~~~
1. composer create-project bagisto/bagisto
~~~
**Now configure your database:**
**Now, configure your database:**
If the above command was completed successfully, then you'll find directory **bagisto** and all of the code will be inside it.
If the above command was completed successfully, then you'll find directory **bagisto** with all of the code inside it.
Find file **.env** inside **bagisto** directory and set the environment variables listed below:
@ -114,7 +113,7 @@ Find file **.env** inside **bagisto** directory and set the environment variable
* **DB_USERNAME**
* **DB_PASSWORD**
Although, mailer environment variables are also required to be set up as **Bagisto** requires emails to send to customers and admins for various functionalities that are built in.
Mailer environment variables are also required to be set up. This is because **Bagisto** needs to send emails to customers and admins depending on what events require notification.
~~~
2. php artisan migrate
@ -142,13 +141,13 @@ 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 uninstall developer dependencies.
Warning: Before going into production mode we recommend you uninstall 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.
Open the specified entry point in your hosts file in your browser or make an entry in hosts file if not done.
~~~
##### On local:
@ -178,13 +177,13 @@ password:admin123
Bagisto is a truly opensource E-Commerce framework which will always be free under the [MIT License](https://github.com/bagisto/bagisto/blob/master/LICENSE).
### Security Vulnerabilities
Please don't disclose security vulnerabilities publicly. If you find any security vulnerability in Bagisto then please write us mail: mailto:support@bagisto.com.
Please don't disclose security vulnerabilities publicly. If you find any security vulnerability in Bagisto then please email us: mailto:support@bagisto.com.
### Miscellaneous
#### Contributors
This project is on [Open Collective](https://opencollective.com/bagisto) and it exists thanks to this people who contribute.
This project is on [Open Collective](https://opencollective.com/bagisto) and it exists thanks to the people who contribute.
<a href="https://github.com/bagisto/bagisto/graphs/contributors"><img src="https://opencollective.com/bagisto/contributors.svg?width=890&button=false"/></a>

View File

@ -30,7 +30,6 @@
"laravel/framework": "5.6.*",
"laravel/tinker": "^1.0",
"maatwebsite/excel": "3.1.11",
"nwidart/laravel-modules": "^3.2",
"prettus/l5-repository": "2.6.32",
"tymon/jwt-auth": "1.0.0-rc.4"
},
@ -69,33 +68,35 @@
},
"autoload": {
"classmap": [
"database/seeds",
"database/factories"
],
"psr-4": {
"App\\": "app/",
"Webkul\\User\\": "packages/Webkul/User/src",
"Webkul\\Admin\\": "packages/Webkul/Admin/src",
"Webkul\\Ui\\": "packages/Webkul/Ui/src",
"Webkul\\Category\\": "packages/Webkul/Category/src",
"Webkul\\Checkout\\": "packages/Webkul/Checkout/src",
"Webkul\\Attribute\\": "packages/Webkul/Attribute/src",
"Webkul\\Shop\\": "packages/Webkul/Shop/src",
"Webkul\\Core\\": "packages/Webkul/Core/src",
"Webkul\\Customer\\": "packages/Webkul/Customer/src",
"Webkul\\Inventory\\": "packages/Webkul/Inventory/src",
"Webkul\\Product\\": "packages/Webkul/Product/src",
"Webkul\\Theme\\": "packages/Webkul/Theme/src",
"Webkul\\Shipping\\": "packages/Webkul/Shipping/src",
"Webkul\\Payment\\": "packages/Webkul/Payment/src",
"Webkul\\Paypal\\": "packages/Webkul/Paypal/src",
"Webkul\\Sales\\": "packages/Webkul/Sales/src",
"Webkul\\Tax\\": "packages/Webkul/Tax/src",
"Webkul\\API\\": "packages/Webkul/API",
"Webkul\\Discount\\": "packages/Webkul/Discount/src",
"Webkul\\CMS\\": "packages/Webkul/CMS/src"
}
"classmap": [
"database/seeds",
"database/factories"
],
"psr-4": {
"App\\": "app/",
"Webkul\\User\\": "packages/Webkul/User/src",
"Webkul\\Admin\\": "packages/Webkul/Admin/src",
"Webkul\\Ui\\": "packages/Webkul/Ui/src",
"Webkul\\Category\\": "packages/Webkul/Category/src",
"Webkul\\Checkout\\": "packages/Webkul/Checkout/src",
"Webkul\\Attribute\\": "packages/Webkul/Attribute/src",
"Webkul\\Shop\\": "packages/Webkul/Shop/src",
"Webkul\\Core\\": "packages/Webkul/Core/src",
"Webkul\\Customer\\": "packages/Webkul/Customer/src",
"Webkul\\Inventory\\": "packages/Webkul/Inventory/src",
"Webkul\\Product\\": "packages/Webkul/Product/src",
"Webkul\\Theme\\": "packages/Webkul/Theme/src",
"Webkul\\Shipping\\": "packages/Webkul/Shipping/src",
"Webkul\\Payment\\": "packages/Webkul/Payment/src",
"Webkul\\Paypal\\": "packages/Webkul/Paypal/src",
"Webkul\\Sales\\": "packages/Webkul/Sales/src",
"Webkul\\Tax\\": "packages/Webkul/Tax/src",
"Webkul\\API\\": "packages/Webkul/API",
"Webkul\\CatalogRule\\": "packages/Webkul/CatalogRule/src",
"Webkul\\CartRule\\": "packages/Webkul/CartRule/src",
"Webkul\\Rule\\": "packages/Webkul/Rule/src",
"Webkul\\CMS\\": "packages/Webkul/CMS/src"
}
},
"autoload-dev": {
@ -131,7 +132,8 @@
"set -e",
"@php artisan migrate:fresh --env=testing",
"vendor/bin/codecept run unit",
"vendor/bin/codecept run functional"
"vendor/bin/codecept run functional",
"vendor/bin/codecept run trigger"
]
},
"config": {

View File

@ -65,7 +65,7 @@ return [
|
*/
'timezone' => 'Asia/Kolkata',
'timezone' => env('APP_TIMEZONE','Asia/Kolkata'),
/*
|--------------------------------------------------------------------------
@ -102,7 +102,7 @@ return [
|
*/
'currency' => 'USD',
'currency' => env('APP_CURRENCY','USD'),
/*
|--------------------------------------------------------------------------
@ -219,6 +219,8 @@ return [
//Laravel Maatwebsite
Maatwebsite\Excel\ExcelServiceProvider::class,
Barryvdh\Debugbar\ServiceProvider::class,
//Repository
Prettus\Repository\Providers\RepositoryServiceProvider::class,
Konekt\Concord\ConcordServiceProvider::class,
@ -245,7 +247,9 @@ return [
Webkul\Sales\Providers\SalesServiceProvider::class,
Webkul\Tax\Providers\TaxServiceProvider::class,
Webkul\API\Providers\APIServiceProvider::class,
Webkul\Discount\Providers\DiscountServiceProvider::class,
Webkul\CatalogRule\Providers\CatalogRuleServiceProvider::class,
Webkul\CartRule\Providers\CartRuleServiceProvider::class,
Webkul\Rule\Providers\RuleServiceProvider::class,
Webkul\CMS\Providers\CMSServiceProvider::class
],

View File

@ -19,7 +19,8 @@ return [
\Webkul\Sales\Providers\ModuleServiceProvider::class,
\Webkul\Tax\Providers\ModuleServiceProvider::class,
\Webkul\User\Providers\ModuleServiceProvider::class,
\Webkul\Discount\Providers\ModuleServiceProvider::class,
\Webkul\CatalogRule\Providers\ModuleServiceProvider::class,
\Webkul\CartRule\Providers\ModuleServiceProvider::class,
\Webkul\CMS\Providers\ModuleServiceProvider::class
]
];

0
config/modules.php Executable file → Normal file
View File

View File

@ -10,14 +10,14 @@
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"axios": "^0.18",
"cross-env": "^5.1.4",
"laravel-mix": "^2.1",
"laravel-mix-merge-manifest": "^0.1.1",
"jquery": "^3.2",
"vue": "^2.1.10"
"axios": "^0.19.0",
"cross-env": "^6.0.3",
"jquery": "^3.4.1",
"laravel-mix": "^5.0.0",
"laravel-mix-merge-manifest": "^0.1.2",
"vue": "^2.6.10"
},
"dependencies": {
"vee-validate": "2.0.0-rc.26"
"vee-validate": "^2.2.15"
}
}

View File

@ -1,4 +1,4 @@
{
"/js/admin.js": "/js/admin.js?id=1a04ed2a8f0437dbe740",
"/js/admin.js": "/js/admin.js?id=d1daece26673ab30942b",
"/css/admin.css": "/css/admin.css?id=c845e7c275d4df7fa03f"
}

View File

@ -394,27 +394,27 @@ return [
], [
'key' => 'promotions',
'name' => 'admin::app.acl.promotions',
'route' => 'admin.cart-rule.index',
'route' => 'admin.cart-rules.index',
'sort' => 7
], [
'key' => 'promotions.cart-rule',
'key' => 'promotions.cart-rules',
'name' => 'admin::app.acl.cart-rules',
'route' => 'admin.cart-rule.index',
'route' => 'admin.cart-rules.index',
'sort' => 1
], [
'key' => 'promotions.cart-rule.create',
'key' => 'promotions.cart-rules.create',
'name' => 'admin::app.acl.create',
'route' => 'admin.cart-rule.create',
'route' => 'admin.cart-rules.create',
'sort' => 1
], [
'key' => 'promotions.cart-rule.edit',
'key' => 'promotions.cart-rules.edit',
'name' => 'admin::app.acl.edit',
'route' => 'admin.cart-rule.edit',
'route' => 'admin.cart-rules.edit',
'sort' => 2
], [
'key' => 'promotions.cart-rule.delete',
'key' => 'promotions.cart-rules.delete',
'name' => 'admin::app.acl.delete',
'route' => 'admin.cart-rule.delete',
'route' => 'admin.cart-rules.delete',
'sort' => 3
],
];

View File

@ -189,22 +189,22 @@ return [
'icon-class' => ''
],[
'key' => 'promotions',
'name' => 'admin::app.layouts.promotion',
'route' => 'admin.cart-rule.index',
'name' => 'admin::app.layouts.promotions',
'route' => 'admin.catalog-rules.index',
'sort' => 5,
'icon-class' => 'promotion-icon'
], [
'key' => 'promotions.cart-rule',
'name' => 'admin::app.promotion.cart-rule',
'route' => 'admin.cart-rule.index',
'key' => 'promotions.catalog-rules',
'name' => 'admin::app.promotions.catalog-rules.title',
'route' => 'admin.catalog-rules.index',
'sort' => 1,
'icon-class' => ''
], [
'key' => 'promotions.catalog-rule',
'name' => 'admin::app.promotion.catalog-rule',
'route' => 'admin.catalog-rule.index',
'sort' => 1,
'icon-class' => '',
'key' => 'promotions.cart-rules',
'name' => 'admin::app.promotions.cart-rules.title',
'route' => 'admin.cart-rules.index',
'sort' => 2,
'icon-class' => ''
], [
'key' => 'cms',
'name' => 'admin::app.layouts.cms',

View File

@ -52,7 +52,7 @@ class AddressDataGrid extends DataGrid
$queryBuilder = DB::table('customer_addresses as ca')
->leftJoin('countries', 'ca.country', '=', 'countries.code')
->leftJoin('customers as c', 'ca.customer_id', '=', 'c.id')
->addSelect('ca.id as address_id', 'ca.address1', 'ca.country', DB::raw(''.DB::getTablePrefix().'countries.name as country_name'), 'ca.state', 'ca.city', 'ca.postcode', 'ca.phone', 'ca.default_address')
->addSelect('ca.id as address_id', 'ca.address1', 'ca.country', DB::raw('' . DB::getTablePrefix() . 'countries.name as country_name'), 'ca.state', 'ca.city', 'ca.postcode', 'ca.phone', 'ca.default_address')
->where('c.id', $customer->id);
$queryBuilder = $queryBuilder->leftJoin('country_states', function($qb) {
@ -62,13 +62,13 @@ class AddressDataGrid extends DataGrid
$queryBuilder
->groupBy('ca.id')
->addSelect(DB::raw(''.DB::getTablePrefix().'country_states.default_name as state_name'));
->addSelect(DB::raw(DB::getTablePrefix() . 'country_states.default_name as state_name'));
$this->addFilter('address_id', 'ca.id');
$this->addFilter('address1', 'ca.address1');
$this->addFilter('city', 'ca.city');
$this->addFilter('state_name', DB::raw(''.DB::getTablePrefix().'country_states.default_name'));
$this->addFilter('country_name', DB::raw(''.DB::getTablePrefix().'countries.name'));
$this->addFilter('state_name', DB::raw(DB::getTablePrefix() . 'country_states.default_name'));
$this->addFilter('country_name', DB::raw(DB::getTablePrefix() . 'countries.name'));
$this->addFilter('postcode', 'ca.postcode');
$this->addFilter('default_address', 'ca.default_address');

View File

@ -0,0 +1,86 @@
<?php
namespace Webkul\Admin\DataGrids;
use Webkul\Ui\DataGrid\DataGrid;
use DB;
/**
* CartRuleCouponDataGrid class
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CartRuleCouponDataGrid extends DataGrid
{
protected $index = 'id';
protected $sortOrder = 'desc';
public function prepareQueryBuilder()
{
$queryBuilder = DB::table('cart_rule_coupons')
->addSelect('id', 'code', 'created_at', 'expired_at', 'times_used')
->where('cart_rule_coupons.cart_rule_id', request('id'));
$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.coupon-code'),
'type' => 'string',
'searchable' => true,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'created_at',
'label' => trans('admin::app.datagrid.created-date'),
'type' => 'datetime',
'sortable' => true,
'searchable' => false,
'filterable' => true
]);
$this->addColumn([
'index' => 'expired_at',
'label' => trans('admin::app.datagrid.expiration-date'),
'type' => 'datetime',
'sortable' => true,
'searchable' => false,
'filterable' => true
]);
$this->addColumn([
'index' => 'times_used',
'label' => trans('admin::app.datagrid.times-used'),
'type' => 'number',
'searchable' => true,
'sortable' => true,
'filterable' => true
]);
}
public function prepareMassActions()
{
$this->addMassAction([
'type' => 'delete',
'action' => route('admin.cart-rule-coupons.mass-delete'),
'label' => trans('admin::app.datagrid.delete'),
'method' => 'DELETE'
]);
}
}

View File

@ -8,20 +8,27 @@ use DB;
/**
* Cart Rule DataGrid class
*
* @author Prashant Singh <prashant.singh852@webkul.com> @prashant-webkul
* @author Jitendra Singh <jitendra@webkul.com>
* @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 $index = 'id';
protected $sortOrder = 'desc'; //asc or desc
protected $sortOrder = 'desc';
public function prepareQueryBuilder()
{
$queryBuilder = DB::table('cart_rules')
->select('id')
->addSelect('id', 'name', 'status', 'end_other_rules', 'action_type', 'disc_amount', 'use_coupon');
->leftJoin('cart_rule_coupons', function($leftJoin) {
$leftJoin->on('cart_rule_coupons.cart_rule_id', '=', 'cart_rules.id')
->where('cart_rule_coupons.is_primary', 1);
})
->addSelect('cart_rules.id', 'name', 'cart_rule_coupons.code as coupon_code', 'status', 'starts_from', 'ends_till', 'sort_order');
$this->addFilter('id', 'cart_rules.id');
$this->addFilter('coupon_code', 'cart_rule_coupons.code');
$this->setQueryBuilder($queryBuilder);
}
@ -30,7 +37,7 @@ class CartRuleDataGrid extends DataGrid
{
$this->addColumn([
'index' => 'id',
'label' => trans('admin::app.id'),
'label' => trans('admin::app.datagrid.id'),
'type' => 'number',
'searchable' => false,
'sortable' => true,
@ -39,13 +46,40 @@ class CartRuleDataGrid extends DataGrid
$this->addColumn([
'index' => 'name',
'label' => trans('admin::app.name'),
'label' => trans('admin::app.datagrid.name'),
'type' => 'string',
'searchable' => true,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'coupon_code',
'label' => trans('admin::app.datagrid.coupon-code'),
'type' => 'string',
'searchable' => true,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'starts_from',
'label' => trans('admin::app.datagrid.start'),
'type' => 'datetime',
'sortable' => true,
'searchable' => false,
'filterable' => true
]);
$this->addColumn([
'index' => 'ends_till',
'label' => trans('admin::app.datagrid.end'),
'type' => 'datetime',
'sortable' => true,
'searchable' => false,
'filterable' => true
]);
$this->addColumn([
'index' => 'status',
'label' => trans('admin::app.status'),
@ -55,89 +89,36 @@ class CartRuleDataGrid extends DataGrid
'filterable' => true,
'wrapper' => function($value) {
if ($value->status == 1)
return 'Active';
return trans('admin::app.datagrid.active');
else
return 'In Active';
return trans('admin::app.datagrid.inactive');
}
]);
$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' => true,
'sortable' => true,
'filterable' => true,
'wrapper' => function($value) {
return config('pricerules.cart.actions')[$value->action_type];
}
]);
$this->addColumn([
'index' => 'disc_amount',
'label' => 'Discount Amount',
'index' => 'sort_order',
'label' => trans('admin::app.datagrid.priority'),
'type' => 'number',
'searchable' => false,
'searchable' => true,
'sortable' => true,
'filterable' => true
]);
$this->addColumn([
'index' => 'use_coupon',
'label' => 'Use Coupon',
'type' => 'boolean',
'searchable' => true,
'sortable' => true,
'filterable' => true,
'wrapper' => function($value) {
if ($value->use_coupon == 1) {
return 'True';
} else {
return 'False';
}
}
]);
}
public function prepareActions()
{
$this->addAction([
'title' => 'Edit CartRule',
'method' => 'GET', //use post only for redirects only
'route' => 'admin.cart-rule.edit',
'title' => 'Edit Cart Rule',
'method' => 'GET',
'route' => 'admin.cart-rules.edit',
'icon' => 'icon pencil-lg-icon'
]);
$this->addAction([
'title' => 'Delete CartRule',
'method' => 'POST', //use post only for requests other than redirects
'route' => 'admin.cart-rule.delete',
'title' => 'Delete Cart Rule',
'method' => 'POST',
'route' => 'admin.cart-rules.delete',
'icon' => 'icon trash-icon'
]);
}
public function prepareMassActions()
{
// $this->addMassAction([
// 'type' => 'delete',
// 'action' => route('admin.catalog.attributes.massdelete'),
// 'label' => 'Delete',
// 'method' => 'DELETE'
// ]);
}
}

View File

@ -8,20 +8,19 @@ use DB;
/**
* Catalog Rule DataGrid class
*
* @author Prashant Singh <prashant.singh852@webkul.com> @prashant-webkul
* @author Jitendra Singh <jitendra@webkul.com>
* @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 $index = 'id';
protected $sortOrder = 'desc'; //asc or desc
protected $sortOrder = 'desc';
public function prepareQueryBuilder()
{
$queryBuilder = DB::table('catalog_rules')
->select('id')
->addSelect('id', 'name', 'starts_from', 'ends_till', 'status', 'end_other_rules', 'action_code');
->addSelect('catalog_rules.id', 'name', 'status', 'starts_from', 'ends_till', 'sort_order');
$this->setQueryBuilder($queryBuilder);
}
@ -48,56 +47,41 @@ class CatalogRuleDataGrid extends DataGrid
$this->addColumn([
'index' => 'starts_from',
'label' => trans('admin::app.datagrid.starts-from'),
'label' => trans('admin::app.datagrid.start'),
'type' => 'datetime',
'searchable' => false,
'sortable' => true,
'searchable' => false,
'filterable' => true
]);
$this->addColumn([
'index' => 'ends_till',
'label' => trans('admin::app.datagrid.ends-till'),
'label' => trans('admin::app.datagrid.end'),
'type' => 'datetime',
'searchable' => false,
'sortable' => true,
'searchable' => false,
'filterable' => true
]);
$this->addColumn([
'index' => 'status',
'label' => trans('admin::app.datagrid.status'),
'label' => trans('admin::app.status'),
'type' => 'boolean',
'searchable' => false,
'searchable' => true,
'sortable' => true,
'filterable' => true,
'wrapper' => function ($value) {
'wrapper' => function($value) {
if ($value->status == 1)
return 'True';
return trans('admin::app.datagrid.active');
else
return 'False';
return trans('admin::app.datagrid.inactive');
}
]);
$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_code',
'label' => 'Action Type',
'type' => 'string',
'index' => 'sort_order',
'label' => trans('admin::app.datagrid.priority'),
'type' => 'number',
'searchable' => true,
'sortable' => true,
'filterable' => true
@ -107,27 +91,17 @@ class CatalogRuleDataGrid extends DataGrid
public function prepareActions()
{
$this->addAction([
'title' => 'Edit CatalogRule',
'method' => 'GET', //use post only for redirects only
'route' => 'admin.catalog-rule.edit',
'title' => 'Edit Catalog Rule',
'method' => 'GET',
'route' => 'admin.catalog-rules.edit',
'icon' => 'icon pencil-lg-icon'
]);
$this->addAction([
'title' => 'Delete CatalogRule',
'method' => 'POST', //use post only for requests other than redirects
'route' => 'admin.catalog-rule.delete',
'title' => 'Delete Catalog Rule',
'method' => 'POST',
'route' => 'admin.catalog-rules.delete',
'icon' => 'icon trash-icon'
]);
}
public function prepareMassActions()
{
// $this->addMassAction([
// 'type' => 'delete',
// 'action' => route('admin.catalog.attributes.massdelete'),
// 'label' => 'Delete',
// 'method' => 'DELETE'
// ]);
}
}

View File

@ -21,7 +21,7 @@ class CategoryDataGrid extends DataGrid
{
$queryBuilder = DB::table('categories as cat')
->select('cat.id as category_id', 'ct.name', 'cat.position', 'cat.status', 'ct.locale',
DB::raw('COUNT(DISTINCT '.DB::getTablePrefix().'pc.product_id) as count'))
DB::raw('COUNT(DISTINCT ' . DB::getTablePrefix() . 'pc.product_id) as count'))
->leftJoin('category_translations as ct', function($leftJoin) {
$leftJoin->on('cat.id', '=', 'ct.category_id')
->where('ct.locale', app()->getLocale());

View File

@ -25,10 +25,10 @@ class CustomerDataGrid extends DataGrid
$queryBuilder = DB::table('customers')
->leftJoin('customer_groups', 'customers.customer_group_id', '=', 'customer_groups.id')
->addSelect('customers.id as customer_id', 'customers.email', 'customer_groups.name', 'customers.phone', 'customers.gender', 'status')
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'customers.first_name, " ", '.DB::getTablePrefix().'customers.last_name) as full_name'));
->addSelect(DB::raw('CONCAT(' . DB::getTablePrefix() . 'customers.first_name, " ", ' . DB::getTablePrefix() . 'customers.last_name) as full_name'));
$this->addFilter('customer_id', 'customers.id');
$this->addFilter('full_name', DB::raw('CONCAT('.DB::getTablePrefix().'customers.first_name, " ", '.DB::getTablePrefix().'customers.last_name)'));
$this->addFilter('full_name', DB::raw('CONCAT(' . DB::getTablePrefix() . 'customers.first_name, " ", ' . DB::getTablePrefix() . 'customers.last_name)'));
$this->addFilter('phone', 'customers.phone');
$this->addFilter('gender', 'customers.gender');

View File

@ -29,11 +29,11 @@ class OrderDataGrid extends DataGrid
->where('order_address_billing.address_type', 'billing');
})
->addSelect('orders.id','orders.increment_id', 'orders.base_sub_total', 'orders.base_grand_total', 'orders.created_at', 'channel_name', 'status')
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_billing.first_name, " ", '.DB::getTablePrefix().'order_address_billing.last_name) as billed_to'))
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name) as shipped_to'));
->addSelect(DB::raw('CONCAT(' . DB::getTablePrefix() . 'order_address_billing.first_name, " ", ' . DB::getTablePrefix() . 'order_address_billing.last_name) as billed_to'))
->addSelect(DB::raw('CONCAT(' . DB::getTablePrefix() . 'order_address_shipping.first_name, " ", ' . DB::getTablePrefix() . 'order_address_shipping.last_name) as shipped_to'));
$this->addFilter('billed_to', DB::raw('CONCAT('.DB::getTablePrefix().'order_address_billing.first_name, " ", '.DB::getTablePrefix().'order_address_billing.last_name)'));
$this->addFilter('shipped_to', DB::raw('CONCAT('.DB::getTablePrefix().'order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name)'));
$this->addFilter('billed_to', DB::raw('CONCAT(' . DB::getTablePrefix() . 'order_address_billing.first_name, " ", ' . DB::getTablePrefix() . 'order_address_billing.last_name)'));
$this->addFilter('shipped_to', DB::raw('CONCAT(' . DB::getTablePrefix() . 'order_address_shipping.first_name, " ", ' . DB::getTablePrefix() . 'order_address_shipping.last_name)'));
$this->addFilter('increment_id', 'orders.increment_id');
$this->addFilter('created_at', 'orders.created_at');

View File

@ -26,9 +26,9 @@ class OrderRefundDataGrid extends DataGrid
$leftJoin->on('order_address_billing.order_id', '=', 'orders.id')
->where('order_address_billing.address_type', 'billing');
})
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_billing.first_name, " ", '.DB::getTablePrefix().'order_address_billing.last_name) as billed_to'));
->addSelect(DB::raw('CONCAT(' . DB::getTablePrefix() . 'order_address_billing.first_name, " ", ' . DB::getTablePrefix() . 'order_address_billing.last_name) as billed_to'));
$this->addFilter('billed_to', DB::raw('CONCAT('.DB::getTablePrefix().'order_address_billing.first_name, " ", '.DB::getTablePrefix().'order_address_billing.last_name)'));
$this->addFilter('billed_to', DB::raw('CONCAT(' . DB::getTablePrefix() . 'order_address_billing.first_name, " ", ' . DB::getTablePrefix() . 'order_address_billing.last_name)'));
$this->addFilter('id', 'refunds.id');
$this->addFilter('increment_id', 'orders.increment_id');
$this->addFilter('state', 'refunds.state');

View File

@ -26,16 +26,17 @@ class OrderShipmentsDataGrid extends DataGrid
})
->leftJoin('orders as ors', 'shipments.order_id', '=', 'ors.id')
->leftJoin('inventory_sources as is', 'shipments.inventory_source_id', '=', 'is.id')
->select('shipments.id as shipment_id', 'ors.increment_id as shipment_order_id', 'shipments.total_qty as shipment_total_qty', 'is.name as inventory_source_name', 'ors.created_at as order_date', 'shipments.created_at as shipment_created_at')
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name) as shipped_to'));
->select('shipments.id as shipment_id', 'ors.increment_id as shipment_order_id', 'shipments.total_qty as shipment_total_qty', 'ors.created_at as order_date', 'shipments.created_at as shipment_created_at')
->addSelect(DB::raw('CONCAT(' . DB::getTablePrefix() . 'order_address_shipping.first_name, " ", ' . DB::getTablePrefix() . 'order_address_shipping.last_name) as shipped_to'))
->selectRaw('IF(' . DB::getTablePrefix() . 'shipments.inventory_source_id IS NOT NULL,' . DB::getTablePrefix() . 'is.name, ' . DB::getTablePrefix() . 'shipments.inventory_source_name) as inventory_source_name');
$this->addFilter('shipment_id', 'shipments.id');
$this->addFilter('shipment_order_id', 'ors.increment_id');
$this->addFilter('shipment_total_qty', 'shipments.total_qty');
$this->addFilter('inventory_source_name', 'is.name');
$this->addFilter('inventory_source_name', DB::raw('IF(' . DB::getTablePrefix() . 'shipments.inventory_source_id IS NOT NULL,' . DB::getTablePrefix() . 'is.name, ' . DB::getTablePrefix() . 'shipments.inventory_source_name)'));
$this->addFilter('order_date', 'ors.created_at');
$this->addFilter('shipment_created_at', 'shipments.created_at');
$this->addFilter('shipped_to', DB::raw(''.DB::getTablePrefix().'CONCAT(order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name)'));
$this->addFilter('shipped_to', DB::raw('CONCAT(' . DB::getTablePrefix() . 'order_address_shipping.first_name, " ", ' . DB::getTablePrefix() . 'order_address_shipping.last_name)'));
$this->setQueryBuilder($queryBuilder);
}

View File

@ -25,7 +25,7 @@ class ProductDataGrid extends DataGrid
->leftJoin('products', 'product_flat.product_id', '=', 'products.id')
->leftJoin('attribute_families', 'products.attribute_family_id', '=', 'attribute_families.id')
->leftJoin('product_inventories', 'product_flat.product_id', '=', 'product_inventories.product_id')
->select('product_flat.product_id as product_id', 'product_flat.sku as product_sku', 'product_flat.name as product_name', 'products.type as product_type', 'product_flat.status', 'product_flat.price', 'attribute_families.name as attribute_family', DB::raw('SUM('.DB::getTablePrefix().'product_inventories.qty) as quantity'))
->select('product_flat.product_id as product_id', 'product_flat.sku as product_sku', 'product_flat.name as product_name', 'products.type as product_type', 'product_flat.status', 'product_flat.price', 'attribute_families.name as attribute_family', DB::raw('SUM(' . DB::getTablePrefix() . 'product_inventories.qty) as quantity'))
->where('channel', core()->getCurrentChannelCode())
->where('locale', app()->getLocale())
->groupBy('product_flat.product_id');

View File

@ -50,7 +50,13 @@ class TaxRateDataGrid extends DataGrid
'type' => 'string',
'searchable' => true,
'sortable' => true,
'filterable' => true
'filterable' => true,
'wrapper' => function($value) {
if (empty($value->state))
return '*';
else
return $value->state;
}
]);
$this->addColumn([

View File

@ -120,9 +120,11 @@ class ConfigurationController extends Controller
{
Event::fire('core.configuration.save.before');
$this->validate(request(), [
'general.design.admin_logo.logo_image' => 'mimes:jpeg,bmp,png,jpg'
]);
if (request()->hasFile('general.design.admin_logo.logo_image')) {
$this->validate(request(), [
'general.design.admin_logo.logo_image' => 'required|mimes:jpeg,bmp,png,jpg'
]);
}
$this->coreConfigRepository->create(request()->all());

View File

@ -186,7 +186,7 @@ class DashboardController extends Controller
->where('order_items.created_at', '>=', $this->startDate)
->where('order_items.created_at', '<=', $this->endDate)
->addSelect(DB::raw('SUM(qty_invoiced - qty_refunded) as total_qty_invoiced'))
->addSelect(DB::raw('COUNT('.DB::getTablePrefix().'products.id) as total_products'))
->addSelect(DB::raw('COUNT(' . DB::getTablePrefix() . 'products.id) as total_products'))
->addSelect('order_items.id', 'categories.id as category_id', 'category_translations.name')
->groupBy('categories.id')
->havingRaw('SUM(qty_invoiced - qty_refunded) > 0')

View File

@ -669,60 +669,56 @@ 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-rules', 'Webkul\Discount\Http\Controllers\CatalogRuleController@index')->defaults('_config', [
'view' => 'admin::promotions.catalog-rule.index'
])->name('admin.catalog-rule.index');
Route::prefix('promotions')->group(function () {
Route::get('cart-rules', 'Webkul\CartRule\Http\Controllers\CartRuleController@index')->defaults('_config', [
'view' => 'admin::promotions.cart-rules.index'
])->name('admin.cart-rules.index');
Route::get('/catalog-rules/create', 'Webkul\Discount\Http\Controllers\CatalogRuleController@create')->defaults('_config', [
'view' => 'admin::promotions.catalog-rule.create'
])->name('admin.catalog-rule.create');
Route::get('cart-rules/create', 'Webkul\CartRule\Http\Controllers\CartRuleController@create')->defaults('_config', [
'view' => 'admin::promotions.cart-rules.create'
])->name('admin.cart-rules.create');
Route::post('/catalog-rules/create', 'Webkul\Discount\Http\Controllers\CatalogRuleController@store')->defaults('_config', [
'redirect' => 'admin.catalog-rule.index'
])->name('admin.catalog-rule.store');
Route::post('cart-rules/create', 'Webkul\CartRule\Http\Controllers\CartRuleController@store')->defaults('_config', [
'redirect' => 'admin.cart-rules.index'
])->name('admin.cart-rules.store');
Route::get('/catalog-rules/edit/{id}', 'Webkul\Discount\Http\Controllers\CatalogRuleController@edit')->defaults('_config', [
'view' => 'admin::promotions.catalog-rule.edit'
])->name('admin.catalog-rule.edit');
Route::get('cart-rules/edit/{id}', 'Webkul\CartRule\Http\Controllers\CartRuleController@edit')->defaults('_config', [
'view' => 'admin::promotions.cart-rules.edit'
])->name('admin.cart-rules.edit');
Route::post('/catalog-rules/edit/{id}', 'Webkul\Discount\Http\Controllers\CatalogRuleController@update')->defaults('_config', [
'redirect' => 'admin.catalog-rule.index'
])->name('admin.catalog-rule.update');
Route::post('cart-rules/edit/{id}', 'Webkul\CartRule\Http\Controllers\CartRuleController@update')->defaults('_config', [
'redirect' => 'admin.cart-rules.index'
])->name('admin.cart-rules.update');
Route::get('/catalog-rules/apply', 'Webkul\Discount\Http\Controllers\CatalogRuleController@applyRules')->defaults('_config', [
'view' => 'admin::promotions.catalog-rule.index'
])->name('admin.catalog-rule.apply');
Route::post('cart-rules/delete/{id}', 'Webkul\CartRule\Http\Controllers\CartRuleController@destroy')->name('admin.cart-rules.delete');
Route::post('/catalog-rules/delete/{id}', 'Webkul\Discount\Http\Controllers\CatalogRuleController@destroy')->name('admin.catalog-rule.delete');
Route::post('cart-rules/generate-coupons/{id?}', 'Webkul\CartRule\Http\Controllers\CartRuleController@generateCoupons')->name('admin.cart-rules.generate-coupons');
Route::get('/catalog-rules/declutter', 'Webkul\Discount\Http\Controllers\CatalogRuleController@deClutter')->defaults('_config', [
'redirect' => 'admin.catalog-rule.index'
])->name('admin.catalog-rule.declut');
Route::post('/massdelete', 'Webkul\CartRule\Http\Controllers\CartRuleCouponController@massDelete')->name('admin.cart-rule-coupons.mass-delete');
Route::post('fetch/options', 'Webkul\Discount\Http\Controllers\CatalogRuleController@fetchAttributeOptions')->name('admin.catalog-rule.options');
Route::get('cart-rules', 'Webkul\Discount\Http\Controllers\CartRuleController@index')->defaults('_config', [
'view' => 'admin::promotions.cart-rule.index'
])->name('admin.cart-rule.index');
//Catalog rules
Route::get('catalog-rules', 'Webkul\CatalogRule\Http\Controllers\CatalogRuleController@index')->defaults('_config', [
'view' => 'admin::promotions.catalog-rules.index'
])->name('admin.catalog-rules.index');
Route::get('cart-rules/create', 'Webkul\Discount\Http\Controllers\CartRuleController@create')->defaults('_config', [
'view' => 'admin::promotions.cart-rule.create'
])->name('admin.cart-rule.create');
Route::get('catalog-rules/create', 'Webkul\CatalogRule\Http\Controllers\CatalogRuleController@create')->defaults('_config', [
'view' => 'admin::promotions.catalog-rules.create'
])->name('admin.catalog-rules.create');
Route::post('cart-rules/store', 'Webkul\Discount\Http\Controllers\CartRuleController@store')->defaults('_config', [
'redirect' => 'admin.cart-rule.index'
])->name('admin.cart-rule.store');
Route::post('catalog-rules/create', 'Webkul\CatalogRule\Http\Controllers\CatalogRuleController@store')->defaults('_config', [
'redirect' => 'admin.catalog-rules.index'
])->name('admin.catalog-rules.store');
Route::get('cart-rules/edit/{id}', 'Webkul\Discount\Http\Controllers\CartRuleController@edit')->defaults('_config', [
'view' => 'admin::promotions.cart-rule.edit'
])->name('admin.cart-rule.edit');
Route::get('catalog-rules/edit/{id}', 'Webkul\CatalogRule\Http\Controllers\CatalogRuleController@edit')->defaults('_config', [
'view' => 'admin::promotions.catalog-rules.edit'
])->name('admin.catalog-rules.edit');
Route::post('cart-rules/update/{id}', 'Webkul\Discount\Http\Controllers\CartRuleController@update')->defaults('_config', [
'redirect' => 'admin.cart-rule.index'
])->name('admin.cart-rule.update');
Route::post('catalog-rules/edit/{id}', 'Webkul\CatalogRule\Http\Controllers\CatalogRuleController@update')->defaults('_config', [
'redirect' => 'admin.catalog-rules.index'
])->name('admin.catalog-rules.update');
Route::post('cart-rules/delete/{id}', 'Webkul\Discount\Http\Controllers\CartRuleController@destroy')->name('admin.cart-rule.delete');
Route::post('catalog-rules/delete/{id}', 'Webkul\CatalogRule\Http\Controllers\CatalogRuleController@destroy')->name('admin.catalog-rules.delete');
});
Route::prefix('cms')->group(function () {

View File

@ -1,7 +1,10 @@
require("./bootstrap");
import Vue from 'vue';
import VeeValidate from 'vee-validate';
window.Vue = require("vue");
window.VeeValidate = require("vee-validate");
import './bootstrap';
window.Vue = Vue;
window.VeeValidate = VeeValidate;
Vue.use(VeeValidate);
Vue.prototype.$http = axios
@ -82,6 +85,10 @@ $(document).ready(function () {
},
addFlashMessages() {
if (typeof flashMessages !== 'undefined') {
return;
};
const flashes = this.$refs.flashes;
flashMessages.forEach(function(flash) {

View File

@ -49,3 +49,28 @@
cursor: pointer;
}
}
.cart-rule-conditions,
.catalog-rule-conditions {
table {
td {
&.attribute {
width: 250px;
padding-left: 0;
}
&.operator,
&.value {
width: 260px;
}
&.actions {
width: 44px;
.icon {
margin-top: 6px;
}
}
}
}
}

View File

@ -852,7 +852,7 @@ return [
],
'footer' => [
'copy-right' => '© Copyright 2018 Webkul Software, All rights reserved.'
'copy-right' => ' مدعوم من <a href="https://bagisto.com/" target="_blank"> Bagisto </a> ، مشروع مجتمع بواسطة <a href="https://webkul.com/" target="_blank">Webkul</a>'
],
'admin' => [

View File

@ -73,7 +73,7 @@ return [
'taxes' => 'Taxes',
'tax-categories' => 'Tax Categories',
'tax-rates' => 'Tax Rates',
'promotion' => 'Promotions',
'promotions' => 'Promotions',
'discount' => 'Discount',
'cms' => 'CMS'
],
@ -110,7 +110,8 @@ return [
'create' => 'Add',
'delete' => 'Delete',
'promotions' => 'Promotions',
'cart-rules' => 'Cart Rules'
'cart-rules' => 'Cart Rules',
'catalog-rules' => 'Catalog Rules',
],
'dashboard' => [
@ -203,7 +204,16 @@ return [
'for-guest' => 'For Guest',
'order_number' => 'Order Number',
'refund-date' => 'Refund Date',
'refunded' => 'Refunded'
'refunded' => 'Refunded',
'start' => 'Start',
'end' => 'End',
'active' => 'Active',
'inactive' => 'Inactive',
'coupon-code' => 'Coupon Code',
'times-used' => 'Times Used',
'created-date' => 'Created Date',
'expiration-date' => 'Expiration Date',
'delete' => 'Delete'
],
'account' => [
@ -944,125 +954,138 @@ return [
]
],
'promotion' => [
'information' => 'Information',
'conditions' => 'Conditions',
'actions' => 'Actions',
'coupons' => 'Coupons',
'zero-unlimited' => 'Zero denotes unlimited usage here',
'catalog-rule' => 'Catalog Rules',
'cart-rule' => 'Cart Rules',
'add-catalog-rule' => 'Add 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' => 'Save',
'save' => 'Save',
'select-cart-attr' => 'Select Cart Attribute',
'select-products' => 'How to choose products ?',
'select-category' => 'Select by Categories',
'select-attribute' => 'Select by Attributes',
'cannot-activate-catalog-rule' => 'Cannot activate catalog rule',
'catalog-rule-already-activated' => 'Catalog rule is already activated',
'catalog-rule-activated' => 'Catalog rule is activated',
'select-attribute' => 'Select :attribute',
'enter-attribute' => 'Enter :attribute',
'add-condition' => 'Add Condition',
'cart-properties' => 'Cart Properties',
'yes' => 'Yes',
'no' => 'No',
'note' => 'Note',
'apply' => 'Apply',
'rule-name' => 'Enter Rule Name',
'rule-desc' => 'Enter Rule Description',
'convert-x-note' => 'If this section is left empty, then rule will get applied to all the products in the cart.',
'declut' => 'Declutter',
'processing-done' => 'Rules processing done',
'declut-success' => 'Catalog rules decluttering successful',
'declut-failure' => 'Catalog rules decluttering failed',
'add-attr-condition' => 'Add Attribute Condition',
'general-info' => [
'sku-like' => 'SKU Like',
'on-shipping' => 'If applied on shipping',
'discount' => 'Discount',
'shipping-apply-info' => 'It signifies how rule behaves when it is used for shipping',
'general-info' => 'General Info',
'options' => 'Options',
'name' => 'Rule Name',
'description' => 'Description',
'starts-from' => 'Start',
'ends-till' => 'End',
'channels' => 'Channels',
'channels-req' => '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',
'apply' => 'Apply',
'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',
'test-mode' => 'Choose how to test conditions',
'labels' => 'Labels',
'prefix' => 'Prefix',
'suffix' => 'Suffix',
'code' => 'Code',
'global-label' => 'Global Label',
'label' => 'Label'
],
'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' => 'Success! 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',
'duplicate-coupon' => 'Coupon already exists, please try again with a different coupon'
],
'catalog' => [
'promotions' => [
'cart-rules' => [
'title' => 'Cart Rules',
'add-title' => 'Add Cart Rule',
'edit-title' => 'Edit Cart Rule',
'save-btn-title' => 'Save Cart Rule',
'rule-information' => 'Rule Information',
'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'
'status' => 'Status',
'is-active' => 'Cart Rule is Active',
'channels' => 'Channels',
'customer-groups' => 'Customer Groups',
'coupon-type' => 'Coupon Type',
'no-coupon' => 'No Coupon',
'specific-coupon' => 'Specific Coupon',
'auto-generate-coupon' => 'Auto Generate Coupon',
'no' => 'No',
'yes' => 'Yes',
'coupon-code' => 'Coupon Code',
'uses-per-coupon' => 'Uses Per Coupon',
'uses-per-customer' => 'Uses Per Customer',
'uses-per-customer-control-info' => 'Will be used for logged in customers only.',
'from' => 'From',
'to' => 'To',
'priority' => 'Priority',
'conditions' => 'Conditions',
'condition-type' => 'Condition Type',
'all-conditions-true' => 'All Conditions are True',
'any-condition-true' => 'Any Condition is True',
'add-condition' => 'Add Condition',
'choose-condition-to-add' => 'Choose a condition to add',
'cart-attribute' => 'Cart Attribute',
'subtotal' => 'Subtotal',
'total-items-qty' => 'Total Items Qty',
'total-weight' => 'Total Weight',
'payment-method' => 'Payment Method',
'shipping-method' => 'Shipping Method',
'shipping-postcode' => 'Shipping Zip/Postcode',
'shipping-state' => 'Shipping State',
'shipping-country' => 'Shipping Country',
'cart-item-attribute' => 'Cart Item Attribute',
'price-in-cart' => 'Price in Cart',
'qty-in-cart' => 'Qty in Cart',
'product-attribute' => 'Product Attribute',
'attribute-name-children-only' => ':attribute_name (Children Only)',
'attribute-name-parent-only' => ':attribute_name (Parent Only)',
'is-equal-to' => 'Is equal to',
'is-not-equal-to' => 'Is not equal to',
'equals-or-greater-than' => 'Equals or greater than',
'equals-or-less-than' => 'Equals or less than',
'greater-than' => 'Greater than',
'less-than' => 'Less than',
'contain' => 'Contain',
'contains' => 'Contains',
'does-not-contain' => 'Does not contain',
'actions' => 'Actions',
'action-type' => 'Action Type',
'percentage-product-price' => 'Percentage of Product Price',
'fixed-amount' => 'Fixed Amount',
'fixed-amount-whole-cart' => 'Fixed Amount to Whole Cart',
'buy-x-get-y-free' => 'Buy X Get Y Free',
'discount-amount' => 'Discount Amount',
'discount-quantity' => 'Maximum Quantity Allowed to be Discounted',
'discount-step' => 'Buy X Quantity',
'free-shipping' => 'Free Shipping',
'apply-to-shipping' => 'Apply to Shipping',
'coupon-codes' => 'Coupon Codes',
'coupon-qty' => 'Coupon Qty',
'code-length' => 'Code Length',
'code-format' => 'Code Format',
'alphanumeric' => 'Alphanumeric',
'alphabetical' => 'Alphabetical',
'numeric' => 'Numeric',
'code-prefix' => 'Code Prefix',
'code-suffix' => 'Code Suffix',
'generate' => 'Generate',
'cart-rule-not-defind-error' => 'Cart rule is not defined',
'mass-delete-success' => 'All the selected index of coupons have been deleted successfully.',
'end-other-rules' => 'End Other Rules',
'children-categories' => 'Categories (Children Only)',
'parent-categories' => 'Categories (Parent Only)',
'categories' => 'Categories',
'attribute_family' => 'Attribute Family'
],
'cart' => [
'buy-atleast' => 'Buy Atleast',
'apply-to-shipping' => 'Apply to shipping'
'catalog-rules' => [
'title' => 'Catalog Rules',
'add-title' => 'Add Catalog Rule',
'edit-title' => 'Edit Catalog Rule',
'save-btn-title' => 'Save Catalog Rule',
'rule-information' => 'Rule Information',
'name' => 'Name',
'description' => 'Description',
'status' => 'Status',
'is-active' => 'Catalog Rule is Active',
'channels' => 'Channels',
'customer-groups' => 'Customer Groups',
'no' => 'No',
'yes' => 'Yes',
'from' => 'From',
'to' => 'To',
'priority' => 'Priority',
'conditions' => 'Conditions',
'condition-type' => 'Condition Type',
'all-conditions-true' => 'All Conditions are True',
'any-condition-true' => 'Any Condition is True',
'add-condition' => 'Add Condition',
'choose-condition-to-add' => 'Choose a condition to add',
'product-attribute' => 'Product Attribute',
'attribute-name-children-only' => ':attribute_name (Children Only)',
'attribute-name-parent-only' => ':attribute_name (Parent Only)',
'is-equal-to' => 'Is equal to',
'is-not-equal-to' => 'Is not equal to',
'equals-or-greater-than' => 'Equals or greater than',
'equals-or-less-than' => 'Equals or less than',
'greater-than' => 'Greater than',
'less-than' => 'Less than',
'contain' => 'Contain',
'contains' => 'Contains',
'does-not-contain' => 'Does not contain',
'actions' => 'Actions',
'action-type' => 'Action Type',
'percentage-product-price' => 'Percentage of Product Price',
'fixed-amount' => 'Fixed Amount',
'fixed-amount-whole-cart' => 'Fixed Amount to Whole Catalog',
'buy-x-get-y-free' => 'Buy X Get Y Free',
'discount-amount' => 'Discount Amount',
'mass-delete-success' => 'All the selected index of coupons have been deleted successfully.',
'end-other-rules' => 'End Other Rules',
'categories' => 'Categories',
'attribute_family' => 'Attribute Family'
]
],
@ -1172,7 +1195,7 @@ return [
],
'footer' => [
'copy-right' => '© Copyright 2018 Webkul Software, All rights reserved.'
'copy-right' => 'Powered by <a href="https://bagisto.com/" target="_blank">Bagisto</a>, A Community Project by <a href="https://webkul.com/" target="_blank">Webkul</a>',
],
'admin' => [

View File

@ -998,7 +998,7 @@ return [
],
'footer' => [
'copy-right' => '© کپی رایت 2019 کلیه حقوق محفوظ است.'
'copy-right' => 'طراحی شده توسط <a href="https://bagisto.com/" target="_blank">Bagisto</a> ، یک پروژه عمومی توسط <a href="https://webkul.com/" target="_blank">Webkul</a>'
],
'admin' => [

View File

@ -976,7 +976,7 @@ return [
],
'footer' => [
'copy-right' => '© Copyright '.date('Y').' Webkul Software, Todos os direitos reservados.'
'copy-right' => 'Desenvolvido por <a href="https://bagisto.com/" target="_blank">Bagisto</a>, um projeto comunitário da <a href="https://webkul.com/" target="_blank">Webkul</a>'
],
'admin' => [

View File

@ -83,7 +83,7 @@
<div class="control-group">
<label for="locale-{{ $locale->code }}">{{ $locale->name . ' (' . $locale->code . ')' }}</label>
<input type="text" class="control" id="locale-{{ $locale->code }}" name="<?php echo $locale->code; ?>[name]" value="{{ old($locale->code)['name'] }}"/>
<input type="text" class="control" id="locale-{{ $locale->code }}" name="<?php echo $locale->code; ?>[name]" value="{{ old($locale->code)['name'] ?? '' }}"/>
</div>
@endforeach

View File

@ -108,7 +108,7 @@
<div class="control-group">
<label for="locale-{{ $locale->code }}">{{ $locale->name . ' (' . $locale->code . ')' }}</label>
<input type="text" class="control" id="locale-{{ $locale->code }}" name="<?php echo $locale->code; ?>[name]" value="{{ old($locale->code)['name'] ?: $attribute->translate($locale->code)['name'] }}"/>
<input type="text" class="control" id="locale-{{ $locale->code }}" name="<?php echo $locale->code; ?>[name]" value="{{ old($locale->code)['name'] ?? $attribute->translate($locale->code)['name'] }}"/>
</div>
@endforeach

View File

@ -52,7 +52,7 @@
<div class="control-group" :class="[errors.has('{{$locale}}[name]') ? 'has-error' : '']">
<label for="name" class="required">{{ __('admin::app.catalog.categories.name') }}</label>
<input type="text" v-validate="'required'" class="control" id="name" name="{{$locale}}[name]" value="{{ old($locale)['name'] ?: $category->translate($locale)['name'] }}" data-vv-as="&quot;{{ __('admin::app.catalog.categories.name') }}&quot;"/>
<input type="text" v-validate="'required'" class="control" id="name" name="{{$locale}}[name]" value="{{ old($locale)['name'] ?? $category->translate($locale)['name'] }}" data-vv-as="&quot;{{ __('admin::app.catalog.categories.name') }}&quot;"/>
<span class="control-error" v-if="errors.has('{{$locale}}[name]')">@{{ errors.first('{!!$locale!!}[name]') }}</span>
</div>
@ -180,23 +180,23 @@
<div class="control-group">
<label for="meta_title">{{ __('admin::app.catalog.categories.meta_title') }}</label>
<input type="text" class="control" id="meta_title" name="{{$locale}}[meta_title]" value="{{ old($locale)['meta_title'] ?: $category->translate($locale)['meta_title'] }}"/>
<input type="text" class="control" id="meta_title" name="{{$locale}}[meta_title]" value="{{ old($locale)['meta_title'] ?? $category->translate($locale)['meta_title'] }}"/>
</div>
<div class="control-group" :class="[errors.has('{{$locale}}[slug]') ? 'has-error' : '']">
<label for="slug" class="required">{{ __('admin::app.catalog.categories.slug') }}</label>
<input type="text" v-validate="'required'" class="control" id="slug" name="{{$locale}}[slug]" value="{{ old($locale)['slug'] ?: $category->translate($locale)['slug'] }}" data-vv-as="&quot;{{ __('admin::app.catalog.categories.slug') }}&quot;" v-slugify/>
<input type="text" v-validate="'required'" class="control" id="slug" name="{{$locale}}[slug]" value="{{ old($locale)['slug'] ?? $category->translate($locale)['slug'] }}" data-vv-as="&quot;{{ __('admin::app.catalog.categories.slug') }}&quot;" v-slugify/>
<span class="control-error" v-if="errors.has('{{$locale}}[slug]')">@{{ errors.first('{!!$locale!!}[slug]') }}</span>
</div>
<div class="control-group">
<label for="meta_description">{{ __('admin::app.catalog.categories.meta_description') }}</label>
<textarea class="control" id="meta_description" name="{{$locale}}[meta_description]">{{ old($locale)['meta_description'] ?: $category->translate($locale)['meta_description'] }}</textarea>
<textarea class="control" id="meta_description" name="{{$locale}}[meta_description]">{{ old($locale)['meta_description'] ?? $category->translate($locale)['meta_description'] }}</textarea>
</div>
<div class="control-group">
<label for="meta_keywords">{{ __('admin::app.catalog.categories.meta_keywords') }}</label>
<textarea class="control" id="meta_keywords" name="{{$locale}}[meta_keywords]">{{ old($locale)['meta_keywords'] ?: $category->translate($locale)['meta_keywords'] }}</textarea>
<textarea class="control" id="meta_keywords" name="{{$locale}}[meta_keywords]">{{ old($locale)['meta_keywords'] ?? $category->translate($locale)['meta_keywords'] }}</textarea>
</div>
{!! view_render_event('bagisto.admin.catalog.category.edit_form_accordian.seo.controls.after', ['category' => $category]) !!}
@ -220,7 +220,7 @@
<div class="control-group" :class="[errors.has('{{$locale}}[description]') ? 'has-error' : '']">
<label for="description" :class="isRequired ? 'required' : ''">{{ __('admin::app.catalog.categories.description') }}</label>
<textarea v-validate="isRequired ? 'required' : ''" class="control" id="description" name="{{$locale}}[description]" data-vv-as="&quot;{{ __('admin::app.catalog.categories.description') }}&quot;">{{ old($locale)['description'] ?: $category->translate($locale)['description'] }}</textarea>
<textarea v-validate="isRequired ? 'required' : ''" class="control" id="description" name="{{$locale}}[description]" data-vv-as="&quot;{{ __('admin::app.catalog.categories.description') }}&quot;">{{ old($locale)['description'] ?? $category->translate($locale)['description'] }}</textarea>
<span class="control-error" v-if="errors.has('{{$locale}}[description]')">@{{ errors.first('{!!$locale!!}[description]') }}</span>
</div>

View File

@ -96,7 +96,7 @@
@if (core()->getConfigData('general.content.footer.footer_content'))
{{ core()->getConfigData('general.content.footer.footer_content') }}
@else
{{ trans('admin::app.footer.copy-right') }}
{!! trans('admin::app.footer.copy-right') !!}
@endif
</p>
</div>

View File

@ -1,630 +0,0 @@
@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" autocomplete="off">
@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="{{ __('admin::app.promotion.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" 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">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Customer Group']) }}</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-req') }}&quot;" multiple="multiple">
<option disabled="disabled">{{ __('admin::app.promotion.select-attribute', ['attribute' => '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.first('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 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('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="{{ __('admin::app.promotion.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" v-model="criteria">
<option value="cart">{{ __('admin::app.promotion.cart-properties') }}</option>
</select>
</div>
</div> --}}
<div class="control-group">
{{ __('admin::app.promotion.general-info.test-mode') }}
<select class="control" v-model="match_criteria" style="margin-right: 15px;">
{{ $i = 0 }}
@foreach(config('pricerules.test_mode') as $key => $value)
<option value="{{ $key }}">{{ $value }}</option>
{{ $i++ }}
@endforeach
</select>
</div>
<div class="condition-set">
<!-- Cart Attributes -->
<div class="control-container mt-20" v-for="(condition, index) in conditions_list" :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; width: 30%;" v-on:change="enableCondition($event, index)">
<option disabled="disabled">{{ __('admin::app.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"' style="display: flex">
<select class="control" name="cart_attributes[]" v-model="conditions_list[index].condition" style="margin-right: 15px;">
<option v-for="(condition, index) in conditions.string" :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">{{ __('admin::app.select-option') }}</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">{{ __('admin::app.select-option') }}</option>
<option v-for="(country, index) in country_and_states.countries" :value="country.code">@{{ country.name }}</option>
</select>
</div>
<input class="control" type="text" 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"' style="display: flex">
<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 class="control" type="number" step="0.1000" name="cart_attributes[]" v-model="conditions_list[index].value" placeholder="Enter Value">
</div>
<span class="icon trash-icon" v-on:click="removeCartAttr(index)"></span>
</div>
</div>
<span class="btn btn-primary btn-lg mt-20" v-on:click="addCondition">{{ __('admin::app.promotion.add-condition') }}</span>
</div>
</accordian>
<accordian :active="false" title="{{ __('admin::app.promotion.actions') }}">
<div slot="body">
<div class="control-group" :class="[errors.has('action_type') ? 'has-error' : '']">
<label for="action_type" class="required">{{ __('admin::app.action') }}</label>
<select class="control" name="action_type" v-model="action_type" v-validate="'required'" value="{{ old('action_type') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.apply') }}&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.0001" class="control" name="disc_amount" v-model="disc_amount" v-validate="'required|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: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="1">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="0">{{ __('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="1" :selected="apply_to_shipping == 0">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="0" :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="{{ __('admin::app.promotion.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">{{ __('admin::app.promotion.general-info.prefix') }}</label>
<input type="text" class="control" name="prefix" v-model="prefix" v-validate="'alpha'" value="{{ old('prefix') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.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">{{ __('admin::app.promotion.general-info.suffix') }}</label>
<input type="text" class="control" name="suffix" v-model="suffix" v-validate="'alpha'" value="{{ old('suffix') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.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">{{ __('admin::app.promotion.general-info.code') }}</label>
<input type="text" class="control" name="code" v-model="code" v-validate="'required'" value="{{ old('code') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.code') }}&quot;">
<span class="control-error" v-if="errors.has('code')">@{{ errors.first('code') }}</span>
</div>
</div>
</div>
</accordian>
<accordian :active="true" title="{{ __('admin::app.promotion.select-products') }}">
<div slot="body">
<input type="hidden" name="all_attributes" v-model="all_attributes">
<div class="control-group" :class="[errors.has('category_values') ? 'has-error' : '']">
<label class="mb-10" for="categories">{{ __('admin::app.promotion.select-category') }}</label>
<multiselect v-model="category_values" :close-on-select="false" :options="category_options" :searchable="false" :custom-label="categoryLabel" :show-labels="true" placeholder="Select Categories" track-by="slug" :multiple="true"></multiselect>
</div>
<label class="mb-10" for="attributes">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Attribute']) }}</label>
<br/>
<div class="control-container mt-20" v-for="(condition, index) in attribute_values" :key="index">
<select class="control" v-model="attribute_values[index].attribute" title="You Can Make Multiple Selections Here" style="margin-right: 15px; width: 30%;" v-on:change="enableAttributeCondition($event, index)">
<option disabled="disabled">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Option']) }}</option>
<option v-for="(attr_ip, index1) in attribute_input" :value="attr_ip.code" :key="index1">@{{ attr_ip.name }}</option>
</select>
<select class="control" v-model="attribute_values[index].condition" style="margin-right: 15px;">
<option v-for="(condition, index) in conditions.string" :value="index" :key="index">@{{ condition }}</option>
</select>
<div v-show='attribute_values[index].type == "select" || attribute_values[index].type == "multiselect"' style="display: flex;">
<select class="control" v-model="attribute_values[index].value" style="margin-right: 15px; height: 100px" :multiple="true">
<option :disabled="true">
{{ __('ui::form.select-attribute', ['attribute' => 'Values']) }}
</option>
<option v-for="(label, index2) in attribute_values[index].options" :value="label.id" :key="index2">@{{ label.admin_name }}</option>
</select>
{{-- <multiselect v-model="attribute_values[index].value" :close-on-select="false" :options="attribute_values[index].options" :searchable="false" :track-by="admin_name" :custom-label="attributeListLabel" :multiple="true" ></multiselect> --}}
</div>
<div v-show='attribute_values[index].type == "text" || attribute_values[index].type == "textarea" || attribute_values[index].type == "price" || attribute_values[index].type == "textarea"' style="display: flex">
<input class="control" v-model="attribute_values[index].value" type="text" placeholder="{{ __('ui::form.enter-attribute', ['attribute' => 'Text']) }}">
</div>
<span class="icon trash-icon" v-on:click="removeAttr(index)"></span>
</div>
<span class="btn btn-primary btn-lg mt-20" v-on:click="addAttributeCondition">{{ __('admin::app.promotion.add-attr-condition') }}</span>
</div>
</accordian>
<accordian :active="false" :title="'{{ __('admin::app.promotion.general-info.labels') }}'">
<div slot="body">
<div class="control-group" :class="[errors.has('label') ? 'has-error' : '']" v-if="dedicated_label">
<label for="label">{{ __('admin::app.promotion.general-info.global-label') }}</label>
<input type="text" class="control" name="label[global]" v-model="label.global" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.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)
@foreach($channel->locales as $locale)
<div class="control-group" :class="[errors.has('label') ? 'has-error' : '']">
<label for="code">{{ __('admin::app.label') }}<span class="locale">[{{ $channel->code }} - {{ $locale->code }}]</span></label>
<input type="text" class="control" name="label[{{ $channel->code }}][{{ $locale->code }}]" v-model="label.{{ $channel->code }}.{{ $locale->code }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.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: ['{{ core()->getCurrentChannel()->id }}'],
customer_groups: ['{{ app('Webkul\Customer\Repositories\CustomerGroupRepository')->findOneWhere(["code" => "general"])->id }}'],
ends_till: null,
starts_from: null,
priority: 0,
per_customer: 0,
status: 0,
use_coupon: 0,
auto_generation: false,
usage_limit: 0,
action_type: null,
apply: null,
apply_to_shipping: 0,
disc_amount: 1,
// disc_threshold: null,
disc_quantity: 1,
end_other_rules: 0,
coupon_type: null,
free_shipping: 0,
all_conditions: [],
match_criteria: 'all_are_true',
all_attributes: {
'categories' : [],
'attributes' : []
},
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: 'cart',
conditions: @json($cart_rule[0]).conditions,
cart_input: @json($cart_rule[0]).attributes,
actions: @json($cart_rule[0]).actions,
cart_object: {
attribute: [],
condition: [],
value: []
},
country_and_states: @json($cart_rule[2]),
category_options: @json($cart_rule[1]),
category_values: [],
attribute_values: [],
attr_object: {
attribute: null,
condition: null,
value: [],
options: []
},
attribute_input: @json($cart_rule[3]),
}
},
methods: {
categoryLabel (option) {
return option.name + ' [ ' + option.slug + ' ]';
},
attributeListLabel(option) {
return option.label;
},
addCondition () {
if (this.criteria == 'product_subselection' || this.criteria == 'cart') {
this.condition_on = this.criteria;
} else {
alert('Please select type of condition');
return false;
}
if (this.condition_on == 'cart') {
this.conditions_list.push(this.cart_object);
this.cart_object = {
attribute: null,
condition: null,
value: []
};
}
},
addAttributeCondition() {
this.attribute_values.push(this.attr_object);
this.attr_object = {
attribute: null,
condition: null,
value: [],
options: []
};
},
checkAutogen() {
},
detectApply() {
return;
},
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;
}
}
},
enableAttributeCondition (event, index) {
selectedIndex = event.target.selectedIndex - 1;
for(i in this.attribute_input) {
if (i == selectedIndex) {
if (this.attribute_input[i].has_options == true) {
this.attribute_values[index].options = this.attribute_input[i].options;
}
this.attribute_values[index].type = this.attribute_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);
},
removeAttr(index) {
this.attribute_values.splice(index, 1);
},
onSubmit: function (e) {
if (this.attribute_values.length != 0 || this.category_values.length != 0) {
for (i in this.attribute_values) {
delete this.attribute_values[i].options;
}
this.all_attributes.categories = this.category_values;
this.all_attributes.attributes = this.attribute_values;
}
this.all_attributes = JSON.stringify(this.all_attributes);
if (this.conditions_list != null && this.conditions_list.length != 0) {
this.conditions_list.push({'criteria': this.match_criteria});
this.all_conditions = JSON.stringify(this.conditions_list);
this.conditions_list.pop();
}
this.$validator.validateAll().then(result => {
if (result) {
e.target.submit();
}
});
},
addFlashMessages() {
const flashes = this.$refs.flashes;
flashMessages.forEach(function(flash) {
flashes.addFlash(flash);
}, this);
}
}
});
</script>
@endpush
@stop

View File

@ -1,740 +0,0 @@
@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.save') }}
</button>
</div>
</div>
<div class="page-content">
<div class="form-container">
<div>
@csrf()
<accordian :active="true" title="{{ __('admin::app.promotion.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'" 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" 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">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Customer Group']) }}</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-req') }}&quot;" multiple="multiple">
<option disabled="disabled">{{ __('admin::app.promotion.select-attribute', ['attribute' => '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.first('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">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Status']) }}</option> --}}
<option value="1">{{ __('admin::app.promotion.yes') }}</option>
<option value="0">{{ __('admin::app.promotion.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;">
<figcaption class="required">* {{ __('admin::app.promotion.zero-unlimited') }}</figcaption>
<span class="control-error" v-if="errors.has('per_customer')">@{{ errors.first('per_customer') }}</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;">
<figcaption class="required">* {{ __('admin::app.promotion.zero-unlimited') }}</figcaption>
<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="{{ __('admin::app.promotion.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" v-model="criteria">
<option value="cart">Cart Properties</option>
</select>
</div>
</div> --}}
<div class="control-group">
{{ __('admin::app.promotion.general-info.test-mode') }}
<select class="control" v-model="match_criteria" style="margin-right: 15px;">
{{ $i = 0 }}
@foreach(config('pricerules.test_mode') as $key => $value)
<option value="{{ $key }}">{{ $value }}</option>
{{ $i++ }}
@endforeach
</select>
</div>
<div class="condition-set">
<!-- Cart Attributes -->
<div class="control-container mt-20" v-for="(condition, index) in conditions_list" :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; width: 30%;" v-on:change="enableCondition($event, index)">
<option disabled="disabled">{{ __('admin::app.promotion.select-attribute', ['attribute' => '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"' style="display: flex">
<select class="control" name="cart_attributes[]" v-model="conditions_list[index].condition" style="margin-right: 15px;">
<option v-for="(condition, index) in conditions.string" :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">{{ __('admin::app.promotion.select-attribute', ['attribute' => '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">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Country']) }}</option>
<option v-for="(country, index) in country_and_states.countries" :value="country.code">@{{ country.name }}</option>
</select>
</div>
<input class="control" type="text" name="cart_attributes[]" v-model="conditions_list[index].value" placeholder="{{ __('admin::app.promotion.enter-attribtue', ['attrbibute' => 'Value']) }}" v-if='conditions_list[index].attribute != "shipping_state" && conditions_list[index].attribute != "shipping_country"'>
</div>
<div v-if='conditions_list[index].type == "numeric"' style="display: flex">
<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 class="control" type="number" step="0.1000" name="cart_attributes[]" v-model="conditions_list[index].value">
</div>
<span class="icon trash-icon" v-on:click="removeCartAttr(index)"></span>
</div>
</div>
<span class="btn btn-primary btn-lg mt-20" v-on:click="addCondition">{{ __('admin::app.promotion.add-condition') }}</span>
</div>
</accordian>
<accordian :active="false" title="{{ __('admin::app.promotion.actions') }}">
<div slot="body">
<div class="control-group" :class="[errors.has('action_type') ? 'has-error' : '']">
<label for="action_type" class="required">{{ __('admin::app.action') }}</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.0001" 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_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: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="1">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="0">{{ __('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="apply_to_shipping" 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="1">{{ __('admin::app.promotion.general-info.is-coupon-yes') }}</option>
<option value="0">{{ __('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="{{ __('admin::app.promotion.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">{{ __('admin::app.promotion.general-info.prefix') }}</label>
<input type="text" class="control" name="prefix" v-model="prefix" value="{{ old('prefix') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.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">{{ __('admin::app.promotion.general-info.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">{{ __('admin::app.promotion.general-info.code') }}</label>
<input type="text" class="control" name="code" v-model="code" v-validate="'required'" value="{{ old('code') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.code') }}&quot;">
<span class="control-error" v-if="errors.has('code')">@{{ errors.first('code') }}</span>
</div>
{{-- </div> --}}
</div>
</accordian>
<accordian :active="true" title="{{ __('admin::app.promotion.select-products') }}">
<div slot="body">
<input type="hidden" name="all_attributes" v-model="all_attributes">
<span class="info mb-20" style="display: block;">
<b>{{ __('admin::app.promotion.note') }}:</b>
{{ __('admin::app.promotion.convert-x-note') }}
</span>
<div class="control-group" :class="[errors.has('category_values') ? 'has-error' : '']">
<label class="mb-10" for="categories">{{ __('admin::app.promotion.select-category') }}</label>
<multiselect v-model="category_values" :close-on-select="false" :options="category_options" :searchable="false" :custom-label="categoryLabel" :show-labels="true" placeholder="{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Categories']) }}" track-by="slug" :multiple="true"></multiselect>
</div>
<label class="mb-10" for="attributes">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Option']) }}</label>
<br/>
<div class="control-container mt-20" v-for="(condition, index) in attribute_values" :key="index">
<select class="control" v-model="attribute_values[index].attribute" title="You Can Make Multiple Selections Here" style="margin-right: 15px; width: 30%;" v-on:change="enableAttributeCondition($event, index)">
<option disabled="disabled">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Option']) }}</option>
<option v-for="(attr_ip, index1) in attribute_input" :value="attr_ip.code" :key="index1">@{{ attr_ip.name }}</option>
</select>
<select class="control" v-model="attribute_values[index].condition" style="margin-right: 15px;">
<option v-for="(condition, index) in conditions.string" :value="index" :key="index">@{{ condition }}</option>
</select>
<div v-show='attribute_values[index].type == "select" || attribute_values[index].type == "multiselect"' style="display: flex;">
<select class="control" v-model="attribute_values[index].value" style="margin-right: 15px; height: 100px" :multiple="true">
<option :disabled="true">
{{ __('ui::form.select-attribute', ['attribute' => 'Values']) }}
</option>
<option v-for="(label, index2) in attribute_values[index].options" :value="label.id" :key="index2">@{{ label.admin_name }}</option>
</select>
{{-- <multiselect v-model="attribute_values[index].value" :close-on-select="false" :options="attribute_values[index].options" :searchable="false" :track-by="admin_name" :custom-label="attributeListLabel" :multiple="true" ></multiselect> --}}
</div>
<div v-show='attribute_values[index].type == "text" || attribute_values[index].type == "textarea" || attribute_values[index].type == "price" || attribute_values[index].type == "textarea"' style="display: flex">
<input class="control" v-model="attribute_values[index].value" type="text" placeholder="{{ __('ui::form.enter-attribute', ['attribute' => 'Text']) }}">
</div>
<span class="icon trash-icon" v-on:click="removeAttr(index)"></span>
</div>
<span class="btn btn-primary btn-lg mt-20" v-on:click="addAttributeCondition">{{ __('admin::app.promotion.add-attr-condition') }}</span>
</div>
</accordian>
<accordian :active="false" :title="'{{ __('admin::app.promotion.general-info.labels') }}'">
<div slot="body">
@foreach($cart_rule[3]->labels as $label)
<div class="control-group" :class="[errors.has('label') ? 'has-error' : '']">
<label for="code">{{ __('admin::app.label') }}
<span class="locale">[{{ $label->channel->code }} - {{ $label->locale->code }}]</span>
</label>
<input type="text" class="control" name="label[{{ $label->channel->code }}][{{ $label->locale->code }}]" value="{{ $label->label }}" 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: '{{ __('admin::app.promotion.rule-name') }}',
description: '{{ __('admin::app.promotion.rule-desc') }}',
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,
action_type: null,
apply: null,
apply_to_shipping: null,
disc_amount: null,
disc_quantity: null,
end_other_rules: null,
coupon_type: null,
free_shipping: null,
all_conditions: null,
match_criteria: 'all_are_true',
all_attributes: {
'categories' : null,
'attributes' : 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: 'cart',
conditions: @json($cart_rule[0]).conditions,
cart_input: @json($cart_rule[0]).attributes,
actions: @json($cart_rule[0]).actions,
conditions_list:[],
cart_object: {
attribute: null,
condition: null,
value: []
},
country_and_states: @json($cart_rule[2]),
category_options: @json($cart_rule[1]),
category_values: [],
attribute_values: [],
attr_object: {
attribute: null,
condition: null,
value: [],
options: []
},
attribute_input: @json($cart_rule[4]),
}
},
mounted () {
data = @json($cart_rule[3]);
this.name = data.name;
this.description = data.description;
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_to_shipping = data.apply_to_shipping;
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.hasOwnProperty('conditions') && data.conditions != null) {
this.conditions_list = JSON.parse(JSON.parse(data.conditions));
this.match_criteria = this.conditions_list.pop().criteria;
}
hasSubSelectionConditions = JSON.parse(data.actions).hasOwnProperty('attribute_conditions');
if (hasSubSelectionConditions) {
if (JSON.parse(data.actions).attribute_conditions.hasOwnProperty('categories')) {
this.category_values = JSON.parse(data.actions).attribute_conditions.categories;
} else {
this.category_values = [];
}
if (JSON.parse(data.actions).attribute_conditions.hasOwnProperty('attributes')) {
this.attribute_values = JSON.parse(data.actions).attribute_conditions.attributes;
} else {
this.attribute_values = [];
}
// creating options and has option param on the frontend
for (i in this.attribute_values) {
for (j in this.attribute_input) {
if (this.attribute_input[j].code == this.attribute_values[i].attribute) {
if (this.attribute_input[j].has_options == true) {
this.attribute_values[i].has_options = true;
this.attribute_values[i].options = this.attribute_input[j].options;
} else {
this.attribute_values[i].has_options = false;
this.attribute_values[i].options = null;
}
}
}
}
}
criteria = null;
},
methods: {
categoryLabel (option) {
return option.name + ' [ ' + option.slug + ' ]';
},
attributeListLabel(option) {
return option.label;
},
addCondition () {
if (this.criteria == 'product_subselection' || this.criteria == 'cart') {
this.condition_on = this.criteria;
} else {
alert('please select type of condition');
return false;
}
if (this.condition_on == 'cart') {
this.conditions_list.push(this.cart_object);
this.cart_object = {
attribute: null,
condition: null,
value: []
};
}
},
addAttributeCondition() {
this.attribute_values.push(this.attr_object);
this.attr_object = {
attribute: null,
condition: null,
value: [],
options: []
};
},
checkAutogen() {
},
detectApply() {
return;
},
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;
}
}
},
enableAttributeCondition (event, index) {
selectedIndex = event.target.selectedIndex - 1;
for(i in this.attribute_input) {
if (i == selectedIndex) {
if (this.attribute_input[i].has_options == true) {
this.attribute_values[index].options = this.attribute_input[i].options;
}
this.attribute_values[index].type = this.attribute_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);
},
removeAttr(index) {
this.attribute_values.splice(index, 1);
},
onSubmit: function (e) {
if (this.attribute_values.length != 0 || this.category_values.length != 0) {
for (i in this.attribute_values) {
delete this.attribute_values[i].options;
}
this.all_attributes.categories = this.category_values;
this.all_attributes.attributes = this.attribute_values;
this.all_attributes = JSON.stringify(this.all_attributes);
} else {
this.all_attributes = null;
}
if (this.conditions_list.length != 0) {
this.conditions_list.push({'criteria': this.match_criteria});
this.all_conditions = JSON.stringify(this.conditions_list);
this.conditions_list.pop();
}
this.$validator.validateAll().then(result => {
if (result) {
e.target.submit();
}
});
},
addFlashMessages() {
const flashes = this.$refs.flashes;
flashMessages.forEach(function(flash) {
flashes.addFlash(flash);
}, this);
}
}
});
</script>
@endpush
@stop

View File

@ -0,0 +1,614 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.promotions.cart-rules.add-title') }}
@stop
@section('content')
<div class="content">
<cart-rule></cart-rule>
</div>
@stop
@push('scripts')
@parent
<script type="text/x-template" id="cart-rule-template">
<div>
<form method="POST" action="{{ route('admin.cart-rules.store') }}" @submit.prevent="onSubmit">
<div class="page-header">
<div class="page-title">
<h1>
<i class="icon angle-left-icon back-link" @click="redirectBack('{{ url('/admin/dashboard') }}')"></i>
{{ __('admin::app.promotions.cart-rules.add-title') }}
</h1>
</div>
<div class="page-action">
<button type="submit" class="btn btn-lg btn-primary">
{{ __('admin::app.promotions.cart-rules.save-btn-title') }}
</button>
</div>
</div>
<div class="page-content">
<div class="form-container">
@csrf()
{!! view_render_event('bagisto.admin.promotions.cart-rules.create.before') !!}
<accordian :title="'{{ __('admin::app.promotions.cart-rules.rule-information') }}'" :active="true">
<div slot="body">
<div class="control-group" :class="[errors.has('name') ? 'has-error' : '']">
<label for="name" class="required">{{ __('admin::app.promotions.cart-rules.name') }}</label>
<input v-validate="'required'" class="control" id="name" name="name" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.name') }}&quot;" value="{{ old('name') }}"/>
<span class="control-error" v-if="errors.has('name')">@{{ errors.first('name') }}</span>
</div>
<div class="control-group">
<label for="description">{{ __('admin::app.promotions.cart-rules.description') }}</label>
<textarea class="control" id="description" name="description">{{ old('description') }}</textarea>
</div>
<div class="control-group">
<label for="status">{{ __('admin::app.promotions.cart-rules.status') }}</label>
<span class="checkbox">
<input type="checkbox" id="status" name="status" value="1">
<label class="checkbox-view" for="status"></label>
{{ __('admin::app.promotions.cart-rules.is-active') }}
</span>
</div>
<div class="control-group" :class="[errors.has('channels[]') ? 'has-error' : '']">
<label for="channels" class="required">{{ __('admin::app.promotions.cart-rules.channels') }}</label>
<select class="control" id="channels" name="channels[]" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.channels') }}&quot;" multiple="multiple">
@foreach(core()->getAllChannels() as $channel)
<option value="{{ $channel->id }}" {{ old('channels') && in_array($channel->id, old('channels')) ? 'selected' : '' }}>
{{ $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('customer_groups[]') ? 'has-error' : '']">
<label for="customer_groups" class="required">{{ __('admin::app.promotions.cart-rules.customer-groups') }}</label>
<select class="control" id="customer_groups" name="customer_groups[]" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.customer-groups') }}&quot;" multiple="multiple">
@foreach(app('Webkul\Customer\Repositories\CustomerGroupRepository')->all() as $customerGroup)
<option value="{{ $customerGroup->id }}" {{ old('customer_groups') && in_array($customerGroup->id, old('customer_groups')) ? 'selected' : '' }}>
{{ $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('coupon_type') ? 'has-error' : '']">
<label for="coupon_type" class="required">{{ __('admin::app.promotions.cart-rules.coupon-type') }}</label>
<select class="control" id="coupon_type" name="coupon_type" v-model="coupon_type" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.coupon-type') }}&quot;">
<option value="0" {{ old('coupon_type') == 0 ? 'selected' : '' }}>{{ __('admin::app.promotions.cart-rules.no-coupon') }}</option>
<option value="1" {{ old('coupon_type') == 1 ? 'selected' : '' }}>{{ __('admin::app.promotions.cart-rules.specific-coupon') }}</option>
</select>
<span class="control-error" v-if="errors.has('coupon_type')">@{{ errors.first('coupon_type') }}</span>
</div>
<div v-if="parseInt(coupon_type)">
<div class="control-group" :class="[errors.has('use_auto_generation') ? 'has-error' : '']">
<label for="use_auto_generation" class="required">{{ __('admin::app.promotions.cart-rules.auto-generate-coupon') }}</label>
<select class="control" id="use_auto_generation" name="use_auto_generation" v-model="use_auto_generation" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.auto-generate-coupon') }}&quot;">
<option value="0">{{ __('admin::app.promotions.cart-rules.no') }}</option>
<option value="1">{{ __('admin::app.promotions.cart-rules.yes') }}</option>
</select>
<span class="control-error" v-if="errors.has('use_auto_generation')">@{{ errors.first('use_auto_generation') }}</span>
</div>
<div v-if="! parseInt(use_auto_generation)">
<div class="control-group" :class="[errors.has('coupon_code') ? 'has-error' : '']">
<label for="coupon_code" class="required">{{ __('admin::app.promotions.cart-rules.coupon-code') }}</label>
<input v-validate="'required'" class="control" id="coupon_code" name="coupon_code" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.coupon-code') }}&quot;" value="{{ old('coupon_code') }}"/>
<span class="control-error" v-if="errors.has('coupon_code')">@{{ errors.first('coupon_code') }}</span>
</div>
</div>
<div class="control-group">
<label for="uses_per_coupon">{{ __('admin::app.promotions.cart-rules.uses-per-coupon') }}</label>
<input class="control" id="uses_per_coupon" name="uses_per_coupon" value="{{ old('uses_per_coupon') }}"/>
</div>
</div>
<div class="control-group">
<label for="usage_per_customer">{{ __('admin::app.promotions.cart-rules.uses-per-customer') }}</label>
<input class="control" id="usage_per_customer" name="usage_per_customer" value="{{ old('usage_per_customer') }}"/>
<span class="control-info">{{ __('admin::app.promotions.cart-rules.uses-per-customer-control-info') }}</span>
</div>
<div class="control-group date">
<label for="starts_from">{{ __('admin::app.promotions.cart-rules.from') }}</label>
<date>
<input type="text" name="starts_from" class="control" value="{{ old('starts_from') }}"/>
</date>
</div>
<div class="control-group date">
<label for="ends_till">{{ __('admin::app.promotions.cart-rules.to') }}</label>
<date>
<input type="text" name="ends_till" class="control" value="{{ old('ends_till') }}"/>
</date>
</div>
<div class="control-group">
<label for="sort_order">{{ __('admin::app.promotions.cart-rules.priority') }}</label>
<input type="text" class="control" id="sort_order" name="sort_order" value="{{ old('sort_order') ?? 0 }}"/>
</div>
</div>
</accordian>
<accordian :title="'{{ __('admin::app.promotions.cart-rules.conditions') }}'" :active="false">
<div slot="body">
<div class="control-group">
<label for="condition_type">{{ __('admin::app.promotions.cart-rules.condition-type') }}</label>
<select class="control" id="condition_type" name="condition_type" v-model="condition_type">
<option value="1">{{ __('admin::app.promotions.cart-rules.all-conditions-true') }}</option>
<option value="2">{{ __('admin::app.promotions.cart-rules.any-condition-true') }}</option>
</select>
</div>
<div class="table cart-rule-conditions" style="margin-top: 20px; overflow-x: unset;">
<table>
<tbody>
<cart-rule-condition-item
v-for='(condition, index) in conditions'
:condition="condition"
:key="index"
:index="index"
@onRemoveCondition="removeCondition($event)">
</cart-rule-condition-item>
</tbody>
</table>
</div>
<button type="button" class="btn btn-lg btn-primary" style="margin-top: 20px;" @click="addCondition">
{{ __('admin::app.promotions.cart-rules.add-condition') }}
</button>
</div>
</accordian>
<accordian :title="'{{ __('admin::app.promotions.cart-rules.actions') }}'" :active="false">
<div slot="body">
<div class="control-group" :class="[errors.has('action_type') ? 'has-error' : '']">
<label for="action_type" class="required">{{ __('admin::app.promotions.cart-rules.action-type') }}</label>
<select class="control" id="action_type" name="action_type" v-validate="'required'" v-model="action_type" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.action-type') }}&quot;">
<option value="by_percent" {{ old('action_type') == 'by_percent' ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.percentage-product-price') }}
</option>
<option value="by_fixed" {{ old('action_type') == 'by_fixed' ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.fixed-amount') }}
</option>
<option value="cart_fixed" {{ old('action_type') == 'cart_fixed' ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.fixed-amount-whole-cart') }}
</option>
<option value="buy_x_get_y" {{ old('action_type') == 'buy_x_get_y' ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.buy-x-get-y-free') }}
</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('discount_amount') ? 'has-error' : '']">
<label for="discount_amount" class="required">{{ __('admin::app.promotions.cart-rules.discount-amount') }}</label>
<input v-validate="'required'" class="control" id="discount_amount" name="discount_amount" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.discount-amount') }}&quot;" value="{{ old('discount_amount') ?? 0 }}"/>
<span class="control-error" v-if="errors.has('discount_amount')">@{{ errors.first('discount_amount') }}</span>
</div>
<div class="control-group">
<label for="discount_quantity">{{ __('admin::app.promotions.cart-rules.discount-quantity') }}</label>
<input class="control" id="discount_quantity" name="discount_quantity" value="{{ old('discount_quantity') ?? 0 }}"/>
</div>
<div class="control-group">
<label for="discount_step">{{ __('admin::app.promotions.cart-rules.discount-step') }}</label>
<input class="control" id="discount_step" name="discount_step" value="{{ old('discount_step') ?? 0 }}"/>
</div>
<div class="control-group">
<label for="apply_to_shipping">{{ __('admin::app.promotions.cart-rules.apply-to-shipping') }}</label>
<select class="control" id="apply_to_shipping" name="apply_to_shipping" :disabled="action_type == 'cart_fixed'">
<option value="0" {{ ! old('apply_to_shipping') ? 'selected' : '' }}>{{ __('admin::app.promotions.cart-rules.no') }}</option>
<option value="1" {{ old('apply_to_shipping') ? 'selected' : '' }}>{{ __('admin::app.promotions.cart-rules.yes') }}</option>
</select>
</div>
<div class="control-group">
<label for="free_shipping">{{ __('admin::app.promotions.cart-rules.free-shipping') }}</label>
<select class="control" id="free_shipping" name="free_shipping">
<option value="0" {{ ! old('free_shipping') ? 'selected' : '' }}>{{ __('admin::app.promotions.cart-rules.no') }}</option>
<option value="1" {{ old('free_shipping') ? 'selected' : '' }}>{{ __('admin::app.promotions.cart-rules.yes') }}</option>
</select>
</div>
<div class="control-group">
<label for="end_other_rules">{{ __('admin::app.promotions.cart-rules.end-other-rules') }}</label>
<select class="control" id="end_other_rules" name="end_other_rules">
<option value="0" {{ ! old('end_other_rules') ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.no') }}
</option>
<option value="1" {{ old('end_other_rules') ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.yes') }}
</option>
</select>
</div>
</div>
</accordian>
{!! view_render_event('bagisto.admin.promotions.cart-rules.create.after') !!}
</div>
</div>
</form>
</div>
</script>
<script type="text/x-template" id="cart-rule-condition-item-template">
<tr>
<td class="attribute">
<div class="control-group">
<select :name="['conditions[' + index + '][attribute]']" class="control" v-model="condition.attribute">
<option value="">{{ __('admin::app.promotions.cart-rules.choose-condition-to-add') }}</option>
<optgroup v-for='(conditionAttribute, index) in condition_attributes' :label="conditionAttribute.label">
<option v-for='(childAttribute, index) in conditionAttribute.children' :value="childAttribute.key">
@{{ childAttribute.label }}
</option>
</optgroup>
</select>
</div>
</td>
<td class="operator">
<div class="control-group" v-if="matchedAttribute">
<select :name="['conditions[' + index + '][operator]']" class="control" v-model="condition.operator">
<option v-for='operator in condition_operators[matchedAttribute.type]' :value="operator.operator">
@{{ operator.label }}
</option>
</select>
</div>
</td>
<td class="value">
<div v-if="matchedAttribute">
<input type="hidden" :name="['conditions[' + index + '][attribute_type]']" v-model="matchedAttribute.type">
<div v-if="matchedAttribute.key == 'product|category_ids' || matchedAttribute.key == 'product|category_ids' || matchedAttribute.key == 'product|parent::category_ids'">
<tree-view value-field="id" id-field="id" :name-field="'conditions[' + index + '][value]'" input-type="checkbox" :items='matchedAttribute.options' :behavior="'no'"></tree-view>
</div>
<div v-else>
<div class="control-group" v-if="matchedAttribute.type == 'text' || matchedAttribute.type == 'price' || matchedAttribute.type == 'decimal' || matchedAttribute.type == 'integer'">
<input class="control" :name="['conditions[' + index + '][value]']" v-model="condition.value"/>
</div>
<div class="control-group date" v-if="matchedAttribute.type == 'date'">
<date>
<input class="control" :name="['conditions[' + index + '][value]']" v-model="condition.value"/>
</date>
</div>
<div class="control-group date" v-if="matchedAttribute.type == 'datetime'">
<datetime>
<input class="control" :name="['conditions[' + index + '][value]']" v-model="condition.value"/>
</datetime>
</div>
<div class="control-group" v-if="matchedAttribute.type == 'boolean'">
<select :name="['conditions[' + index + '][value]']" class="control" v-model="condition.value">
<option value="1">{{ __('admin::app.promotions.cart-rules.yes') }}</option>
<option value="0">{{ __('admin::app.promotions.cart-rules.no') }}</option>
</select>
</div>
<div class="control-group" v-if="matchedAttribute.type == 'select' || matchedAttribute.type == 'radio'">
<select :name="['conditions[' + index + '][value]']" class="control" v-model="condition.value" v-if="matchedAttribute.key != 'cart|state'">
<option v-for='option in matchedAttribute.options' :value="option.id">
@{{ option.admin_name }}
</option>
</select>
<select :name="['conditions[' + index + '][value]']" class="control" v-model="condition.value" v-else>
<optgroup v-for='option in matchedAttribute.options' :label="option.admin_name">
<option v-for='state in option.states' :value="state.code">
@{{ state.admin_name }}
</option>
</optgroup>
</select>
</div>
<div class="control-group" v-if="matchedAttribute.type == 'multiselect' || matchedAttribute.type == 'checkbox'">
<select :name="['conditions[' + index + '][value][]']" class="control" v-model="condition.value" multiple>
<option v-for='option in matchedAttribute.options' :value="option.id">
@{{ option.admin_name }}
</option>
</select>
</div>
</div>
</div>
</td>
<td class="actions">
<i class="icon trash-icon" @click="removeCondition"></i>
</td>
</tr>
</script>
<script>
Vue.component('cart-rule', {
template: '#cart-rule-template',
inject: ['$validator'],
data: function() {
return {
coupon_type: 0,
use_auto_generation: 0,
condition_type: 1,
conditions: [],
action_type: "{{ old('action_type') ?: 'by_percent' }}"
}
},
methods: {
addCondition: function() {
this.conditions.push({
'attribute': '',
'operator': '==',
'value': '',
});
},
removeCondition: function(condition) {
let index = this.conditions.indexOf(condition)
this.conditions.splice(index, 1)
},
onSubmit: function(e) {
this.$root.onSubmit(e)
},
onSubmit: function(e) {
this.$root.onSubmit(e)
},
redirectBack: function(fallbackUrl) {
this.$root.redirectBack(fallbackUrl)
}
}
});
Vue.component('cart-rule-condition-item', {
template: '#cart-rule-condition-item-template',
props: ['index', 'condition'],
data: function() {
return {
condition_attributes: @json(app('\Webkul\CartRule\Repositories\CartRuleRepository')->getConditionAttributes()),
attribute_type_indexes: {
'cart': 0,
'cart_item': 1,
'product': 2
},
condition_operators: {
'price': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-less-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.less-than') }}'
}],
'decimal': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.cart-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.cart-rules.less-than') }}'
}],
'integer': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.cart-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.cart-rules.less-than') }}'
}],
'text': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}, {
'operator': '{}',
'label': '{{ __('admin::app.promotions.cart-rules.contain') }}'
}, {
'operator': '!{}',
'label': '{{ __('admin::app.promotions.cart-rules.does-not-contain') }}'
}],
'boolean': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}],
'date': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.cart-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.cart-rules.less-than') }}'
}],
'datetime': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.cart-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.cart-rules.less-than') }}'
}],
'select': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}],
'radio': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}],
'multiselect': [{
'operator': '{}',
'label': '{{ __('admin::app.promotions.cart-rules.contains') }}'
}, {
'operator': '!{}',
'label': '{{ __('admin::app.promotions.cart-rules.does-not-contain') }}'
}],
'checkbox': [{
'operator': '{}',
'label': '{{ __('admin::app.promotions.cart-rules.contains') }}'
}, {
'operator': '!{}',
'label': '{{ __('admin::app.promotions.cart-rules.does-not-contain') }}'
}]
}
}
},
computed: {
matchedAttribute: function () {
if (this.condition.attribute == '')
return;
var this_this = this;
var attributeIndex = this.attribute_type_indexes[this.condition.attribute.split("|")[0]];
matchedAttribute = this.condition_attributes[attributeIndex]['children'].filter(function (attribute) {
return attribute.key == this_this.condition.attribute;
});
if (matchedAttribute[0]['type'] == 'multiselect' || matchedAttribute[0]['type'] == 'checkbox') {
this.condition.operator = '{}';
this.condition.value = [];
}
return matchedAttribute[0];
}
},
methods: {
removeCondition: function() {
this.$emit('onRemoveCondition', this.condition)
}
}
});
</script>
@endpush

View File

@ -0,0 +1,753 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.promotions.cart-rules.edit-title') }}
@stop
@section('content')
<div class="content">
<cart-rule></cart-rule>
</div>
@stop
@push('scripts')
@parent
<script type="text/x-template" id="cart-rule-template">
<div>
<form method="POST" action="{{ route('admin.cart-rules.update', $cartRule->id) }}" @submit.prevent="onSubmit">
<div class="page-header">
<div class="page-title">
<h1>
<i class="icon angle-left-icon back-link" @click="redirectBack('{{ url('/admin/dashboard') }}')"></i>
{{ __('admin::app.promotions.cart-rules.edit-title') }}
</h1>
</div>
<div class="page-action">
<button type="submit" class="btn btn-lg btn-primary">
{{ __('admin::app.promotions.cart-rules.save-btn-title') }}
</button>
</div>
</div>
<div class="page-content">
<div class="form-container">
@csrf()
{!! view_render_event('bagisto.admin.promotions.cart-rules.create.before') !!}
<accordian :title="'{{ __('admin::app.promotions.cart-rules.rule-information') }}'" :active="true">
<div slot="body">
<div class="control-group" :class="[errors.has('name') ? 'has-error' : '']">
<label for="name" class="required">{{ __('admin::app.promotions.cart-rules.name') }}</label>
<input v-validate="'required'" class="control" id="name" name="name" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.name') }}&quot;" value="{{ old('name') ?: $cartRule->name }}"/>
<span class="control-error" v-if="errors.has('name')">@{{ errors.first('name') }}</span>
</div>
<div class="control-group">
<label for="description">{{ __('admin::app.promotions.cart-rules.description') }}</label>
<textarea class="control" id="description" name="description">{{ old('description') ?: $cartRule->description }}</textarea>
</div>
<div class="control-group">
<label for="status">{{ __('admin::app.promotions.cart-rules.status') }}</label>
<span class="checkbox">
<input type="checkbox" id="status" name="status" value="{{ $cartRule->status }}" {{ $cartRule->status ? 'checked' : '' }}>
<label class="checkbox-view" for="status"></label>
{{ __('admin::app.promotions.cart-rules.is-active') }}
</span>
</div>
<div class="control-group" :class="[errors.has('channels[]') ? 'has-error' : '']">
<label for="channels" class="required">{{ __('admin::app.promotions.cart-rules.channels') }}</label>
<?php $selectedOptionIds = old('channels') ?: $cartRule->channels->pluck('id')->toArray() ?>
<select class="control" id="channels" name="channels[]" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.channels') }}&quot;" multiple="multiple">
@foreach(core()->getAllChannels() as $channel)
<option value="{{ $channel->id }}" {{ in_array($channel->id, $selectedOptionIds) ? 'selected' : '' }}>
{{ $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('customer_groups[]') ? 'has-error' : '']">
<label for="customer_groups" class="required">{{ __('admin::app.promotions.cart-rules.customer-groups') }}</label>
<?php $selectedOptionIds = old('customer_groups') ?: $cartRule->customer_groups->pluck('id')->toArray() ?>
<select class="control" id="customer_groups" name="customer_groups[]" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.customer-groups') }}&quot;" multiple="multiple">
@foreach(app('Webkul\Customer\Repositories\CustomerGroupRepository')->all() as $customerGroup)
<option value="{{ $customerGroup->id }}" {{ in_array($customerGroup->id, $selectedOptionIds) ? 'selected' : '' }}>
{{ $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('coupon_type') ? 'has-error' : '']">
<label for="coupon_type" class="required">{{ __('admin::app.promotions.cart-rules.coupon-type') }}</label>
<select class="control" id="coupon_type" name="coupon_type" v-model="coupon_type" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.coupon-type') }}&quot;">
<option value="0" {{ old('coupon_type') == 0 ? 'selected' : '' }}>{{ __('admin::app.promotions.cart-rules.no-coupon') }}</option>
<option value="1" {{ old('coupon_type') == 1 ? 'selected' : '' }}>{{ __('admin::app.promotions.cart-rules.specific-coupon') }}</option>
</select>
<span class="control-error" v-if="errors.has('coupon_type')">@{{ errors.first('coupon_type') }}</span>
</div>
<div v-if="parseInt(coupon_type)">
<div class="control-group" :class="[errors.has('use_auto_generation') ? 'has-error' : '']">
<label for="use_auto_generation" class="required">{{ __('admin::app.promotions.cart-rules.auto-generate-coupon') }}</label>
<select class="control" id="use_auto_generation" name="use_auto_generation" v-model="use_auto_generation" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.auto-generate-coupon') }}&quot;">
<option value="0">{{ __('admin::app.promotions.cart-rules.no') }}</option>
<option value="1">{{ __('admin::app.promotions.cart-rules.yes') }}</option>
</select>
<span class="control-error" v-if="errors.has('use_auto_generation')">@{{ errors.first('use_auto_generation') }}</span>
</div>
<div v-if="! parseInt(use_auto_generation)">
<div class="control-group" :class="[errors.has('coupon_code') ? 'has-error' : '']">
<label for="coupon_code" class="required">{{ __('admin::app.promotions.cart-rules.coupon-code') }}</label>
<input v-validate="'required'" class="control" id="coupon_code" name="coupon_code" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.coupon-code') }}&quot;" value="{{ old('coupon_code') ?: $cartRule->coupon_code }}"/>
<span class="control-error" v-if="errors.has('coupon_code')">@{{ errors.first('coupon_code') }}</span>
</div>
</div>
<div class="control-group">
<label for="uses_per_coupon">{{ __('admin::app.promotions.cart-rules.uses-per-coupon') }}</label>
<input class="control" id="uses_per_coupon" name="uses_per_coupon" value="{{ old('uses_per_coupon') ?: $cartRule->uses_per_coupon }}"/>
</div>
</div>
<div class="control-group">
<label for="usage_per_customer">{{ __('admin::app.promotions.cart-rules.uses-per-customer') }}</label>
<input class="control" id="usage_per_customer" name="usage_per_customer" value="{{ old('usage_per_customer') ?: $cartRule->usage_per_customer }}"/>
<span class="control-info">{{ __('admin::app.promotions.cart-rules.uses-per-customer-control-info') }}</span>
</div>
<div class="control-group date">
<label for="starts_from">{{ __('admin::app.promotions.cart-rules.from') }}</label>
<date>
<input type="text" name="starts_from" class="control" value="{{ old('starts_from') ?: $cartRule->starts_from }}"/>
</date>
</div>
<div class="control-group date">
<label for="ends_till">{{ __('admin::app.promotions.cart-rules.to') }}</label>
<date>
<input type="text" name="ends_till" class="control" value="{{ old('ends_till') ?: $cartRule->ends_till }}"/>
</date>
</div>
<div class="control-group">
<label for="sort_order">{{ __('admin::app.promotions.cart-rules.priority') }}</label>
<input type="text" class="control" id="sort_order" name="sort_order" value="{{ $cartRule->sort_order }}" {{ $cartRule->sort_order ? 'checked' : '' }}/>
</div>
</div>
</accordian>
<accordian :title="'{{ __('admin::app.promotions.cart-rules.conditions') }}'" :active="false">
<div slot="body">
<div class="control-group">
<label for="condition_type">{{ __('admin::app.promotions.cart-rules.condition-type') }}</label>
<select class="control" id="condition_type" name="condition_type" v-model="condition_type">
<option value="1">{{ __('admin::app.promotions.cart-rules.all-conditions-true') }}</option>
<option value="2">{{ __('admin::app.promotions.cart-rules.any-condition-true') }}</option>
</select>
</div>
<div class="table cart-rule-conditions" style="margin-top: 20px; overflow-x: unset;">
<table>
<tbody>
<cart-rule-condition-item
v-for='(condition, index) in conditions'
:condition="condition"
:key="index"
:index="index"
@onRemoveCondition="removeCondition($event)">
</cart-rule-condition-item>
</tbody>
</table>
</div>
<button type="button" class="btn btn-lg btn-primary" style="margin-top: 20px;" @click="addCondition">
{{ __('admin::app.promotions.cart-rules.add-condition') }}
</button>
</div>
</accordian>
<accordian :title="'{{ __('admin::app.promotions.cart-rules.actions') }}'" :active="false">
<div slot="body">
<div class="control-group" :class="[errors.has('action_type') ? 'has-error' : '']">
<label for="action_type" class="required">{{ __('admin::app.promotions.cart-rules.action-type') }}</label>
<?php $selectedOption = old('action_type') ?: $cartRule->action_type ?>
<select class="control" id="action_type" name="action_type" v-validate="'required'" v-model="action_type" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.action-type') }}&quot;">
<option value="by_percent" {{ $selectedOption == 'by_percent' ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.percentage-product-price') }}
</option>
<option value="by_fixed" {{ $selectedOption == 'by_fixed' ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.fixed-amount') }}
</option>
<option value="cart_fixed" {{ $selectedOption == 'cart_fixed' ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.fixed-amount-whole-cart') }}
</option>
<option value="buy_x_get_y" {{ $selectedOption == 'buy_x_get_y' ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.buy-x-get-y-free') }}
</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('discount_amount') ? 'has-error' : '']">
<label for="discount_amount" class="required">{{ __('admin::app.promotions.cart-rules.discount-amount') }}</label>
<input v-validate="'required'" class="control" id="discount_amount" name="discount_amount" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.discount-amount') }}&quot;" value="{{ old('discount_amount') ?: $cartRule->discount_amount }}"/>
<span class="control-error" v-if="errors.has('discount_amount')">@{{ errors.first('discount_amount') }}</span>
</div>
<div class="control-group">
<label for="discount_quantity">{{ __('admin::app.promotions.cart-rules.discount-quantity') }}</label>
<input class="control" id="discount_quantity" name="discount_quantity" value="{{ old('discount_quantity') ?: $cartRule->discount_quantity }}"/>
</div>
<div class="control-group">
<label for="discount_step">{{ __('admin::app.promotions.cart-rules.discount-step') }}</label>
<input class="control" id="discount_step" name="discount_step" value="{{ old('discount_step') ?: $cartRule->discount_step }}"/>
</div>
<div class="control-group">
<label for="apply_to_shipping">{{ __('admin::app.promotions.cart-rules.apply-to-shipping') }}</label>
<?php $selectedOption = old('apply_to_shipping') ?: $cartRule->apply_to_shipping ?>
<select class="control" id="apply_to_shipping" name="apply_to_shipping" :disabled="action_type == 'cart_fixed'">
<option value="0" {{ ! $selectedOption ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.no') }}
</option>
<option value="1" {{ $selectedOption ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.yes') }}
</option>
</select>
</div>
<div class="control-group">
<label for="free_shipping">{{ __('admin::app.promotions.cart-rules.free-shipping') }}</label>
<?php $selectedOption = old('free_shipping') ?: $cartRule->free_shipping ?>
<select class="control" id="free_shipping" name="free_shipping">
<option value="0" {{ ! $selectedOption ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.no') }}
</option>
<option value="1" {{ $selectedOption ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.yes') }}
</option>
</select>
</div>
<div class="control-group">
<label for="end_other_rules">{{ __('admin::app.promotions.cart-rules.end-other-rules') }}</label>
<?php $selectedOption = old('end_other_rules') ?: $cartRule->end_other_rules ?>
<select class="control" id="end_other_rules" name="end_other_rules">
<option value="0" {{ ! $selectedOption ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.no') }}
</option>
<option value="1" {{ $selectedOption ? 'selected' : '' }}>
{{ __('admin::app.promotions.cart-rules.yes') }}
</option>
</select>
</div>
</div>
</accordian>
{!! view_render_event('bagisto.admin.promotions.cart-rules.create.after') !!}
</div>
</div>
</form>
<accordian :title="'{{ __('admin::app.promotions.cart-rules.coupon-codes') }}'" :active="false" v-if="coupon_type && use_auto_generation">
<div slot="body">
<create-coupon-form></create-coupon-form>
</div>
</accordian>
</div>
</script>
<script type="text/x-template" id="cart-rule-condition-item-template">
<tr>
<td class="attribute">
<div class="control-group">
<select :name="['conditions[' + index + '][attribute]']" class="control" v-model="condition.attribute">
<option value="">{{ __('admin::app.promotions.cart-rules.choose-condition-to-add') }}</option>
<optgroup v-for='conditionAttribute in condition_attributes' :label="conditionAttribute.label">
<option v-for='childAttribute in conditionAttribute.children' :value="childAttribute.key">
@{{ childAttribute.label }}
</option>
</optgroup>
</select>
</div>
</td>
<td class="operator">
<div class="control-group" v-if="matchedAttribute">
<select :name="['conditions[' + index + '][operator]']" class="control" v-model="condition.operator">
<option v-for='operator in condition_operators[matchedAttribute.type]' :value="operator.operator">
@{{ operator.label }}
</option>
</select>
</div>
</td>
<td class="value">
<div v-if="matchedAttribute">
<input type="hidden" :name="['conditions[' + index + '][attribute_type]']" v-model="matchedAttribute.type">
<div v-if="matchedAttribute.key == 'product|children::category_ids' || matchedAttribute.key == 'product|category_ids' || matchedAttribute.key == 'product|parent::category_ids'">
<tree-view value-field="id" id-field="id" :name-field="'conditions[' + index + '][value]'" input-type="checkbox" :items='matchedAttribute.options' :value='condition.value' :behavior="'no'"></tree-view>
</div>
<div v-else>
<div class="control-group" v-if="matchedAttribute.type == 'text' || matchedAttribute.type == 'price' || matchedAttribute.type == 'decimal' || matchedAttribute.type == 'integer'">
<input class="control" :name="['conditions[' + index + '][value]']" v-model="condition.value"/>
</div>
<div class="control-group date" v-if="matchedAttribute.type == 'date'">
<date>
<input class="control" :name="['conditions[' + index + '][value]']" v-model="condition.value"/>
</date>
</div>
<div class="control-group date" v-if="matchedAttribute.type == 'datetime'">
<datetime>
<input class="control" :name="['conditions[' + index + '][value]']" v-model="condition.value"/>
</datetime>
</div>
<div class="control-group" v-if="matchedAttribute.type == 'boolean'">
<select :name="['conditions[' + index + '][value]']" class="control" v-model="condition.value">
<option value="1">{{ __('admin::app.promotions.cart-rules.yes') }}</option>
<option value="0">{{ __('admin::app.promotions.cart-rules.no') }}</option>
</select>
</div>
<div class="control-group" v-if="matchedAttribute.type == 'select' || matchedAttribute.type == 'radio'">
<select :name="['conditions[' + index + '][value]']" class="control" v-model="condition.value" v-if="matchedAttribute.key != 'cart|state'">
<option v-for='option in matchedAttribute.options' :value="option.id">
@{{ option.admin_name }}
</option>
</select>
<select :name="['conditions[' + index + '][value]']" class="control" v-model="condition.value" v-else>
<optgroup v-for='option in matchedAttribute.options' :label="option.admin_name">
<option v-for='state in option.states' :value="state.code">
@{{ state.admin_name }}
</option>
</optgroup>
</select>
</div>
<div class="control-group" v-if="matchedAttribute.type == 'multiselect' || matchedAttribute.type == 'checkbox'">
<select :name="['conditions[' + index + '][value][]']" class="control" v-model="condition.value" multiple>
<option v-for='option in matchedAttribute.options' :value="option.id">
@{{ option.admin_name }}
</option>
</select>
</div>
</div>
</div>
</td>
<td class="actions">
<i class="icon trash-icon" @click="removeCondition"></i>
</td>
</tr>
</script>
<script type="text/x-template" id="create-coupon-form-template">
<div class="">
<form method="POST" data-vv-scope="create-coupun-form" @submit.prevent="generateCopuns('create-coupun-form')">
<div class="control-group" :class="[errors.has('create-coupun-form.coupon_qty') ? 'has-error' : '']">
<label for="coupon_qty" class="required">{{ __('admin::app.promotions.cart-rules.coupon-qty') }}</label>
<input v-validate="'required|min_value:1'" class="control" id="coupon_qty" name="coupon_qty" v-model="coupon_format.coupon_qty" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.coupon-qty') }}&quot;"/>
<span class="control-error" v-if="errors.has('create-coupun-form.coupon_qty')">
@{{ errors.first('create-coupun-form.coupon_qty') }}
</span>
</div>
<div class="control-group" :class="[errors.has('create-coupun-form.code_length') ? 'has-error' : '']">
<label for="code_length" class="required">{{ __('admin::app.promotions.cart-rules.code-length') }}</label>
<input v-validate="'required|min_value:10'" class="control" id="code_length" name="code_length" v-model="coupon_format.code_length" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.code-length') }}&quot;"/>
<span class="control-error" v-if="errors.has('create-coupun-form.code_length')">
@{{ errors.first('create-coupun-form.code_length') }}
</span>
</div>
<div class="control-group" :class="[errors.has('create-coupun-form.code_format') ? 'has-error' : '']">
<label for="code_format" class="required">{{ __('admin::app.promotions.cart-rules.code-format') }}</label>
<select class="control" id="code_format" name="code_format" v-model="coupon_format.code_format" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.cart-rules.code-format') }}&quot;">
<option value="alphanumeric">{{ __('admin::app.promotions.cart-rules.alphanumeric') }}</option>
<option value="alphabetical">{{ __('admin::app.promotions.cart-rules.alphabetical') }}</option>
<option value="numeric">{{ __('admin::app.promotions.cart-rules.numeric') }}</option>
</select>
<span class="control-error" v-if="errors.has('create-coupun-form.code_format')">
@{{ errors.first('create-coupun-form.code_format') }}
</span>
</div>
<div class="control-group">
<label for="code_prefix">{{ __('admin::app.promotions.cart-rules.code-prefix') }}</label>
<input class="control" id="code_prefix" name="code_prefix" v-model="coupon_format.code_prefix"/>
</div>
<div class="control-group">
<label for="code_suffix">{{ __('admin::app.promotions.cart-rules.code-suffix') }}</label>
<input class="control" id="code_suffix" name="code_suffix" v-model="coupon_format.code_suffix"/>
</div>
<div class="button-group">
<button class="btn btn-xl btn-primary">{{ __('admin::app.promotions.cart-rules.generate') }}</button>
</div>
</form>
@inject('cartRuleCouponGrid','Webkul\Admin\DataGrids\CartRuleCouponDataGrid')
{!! $cartRuleCouponGrid->render() !!}
</div>
</script>
<script>
Vue.component('cart-rule', {
template: '#cart-rule-template',
inject: ['$validator'],
data: function() {
return {
coupon_type: {{ old('coupon_type') ?: $cartRule->coupon_type }},
use_auto_generation: {{ old('use_auto_generation') ?: $cartRule->use_auto_generation }},
condition_type: {{ old('condition_type') ?: $cartRule->condition_type }},
conditions: @json($cartRule->conditions ?: []),
action_type: "{{ old('action_type') ?: $cartRule->action_type }}"
}
},
methods: {
addCondition: function() {
this.conditions.push({
'attribute': '',
'operator': '==',
'value': '',
});
},
removeCondition: function(condition) {
let index = this.conditions.indexOf(condition)
this.conditions.splice(index, 1)
},
onSubmit: function(e) {
this.$root.onSubmit(e)
},
redirectBack: function(fallbackUrl) {
this.$root.redirectBack(fallbackUrl)
}
}
});
Vue.component('cart-rule-condition-item', {
template: '#cart-rule-condition-item-template',
props: ['index', 'condition'],
data: function() {
return {
condition_attributes: @json(app('\Webkul\CartRule\Repositories\CartRuleRepository')->getConditionAttributes()),
attribute_type_indexes: {
'cart': 0,
'cart_item': 1,
'product': 2
},
condition_operators: {
'price': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-less-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.less-than') }}'
}],
'decimal': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.cart-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.cart-rules.less-than') }}'
}],
'integer': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.cart-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.cart-rules.less-than') }}'
}],
'text': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}, {
'operator': '{}',
'label': '{{ __('admin::app.promotions.cart-rules.contain') }}'
}, {
'operator': '!{}',
'label': '{{ __('admin::app.promotions.cart-rules.does-not-contain') }}'
}],
'boolean': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}],
'date': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.cart-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.cart-rules.less-than') }}'
}],
'datetime': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.cart-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.cart-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.cart-rules.less-than') }}'
}],
'select': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}],
'radio': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.cart-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.cart-rules.is-not-equal-to') }}'
}],
'multiselect': [{
'operator': '{}',
'label': '{{ __('admin::app.promotions.cart-rules.contains') }}'
}, {
'operator': '!{}',
'label': '{{ __('admin::app.promotions.cart-rules.does-not-contain') }}'
}],
'checkbox': [{
'operator': '{}',
'label': '{{ __('admin::app.promotions.cart-rules.contains') }}'
}, {
'operator': '!{}',
'label': '{{ __('admin::app.promotions.cart-rules.does-not-contain') }}'
}]
}
}
},
computed: {
matchedAttribute: function () {
if (this.condition.attribute == '')
return;
var this_this = this;
var attributeIndex = this.attribute_type_indexes[this.condition.attribute.split("|")[0]];
matchedAttribute = this.condition_attributes[attributeIndex]['children'].filter(function (attribute) {
return attribute.key == this_this.condition.attribute;
});
if (matchedAttribute[0]['type'] == 'multiselect' || matchedAttribute[0]['type'] == 'checkbox') {
this.condition.operator = '{}';
this.condition.value = this.condition.value == '' && this.condition.value != undefined ? [] : this.condition.value;
}
return matchedAttribute[0];
}
},
methods: {
removeCondition: function() {
this.$emit('onRemoveCondition', this.condition)
}
}
});
Vue.component('create-coupon-form', {
template: '#create-coupon-form-template',
inject: ['$validator'],
data: function() {
return {
coupon_format: {
coupon_qty: '',
code_length: 12,
code_format: 'alphanumeric',
code_prefix: '',
code_suffix: ''
}
}
},
methods: {
generateCopuns: function(formScope) {
var this_this = this;
this.$validator.validateAll(formScope).then(function (result) {
if (result) {
this_this.$http.post("{{ route('admin.cart-rules.generate-coupons', $cartRule->id) }}", this_this.coupon_format)
.then(function(response) {
window.flashMessages = [{
'type': 'alert-success',
'message': response.data.message
}];
this_this.$root.addFlashMessages()
})
.catch(function (error) {
window.flashMessages = [{
'type': 'alert-error',
'message': error.response.data.message
}];
this_this.$root.addFlashMessages()
})
}
});
}
}
});
</script>
@endpush

View File

@ -1,7 +1,7 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.promotion.cart-rule') }}
{{ __('admin::app.promotions.cart-rules.title') }}
@stop
@section('content')
@ -9,12 +9,12 @@
<div class="content">
<div class="page-header">
<div class="page-title">
<h1>{{ __('admin::app.promotion.cart-rule') }}</h1>
<h1>{{ __('admin::app.promotions.cart-rules.title') }}</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 href="{{ route('admin.cart-rules.create') }}" class="btn btn-lg btn-primary">
{{ __('admin::app.promotions.cart-rules.add-title') }}
</a>
</div>
</div>

View File

@ -1,415 +0,0 @@
@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="{{ __('admin::app.promotion.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" class="required">{{ __('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">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Option']) }}</option>
<option value="1">{{ __('admin::app.promotion.yes') }}</option>
<option value="0">{{ __('admin::app.promotion.no') }}</option>
</select>
<span class="control-error" v-if="errors.has('status')">@{{ errors.first('status') }}</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>
</accordian>
<accordian :active="false" title="{{ __('admin::app.promotion.conditions') }}">
<div slot="body">
<input type="hidden" name="all_conditions" v-model="all_conditions">
<!-- Categories selection input block -->
<div class="control-group" :class="[errors.has('category_values') ? 'has-error' : '']">
<label class="mb-10" for="categories">{{ __('admin::app.promotion.select-category') }}</label>
<multiselect v-model="category_values" :close-on-select="false" :options="category_options" :searchable="false" :custom-label="categoryLabel" :show-labels="true" placeholder="Select Categories" track-by="slug" :multiple="true"></multiselect>
</div>
<label class="mb-10" for="attributes">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Attribute']) }}</label>
<br/>
<!-- Product attributes conditions block -->
<div class="control-container mt-20" v-for="(condition, index) in attribute_values" :key="index">
<select class="control" v-model="attribute_values[index].attribute" style="margin-right: 15px; width: 30%;" v-on:change="enableAttributeCondition($event, index)">
<option disabled="disabled">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Option']) }}</option>
<option v-for="(attr_ip, index1) in attribute_input" :value="attr_ip.code" :key="index1">@{{ attr_ip.name }}</option>
</select>
<select v-show='attribute_values[index].type == "select" || attribute_values[index].type == "multiselect"' class="control" v-model="attribute_values[index].condition" style="margin-right: 15px;">
<option v-for="(condition, index) in conditions.select" :value="index" :key="index">@{{ condition }}</option>
</select>
<select v-show='attribute_values[index].type == "text" || attribute_values[index].type == "textarea" || attribute_values[index].type == "price"' class="control" v-model="attribute_values[index].condition" style="margin-right: 15px;">
<option v-for="(condition, index) in conditions.string" :value="index" :key="index">@{{ condition }}</option>
</select>
<div v-show='attribute_values[index].type == "select" || attribute_values[index].type == "multiselect"' style="display: flex;">
<select class="control" v-model="attribute_values[index].value" style="margin-right: 15px; height: 100px" :multiple="true">
<option :disabled="true">
{{ __('ui::form.select-attribute', ['attribute' => 'Values']) }}
</option>
<option v-for="(label, index2) in attribute_values[index].options" :value="index2" :key="index2">@{{ label.admin_name }}</option>
</select>
{{-- <multiselect v-model="attribute_values[index].value" :close-on-select="false" :options="attribute_values[index].options" :searchable="false" :track-by="admin_name" :custom-label="attributeListLabel" :multiple="true" ></multiselect> --}}
</div>
<div v-show='attribute_values[index].type == "text" || attribute_values[index].type == "textarea" || attribute_values[index].type == "price" || attribute_values[index].type == "textarea"' style="display: flex">
<input class="control" v-model="attribute_values[index].value" type="text" placeholder="{{ __('ui::form.enter-attribute', ['attribute' => 'Text']) }}">
</div>
<span class="icon trash-icon" v-on:click="removeAttr(index)"></span>
</div>
<span class="btn btn-primary btn-lg mt-20" v-on:click="addAttributeCondition">{{ __('admin::app.promotion.add-attr-condition') }}</span>
</div>
</accordian>
<accordian :active="false" title="{{ __('admin::app.promotion.actions') }}">
<div slot="body">
<div class="control-group" :class="[errors.has('action_type') ? 'has-error' : '']">
<label for="action_type" class="required">{{ __('admin::app.promotion.general-info.apply') }}</label>
<select class="control" name="action_type" v-model="action_type" v-validate="'required'" value="{{ old('action_type') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.apply') }}&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|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('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">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Option']) }}</option>
<option value="1">{{ __('admin::app.promotion.yes') }}</option>
<option value="0">{{ __('admin::app.promotion.no') }}</option>
</select>
<span class="control-error" v-if="errors.has('end_other_rules')">@{{ errors.first('end_other_rules') }}</span>
</div>
</div>
</accordian>
</div>
</div>
</form>
</div>
</script>
<script>
Vue.component('catalog-rule', {
template: '#catalog-rule-form-template',
inject: ['$validator'],
data () {
return {
name: null,
description: null,
conditions_list: [],
channels: [],
customer_groups: [],
ends_till: null,
starts_from: null,
per_customer: 0,
status: 1,
action_type: null,
apply: null,
apply_amt: false,
disc_amount: null,
end_other_rules: 0,
all_conditions: [],
all_attributes: {
'categories' : [],
'attributes' : []
},
criteria: 'cart',
actions: @json($catalog_rule[0]).actions,
category_options: @json($catalog_rule[1]),
category_values: [],
conditions: @json($catalog_rule[0]).conditions,
attribute_values: [],
attr_object: {
attribute: null,
condition: null,
value: [],
options: []
},
attribute_input: @json($catalog_rule[3])
}
},
mounted () {
},
methods: {
categoryLabel (option) {
return option.name + ' [ ' + option.slug + ' ]';
},
attributeListLabel(option) {
return option.label;
},
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
};
}
},
addAttributeCondition() {
this.attribute_values.push(this.attr_object);
this.attr_object = {
attribute: null,
condition: null,
value: [],
options: []
};
},
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 = [];
}
},
enableAttributeCondition (event, index) {
selectedIndex = event.target.selectedIndex - 1;
for(i in this.attribute_input) {
if (i == selectedIndex) {
if (this.attribute_input[i].has_options == true) {
this.selectedIndex
this.attribute_values[index].options = this.attribute_input[i].options;
}
this.attribute_values[index].type = this.attribute_input[i].type;
}
}
},
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.attribute_values.splice(index, 1);
},
removeCat(index) {
this.cats.splice(index, 1);
},
onSubmit: function (e) {
if (this.attribute_values.length > 0 || this.category_values.length > 0) {
for (i in this.attribute_values) {
delete this.attribute_values[i].options;
}
if (this.category_values.length > 0) {
this.all_attributes.categories = this.category_values;
}
if (this.attribute_values.length > 0) {
this.all_attributes.attributes = this.attribute_values;
}
}
this.all_conditions = JSON.stringify(this.all_attributes);
// this.all_conditions = JSON.stringify(this.conditions_list);
// if (this.conditions_list.length != 0) {
// this.conditions_list.push({'criteria': this.match_criteria});
// this.all_conditions = JSON.stringify(this.conditions_list);
// }
// return false;
this.$validator.validateAll().then(result => {
if (result) {
e.target.submit();
}
});
},
genericGroupCondition() {
this.generic_condition = false;
},
addFlashMessages() {
const flashes = this.$refs.flashes;
flashMessages.forEach(function(flash) {
flashes.addFlash(flash);
}, this);
}
}
});
</script>
@endpush
@stop

View File

@ -1,414 +0,0 @@
@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="{{ __('admin::app.promotion.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" class="required">{{ __('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">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Option']) }}</option>
<option value="1">{{ __('admin::app.promotion.yes') }}</option>
<option value="0">{{ __('admin::app.promotion.no') }}</option>
</select>
<span class="control-error" v-if="errors.has('status')">@{{ errors.first('status') }}</span>
</div>
@php
$now = new \Carbon\Carbon();
@endphp
<date :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>
</date>
<date :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>
</date>
</div>
</accordian>
<accordian :active="false" title="{{ __('admin::app.promotion.conditions') }}">
<div slot="body">
<input type="hidden" name="all_conditions" v-model="all_conditions">
<!-- Categories selection input block -->
<div class="control-group" :class="[errors.has('category_values') ? 'has-error' : '']">
<label class="mb-10" for="categories">{{ __('admin::app.promotion.select-category') }}</label>
<multiselect v-model="category_values" :close-on-select="false" :options="category_options" :searchable="false" :custom-label="categoryLabel" :show-labels="true" placeholder="Select Categories" track-by="slug" :multiple="true"></multiselect>
</div>
<label class="mb-10" for="attributes">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Attribute']) }}</label>
<br/>
<!-- Product attributes conditions block -->
<div class="control-container mt-20" v-for="(condition, index) in attribute_values" :key="index">
<select class="control" v-model="attribute_values[index].attribute" style="margin-right: 15px; width: 30%;" v-on:change="enableAttributeCondition($event, index)">
<option disabled="disabled">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Option']) }}</option>
<option v-for="(attr_ip, index1) in attribute_input" :value="attr_ip.code" :key="index1">@{{ attr_ip.name }}</option>
</select>
<select v-show='attribute_values[index].type == "select" || attribute_values[index].type == "multiselect"' class="control" v-model="attribute_values[index].condition" style="margin-right: 15px;">
<option v-for="(condition, index) in conditions.select" :value="index" :key="index">@{{ condition }}</option>
</select>
<select v-show='attribute_values[index].type == "text" || attribute_values[index].type == "textarea" || attribute_values[index].type == "price"' class="control" v-model="attribute_values[index].condition" style="margin-right: 15px;">
<option v-for="(condition, index) in conditions.string" :value="index" :key="index">@{{ condition }}</option>
</select>
<div v-show='attribute_values[index].type == "select" || attribute_values[index].type == "multiselect"' style="display: flex;">
<select class="control" v-model="attribute_values[index].value" style="margin-right: 15px; height: 100px" :multiple="true">
<option :disabled="true">
{{ __('ui::form.select-attribute', ['attribute' => 'Values']) }}
</option>
<option v-for="(label, index2) in attribute_values[index].options" :value="index2" :key="index2">@{{ label.admin_name }}</option>
</select>
{{-- <multiselect v-model="attribute_values[index].value" :close-on-select="false" :options="attribute_values[index].options" :searchable="false" :track-by="admin_name" :custom-label="attributeListLabel" :multiple="true" ></multiselect> --}}
</div>
<div v-show='attribute_values[index].type == "text" || attribute_values[index].type == "textarea" || attribute_values[index].type == "price" || attribute_values[index].type == "textarea"' style="display: flex">
<input class="control" v-model="attribute_values[index].value" type="text" placeholder="{{ __('ui::form.enter-attribute', ['attribute' => 'Text']) }}">
</div>
<span class="icon trash-icon" v-on:click="removeAttr(index)"></span>
</div>
<span class="btn btn-primary btn-lg mt-20" v-on:click="addAttributeCondition">{{ __('admin::app.promotion.add-attr-condition') }}</span>
</div>
</accordian>
<accordian :active="false" title="{{ __('admin::app.promotion.actions') }}">
<div slot="body">
<div class="control-group" :class="[errors.has('action_type') ? 'has-error' : '']">
<label for="action_type" class="required">{{ __('admin::app.promotion.general-info.apply') }}</label>
<select class="control" name="action_type" v-model="action_type" v-validate="'required'" value="{{ old('action_type') }}" data-vv-as="&quot;{{ __('admin::app.promotion.general-info.apply') }}&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|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('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">{{ __('admin::app.promotion.select-attribute', ['attribute' => 'Option']) }}</option>
<option value="1">{{ __('admin::app.promotion.yes') }}</option>
<option value="0">{{ __('admin::app.promotion.no') }}</option>
</select>
<span class="control-error" v-if="errors.has('end_other_rules')">@{{ errors.first('end_other_rules') }}</span>
</div>
</div>
</accordian>
</div>
</div>
</form>
</div>
</script>
<script>
Vue.component('catalog-rule', {
template: '#catalog-rule-form-template',
inject: ['$validator'],
data () {
return {
name: '{{ $catalog_rule[5]->name }}',
description: '{{ $catalog_rule[5]->description }}',
conditions_list: [],
channels: [],
customer_groups: [],
ends_till: '{{ $catalog_rule[5]->ends_till }}',
starts_from: '{{ $catalog_rule[5]->starts_from }}',
status: '{{ $catalog_rule[5]->status }}',
actions: @json($catalog_rule[3]).actions,
action_type: '{{ $catalog_rule[5]->action_code }}',
disc_amount: null,
end_other_rules: '{{ $catalog_rule[5]->end_other_rules }}',
all_conditions: [],
all_attributes: {
'categories' : [],
'attributes' : []
},
criteria: 'cart',
category_options: @json($catalog_rule[1]),
category_values: [],
conditions: @json($catalog_rule[3]).conditions,
attribute_values: [],
attr_object: {
attribute: null,
condition: null,
value: [],
options: []
},
attribute_input: @json($catalog_rule[0])
}
},
mounted () {
catalog_rule = @json($catalog_rule[5]);
channels = @json($catalog_rule[5]->channels);
this.channels = [];
for (i in channels) {
this.channels.push(channels[i].channel_id);
}
customer_groups = @json($catalog_rule[5]->customer_groups);
for (i in customer_groups) {
this.customer_groups.push(customer_groups[i].customer_group_id);
}
data = @json($catalog_rule[5]->conditions);
if (JSON.parse(JSON.parse(data))) {
this.category_values = JSON.parse(JSON.parse(data)).categories;
this.attribute_values = JSON.parse(JSON.parse(data)).attributes;
// creating options and has option param on the frontend
for (i in this.attribute_values) {
for (j in this.attribute_input) {
if (this.attribute_input[j].code == this.attribute_values[i].attribute) {
if (this.attribute_input[j].has_options == true) {
this.attribute_values[i].has_options = true;
this.attribute_values[i].options = this.attribute_input[j].options;
} else {
this.attribute_values[i].has_options = false;
this.attribute_values[i].options = null;
}
}
}
}
}
this.action_type = '{{ $catalog_rule[5]->action_code }}';
this.disc_amount = catalog_rule.discount_amount;
this.end_other_rules = '{{ $catalog_rule[5]->end_other_rules }}';
},
methods: {
created() {
VeeValidate.Validator.extend('is_time', {
getMessage: field => `The format must be HH:MM:SS`,
validate: (value) => new Promise(resolve => {
let regex = new RegExp("([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])");
resolve({
valid: value && regex.test(value)
});
})
});
},
categoryLabel (option) {
return option.name + ' [ ' + option.slug + ' ]';
},
attributeListLabel(option) {
return option.label;
},
addAttributeCondition() {
this.attribute_values.push(this.attr_object);
this.attr_object = {
attribute: null,
condition: null,
value: [],
options: []
};
},
enableAttributeCondition (event, index) {
selectedIndex = event.target.selectedIndex - 1;
for(i in this.attribute_input) {
if (i == selectedIndex) {
if (this.attribute_input[i].has_options == true) {
this.attribute_values[index].options = this.attribute_input[i].options;
}
this.attribute_values[index].type = this.attribute_input[i].type;
}
}
},
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.attribute_values.splice(index, 1);
},
removeCat(index) {
this.cats.splice(index, 1);
},
onSubmit: function (e) {
if (this.attribute_values.length > 0 || this.category_values.length > 0) {
for (i in this.attribute_values) {
delete this.attribute_values[i].options;
}
if (this.category_values.length > 0) {
this.all_attributes.categories = this.category_values;
}
this.all_attributes.attributes = this.attribute_values;
}
this.all_conditions = JSON.stringify(this.all_attributes);
// this.all_conditions = JSON.stringify(this.conditions_list);
// if (this.conditions_list.length != 0) {
// this.conditions_list.push({'criteria': this.match_criteria});
// this.all_conditions = JSON.stringify(this.conditions_list);
// }
// return false;
this.$validator.validateAll().then(result => {
if (result) {
e.target.submit();
}
});
},
genericGroupCondition() {
this.generic_condition = false;
},
addFlashMessages() {
const flashes = this.$refs.flashes;
flashMessages.forEach(function(flash) {
flashes.addFlash(flash);
}, this);
}
}
});
</script>
@endpush
@stop

View File

@ -1,35 +0,0 @@
@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.create') }}" class="btn btn-lg btn-primary">
{{ __('admin::app.promotion.add-catalog-rule') }}
</a>
<a href="{{ route('admin.catalog-rule.apply') }}" class="btn btn-lg btn-primary">
{{ __('admin::app.promotion.apply') }}
</a>
<a href="{{ route('admin.catalog-rule.declut') }}" class="btn btn-lg btn-primary">
{{ __('admin::app.promotion.declut') }}
</a>
</div>
</div>
<div class="page-content">
@inject('catalogRuleGrid','Webkul\Admin\DataGrids\CatalogRuleDataGrid')
{!! $catalogRuleGrid->render() !!}
</div>
</div>
@endsection

View File

@ -0,0 +1,528 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.promotions.catalog-rules.add-title') }}
@stop
@section('content')
<div class="content">
<catalog-rule></catalog-rule>
</div>
@stop
@push('scripts')
@parent
<script type="text/x-template" id="catalog-rule-template">
<div>
<form method="POST" action="{{ route('admin.catalog-rules.store') }}" @submit.prevent="onSubmit">
<div class="page-header">
<div class="page-title">
<h1>
<i class="icon angle-left-icon back-link" @click="redirectBack('{{ url('/admin/dashboard') }}')"></i>
{{ __('admin::app.promotions.catalog-rules.add-title') }}
</h1>
</div>
<div class="page-action">
<button type="submit" class="btn btn-lg btn-primary">
{{ __('admin::app.promotions.catalog-rules.save-btn-title') }}
</button>
</div>
</div>
<div class="page-content">
<div class="form-container">
@csrf()
{!! view_render_event('bagisto.admin.promotions.catalog-rules.create.before') !!}
<accordian :title="'{{ __('admin::app.promotions.catalog-rules.rule-information') }}'" :active="true">
<div slot="body">
<div class="control-group" :class="[errors.has('name') ? 'has-error' : '']">
<label for="name" class="required">{{ __('admin::app.promotions.catalog-rules.name') }}</label>
<input v-validate="'required'" class="control" id="name" name="name" data-vv-as="&quot;{{ __('admin::app.promotions.catalog-rules.name') }}&quot;" value="{{ old('name') }}"/>
<span class="control-error" v-if="errors.has('name')">@{{ errors.first('name') }}</span>
</div>
<div class="control-group">
<label for="description">{{ __('admin::app.promotions.catalog-rules.description') }}</label>
<textarea class="control" id="description" name="description">{{ old('description') }}</textarea>
</div>
<div class="control-group">
<label for="status">{{ __('admin::app.promotions.catalog-rules.status') }}</label>
<span class="checkbox">
<input type="checkbox" id="status" name="status" value="1">
<label class="checkbox-view" for="status"></label>
{{ __('admin::app.promotions.catalog-rules.is-active') }}
</span>
</div>
<div class="control-group" :class="[errors.has('channels[]') ? 'has-error' : '']">
<label for="channels" class="required">{{ __('admin::app.promotions.catalog-rules.channels') }}</label>
<select class="control" id="channels" name="channels[]" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.catalog-rules.channels') }}&quot;" multiple="multiple">
@foreach(core()->getAllChannels() as $channel)
<option value="{{ $channel->id }}" {{ old('channels') && in_array($channel->id, old('channels')) ? 'selected' : '' }}>
{{ $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('customer_groups[]') ? 'has-error' : '']">
<label for="customer_groups" class="required">{{ __('admin::app.promotions.catalog-rules.customer-groups') }}</label>
<select class="control" id="customer_groups" name="customer_groups[]" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.catalog-rules.customer-groups') }}&quot;" multiple="multiple">
@foreach(app('Webkul\Customer\Repositories\CustomerGroupRepository')->all() as $customerGroup)
<option value="{{ $customerGroup->id }}" {{ old('customer_groups') && in_array($customerGroup->id, old('customer_groups')) ? 'selected' : '' }}>
{{ $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 date">
<label for="starts_from">{{ __('admin::app.promotions.catalog-rules.from') }}</label>
<date>
<input type="text" name="starts_from" class="control" value="{{ old('starts_from') }}"/>
</date>
</div>
<div class="control-group date">
<label for="ends_till">{{ __('admin::app.promotions.catalog-rules.to') }}</label>
<date>
<input type="text" name="ends_till" class="control" value="{{ old('ends_till') }}"/>
</date>
</div>
<div class="control-group">
<label for="sort_order">{{ __('admin::app.promotions.catalog-rules.priority') }}</label>
<input type="text" class="control" id="sort_order" name="sort_order" value="{{ old('sort_order') ?? 0 }}"/>
</div>
</div>
</accordian>
<accordian :title="'{{ __('admin::app.promotions.catalog-rules.conditions') }}'" :active="false">
<div slot="body">
<div class="control-group">
<label for="condition_type">{{ __('admin::app.promotions.catalog-rules.condition-type') }}</label>
<select class="control" id="condition_type" name="condition_type" v-model="condition_type">
<option value="1">{{ __('admin::app.promotions.catalog-rules.all-conditions-true') }}</option>
<option value="2">{{ __('admin::app.promotions.catalog-rules.any-condition-true') }}</option>
</select>
</div>
<div class="table catalog-rule-conditions" style="margin-top: 20px; overflow-x: unset;">
<table>
<tbody>
<catalog-rule-condition-item
v-for='(condition, index) in conditions'
:condition="condition"
:key="index"
:index="index"
@onRemoveCondition="removeCondition($event)">
</catalog-rule-condition-item>
</tbody>
</table>
</div>
<button type="button" class="btn btn-lg btn-primary" style="margin-top: 20px;" @click="addCondition">
{{ __('admin::app.promotions.catalog-rules.add-condition') }}
</button>
</div>
</accordian>
<accordian :title="'{{ __('admin::app.promotions.catalog-rules.actions') }}'" :active="false">
<div slot="body">
<div class="control-group" :class="[errors.has('action_type') ? 'has-error' : '']">
<label for="action_type" class="required">{{ __('admin::app.promotions.catalog-rules.action-type') }}</label>
<select class="control" id="action_type" name="action_type" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.catalog-rules.action-type') }}&quot;">
<option value="by_percent" {{ old('action_type') == 'by_percent' ? 'selected' : '' }}>
{{ __('admin::app.promotions.catalog-rules.percentage-product-price') }}
</option>
<option value="by_fixed" {{ old('action_type') == 'by_fixed' ? 'selected' : '' }}>
{{ __('admin::app.promotions.catalog-rules.fixed-amount') }}
</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('discount_amount') ? 'has-error' : '']">
<label for="discount_amount" class="required">{{ __('admin::app.promotions.catalog-rules.discount-amount') }}</label>
<input v-validate="'required'" class="control" id="discount_amount" name="discount_amount" data-vv-as="&quot;{{ __('admin::app.promotions.catalog-rules.discount-amount') }}&quot;" value="{{ old('discount_amount') ?? 0 }}"/>
<span class="control-error" v-if="errors.has('discount_amount')">@{{ errors.first('discount_amount') }}</span>
</div>
<div class="control-group">
<label for="end_other_rules">{{ __('admin::app.promotions.catalog-rules.end-other-rules') }}</label>
<select class="control" id="end_other_rules" name="end_other_rules">
<option value="0" {{ ! old('end_other_rules') ? 'selected' : '' }}>
{{ __('admin::app.promotions.catalog-rules.no') }}
</option>
<option value="1" {{ old('end_other_rules') ? 'selected' : '' }}>
{{ __('admin::app.promotions.catalog-rules.yes') }}
</option>
</select>
</div>
</div>
</accordian>
{!! view_render_event('bagisto.admin.promotions.catalog-rules.create.after') !!}
</div>
</div>
</form>
</div>
</script>
<script type="text/x-template" id="catalog-rule-condition-item-template">
<tr>
<td class="attribute">
<div class="control-group">
<select :name="['conditions[' + index + '][attribute]']" class="control" v-model="condition.attribute">
<option value="">{{ __('admin::app.promotions.catalog-rules.choose-condition-to-add') }}</option>
<optgroup v-for='(conditionAttribute, index) in condition_attributes' :label="conditionAttribute.label">
<option v-for='(childAttribute, index) in conditionAttribute.children' :value="childAttribute.key">
@{{ childAttribute.label }}
</option>
</optgroup>
</select>
</div>
</td>
<td class="operator">
<div class="control-group" v-if="matchedAttribute">
<select :name="['conditions[' + index + '][operator]']" class="control" v-model="condition.operator">
<option v-for='operator in condition_operators[matchedAttribute.type]' :value="operator.operator">
@{{ operator.label }}
</option>
</select>
</div>
</td>
<td class="value">
<div v-if="matchedAttribute">
<input type="hidden" :name="['conditions[' + index + '][attribute_type]']" v-model="matchedAttribute.type">
<div v-if="matchedAttribute.key == 'product|category_ids'">
<tree-view value-field="id" id-field="id" :name-field="'conditions[' + index + '][value]'" input-type="checkbox" :items='matchedAttribute.options' :behavior="'no'"></tree-view>
</div>
<div v-else>
<div class="control-group" v-if="matchedAttribute.type == 'text' || matchedAttribute.type == 'price' || matchedAttribute.type == 'decimal' || matchedAttribute.type == 'integer'">
<input class="control" :name="['conditions[' + index + '][value]']" v-model="condition.value"/>
</div>
<div class="control-group date" v-if="matchedAttribute.type == 'date'">
<date>
<input class="control" :name="['conditions[' + index + '][value]']" v-model="condition.value"/>
</date>
</div>
<div class="control-group date" v-if="matchedAttribute.type == 'datetime'">
<datetime>
<input class="control" :name="['conditions[' + index + '][value]']" v-model="condition.value"/>
</datetime>
</div>
<div class="control-group" v-if="matchedAttribute.type == 'boolean'">
<select :name="['conditions[' + index + '][value]']" class="control" v-model="condition.value">
<option value="1">{{ __('admin::app.promotions.catalog-rules.yes') }}</option>
<option value="0">{{ __('admin::app.promotions.catalog-rules.no') }}</option>
</select>
</div>
<div class="control-group" v-if="matchedAttribute.type == 'select' || matchedAttribute.type == 'radio'">
<select :name="['conditions[' + index + '][value]']" class="control" v-model="condition.value" v-if="matchedAttribute.key != 'catalog|state'">
<option v-for='option in matchedAttribute.options' :value="option.id">
@{{ option.admin_name }}
</option>
</select>
<select :name="['conditions[' + index + '][value]']" class="control" v-model="condition.value" v-else>
<optgroup v-for='option in matchedAttribute.options' :label="option.admin_name">
<option v-for='state in option.states' :value="state.code">
@{{ state.admin_name }}
</option>
</optgroup>
</select>
</div>
<div class="control-group" v-if="matchedAttribute.type == 'multiselect' || matchedAttribute.type == 'checkbox'">
<select :name="['conditions[' + index + '][value][]']" class="control" v-model="condition.value" multiple>
<option v-for='option in matchedAttribute.options' :value="option.id">
@{{ option.admin_name }}
</option>
</select>
</div>
</div>
</div>
</td>
<td class="actions">
<i class="icon trash-icon" @click="removeCondition"></i>
</td>
</tr>
</script>
<script>
Vue.component('catalog-rule', {
template: '#catalog-rule-template',
inject: ['$validator'],
data: function() {
return {
condition_type: 1,
conditions: []
}
},
methods: {
addCondition: function() {
this.conditions.push({
'attribute': '',
'operator': '==',
'value': '',
});
},
removeCondition: function(condition) {
let index = this.conditions.indexOf(condition)
this.conditions.splice(index, 1)
},
onSubmit: function(e) {
this.$root.onSubmit(e)
},
onSubmit: function(e) {
this.$root.onSubmit(e)
},
redirectBack: function(fallbackUrl) {
this.$root.redirectBack(fallbackUrl)
}
}
});
Vue.component('catalog-rule-condition-item', {
template: '#catalog-rule-condition-item-template',
props: ['index', 'condition'],
data: function() {
return {
condition_attributes: @json(app('\Webkul\CatalogRule\Repositories\CatalogRuleRepository')->getConditionAttributes()),
attribute_type_indexes: {
'product': 0
},
condition_operators: {
'price': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-less-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.less-than') }}'
}],
'decimal': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.catalog-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.catalog-rules.less-than') }}'
}],
'integer': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.catalog-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.catalog-rules.less-than') }}'
}],
'text': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}, {
'operator': '{}',
'label': '{{ __('admin::app.promotions.catalog-rules.contain') }}'
}, {
'operator': '!{}',
'label': '{{ __('admin::app.promotions.catalog-rules.does-not-contain') }}'
}],
'boolean': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}],
'date': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.catalog-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.catalog-rules.less-than') }}'
}],
'datetime': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.catalog-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.catalog-rules.less-than') }}'
}],
'select': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}],
'radio': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}],
'multiselect': [{
'operator': '{}',
'label': '{{ __('admin::app.promotions.catalog-rules.contains') }}'
}, {
'operator': '!{}',
'label': '{{ __('admin::app.promotions.catalog-rules.does-not-contain') }}'
}],
'checkbox': [{
'operator': '{}',
'label': '{{ __('admin::app.promotions.catalog-rules.contains') }}'
}, {
'operator': '!{}',
'label': '{{ __('admin::app.promotions.catalog-rules.does-not-contain') }}'
}]
}
}
},
computed: {
matchedAttribute: function () {
if (this.condition.attribute == '')
return;
var this_this = this;
var attributeIndex = this.attribute_type_indexes[this.condition.attribute.split("|")[0]];
matchedAttribute = this.condition_attributes[attributeIndex]['children'].filter(function (attribute) {
return attribute.key == this_this.condition.attribute;
});
if (matchedAttribute[0]['type'] == 'multiselect' || matchedAttribute[0]['type'] == 'checkbox') {
this.condition.operator = '{}';
this.condition.value = [];
}
return matchedAttribute[0];
}
},
methods: {
removeCondition: function() {
this.$emit('onRemoveCondition', this.condition)
}
}
});
</script>
@endpush

View File

@ -0,0 +1,533 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.promotions.catalog-rules.edit-title') }}
@stop
@section('content')
<div class="content">
<catalog-rule></catalog-rule>
</div>
@stop
@push('scripts')
@parent
<script type="text/x-template" id="catalog-rule-template">
<div>
<form method="POST" action="{{ route('admin.catalog-rules.update', $catalogRule->id) }}" @submit.prevent="onSubmit">
<div class="page-header">
<div class="page-title">
<h1>
<i class="icon angle-left-icon back-link" @click="redirectBack('{{ url('/admin/dashboard') }}')"></i>
{{ __('admin::app.promotions.catalog-rules.edit-title') }}
</h1>
</div>
<div class="page-action">
<button type="submit" class="btn btn-lg btn-primary">
{{ __('admin::app.promotions.catalog-rules.save-btn-title') }}
</button>
</div>
</div>
<div class="page-content">
<div class="form-container">
@csrf()
{!! view_render_event('bagisto.admin.promotions.catalog-rules.create.before') !!}
<accordian :title="'{{ __('admin::app.promotions.catalog-rules.rule-information') }}'" :active="true">
<div slot="body">
<div class="control-group" :class="[errors.has('name') ? 'has-error' : '']">
<label for="name" class="required">{{ __('admin::app.promotions.catalog-rules.name') }}</label>
<input v-validate="'required'" class="control" id="name" name="name" data-vv-as="&quot;{{ __('admin::app.promotions.catalog-rules.name') }}&quot;" value="{{ old('name') ?: $catalogRule->name }}"/>
<span class="control-error" v-if="errors.has('name')">@{{ errors.first('name') }}</span>
</div>
<div class="control-group">
<label for="description">{{ __('admin::app.promotions.catalog-rules.description') }}</label>
<textarea class="control" id="description" name="description">{{ old('description') ?: $catalogRule->description }}</textarea>
</div>
<div class="control-group">
<label for="status">{{ __('admin::app.promotions.catalog-rules.status') }}</label>
<span class="checkbox">
<input type="checkbox" id="status" name="status" value="{{ $catalogRule->status }}" {{ $catalogRule->status ? 'checked' : '' }}>
<label class="checkbox-view" for="status"></label>
{{ __('admin::app.promotions.catalog-rules.is-active') }}
</span>
</div>
<div class="control-group" :class="[errors.has('channels[]') ? 'has-error' : '']">
<label for="channels" class="required">{{ __('admin::app.promotions.catalog-rules.channels') }}</label>
<?php $selectedOptionIds = old('channels') ?: $catalogRule->channels->pluck('id')->toArray() ?>
<select class="control" id="channels" name="channels[]" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.catalog-rules.channels') }}&quot;" multiple="multiple">
@foreach(core()->getAllChannels() as $channel)
<option value="{{ $channel->id }}" {{ in_array($channel->id, $selectedOptionIds) ? 'selected' : '' }}>
{{ $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('customer_groups[]') ? 'has-error' : '']">
<label for="customer_groups" class="required">{{ __('admin::app.promotions.catalog-rules.customer-groups') }}</label>
<?php $selectedOptionIds = old('customer_groups') ?: $catalogRule->customer_groups->pluck('id')->toArray() ?>
<select class="control" id="customer_groups" name="customer_groups[]" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.catalog-rules.customer-groups') }}&quot;" multiple="multiple">
@foreach(app('Webkul\Customer\Repositories\CustomerGroupRepository')->all() as $customerGroup)
<option value="{{ $customerGroup->id }}" {{ in_array($customerGroup->id, $selectedOptionIds) ? 'selected' : '' }}>
{{ $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 date">
<label for="starts_from">{{ __('admin::app.promotions.catalog-rules.from') }}</label>
<date>
<input type="text" name="starts_from" class="control" value="{{ old('starts_from') ?: $catalogRule->starts_from }}"/>
</date>
</div>
<div class="control-group date">
<label for="ends_till">{{ __('admin::app.promotions.catalog-rules.to') }}</label>
<date>
<input type="text" name="ends_till" class="control" value="{{ old('ends_till') ?: $catalogRule->ends_till }}"/>
</date>
</div>
<div class="control-group">
<label for="sort_order">{{ __('admin::app.promotions.catalog-rules.priority') }}</label>
<input type="text" class="control" id="sort_order" name="sort_order" value="{{ $catalogRule->sort_order }}" {{ $catalogRule->status ? 'checked' : '' }}/>
</div>
</div>
</accordian>
<accordian :title="'{{ __('admin::app.promotions.catalog-rules.conditions') }}'" :active="false">
<div slot="body">
<div class="control-group">
<label for="condition_type">{{ __('admin::app.promotions.catalog-rules.condition-type') }}</label>
<select class="control" id="condition_type" name="condition_type" v-model="condition_type">
<option value="1">{{ __('admin::app.promotions.catalog-rules.all-conditions-true') }}</option>
<option value="2">{{ __('admin::app.promotions.catalog-rules.any-condition-true') }}</option>
</select>
</div>
<div class="table catalog-rule-conditions" style="margin-top: 20px; overflow-x: unset;">
<table>
<tbody>
<catalog-rule-condition-item
v-for='(condition, index) in conditions'
:condition="condition"
:key="index"
:index="index"
@onRemoveCondition="removeCondition($event)">
</catalog-rule-condition-item>
</tbody>
</table>
</div>
<button type="button" class="btn btn-lg btn-primary" style="margin-top: 20px;" @click="addCondition">
{{ __('admin::app.promotions.catalog-rules.add-condition') }}
</button>
</div>
</accordian>
<accordian :title="'{{ __('admin::app.promotions.catalog-rules.actions') }}'" :active="false">
<div slot="body">
<div class="control-group" :class="[errors.has('action_type') ? 'has-error' : '']">
<label for="action_type" class="required">{{ __('admin::app.promotions.catalog-rules.action-type') }}</label>
<?php $selectedOption = old('action_type') ?: $catalogRule->action_type ?>
<select class="control" id="action_type" name="action_type" v-validate="'required'" data-vv-as="&quot;{{ __('admin::app.promotions.catalog-rules.action-type') }}&quot;">
<option value="by_percent" {{ $selectedOption == 'by_percent' ? 'selected' : '' }}>
{{ __('admin::app.promotions.catalog-rules.percentage-product-price') }}
</option>
<option value="by_fixed" {{ $selectedOption == 'by_fixed' ? 'selected' : '' }}>
{{ __('admin::app.promotions.catalog-rules.fixed-amount') }}
</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('discount_amount') ? 'has-error' : '']">
<label for="discount_amount" class="required">{{ __('admin::app.promotions.catalog-rules.discount-amount') }}</label>
<input v-validate="'required'" class="control" id="discount_amount" name="discount_amount" data-vv-as="&quot;{{ __('admin::app.promotions.catalog-rules.discount-amount') }}&quot;" value="{{ old('discount_amount') ?: $catalogRule->discount_amount }}"/>
<span class="control-error" v-if="errors.has('discount_amount')">@{{ errors.first('discount_amount') }}</span>
</div>
<div class="control-group">
<label for="end_other_rules">{{ __('admin::app.promotions.catalog-rules.end-other-rules') }}</label>
<?php $selectedOption = old('end_other_rules') ?: $catalogRule->end_other_rules ?>
<select class="control" id="end_other_rules" name="end_other_rules">
<option value="0" {{ ! $selectedOption ? 'selected' : '' }}>
{{ __('admin::app.promotions.catalog-rules.no') }}
</option>
<option value="1" {{ $selectedOption ? 'selected' : '' }}>
{{ __('admin::app.promotions.catalog-rules.yes') }}
</option>
</select>
</div>
</div>
</accordian>
{!! view_render_event('bagisto.admin.promotions.catalog-rules.create.after') !!}
</div>
</div>
</form>
</div>
</script>
<script type="text/x-template" id="catalog-rule-condition-item-template">
<tr>
<td class="attribute">
<div class="control-group">
<select :name="['conditions[' + index + '][attribute]']" class="control" v-model="condition.attribute">
<option value="">{{ __('admin::app.promotions.catalog-rules.choose-condition-to-add') }}</option>
<optgroup v-for='(conditionAttribute, index) in condition_attributes' :label="conditionAttribute.label">
<option v-for='(childAttribute, index) in conditionAttribute.children' :value="childAttribute.key">
@{{ childAttribute.label }}
</option>
</optgroup>
</select>
</div>
</td>
<td class="operator">
<div class="control-group" v-if="matchedAttribute">
<select :name="['conditions[' + index + '][operator]']" class="control" v-model="condition.operator">
<option v-for='operator in condition_operators[matchedAttribute.type]' :value="operator.operator">
@{{ operator.label }}
</option>
</select>
</div>
</td>
<td class="value">
<div v-if="matchedAttribute">
<input type="hidden" :name="['conditions[' + index + '][attribute_type]']" v-model="matchedAttribute.type">
<div v-if="matchedAttribute.key == 'product|category_ids'">
<tree-view value-field="id" id-field="id" :name-field="'conditions[' + index + '][value]'" input-type="checkbox" :items='matchedAttribute.options' :value='condition.value' :behavior="'no'"></tree-view>
</div>
<div v-else>
<div class="control-group" v-if="matchedAttribute.type == 'text' || matchedAttribute.type == 'price' || matchedAttribute.type == 'decimal' || matchedAttribute.type == 'integer'">
<input class="control" :name="['conditions[' + index + '][value]']" v-model="condition.value"/>
</div>
<div class="control-group date" v-if="matchedAttribute.type == 'date'">
<date>
<input class="control" :name="['conditions[' + index + '][value]']" v-model="condition.value"/>
</date>
</div>
<div class="control-group date" v-if="matchedAttribute.type == 'datetime'">
<datetime>
<input class="control" :name="['conditions[' + index + '][value]']" v-model="condition.value"/>
</datetime>
</div>
<div class="control-group" v-if="matchedAttribute.type == 'boolean'">
<select :name="['conditions[' + index + '][value]']" class="control" v-model="condition.value">
<option value="1">{{ __('admin::app.promotions.catalog-rules.yes') }}</option>
<option value="0">{{ __('admin::app.promotions.catalog-rules.no') }}</option>
</select>
</div>
<div class="control-group" v-if="matchedAttribute.type == 'select' || matchedAttribute.type == 'radio'">
<select :name="['conditions[' + index + '][value]']" class="control" v-model="condition.value" v-if="matchedAttribute.key != 'catalog|state'">
<option v-for='option in matchedAttribute.options' :value="option.id">
@{{ option.admin_name }}
</option>
</select>
<select :name="['conditions[' + index + '][value]']" class="control" v-model="condition.value" v-else>
<optgroup v-for='option in matchedAttribute.options' :label="option.admin_name">
<option v-for='state in option.states' :value="state.code">
@{{ state.admin_name }}
</option>
</optgroup>
</select>
</div>
<div class="control-group" v-if="matchedAttribute.type == 'multiselect' || matchedAttribute.type == 'checkbox'">
<select :name="['conditions[' + index + '][value][]']" class="control" v-model="condition.value" multiple>
<option v-for='option in matchedAttribute.options' :value="option.id">
@{{ option.admin_name }}
</option>
</select>
</div>
</div>
</div>
</td>
<td class="actions">
<i class="icon trash-icon" @click="removeCondition"></i>
</td>
</tr>
</script>
<script>
Vue.component('catalog-rule', {
template: '#catalog-rule-template',
inject: ['$validator'],
data: function() {
return {
condition_type: {{ old('condition_type') ?: $catalogRule->condition_type }},
conditions: @json($catalogRule->conditions ?: [])
}
},
methods: {
addCondition: function() {
this.conditions.push({
'attribute': '',
'operator': '==',
'value': '',
});
},
removeCondition: function(condition) {
let index = this.conditions.indexOf(condition)
this.conditions.splice(index, 1)
},
onSubmit: function(e) {
this.$root.onSubmit(e)
},
redirectBack: function(fallbackUrl) {
this.$root.redirectBack(fallbackUrl)
}
}
});
Vue.component('catalog-rule-condition-item', {
template: '#catalog-rule-condition-item-template',
props: ['index', 'condition'],
data: function() {
return {
condition_attributes: @json(app('\Webkul\CatalogRule\Repositories\CatalogRuleRepository')->getConditionAttributes()),
attribute_type_indexes: {
'product': 0
},
condition_operators: {
'price': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-less-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.less-than') }}'
}],
'decimal': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.catalog-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.catalog-rules.less-than') }}'
}],
'integer': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.catalog-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.catalog-rules.less-than') }}'
}],
'text': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}, {
'operator': '{}',
'label': '{{ __('admin::app.promotions.catalog-rules.contain') }}'
}, {
'operator': '!{}',
'label': '{{ __('admin::app.promotions.catalog-rules.does-not-contain') }}'
}],
'boolean': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}],
'date': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.catalog-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.catalog-rules.less-than') }}'
}],
'datetime': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}, {
'operator': '>=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-greater-than') }}'
}, {
'operator': '<=',
'label': '{{ __('admin::app.promotions.catalog-rules.equals-or-less-than') }}'
}, {
'operator': '>',
'label': '{{ __('admin::app.promotions.catalog-rules.greater-than') }}'
}, {
'operator': '<',
'label': '{{ __('admin::app.promotions.catalog-rules.less-than') }}'
}],
'select': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}],
'radio': [{
'operator': '==',
'label': '{{ __('admin::app.promotions.catalog-rules.is-equal-to') }}'
}, {
'operator': '!=',
'label': '{{ __('admin::app.promotions.catalog-rules.is-not-equal-to') }}'
}],
'multiselect': [{
'operator': '{}',
'label': '{{ __('admin::app.promotions.catalog-rules.contains') }}'
}, {
'operator': '!{}',
'label': '{{ __('admin::app.promotions.catalog-rules.does-not-contain') }}'
}],
'checkbox': [{
'operator': '{}',
'label': '{{ __('admin::app.promotions.catalog-rules.contains') }}'
}, {
'operator': '!{}',
'label': '{{ __('admin::app.promotions.catalog-rules.does-not-contain') }}'
}]
}
}
},
computed: {
matchedAttribute: function () {
if (this.condition.attribute == '')
return;
var this_this = this;
var attributeIndex = this.attribute_type_indexes[this.condition.attribute.split("|")[0]];
matchedAttribute = this.condition_attributes[attributeIndex]['children'].filter(function (attribute) {
return attribute.key == this_this.condition.attribute;
});
if (matchedAttribute[0]['type'] == 'multiselect' || matchedAttribute[0]['type'] == 'checkbox') {
this.condition.operator = '{}';
this.condition.value = this.condition.value == '' && this.condition.value != undefined ? [] : this.condition.value;
}
return matchedAttribute[0];
}
},
methods: {
removeCondition: function() {
this.$emit('onRemoveCondition', this.condition)
}
}
});
</script>
@endpush

View File

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

View File

@ -332,7 +332,13 @@
@if ($order->base_discount_amount > 0)
<tr>
<td>{{ __('admin::app.sales.orders.discount') }}</td>
<td>
{{ __('admin::app.sales.orders.discount') }}
@if ($order->coupon_code)
({{ $order->coupon_code }})
@endif
</td>
<td>-</td>
<td>{{ core()->formatBasePrice($order->base_discount_amount) }}</td>
</tr>

View File

@ -195,14 +195,14 @@
</span>
</div>
@if ($shipment->inventory_source)
@if ($shipment->inventory_source || $shipment->inventory_source_name)
<div class="row">
<span class="title">
{{ __('admin::app.sales.shipments.inventory-source') }}
</span>
<span class="value">
{{ $shipment->inventory_source->name }}
{{ $shipment->inventory_source ? $shipment->inventory_source->name : $shipment->inventory_source_name }}
</span>
</div>
@endif

View File

@ -45,7 +45,7 @@
<span class="control-error" v-if="errors.has('identifier')">@{{ errors.first('identifier') }}</span>
</div>
@include ('admin::customers.country-state', ['countryCode' => old('country'), 'stateCode' => old('state')])
<country-state></country-state>
<div class="control-group">
<span class="checkbox">
@ -83,6 +83,50 @@
</div>
</script>
<script type="text/x-template" id="country-state-template">
<div>
<div class="control-group" :class="[errors.has('country') ? 'has-error' : '']">
<label for="country" class="required">
{{ __('admin::app.customers.customers.country') }}
</label>
<select type="text" v-validate="'required'" class="control" id="country" name="country" v-model="country" data-vv-as="&quot;{{ __('admin::app.customers.customers.country') }}&quot;">
<option value=""></option>
@foreach (core()->countries() as $country)
<option value="{{ $country->code }}">{{ $country->name }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('country')">
@{{ errors.first('country') }}
</span>
</div>
<div class="control-group" :class="[errors.has('state') ? 'has-error' : '']">
<label for="state">
{{ __('admin::app.customers.customers.state') }}
</label>
<select class="control" id="state" name="state" v-model="state">
<option value="" :selected="! haveStates()">*</option>
<option v-for='(state, index) in countryStates[country]' :value="state.code">
@{{ state.default_name }}
</option>
</select>
<span class="control-error" v-if="errors.has('state')">
@{{ errors.first('state') }}
</span>
</div>
</div>
</script>
<script>
Vue.component('tax-rate-form', {
@ -96,5 +140,31 @@
}
},
});
Vue.component('country-state', {
template: '#country-state-template',
inject: ['$validator'],
data: function () {
return {
country: "{{ old('country') }}",
state: "{{ old('state') }}",
countryStates: @json(core()->groupedStatesByCountries())
}
},
methods: {
haveStates: function () {
if (this.countryStates[this.country] && this.countryStates[this.country].length)
return true;
return false;
},
}
});
</script>
@endpush

View File

@ -36,7 +36,7 @@
<span class="control-error" v-if="errors.has('identifier')">@{{ errors.first('identifier') }}</span>
</div>
@include ('admin::customers.country-state', ['countryCode' => old('country') ?? $taxRate->country, 'stateCode' => old('state') ?? $taxRate->state])
<country-state></country-state>
@if ($taxRate->is_zip)
<input type="hidden" id="is_zip" name="is_zip" value="{{ $taxRate->is_zip }}">
@ -71,3 +71,78 @@
</form>
</div>
@stop
@push('scripts')
<script type="text/x-template" id="country-state-template">
<div>
<div class="control-group" :class="[errors.has('country') ? 'has-error' : '']">
<label for="country" class="required">
{{ __('admin::app.customers.customers.country') }}
</label>
<select type="text" v-validate="'required'" class="control" id="country" name="country" v-model="country" data-vv-as="&quot;{{ __('admin::app.customers.customers.country') }}&quot;">
<option value=""></option>
@foreach (core()->countries() as $country)
<option value="{{ $country->code }}">{{ $country->name }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('country')">
@{{ errors.first('country') }}
</span>
</div>
<div class="control-group" :class="[errors.has('state') ? 'has-error' : '']">
<label for="state">
{{ __('admin::app.customers.customers.state') }}
</label>
<select class="control" id="state" name="state" v-model="state">
<option value="" :selected="! haveStates()">*</option>
<option v-for='(state, index) in countryStates[country]' :value="state.code">
@{{ state.default_name }}
</option>
</select>
<span class="control-error" v-if="errors.has('state')">
@{{ errors.first('state') }}
</span>
</div>
</div>
</script>
<script>
Vue.component('country-state', {
template: '#country-state-template',
inject: ['$validator'],
data: function () {
return {
country: "{{ old('country') ?? $taxRate->country }}",
state: "{{ old('state') ?? $taxRate->state }}",
countryStates: @json(core()->groupedStatesByCountries())
}
},
methods: {
haveStates: function () {
if (this.countryStates[this.country] && this.countryStates[this.country].length)
return true;
return false;
},
}
});
</script>
@endpush

View File

@ -22,6 +22,10 @@ mix.js(__dirname + "/src/Resources/assets/js/app.js", "js/admin.js")
processCssUrls: false
});
if (!mix.inProduction()) {
mix.sourceMaps();
}
if (mix.inProduction()) {
mix.version();
}

View File

@ -3,9 +3,8 @@
namespace Webkul\CMS\Http\Controllers\Shop;
use Webkul\CMS\Http\Controllers\Controller;
use Webkul\CMS\Repositories\CMSRepository as CMS;
use Webkul\Core\Repositories\ChannelRepository as Channel;
use Webkul\Core\Repositories\LocaleRepository as Locale;
use Webkul\CMS\Repositories\CMSRepository;
use Webkul\Core\Repositories\LocaleRepository;
/**
* PagePresenter controller
@ -13,73 +12,58 @@ use Webkul\Core\Repositories\LocaleRepository as Locale;
* @author Prashant Singh <prashant.singh852@webkul.com> @prashant-webkul
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class PagePresenterController extends Controller
class PagePresenterController extends Controller
{
/**
* To hold the request variables from route file
* CMSRepository object
*
* @var Object
*/
protected $_config;
protected $cmsRepository;
/**
* To hold the channel reposotry instance
* LocaleRepository object
*
* @var Object
*/
protected $channel;
protected $localeRepository;
/**
* To hold the locale reposotry instance
* Create a new controller instance.
*
* @param \Webkul\CMS\Repositories\CMSRepository $cmsRepository
* @param \Webkul\Core\Repositories\LocaleRepository $localeRepository
* @return void
*/
protected $locale;
/**
* To hold the CMSRepository instance
*/
protected $cms;
public function __construct(Channel $channel, Locale $locale, CMS $cms)
public function __construct(
CMSRepository $cmsRepository,
LocaleRepository $localeRepository
)
{
/**
* Channel repository instance
*/
$this->channel = $channel;
$this->cmsRepository = $cmsRepository;
/**
* Locale repository instance
*/
$this->locale = $locale;
/**
* CMS repository instance
*/
$this->cms = $cms;
$this->_config = request('_config');
$this->localeRepository = $localeRepository;
}
/**
* To extract the page content and load it in the respective view file\
* To extract the page content and load it in the respective view file
*
* @return view
* @param string $slug
* @return \Illuminate\View\View
*/
public function presenter($slug)
{
$currentChannel = core()->getCurrentChannel();
$currentLocale = app()->getLocale();
$currentLocale = $this->localeRepository->findOneByField('code', app()->getLocale());
$currentLocale = $this->locale->findOneWhere([
'code' => $currentLocale
]);
$page = $this->cmsRepository->findOneWhere([
'url_key' => $slug,
'locale_id' => $currentLocale->id,
'channel_id' => core()->getCurrentChannel()->id
]);
$page = $this->cms->findOneWhere([
'url_key' => $slug,
'locale_id' => $currentLocale->id,
'channel_id' => $currentChannel->id
]);
if ($page) {
return view('shop::cms.page')->with('page', $page);
} else {
if (! $page)
abort(404);
}
return view('shop::cms.page')->with('page', $page);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,43 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class RemoveTables extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::dropIfExists('cart_rule_cart');
Schema::dropIfExists('cart_rule_labels');
Schema::dropIfExists('cart_rule_channels');
Schema::dropIfExists('cart_rule_customer_groups');
Schema::dropIfExists('cart_rule_customers');
Schema::dropIfExists('cart_rule_coupons');
Schema::dropIfExists('cart_rule_coupon_usage');
Schema::dropIfExists('cart_rule_coupons_usage');
Schema::dropIfExists('cart_rules');
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCartRuleTable extends Migration
class CreateCartRulesTable extends Migration
{
/**
* Run the migrations.
@ -13,38 +13,29 @@ class CreateCartRuleTable extends Migration
*/
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->date('starts_from')->nullable();
$table->date('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(1);
$table->string('disc_threshold')->default(1);
$table->integer('coupon_type')->default(1);
$table->boolean('auto_generation')->default(0);
$table->boolean('use_auto_generation')->default(0);
$table->integer('usage_per_customer')->default(0);
$table->integer('uses_per_coupon')->default(0);
$table->integer('times_used')->unsigned()->default(0);
$table->boolean('condition_type')->default(1);
$table->json('conditions')->nullable();
$table->boolean('end_other_rules')->default(0);
$table->boolean('uses_attribute_conditions')->default(0);
$table->string('action_type')->nullable();
$table->decimal('discount_amount', 12, 4)->default(0);
$table->integer('discount_quantity')->default(1);
$table->string('discount_step')->default(1);
$table->boolean('apply_to_shipping')->default(0);
$table->boolean('free_shipping')->default(0);
$table->integer('sort_order')->unsigned()->default(0);
$table->timestamps();
});
}

View File

@ -14,12 +14,13 @@ class CreateCartRuleChannelsTable extends Migration
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->primary(['cart_rule_id', 'channel_id']);
$table->foreign('cart_rule_id')->references('id')->on('cart_rules')->onDelete('cascade');
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
$table->timestamps();
});
}

View File

@ -14,12 +14,14 @@ class CreateCartRuleCustomerGroupsTable extends Migration
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->primary(['cart_rule_id', 'customer_group_id']);
$table->foreign('cart_rule_id')->references('id')->on('cart_rules')->onDelete('cascade');
$table->foreign('customer_group_id')->references('id')->on('customer_groups')->onDelete('cascade');
$table->timestamps();
});
}

View File

@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCartRuleCartTable extends Migration
class CreateCartRuleTranslationsTable extends Migration
{
/**
* Run the migrations.
@ -13,13 +13,13 @@ class CreateCartRuleCartTable extends Migration
*/
public function up()
{
Schema::create('cart_rule_cart', function (Blueprint $table) {
Schema::create('cart_rule_translations', function (Blueprint $table) {
$table->increments('id');
$table->integer('cart_rule_id')->unsigned()->nullable();
$table->string('locale');
$table->text('label')->nullable();
$table->integer('cart_rule_id')->unsigned();
$table->unique(['cart_rule_id', 'locale']);
$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();
});
}
@ -30,6 +30,6 @@ class CreateCartRuleCartTable extends Migration
*/
public function down()
{
Schema::dropIfExists('cart_rule_cart');
Schema::dropIfExists('cart_rule_translations');
}
}

View File

@ -15,12 +15,13 @@ class CreateCartruleCustomersTable extends Migration
{
Schema::create('cart_rule_customers', function (Blueprint $table) {
$table->increments('id');
$table->bigInteger('times_used')->unsigned()->default(0);
$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();
});
}
@ -31,6 +32,6 @@ class CreateCartruleCustomersTable extends Migration
*/
public function down()
{
Schema::dropIfExists('cart_rule_customers');
Schema::dropIfExists('cartrule_customers');
}
}

View File

@ -15,15 +15,16 @@ class CreateCartruleCouponsTable extends Migration
{
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('times_used')->unsigned()->default(0);
$table->integer('type')->unsigned()->default(0);
$table->boolean('is_primary')->default(0);
$table->date('expired_at')->nullable();
$table->integer('cart_rule_id')->unsigned();
$table->foreign('cart_rule_id')->references('id')->on('cart_rules')->onDelete('cascade');
$table->timestamps();
});
}
@ -35,6 +36,6 @@ class CreateCartruleCouponsTable extends Migration
*/
public function down()
{
Schema::dropIfExists('cart_rule_coupons');
Schema::dropIfExists('cartrule_coupons');
}
}

View File

@ -0,0 +1,37 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCartRuleCouponUsageTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('cart_rule_coupon_usage', function (Blueprint $table) {
$table->increments('id');
$table->integer('times_used')->default(0);
$table->integer('cart_rule_coupon_id')->unsigned();
$table->foreign('cart_rule_coupon_id')->references('id')->on('cart_rule_coupons')->onDelete('cascade');
$table->integer('customer_id')->unsigned();
$table->foreign('customer_id')->references('id')->on('customers')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('cart_rule_coupon_usage');
}
}

View File

@ -0,0 +1,523 @@
<?php
namespace Webkul\CartRule\Helpers;
use Carbon\Carbon;
use Webkul\CartRule\Repositories\CartRuleRepository;
use Webkul\CartRule\Repositories\CartRuleCouponRepository;
use Webkul\CartRule\Repositories\CartRuleCouponUsageRepository;
use Webkul\CartRule\Repositories\CartRuleCustomerRepository;
use Webkul\Customer\Repositories\CustomerGroupRepository;
use Webkul\Rule\Helpers\Validator;
use Webkul\Checkout\Facades\Cart;
class CartRule
{
/**
* CartRuleRepository object
*
* @var CartRuleRepository
*/
protected $cartRuleRepository;
/**
* CartRuleCouponRepository object
*
* @var CartRuleCouponRepository
*/
protected $cartRuleCouponRepository;
/**
* CartRuleCouponUsageRepository object
*
* @var CartRuleCouponUsageRepository
*/
protected $cartRuleCouponUsageRepository;
/**
* CartRuleCustomerRepository object
*
* @var CartRuleCustomerRepository
*/
protected $cartRuleCustomerRepository;
/**
* CustomerGroupRepository object
*
* @var CustomerGroupRepository
*/
protected $customerGroupRepository;
/**
* Validator object
*
* @var Validator
*/
protected $validator;
/**
* @var array
*/
protected $itemTotals = [];
/**
* Create a new helper instance.
*
* @param Webkul\CartRule\Repositories\CartRuleRepository $cartRuleRepository
* @param Webkul\CartRule\Repositories\CartRuleCouponRepository $cartRuleCouponRepository
* @param Webkul\CartRule\Repositories\CartRuleCouponUsageRepository $cartRuleCouponUsageRepository
* @param Webkul\CartRule\Repositories\CartRuleCustomerRepository $cartRuleCustomerRepository
* @param Webkul\Customer\Repositories\CustomerGroupRepository $customerGroupRepository
* @param Webkul\Rule\Helpers\Validator $validator
* @return void
*/
public function __construct(
CartRuleRepository $cartRuleRepository,
CartRuleCouponRepository $cartRuleCouponRepository,
CartRuleCouponUsageRepository $cartRuleCouponUsageRepository,
CartRuleCustomerRepository $cartRuleCustomerRepository,
CustomerGroupRepository $customerGroupRepository,
Validator $validator
)
{
$this->cartRuleRepository = $cartRuleRepository;
$this->cartRuleCouponRepository = $cartRuleCouponRepository;
$this->cartRuleCouponUsageRepository = $cartRuleCouponUsageRepository;
$this->cartRuleCustomerRepository = $cartRuleCustomerRepository;
$this->customerGroupRepository = $customerGroupRepository;
$this->validator = $validator;
}
/**
* Collect discount on cart
*
* @return void
*/
public function collect()
{
$cart = Cart::getCart();
$this->calculateCartItemTotals($cart->items()->get());
foreach ($cart->items()->get() as $item) {
$this->process($item);
if ($item->children()->count() && $item->product->getTypeInstance()->isChildrenCalculated())
$this->devideDiscount($item);
}
$this->processShippingDiscount($cart);
$this->processFreeShippingDiscount($cart);
$this->validateCouponCode();
}
/**
* Returns cart rules
*
* @return Collection
*/
public function getCartRules()
{
static $cartRules;
if ($cartRules)
return $cartRules;
$customerGroupId = null;
if (Cart::getCurrentCustomer()->check()) {
$customerGroupId = Cart::getCurrentCustomer()->user()->customer_group_id;
} else {
if ($customerGuestGroup = $this->customerGroupRepository->findOneByField('code', 'guest'))
$customerGroupId = $customerGuestGroup->id;
}
$cartRules = $this->cartRuleRepository->scopeQuery(function($query) use ($customerGroupId) {
return $query->leftJoin('cart_rule_customer_groups', 'cart_rules.id', '=', 'cart_rule_customer_groups.cart_rule_id')
->leftJoin('cart_rule_channels', 'cart_rules.id', '=', 'cart_rule_channels.cart_rule_id')
->where('cart_rule_customer_groups.customer_group_id', $customerGroupId)
->where('cart_rule_channels.channel_id', core()->getCurrentChannel()->id)
->where(function ($query1) {
$query1->where('cart_rules.starts_from', '<=', Carbon::now()->format('Y-m-d'))->orWhereNull('cart_rules.starts_from');
})
->where(function ($query2) {
$query2->where('cart_rules.ends_till', '>=', Carbon::now()->format('Y-m-d'))->orWhereNull('cart_rules.ends_till');
})
->orderBy('sort_order', 'asc');
})->findWhere(['status' => 1]);
return $cartRules;
}
/**
* Check if cart rule can be applied
*
* @param CartRule $rule
* @return boolean
*/
public function canProcessRule($rule)
{
$cart = Cart::getCart();
if ($rule->coupon_type) {
if (strlen($cart->coupon_code)) {
$coupon = $this->cartRuleCouponRepository->findOneWhere([
'cart_rule_id' => $rule->id,
'code' => $cart->coupon_code,
]);
if ($coupon) {
if ($coupon->usage_limit && $coupon->times_used >= $coupon->usage_limit)
return false;
if ($cart->customer_id && $coupon->usage_per_customer) {
$couponUsage = $this->cartRuleCouponUsageRepository->findOneWhere([
'cart_rule_coupon_id' => $coupon->id,
'customer_id' => $cart->customer_id
]);
if ($couponUsage && $couponUsage->times_used >= $coupon->usage_per_customer)
return false;
}
} else {
return false;
}
} else {
return false;
}
}
if ($rule->usage_per_customer) {
$ruleCustomer = $this->cartRuleCustomerRepository->findOneWhere([
'cart_rule_id' => $rule->id,
'customer_id' => $cart->customer_id,
]);
if ($ruleCustomer && $ruleCustomer->times_used >= $rule->usage_per_customer)
return false;
}
return true;
}
/**
* Cart item discount calculation process
*
* @param CartItem $item
* @return void
*/
public function process($item)
{
$item->discount_percent = 0;
$item->discount_amount = 0;
$item->base_discount_amount = 0;
$cart = $item->cart;
$cart->applied_cart_rule_ids = null;
$appliedRuleIds = [];
foreach ($this->getCartRules() as $rule) {
if (! $this->canProcessRule($rule))
continue;
if (! $this->validator->validate($rule, $item))
continue;
$quantity = $rule->discount_quantity ? min($item->quantity, $rule->discount_quantity) : $item->quantity;
$discountAmount = $baseDiscountAmount = 0;
switch ($rule->action_type) {
case 'by_percent':
$rulePercent = min(100, $rule->discount_amount);
$discountAmount = ($quantity * $item->price - $item->discount_amount) * ($rulePercent / 100);
$baseDiscountAmount = ($quantity * $item->base_price - $item->base_discount_amount) * ($rulePercent / 100);
if (! $rule->discount_quantity || $rule->discount_quantity > $quantity) {
$discountPercent = min(100, $item->discount_percent + $rulePercent);
$item->discount_percent = $discountPercent;
}
break;
case 'by_fixed':
$discountAmount = $quantity * core()->convertPrice($rule->discount_amount);
$baseDiscountAmount = $quantity * $rule->discount_amount;
break;
case 'cart_fixed':
if ($this->itemTotals[$rule->id]['total_items'] <= 1) {
$discountAmount = core()->convertPrice($rule->discount_amount);
$baseDiscountAmount = min($item->base_price * $quantity, $rule->discount_amount);
} else {
$discountRate = $item->base_price * $quantity / $this->itemTotals[$rule->id]['base_total_price'];
$maxDiscount = $rule->discount_amount * $discountRate;
$discountAmount = core()->convertPrice($maxDiscount);
$baseDiscountAmount = min($item->base_price * $quantity, $maxDiscount);
}
$discountAmount = min($item->price * $quantity, $discountAmount);
break;
case 'buy_x_get_y':
if (! $rule->discount_step || $rule->discount_amount > $rule->discount_step)
break;
$buyAndDiscountQty = $rule->discount_step + $rule->discount_amount;
$qtyPeriod = floor($quantity / $buyAndDiscountQty);
$freeQty = $quantity - $qtyPeriod * $buyAndDiscountQty;
$discountQty = $qtyPeriod * $rule->discount_amount;
if ($freeQty > $rule->discount_step)
$discountQty += $freeQty - $rule->discount_step;
$discountAmount = $discountQty * $item->price;
$baseDiscountAmount = $discountQty * $item->base_price;
break;
}
$item->discount_amount = min($item->discount_amount + $discountAmount, $item->price * $quantity);
$item->base_discount_amount = min($item->base_discount_amount + $baseDiscountAmount, $item->base_price * $quantity);
$appliedRuleIds[$rule->id] = $rule->id;
if ($rule->end_other_rules)
break;
}
$item->applied_cart_rule_ids = join(',', $appliedRuleIds);
$item->save();
$cartAppliedCartRuleIds = array_merge(explode(',', $cart->applied_cart_rule_ids), $appliedRuleIds);
$cartAppliedCartRuleIds = array_filter($cartAppliedCartRuleIds);
$cartAppliedCartRuleIds = array_unique($cartAppliedCartRuleIds);
$cart->applied_cart_rule_ids = join(',', $cartAppliedCartRuleIds);
$cart->save();
}
/**
* Cart shipping discount calculation process
*
* @param Cart $cart
* @return void
*/
public function processShippingDiscount($cart)
{
if (! $selectedShipping = $cart->selected_shipping_rate)
return;
$selectedShipping->discount_amount = 0;
$selectedShipping->base_discount_amount = 0;
$appliedRuleIds = [];
foreach ($this->getCartRules() as $rule) {
if (! $this->canProcessRule($rule))
continue;
if (! $this->validator->validate($rule, $cart))
continue;
if (! $rule || ! $rule->apply_to_shipping)
continue;
$discountAmount = $baseDiscountAmount = 0;
switch ($rule->action_type) {
case 'by_percent':
$rulePercent = min(100, $rule->discount_amount);
$discountAmount = ($selectedShipping->price - $selectedShipping->discount_amount) * $rulePercent / 100;
$baseDiscountAmount = ($selectedShipping->base_price - $selectedShipping->base_discount_amount) * $rulePercent / 100;
break;
case 'by_fixed':
$discountAmount = core()->convertPrice($rule->discount_amount);
$baseDiscountAmount = $rule->discount_amount;
break;
}
$selectedShipping->discount_amount = min($selectedShipping->discount_amount + $discountAmount, $selectedShipping->price);
$selectedShipping->base_discount_amount = min(
$selectedShipping->base_discount_amount + $baseDiscountAmount,
$selectedShipping->base_price
);
$selectedShipping->save();
$appliedRuleIds[$rule->id] = $rule->id;
if ($rule->end_other_rules)
break;
}
$selectedShipping->save();
$cartAppliedCartRuleIds = array_merge(explode(',', $cart->applied_cart_rule_ids), $appliedRuleIds);
$cartAppliedCartRuleIds = array_filter($cartAppliedCartRuleIds);
$cartAppliedCartRuleIds = array_unique($cartAppliedCartRuleIds);
$cart->applied_cart_rule_ids = join(',', $cartAppliedCartRuleIds);
$cart->save();
return $this;
}
/**
* Cart free shipping discount calculation process
*
* @param Cart $cart
* @return void
*/
public function processFreeShippingDiscount($cart)
{
if (! $selectedShipping = $cart->selected_shipping_rate)
return;
$selectedShipping->discount_amount = 0;
$selectedShipping->base_discount_amount = 0;
$appliedRuleIds = [];
foreach ($this->getCartRules() as $rule) {
if (! $this->canProcessRule($rule))
continue;
if (! $this->validator->validate($rule, $cart))
continue;
if (! $rule || ! $rule->free_shipping)
continue;
$selectedShipping->price = 0;
$selectedShipping->base_price = 0;
$selectedShipping->save();
$appliedRuleIds[$rule->id] = $rule->id;
if ($rule->end_other_rules)
break;
}
$cartAppliedCartRuleIds = array_merge(explode(',', $cart->applied_cart_rule_ids), $appliedRuleIds);
$cartAppliedCartRuleIds = array_filter($cartAppliedCartRuleIds);
$cartAppliedCartRuleIds = array_unique($cartAppliedCartRuleIds);
$cart->applied_cart_rule_ids = join(',', $cartAppliedCartRuleIds);
$cart->save();
}
/**
* Calculate cart item totals for each rule
*
* @param mixed $items
* @return Validator
*/
public function calculateCartItemTotals($items)
{
foreach ($this->getCartRules() as $rule) {
if ($rule->action_type == 'cart_fixed') {
$totalPrice = $totalBasePrice = $validCount = 0;
foreach ($items as $item) {
if (! $this->canProcessRule($rule, $item))
continue;
$quantity = $rule->discount_quantity ? min($item->quantity, $rule->discount_quantity) : $item->quantity;
$totalBasePrice += $item->base_price * $quantity;
$validCount++;
}
$this->itemTotals[$rule->id] = [
'base_total_price' => $totalBasePrice,
'total_items' => $validCount,
];
}
}
}
/**
* Check if coupon code is valid or not, if not remove from cart
*
* @return void
*/
public function validateCouponCode()
{
$cart = Cart::getCart();
if (! $cart->coupon_code)
return;
$coupon = $this->cartRuleCouponRepository->findOneByField('code', $cart->coupon_code);
if (! $coupon || ! in_array($coupon->cart_rule_id, explode(',', $cart->applied_cart_rule_ids)))
Cart::removeCouponCode();
}
/**
* Devide discount amount to children
*
* @param CartItem $item
* @return void
*/
protected function devideDiscount($item)
{
foreach ($item->children as $child) {
$ratio = $item->base_total != 0 ? $child->base_total / $item->base_total : 0;
foreach (['discount_amount', 'base_discount_amount'] as $column) {
if (! $item->{$column})
continue;
$child->{$column} = round(($item->{$column} * $ratio), 4);
$child->save();
}
}
}
}

View File

@ -0,0 +1,206 @@
<?php
namespace Webkul\CartRule\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Event;
use Webkul\CartRule\Repositories\CartRuleRepository;
use Webkul\CartRule\Repositories\CartRuleCouponRepository;
/**
* Cart Rule controller
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CartRuleController extends Controller
{
/**
* Initialize _config, a default request parameter with route
*
* @param array
*/
protected $_config;
/**
* To hold Cart repository instance
*
* @var CartRuleRepository
*/
protected $cartRuleRepository;
/**
* To hold CartRuleCouponRepository repository instance
*
* @var CartRuleCouponRepository
*/
protected $cartRuleCouponRepository;
/**
* Create a new controller instance.
*
* @param \Webkul\CartRule\Repositories\CartRuleRepository $cartRuleRepository
* @param \Webkul\CartRule\Repositories\CartRuleCouponRepository $cartRuleCouponRepository
* @return void
*/
public function __construct(
CartRuleRepository $cartRuleRepository,
CartRuleCouponRepository $cartRuleCouponRepository
)
{
$this->_config = request('_config');
$this->cartRuleRepository = $cartRuleRepository;
$this->cartRuleCouponRepository = $cartRuleCouponRepository;
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
return view($this->_config['view']);
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view($this->_config['view']);
}
/**
* Store a newly created resource in storage.
*
* @return \Illuminate\Http\Response
*/
public function store()
{
$this->validate(request(), [
'name' => 'required',
'channels' => 'required|array|min:1',
'customer_groups' => 'required|array|min:1',
'coupon_type' => 'required',
'use_auto_generation' => 'required_if:coupon_type,==,1',
'coupon_code' => 'required_if:use_auto_generation,==,0',
'starts_from' => 'nullable|date',
'ends_till' => 'nullable|date|after_or_equal:starts_from',
'action_type' => 'required',
'discount_amount' => 'required|numeric'
]);
$data = request()->all();
Event::fire('promotions.cart_rule.create.before');
$cartRule = $this->cartRuleRepository->create($data);
Event::fire('promotions.cart_rule.create.after', $cartRule);
session()->flash('success', trans('admin::app.response.create-success', ['name' => 'Cart Rule']));
return redirect()->route($this->_config['redirect']);
}
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$cartRule = $this->cartRuleRepository->findOrFail($id);
return view($this->_config['view'], compact('cartRule'));
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->validate(request(), [
'name' => 'required',
'channels' => 'required|array|min:1',
'customer_groups' => 'required|array|min:1',
'coupon_type' => 'required',
'use_auto_generation' => 'required_if:coupon_type,==,1',
'coupon_code' => 'required_if:use_auto_generation,==,0',
'starts_from' => 'nullable|date',
'ends_till' => 'nullable|date|after_or_equal:starts_from',
'action_type' => 'required',
'discount_amount' => 'required|numeric'
]);
$cartRule = $this->cartRuleRepository->findOrFail($id);
Event::fire('promotions.cart_rule.update.before', $cartRule);
$cartRule = $this->cartRuleRepository->update(request()->all(), $id);
Event::fire('promotions.cart_rule.update.after', $cartRule);
session()->flash('success', trans('admin::app.response.update-success', ['name' => 'Cart Rule']));
return redirect()->route($this->_config['redirect']);
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$cartRule = $this->cartRuleRepository->findOrFail($id);
try {
Event::fire('promotions.cart_rule.delete.before', $id);
$this->cartRuleRepository->delete($id);
Event::fire('promotions.cart_rule.delete.after', $id);
session()->flash('success', trans('admin::app.response.delete-success', ['name' => 'Cart Rule']));
return response()->json(['message' => true], 200);
} catch(\Exception $e) {
session()->flash('error', trans('admin::app.response.delete-failed', ['name' => 'Cart Rule']));
}
return response()->json(['message' => false], 400);
}
/**
* Generate coupon code for cart rule
*
* @return \Illuminate\Http\JsonResponse
*/
public function generateCoupons()
{
$this->validate(request(), [
'coupon_qty' => 'required|integer|min:1',
'code_length' => 'required|integer|min:10',
'code_format' => 'required'
]);
if (! request('id'))
return response()->json(['message' => trans('admin::app.promotions.cart-rules.cart-rule-not-defind-error')], 400);
$this->cartRuleCouponRepository->generateCoupons(request()->all(), request('id'));
return response()->json(['message' => trans('admin::app.response.create-success', ['name' => 'Cart rule coupons'])]);
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace Webkul\CartRule\Http\Controllers;
use Illuminate\Http\Request;
use Webkul\CartRule\Repositories\CartRuleCouponRepository;
/**
* Cart Rule Coupon controller
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CartRuleCouponController extends Controller
{
/**
* To hold CartRuleCouponRepository repository instance
*
* @var CartRuleCouponRepository
*/
protected $cartRuleCouponRepository;
/**
* Create a new controller instance.
*
* @param \Webkul\CartRule\Repositories\CartRuleCouponRepository $cartRuleCouponRepository
* @return void
*/
public function __construct(CartRuleCouponRepository $cartRuleCouponRepository)
{
$this->cartRuleCouponRepository = $cartRuleCouponRepository;
}
/**
* Mass Delete the coupons
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function massDelete()
{
$couponIds = explode(',', request()->input('indexes'));
foreach ($couponIds as $couponId) {
$coupon = $this->cartRuleCouponRepository->find($couponId);
if ($coupon)
$this->cartRuleCouponRepository->delete($couponId);
}
session()->flash('success', trans('admin::app.promotions.cart-rules.mass-delete-success'));
return redirect()->back();
}
}

View File

@ -1,9 +1,9 @@
<?php
namespace Webkul\Discount\Http\Controllers;
namespace Webkul\CartRule\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

View File

@ -0,0 +1,43 @@
<?php
namespace Webkul\CartRule\Listeners;
use Webkul\CartRule\Helpers\CartRule;
/**
* Cart event handler
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class Cart
{
/**
* CartRule object
*
* @var CartRule
*/
protected $cartRuleHepler;
/**
* Create a new listener instance.
*
* @param Webkul\CartRule\Repositories\CartRule $cartRuleHepler
* @return void
*/
public function __construct(CartRule $cartRuleHepler)
{
$this->cartRuleHepler = $cartRuleHepler;
}
/**
* Aplly valid cart rules to cart
*
* @param Cart $cart
* @return void
*/
public function applyCartRules($cart)
{
$this->cartRuleHepler->collect();
}
}

View File

@ -0,0 +1,139 @@
<?php
namespace Webkul\CartRule\Listeners;
use Webkul\CartRule\Repositories\CartRuleRepository;
use Webkul\CartRule\Repositories\CartRuleCustomerRepository;
use Webkul\CartRule\Repositories\CartRuleCouponRepository;
use Webkul\CartRule\Repositories\CartRuleCouponUsageRepository;
/**
* Order event handler
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class Order
{
/**
* CartRuleRepository object
*
* @var CartRuleRepository
*/
protected $cartRuleRepository;
/**
* CartRuleCustomerRepository object
*
* @var CartRuleCustomerRepository
*/
protected $cartRuleCustomerRepository;
/**
* CartRuleCouponRepository object
*
* @var CartRuleCouponRepository
*/
protected $cartRuleCouponRepository;
/**
* CartRuleCouponUsageRepository object
*
* @var CartRuleCouponUsageRepository
*/
protected $cartRuleCouponUsageRepository;
/**
* Create a new listener instance.
*
* @param Webkul\CartRule\Repositories\CartRuleRepository $cartRuleRepository
* @param Webkul\CartRule\Repositories\CartRuleCustomerRepository $cartRuleCustomerRepository
* @param Webkul\CartRule\Repositories\CartRuleCouponRepository $cartRuleCouponRepository
* @param Webkul\CartRule\Repositories\CartRuleCouponUsageRepository $cartRuleCouponUsageRepository
* @return void
*/
public function __construct(
CartRuleRepository $cartRuleRepository,
CartRuleCustomerRepository $cartRuleCustomerRepository,
CartRuleCouponRepository $cartRuleCouponRepository,
CartRuleCouponUsageRepository $cartRuleCouponUsageRepository
)
{
$this->cartRuleRepository = $cartRuleRepository;
$this->cartRuleCustomerRepository = $cartRuleCustomerRepository;
$this->cartRuleCouponRepository = $cartRuleCouponRepository;
$this->cartRuleCouponUsageRepository = $cartRuleCouponUsageRepository;
}
/**
* Save cart rule and cart rule coupon properties after place order
*
* @param Order $order
* @return void
*/
public function manageCartRule($order)
{
if (! $order->discount_amount)
return;
$cartRuleIds = explode(',', $order->applied_cart_rule_ids);
$cartRuleIds = array_unique($cartRuleIds);
foreach ($cartRuleIds as $ruleId) {
$rule = $this->cartRuleRepository->find($ruleId);
if (! $rule)
continue;
$rule->update(['times_used' => $rule->times_used + 1]);
if (! $order->customer_id)
continue;
$ruleCustomer = $this->cartRuleCustomerRepository->findOneWhere([
'customer_id' => $order->customer_id,
'cart_rule_id' => $ruleId
]);
if ($ruleCustomer) {
$this->cartRuleCustomerRepository->update(['times_used' => $ruleCustomer->times_used + 1], $ruleCustomer->id);
} else {
$this->cartRuleCustomerRepository->create([
'customer_id' => $order->customer_id,
'cart_rule_id' => $ruleId,
'times_used' => 1
]);
}
}
if (! $order->coupon_code)
return;
$coupon = $this->cartRuleCouponRepository->findOneByField('code', $order->coupon_code);
if ($coupon) {
$this->cartRuleCouponRepository->update(['times_used' => $coupon->times_used + 1], $coupon->id);
if ($order->customer_id) {
$couponUsage = $this->cartRuleCouponUsageRepository->findOneWhere([
'customer_id' => $order->customer_id,
'cart_rule_coupon_id' => $coupon->id
]);
if ($couponUsage) {
$this->cartRuleCouponUsageRepository->update(['times_used' => $couponUsage->times_used + 1], $couponUsage->id);
} else {
$this->cartRuleCouponUsageRepository->create([
'customer_id' => $order->customer_id,
'cart_rule_coupon_id' => $coupon->id,
'times_used' => 1
]);
}
}
}
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace Webkul\CartRule\Models;
// use Webkul\Core\Eloquent\TranslatableModel;
use Illuminate\Database\Eloquent\Model;
use Webkul\CartRule\Contracts\CartRule as CartRuleContract;
use Webkul\Core\Models\ChannelProxy;
use Webkul\Customer\Models\CustomerGroupProxy;
// class CartRule extends TranslatableModel implements CartRuleContract
class CartRule extends Model implements CartRuleContract
{
protected $fillable = ['name', 'description', 'starts_from', 'ends_till', 'status', 'coupon_type', 'use_auto_generation', 'usage_per_customer', 'uses_per_coupon', 'times_used', 'condition_type', 'conditions', 'actions', 'end_other_rules', 'uses_attribute_conditions', 'action_type', 'discount_amount', 'discount_quantity', 'discount_step', 'apply_to_shipping', 'free_shipping', 'sort_order'];
protected $casts = [
'conditions' => 'array',
];
// public $translatedAttributes = ['name'];
/**
* Get the channels that owns the cart rule.
*/
public function channels()
{
return $this->belongsToMany(ChannelProxy::modelClass(), 'cart_rule_channels');
}
/**
* Get the customer groups that owns the cart rule.
*/
public function customer_groups()
{
return $this->belongsToMany(CustomerGroupProxy::modelClass(), 'cart_rule_customer_groups');
}
/**
* Get the coupons that owns the cart rule.
*/
public function coupons()
{
return $this->hasOne(CartRuleCouponProxy::modelClass());
}
/**
* Get primary coupon code for cart rule.
*/
public function coupon_code()
{
return $this->coupons()->where('is_primary', 1);
}
/**
* Get primary coupon code for cart rule.
*/
public function getCouponCodeAttribute()
{
$coupon = $this->coupon_code()->first();
if (! $coupon)
return;
return $coupon->code;
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Webkul\CartRule\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\CartRule\Contracts\CartRuleCoupon as CartRuleCouponContract;
class CartRuleCoupon extends Model implements CartRuleCouponContract
{
protected $fillable = ['code', 'usage_limit', 'usage_per_customer', 'times_used', 'type', 'cart_rule_id', 'expired_at', 'is_primary'];
/**
* Get the cart rule that owns the cart rule coupon.
*/
public function cart_rule()
{
return $this->belongsTo(CartRuleProxy::modelClass());
}
}

View File

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

View File

@ -0,0 +1,15 @@
<?php
namespace Webkul\CartRule\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\CartRule\Contracts\CartRuleCouponUsage as CartRuleCouponUsageContract;
class CartRuleCouponUsage extends Model implements CartRuleCouponUsageContract
{
public $timestamps = false;
protected $table = 'cart_rule_coupon_usage';
protected $guarded = ['created_at', 'updated_at'];
}

View File

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

View File

@ -0,0 +1,13 @@
<?php
namespace Webkul\CartRule\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\CartRule\Contracts\CartRuleCustomer as CartRuleCustomerContract;
class CartRuleCustomer extends Model implements CartRuleCustomerContract
{
public $timestamps = false;
protected $fillable = ['times_used', 'cart_rule_id', 'customer_id'];
}

View File

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

View File

@ -1,6 +1,6 @@
<?php
namespace Webkul\Discount\Models;
namespace Webkul\CartRule\Models;
use Konekt\Concord\Proxies\ModelProxy;

View File

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

View File

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

View File

@ -0,0 +1,30 @@
<?php
namespace Webkul\CartRule\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Routing\Router;
class CartRuleServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* @return void
*/
public function boot(Router $router)
{
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
$this->app->register(EventServiceProvider::class);
}
/**
* Register services.
*
* @return void
*/
public function register()
{
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Webkul\CartRule\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Event;
class EventServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
Event::listen('checkout.order.save.after', 'Webkul\CartRule\Listeners\Order@manageCartRule');
Event::listen('checkout.cart.collect.totals.before', 'Webkul\CartRule\Listeners\Cart@applyCartRules');
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Webkul\CartRule\Providers;
use Konekt\Concord\BaseModuleServiceProvider;
class ModuleServiceProvider extends BaseModuleServiceProvider
{
protected $models = [
\Webkul\CartRule\Models\CartRule::class,
\Webkul\CartRule\Models\CartRuleTranslation::class,
\Webkul\CartRule\Models\CartRuleCustomer::class,
\Webkul\CartRule\Models\CartRuleCoupon::class,
\Webkul\CartRule\Models\CartRuleCouponUsage::class
];
}

View File

@ -0,0 +1,74 @@
<?php
namespace Webkul\CartRule\Repositories;
use Webkul\Core\Eloquent\Repository;
/**
* CartRuleCoupon Reposotory
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CartRuleCouponRepository extends Repository
{
/**
* @var array
*/
protected $charsets = [
'alphanumeric' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
'alphabetical' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'numeric' => '0123456789'
];
/**
* Specify Model class name
*
* @return mixed
*/
function model()
{
return 'Webkul\CartRule\Contracts\CartRuleCoupon';
}
/**
* Creates coupons for cart rule
*
* @param array $data
* @param integer $cartRuleId
* @return void
*/
public function generateCoupons($data, $cartRuleId)
{
$cartRule = app('Webkul\CartRule\Repositories\CartRuleRepository')->findOrFail($cartRuleId);
for ($i = 0; $i < $data['coupon_qty']; $i++) {
parent::create([
'cart_rule_id' => $cartRuleId,
'code' => $data['code_prefix'] . $this->getRandomString($data['code_format'], $data['code_length']) . $data['code_suffix'],
'usage_limit' => $cartRule->uses_per_coupon ?? 0,
'usage_per_customer' => $cartRule->usage_per_customer ?? 0,
'is_primary' => 0,
'expired_at' => $cartRule->ends_till ?: null
]);
}
}
/**
* Creates coupons for cart rule
*
* @param string $format
* @param integer $length
* @return string
*/
public function getRandomString($format, $length)
{
$couponCode = '';
for ($i = 0; $i < $length; $i++) {
$couponCode .= $this->charsets[$format][rand(0, strlen($this->charsets[$format]) - 1)];
}
return $couponCode;
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace Webkul\CartRule\Repositories;
use Webkul\Core\Eloquent\Repository;
/**
* CartRuleCouponUsage Reposotory
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CartRuleCouponUsageRepository extends Repository
{
/**
* Specify Model class name
*
* @return mixed
*/
function model()
{
return 'Webkul\CartRule\Contracts\CartRuleCouponUsage';
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace Webkul\CartRule\Repositories;
use Webkul\Core\Eloquent\Repository;
/**
* CartRuleCustomer Reposotory
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CartRuleCustomerRepository extends Repository
{
/**
* Specify Model class name
*
* @return mixed
*/
function model()
{
return 'Webkul\CartRule\Contracts\CartRuleCustomer';
}
}

View File

@ -0,0 +1,478 @@
<?php
namespace Webkul\CartRule\Repositories;
use Illuminate\Container\Container as App;
use Webkul\Core\Eloquent\Repository;
use Webkul\Attribute\Repositories\AttributeFamilyRepository;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Category\Repositories\CategoryRepository;
use Webkul\Tax\Repositories\TaxCategoryRepository;
use Webkul\Core\Repositories\CountryRepository;
use Webkul\Core\Repositories\CountryStateRepository;
/**
* CartRule Reposotory
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CartRuleRepository extends Repository
{
/**
* AttributeFamilyRepository object
*
* @var AttributeFamilyRepository
*/
protected $attributeFamilyRepository;
/**
* AttributeRepository object
*
* @var AttributeRepository
*/
protected $attributeRepository;
/**
* CategoryRepository class
*
* @var CategoryRepository
*/
protected $categoryRepository;
/**
* CartRuleCouponRepository object
*
* @var CartRuleCouponRepository
*/
protected $cartRuleCouponRepository;
/**
* TaxCategoryRepository class
*
* @var TaxCategoryRepository
*/
protected $taxCategoryRepository;
/**
* CountryRepository class
*
* @var CountryRepository
*/
protected $countryRepository;
/**
* CountryStateRepository class
*
* @var CountryStateRepository
*/
protected $countryStateRepository;
/**
* Create a new repository instance.
*
* @param Webkul\Attribute\Repositories\AttributeFamilyRepository $attributeFamilyRepository
* @param Webkul\Attribute\Repositories\AttributeRepository $attributeRepository
* @param Webkul\Category\Repositories\CategoryRepository $categoryRepository
* @param Webkul\CartRule\Repositories\CartRuleCouponRepository $cartRuleCouponRepository
* @param Webkul\Tax\Repositories\TaxCategoryRepository $taxCategoryRepository
* @param Webkul\Core\Repositories\CountryRepository $countryRepository
* @param Webkul\Core\Repositories\CountryStateRepository $countryStateRepository
* @param Illuminate\Container\Container $app
* @return void
*/
public function __construct(
AttributeFamilyRepository $attributeFamilyRepository,
AttributeRepository $attributeRepository,
CategoryRepository $categoryRepository,
CartRuleCouponRepository $cartRuleCouponRepository,
TaxCategoryRepository $taxCategoryRepository,
CountryRepository $countryRepository,
CountryStateRepository $countryStateRepository,
App $app
)
{
$this->attributeFamilyRepository = $attributeFamilyRepository;
$this->attributeRepository = $attributeRepository;
$this->categoryRepository = $categoryRepository;
$this->cartRuleCouponRepository = $cartRuleCouponRepository;
$this->taxCategoryRepository = $taxCategoryRepository;
$this->countryRepository = $countryRepository;
$this->countryStateRepository = $countryStateRepository;
parent::__construct($app);
}
/**
* Specify Model class name
*
* @return mixed
*/
function model()
{
return 'Webkul\CartRule\Contracts\CartRule';
}
/**
* @param array $data
* @return mixed
*/
public function create(array $data)
{
$data['starts_from'] = $data['starts_from'] ?: null;
$data['ends_till'] = $data['ends_till'] ?: null;
$data['status'] = ! isset($data['status']) ? 0 : 1;
$cartRule = parent::create($data);
$cartRule->channels()->sync($data['channels']);
$cartRule->customer_groups()->sync($data['customer_groups']);
if ($data['coupon_type'] && ! $data['use_auto_generation']) {
$this->cartRuleCouponRepository->create([
'cart_rule_id' => $cartRule->id,
'code' => $data['coupon_code'],
'usage_limit' => $data['usage_per_customer'] ?? 0,
'usage_per_customer' => $data['usage_per_customer'] ?? 0,
'is_primary' => 1,
'expired_at' => $data['ends_till'] ?: null
]);
}
return $cartRule;
}
/**
* @param array $data
* @param array $id
* @param string $attribute
* @return mixed
*/
public function update(array $data, $id, $attribute = "id")
{
$data['starts_from'] = $data['starts_from'] ?: null;
$data['ends_till'] = $data['ends_till'] ?: null;
$data['status'] = ! isset($data['status']) ? 0 : 1;
$data['conditions'] = $data['conditions'] ?? [];
$cartRule = $this->find($id);
parent::update($data, $id, $attribute);
$cartRule->channels()->sync($data['channels']);
$cartRule->customer_groups()->sync($data['customer_groups']);
if ($data['coupon_type']) {
if (! $data['use_auto_generation']) {
$cartRuleCoupon = $this->cartRuleCouponRepository->findOneWhere(['is_primary' => 1, 'cart_rule_id' => $cartRule->id]);
if ($cartRuleCoupon) {
$this->cartRuleCouponRepository->update([
'code' => $data['coupon_code'],
'usage_limit' => $data['uses_per_coupon'] ?? 0,
'usage_per_customer' => $data['usage_per_customer'] ?? 0,
'expired_at' => $data['ends_till'] ?: null
], $cartRuleCoupon->id);
} else {
$this->cartRuleCouponRepository->create([
'cart_rule_id' => $cartRule->id,
'code' => $data['coupon_code'],
'usage_limit' => $data['uses_per_coupon'] ?? 0,
'usage_per_customer' => $data['usage_per_customer'] ?? 0,
'is_primary' => 1,
'expired_at' => $data['ends_till'] ?: null
]);
}
} else {
$this->cartRuleCouponRepository->deleteWhere(['is_primary' => 1, 'cart_rule_id' => $cartRule->id]);
$this->cartRuleCouponRepository->getModel()->where('cart_rule_id', $cartRule->id)->update([
'usage_limit' => $data['uses_per_coupon'] ?? 0,
'usage_per_customer' => $data['usage_per_customer'] ?? 0,
'expired_at' => $data['ends_till'] ?: null
]);
}
} else {
$cartRuleCoupon = $this->cartRuleCouponRepository->deleteWhere(['is_primary' => 1, 'cart_rule_id' => $cartRule->id]);
}
return $cartRule;
}
/**
* Returns attributes for cart rule conditions
*
* @return array
*/
public function getConditionAttributes()
{
$attributes = [
[
'key' => 'cart',
'label' => trans('admin::app.promotions.cart-rules.cart-attribute'),
'children' => [
[
'key' => 'cart|base_sub_total',
'type' => 'price',
'label' => trans('admin::app.promotions.cart-rules.subtotal')
], [
'key' => 'cart|items_qty',
'type' => 'integer',
'label' => trans('admin::app.promotions.cart-rules.total-items-qty')
], [
'key' => 'cart|payment_method',
'type' => 'select',
'options' => $this->getPaymentMethods(),
'label' => trans('admin::app.promotions.cart-rules.payment-method')
], [
'key' => 'cart|shipping_method',
'type' => 'select',
'options' => $this->getShippingMethods(),
'label' => trans('admin::app.promotions.cart-rules.shipping-method')
], [
'key' => 'cart|postcode',
'type' => 'text',
'label' => trans('admin::app.promotions.cart-rules.shipping-postcode')
], [
'key' => 'cart|state',
'type' => 'select',
'options' => $this->groupedStatesByCountries(),
'label' => trans('admin::app.promotions.cart-rules.shipping-state')
], [
'key' => 'cart|country',
'type' => 'select',
'options' => $this->getCountries(),
'label' => trans('admin::app.promotions.cart-rules.shipping-country')
]
]
], [
'key' => 'cart_item',
'label' => trans('admin::app.promotions.cart-rules.cart-item-attribute'),
'children' => [
[
'key' => 'cart_item|base_price',
'type' => 'price',
'label' => trans('admin::app.promotions.cart-rules.price-in-cart')
], [
'key' => 'cart_item|quantity',
'type' => 'integer',
'label' => trans('admin::app.promotions.cart-rules.qty-in-cart')
], [
'key' => 'cart_item|base_total_weight',
'type' => 'decimal',
'label' => trans('admin::app.promotions.cart-rules.total-weight')
], [
'key' => 'cart_item|base_total',
'type' => 'price',
'label' => trans('admin::app.promotions.cart-rules.subtotal')
]
]
], [
'key' => 'product',
'label' => trans('admin::app.promotions.cart-rules.product-attribute'),
'children' => [
[
'key' => 'product|category_ids',
'type' => 'multiselect',
'label' => trans('admin::app.promotions.cart-rules.categories'),
'options' => $categories = $this->categoryRepository->getCategoryTree()
], [
'key' => 'product|children::category_ids',
'type' => 'multiselect',
'label' => trans('admin::app.promotions.cart-rules.children-categories'),
'options' => $categories
], [
'key' => 'product|parent::category_ids',
'type' => 'multiselect',
'label' => trans('admin::app.promotions.cart-rules.parent-categories'),
'options' => $categories
], [
'key' => 'product|attribute_family_id',
'type' => 'select',
'label' => trans('admin::app.promotions.cart-rules.attribute_family'),
'options' => $this->getAttributeFamilies()
]
]
]
];
foreach ($this->attributeRepository->findWhereNotIn('type', ['textarea', 'image', 'file']) as $attribute) {
$attributeType = $attribute->type;
if ($attribute->code == 'tax_category_id') {
$options = $this->getTaxCategories();
} else {
$options = $attribute->options;
}
if ($attribute->validation == 'decimal')
$attributeType = 'decimal';
if ($attribute->validation == 'numeric')
$attributeType = 'integer';
$attributes[2]['children'][] = [
'key' => 'product|' . $attribute->code,
'type' => $attribute->type,
'label' => $attribute->name,
'options' => $options
];
$attributes[2]['children'][] = [
'key' => 'product|children::' . $attribute->code,
'type' => $attribute->type,
'label' => trans('admin::app.promotions.cart-rules.attribute-name-children-only', ['attribute_name' => $attribute->name]),
'options' => $options
];
$attributes[2]['children'][] = [
'key' => 'product|parent::' . $attribute->code,
'type' => $attribute->type,
'label' => trans('admin::app.promotions.cart-rules.attribute-name-parent-only', ['attribute_name' => $attribute->name]),
'options' => $options
];
}
return $attributes;
}
/**
* Returns all payment methods
*
* @return array
*/
public function getPaymentMethods()
{
$methods = [];
foreach (config('paymentmethods') as $paymentMethod) {
$object = app($paymentMethod['class']);
$methods[] = [
'id' => $object->getCode(),
'admin_name' => $object->getTitle()
];
}
return $methods;
}
/**
* Returns all shipping methods
*
* @return array
*/
public function getShippingMethods()
{
$methods = [];
foreach (config('carriers') as $shippingMethod) {
$object = app($shippingMethod['class']);
$methods[] = [
'id' => $object->getCode(),
'admin_name' => $object->getTitle()
];
}
return $methods;
}
/**
* Returns all countries
*
* @return array
*/
public function getTaxCategories()
{
$taxCategories = [];
foreach ($this->taxCategoryRepository->all() as $taxCategory) {
$taxCategories[] = [
'id' => $taxCategory->id,
'admin_name' => $taxCategory->name,
];
}
return $taxCategories;
}
/**
* Returns all attribute families
*
* @return array
*/
public function getAttributeFamilies()
{
$attributeFamilies = [];
foreach ($this->attributeFamilyRepository->all() as $attributeFamily) {
$attributeFamilies[] = [
'id' => $attributeFamily->id,
'admin_name' => $attributeFamily->name,
];
}
return $attributeFamilies;
}
/**
* Returns all countries
*
* @return array
*/
public function getCountries()
{
$countries = [];
foreach ($this->countryRepository->all() as $country) {
$countries[] = [
'id' => $country->code,
'admin_name' => $country->name,
];
}
return $countries;
}
/**
* Retrieve all grouped states by country code
*
* @return array
*/
public function groupedStatesByCountries()
{
$collection = [];
foreach ($this->countryRepository->all() as $country) {
$countryStates = $this->countryStateRepository->findWhere(
['country_id' => $country->id],
['code', 'default_name as admin_name']
)->toArray();
if (! count($countryStates))
continue;
$collection[] = [
'id' => $country->code,
'admin_name' => $country->name,
'states' => $countryStates
];
}
return $collection;
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace Webkul\CatalogRule\Console\Commands;
use Illuminate\Console\Command;
use Webkul\CatalogRule\Helpers\CatalogRuleIndex;
class PriceRuleIndex extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'product:price-rule:index';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Automatically updates catalog rule price index information (eg. rule_price)';
/**
* CatalogRuleIndex object
*
* @var Object
*/
protected $catalogRuleIndexHelper;
/**
* Create a new command instance.
*
* @param Webkul\CatalogRuleProduct\Helpers\CatalogRuleIndex $catalogRuleIndexHelper
* @return void
*/
public function __construct(CatalogRuleIndex $catalogRuleIndexHelper)
{
$this->catalogRuleIndexHelper = $catalogRuleIndexHelper;
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// $this->catalogRuleIndexHelper->reindexComplete();
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateRemoveCatalogRuleTables extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::dropIfExists('catalog_rule_customer_groups');
Schema::dropIfExists('catalog_rule_channels');
Schema::dropIfExists('catalog_rule_products');
Schema::dropIfExists('catalog_rule_products_price');
Schema::dropIfExists('catalog_rules');
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
}

View File

@ -15,16 +15,17 @@ class CreateCatalogRulesTable extends Migration
{
Schema::create('catalog_rules', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->text('description');
$table->datetime('starts_from')->nullable();
$table->datetime('ends_till')->nullable();
$table->string('name')->nullable();
$table->string('description')->nullable();
$table->date('starts_from')->nullable();
$table->date('ends_till')->nullable();
$table->boolean('status')->default(0);
$table->boolean('condition_type')->default(1);
$table->json('conditions')->nullable();
$table->json('actions')->nullable();
$table->boolean('end_other_rules')->default(0);
$table->string('action_code')->nullable();
$table->decimal('discount_amount', 20, 4)->default(0.0000);
$table->string('action_type')->nullable();
$table->decimal('discount_amount', 12, 4)->default(0);
$table->integer('sort_order')->unsigned()->default(0);
$table->timestamps();
});
}

View File

@ -14,12 +14,13 @@ class CreateCatalogRuleChannelsTable extends Migration
public function up()
{
Schema::create('catalog_rule_channels', function (Blueprint $table) {
$table->increments('id');
$table->integer('channel_id')->unsigned();
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
$table->integer('catalog_rule_id')->unsigned();
$table->integer('channel_id')->unsigned();
$table->primary(['catalog_rule_id', 'channel_id']);
$table->foreign('catalog_rule_id')->references('id')->on('catalog_rules')->onDelete('cascade');
$table->timestamps();
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
});
}

View File

@ -14,12 +14,14 @@ class CreateCatalogRuleCustomerGroupsTable extends Migration
public function up()
{
Schema::create('catalog_rule_customer_groups', function (Blueprint $table) {
$table->increments('id');
$table->integer('customer_group_id')->unsigned();
$table->foreign('customer_group_id')->references('id')->on('customer_groups')->onDelete('cascade');
$table->integer('catalog_rule_id')->unsigned();
$table->integer('customer_group_id')->unsigned();
$table->primary(['catalog_rule_id', 'customer_group_id'], 'catalog_rule_id_customer_group_id_primary');
$table->foreign('catalog_rule_id')->references('id')->on('catalog_rules')->onDelete('cascade');
$table->timestamps();
$table->foreign('customer_group_id')->references('id')->on('customer_groups')->onDelete('cascade');
});
}

View File

@ -14,20 +14,25 @@ class CreateCatalogRuleProductsTable extends Migration
public function up()
{
Schema::create('catalog_rule_products', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('catalog_rule_id')->unsigned();
$table->foreign('catalog_rule_id')->references('id')->on('catalog_rules')->onDelete('cascade');
$table->increments('id');
$table->datetime('starts_from')->nullable();
$table->datetime('ends_till')->nullable();
$table->integer('customer_group_id')->unsigned();
$table->foreign('customer_group_id')->references('id')->on('customer_groups')->onDelete('cascade');
$table->integer('channel_id')->unsigned();
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
$table->boolean('end_other_rules')->default(0);
$table->string('action_type')->nullable();
$table->decimal('discount_amount', 12, 4)->default(0);
$table->integer('sort_order')->unsigned()->default(0);
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->string('action_code')->nullable();
$table->decimal('action_amount', 20, 4)->default(0.0000);
$table->timestamps();
$table->integer('customer_group_id')->unsigned();
$table->foreign('customer_group_id')->references('id')->on('customer_groups')->onDelete('cascade');
$table->integer('catalog_rule_id')->unsigned();
$table->foreign('catalog_rule_id')->references('id')->on('catalog_rules')->onDelete('cascade');
$table->integer('channel_id')->unsigned();
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
});
}

View File

@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCatalogRuleProductsPriceTable extends Migration
class CreateCatalogRuleProductPricesTable extends Migration
{
/**
* Run the migrations.
@ -13,20 +13,24 @@ class CreateCatalogRuleProductsPriceTable extends Migration
*/
public function up()
{
Schema::create('catalog_rule_products_price', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('channel_id')->unsigned();
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
$table->integer('customer_group_id')->unsigned();
$table->foreign('customer_group_id')->references('id')->on('customer_groups')->onDelete('cascade');
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->integer('catalog_rule_id')->unsigned();
$table->foreign('catalog_rule_id')->references('id')->on('catalog_rules')->onDelete('cascade');
$table->decimal('rule_price', 20, 4)->default(0.0000);
Schema::create('catalog_rule_product_prices', function (Blueprint $table) {
$table->increments('id');
$table->decimal('price', 12, 4)->default(0);
$table->date('rule_date');
$table->datetime('starts_from')->nullable();
$table->datetime('ends_till')->nullable();
$table->timestamps();
$table->integer('product_id')->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->integer('customer_group_id')->unsigned();
$table->foreign('customer_group_id')->references('id')->on('customer_groups')->onDelete('cascade');
$table->integer('catalog_rule_id')->unsigned();
$table->foreign('catalog_rule_id')->references('id')->on('catalog_rules')->onDelete('cascade');
$table->integer('channel_id')->unsigned();
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
});
}
@ -37,6 +41,6 @@ class CreateCatalogRuleProductsPriceTable extends Migration
*/
public function down()
{
Schema::dropIfExists('catalog_rule_products_price');
Schema::dropIfExists('catalog_rule_product_prices');
}
}

View File

@ -0,0 +1,137 @@
<?php
namespace Webkul\CatalogRule\Helpers;
use Carbon\Carbon;
use Webkul\CatalogRule\Repositories\CatalogRuleRepository;
class CatalogRuleIndex
{
/**
* CatalogRuleRepository object
*
* @var Object
*/
protected $catalogRuleRepository;
/**
* CatalogRuleProduct object
*
* @var Object
*/
protected $catalogRuleHelper;
/**
* CatalogRuleProductPrice object
*
* @var Object
*/
protected $catalogRuleProductPriceHelper;
/**
* Create a new helper instance.
*
* @param Webkul\CatalogRule\Repositories\CatalogRuleRepository $catalogRuleRepository
* @param Webkul\CatalogRuleProduct\Helpers\CatalogRuleProduct $catalogRuleProductHelper
* @param Webkul\CatalogRuleProduct\Helpers\CatalogRuleProductPrice $catalogRuleProductPriceHelper
* @return void
*/
public function __construct(
CatalogRuleRepository $catalogRuleRepository,
CatalogRuleProduct $catalogRuleProductHelper,
CatalogRuleProductPrice $catalogRuleProductPriceHelper
)
{
$this->catalogRuleRepository = $catalogRuleRepository;
$this->catalogRuleProductHelper = $catalogRuleProductHelper;
$this->catalogRuleProductPriceHelper = $catalogRuleProductPriceHelper;
}
/**
* Full reindex
*
* @return void
*/
public function reindexComplete()
{
try {
$this->cleanIndexes();
foreach ($this->getCatalogRules() as $rule) {
$this->catalogRuleProductHelper->insertRuleProduct($rule);
}
$this->catalogRuleProductPriceHelper->indexRuleProductPrice(1000);
} catch (\Exception $e) {
}
}
/**
* Full reindex
*
* @param Product $product
* @return void
*/
public function reindexProduct($product)
{
try {
if (! $product->getTypeInstance()->priceRuleCanBeApplied())
return;
$productIds = $product->getTypeInstance()->isComposite()
? $product->getTypeInstance()->getChildrenIds()
: [$product->id];
$this->cleanIndexes($productIds);
foreach ($this->getCatalogRules() as $rule) {
$this->catalogRuleProductHelper->insertRuleProduct($rule, 1000, $product);
}
$this->catalogRuleProductPriceHelper->indexRuleProductPrice(1000, $product);
} catch (\Exception $e) {
}
}
/**
* Deletes catalog rule product and catalog rule product price indexes
*
* @param array $productIds
* @return void
*/
public function cleanIndexes($productIds = [])
{
$this->catalogRuleProductHelper->cleanProductIndex($productIds);
$this->catalogRuleProductPriceHelper->cleanProductPriceIndex($productIds);
}
/**
* Returns catalog rules
*
* @return Collection
*/
public function getCatalogRules()
{
static $catalogRules;
if ($catalogRules)
return $catalogRules;
$catalogRules = $this->catalogRuleRepository->scopeQuery(function($query) {
return $query->where(function ($query1) {
$query1->where('catalog_rules.starts_from', '<=', Carbon::now()->format('Y-m-d'))->orWhereNull('catalog_rules.starts_from');
})
->where(function ($query2) {
$query2->where('catalog_rules.ends_till', '>=', Carbon::now()->format('Y-m-d'))->orWhereNull('catalog_rules.ends_till');
})
->orderBy('sort_order', 'asc');
})->findWhere(['status' => 1]);
return $catalogRules;
}
}

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