Merge branch 'bagisto/master' into add-channel-and-locale-filter-to-product-grid

This commit is contained in:
Florian Bosdorff 2020-01-30 16:04:57 +01:00
commit 389b40fdce
140 changed files with 12086 additions and 419 deletions

View File

@ -13,6 +13,7 @@ DB_PORT=3306
DB_DATABASE=bagisto_testing
DB_USERNAME=bagisto
DB_PASSWORD=secret
DB_PREFIX=
BROADCAST_DRIVER=log
CACHE_DRIVER=file

1
.gitignore vendored
View File

@ -7,6 +7,7 @@
/public/js
/public/vendor
/public/themes
/public/fonts
/vendor
/.idea
/.vscode

75
CHANGELOG for v1.x.x.md Normal file
View File

@ -0,0 +1,75 @@
# CHANGELOG for v1.x.x
#### This changelog consists the bug & security fixes and new features being included in the releases listed below.
## **v0.1.0(28th of January 2020)** - *Release*
* [feature] Updated to laravel version 6.
* [feature] Added four new product types - Group, Bundle, Downloadable and Virtaul.
* [feature] Povided default theme (Velocity).
* #1971 [fixed] - Filter is not working properly for id column in autogenerated coupon codes in cart rule.
* #1977 [fixed] - On editing the product, selected category for that product is not checked.
* #1978 [fixed] - Getting exception if changing the locale from cart page, if translation is not written for that product.
* #1979 [fixed] - Wrong calculation at customer as well as at admin end in due amount and grandtotal.
* #1983 [fixed] - Getting exception on deleting admin logo.
* #1986 [fixed] - Subscribe to newsletter does not work.
* #1997 [fixed] - Getting exception on adding attribute or creating product in bagisto on php version 7.4 .
* #1998 [fixed] - Showing product sale amount as zero when creating a product, and a existing catalog rule apply on it.
* #2001 [fixed] - php artisan route:list throws error.
* #2012 [fixed] - FGetting exception when clicking on view all under review section at product page.
* #2045 [fixed] - Login option is not coming while checkout with existing customer mail id.
* #2051 [fixed] - Forgot password not working due to recent changes in mail keys.
* #2054 [fixed] -Automatically 1st item of bundle is getting selected as a default after saving product.
* #2058 [fixed] - Not getting any validation message if entered admin credentials are wrong.
* #2066 [fixed] - Exception while writing product review.
* #2071 [fixed] - Customer is not getting forget password email.
* #2074 [fixed] - Getting exception while creating bundle type product.
* #2075 [fixed] - Getting exception if trying to select any parent category of root.
* #2087 [fixed] - Getting exception while adding configurable/bundle/grouped/Downloadable Type product to cart.
* #2088 [fixed] - Getting exception on customer login.
* #2089 [fixed] - Info missing on printing invoice at customer and admin end.
* #2114 [fixed] - getting exception while recovering admin password in case admin did not enter the details in env.
* #2118 [fixed] - Installation issue, getting exception on migrate command.
* #2119 [fixed] - confirm password is not matching even if admin is entering similar password in password and confirm password.
* #2120 [fixed] - Not able to add new user as while creating user password its giving error confirm password doesn't match.
* #2124 [fixed] - Able to make all product as default while creating bundle product in select type option.
* #2128 [fixed] - Click on add attribute, error is thrown.
* #2132 [fixed] - Price range slider not displaying.
* #2145 [fixed] - Emails don't work on registration.
* #2146 [fixed] - Getting exception on creating bundle product without any option.
* #2147 [fixed] - Sort order of bundle product doesn't work..
* #2168 [fixed] - locale direction drop down always select ltr.

View File

@ -61,6 +61,7 @@ class GenerateProducts extends Command
try {
$result = $this->generateProduct->create();
} catch (\Exception $e) {
report($e);
continue;
}

1
bin/codecept Symbolic link
View File

@ -0,0 +1 @@
../vendor/bin/codecept

49
bin/test.sh Executable file
View File

@ -0,0 +1,49 @@
#!/bin/bash
printf "### start preparation ###\n"
WORKPATH=$(dirname ${0})
printf ">> workpath is %s\n" ${WORKPATH}
LOG_DIR="${WORKPATH}/../storage/logs/tests"
printf ">> log-dir is %s\n" ${LOG_DIR}
printf ">> create and truncate log dir\n"
mkdir -p ${LOG_DIR}
rm -rf ${LOG_DIR}/*
printf ">> truncate and migrate database\n"
php artisan migrate:fresh --env=testing --quiet
printf "### finish preparation ###\n"
printf "### start tests ###\n"
SUCCESS=1
execSuite() {
${WORKPATH}/../vendor/bin/codecept run ${1} \
--xml report_${1}.xml ${CODECEPT_OPTIONS} | tee ${LOG_DIR}/tests_${1}.log
if [[ ${PIPESTATUS[0]} -ne 0 ]]
then
SUCCESS=0
fi
}
execSuite unit
execSuite functional
if [[ ${?} -ne 0 ]]
then
SUCCESS=0
fi
printf "### finish tests ###\n"
if [[ ${SUCCESS} -eq 1 ]]
then
printf ">> all tests are \e[01;32mgreen\e[0m\n"
exit 0
else
printf ">> at least one test is \e[01;31mred\e[0m\n"
exit 1
fi

View File

@ -21,7 +21,7 @@
"resolve-url-loader": "^3.1.0",
"sass": "^1.24.5",
"sass-loader": "^8.0.2",
"vue": "^2.5.7",
"vue": "^2.6.11",
"vue-template-compiler": "^2.6.11"
},
"dependencies": {

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.company_name', '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) {
@ -65,6 +65,7 @@ class AddressDataGrid extends DataGrid
->addSelect(DB::raw(DB::getTablePrefix() . 'country_states.default_name as state_name'));
$this->addFilter('address_id', 'ca.id');
$this->addFilter('company_name', 'ca.company_name');
$this->addFilter('address1', 'ca.address1');
$this->addFilter('city', 'ca.city');
$this->addFilter('state_name', DB::raw(DB::getTablePrefix() . 'country_states.default_name'));
@ -77,6 +78,7 @@ class AddressDataGrid extends DataGrid
public function addColumns()
{
$this->addColumn([
'index' => 'address_id',
'label' => trans('admin::app.customers.addresses.address-id'),

View File

@ -135,7 +135,8 @@ class CustomerDataGrid extends DataGrid
'type' => 'Edit',
'method' => 'GET', //use post only for redirects only
'route' => 'admin.customer.addresses.index',
'icon' => 'icon list-icon'
'icon' => 'icon list-icon',
'title' => trans('admin::app.customers.customers.addresses')
]);
$this->addAction([

View File

@ -3,7 +3,7 @@
namespace Webkul\Admin\DataGrids;
use Webkul\Ui\DataGrid\DataGrid;
use DB;
use Illuminate\Support\Facades\DB;
/**
* ProductDataGrid Class
@ -37,7 +37,16 @@ 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',
'products.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')
);
if ($this->locale !== 'all') {
$queryBuilder->where('locale', $this->locale);
@ -51,7 +60,7 @@ class ProductDataGrid extends DataGrid
$this->addFilter('product_id', 'product_flat.product_id');
$this->addFilter('product_name', 'product_flat.name');
$this->addFilter('product_sku', 'product_flat.sku');
$this->addFilter('product_sku', 'products.sku');
$this->addFilter('status', 'product_flat.status');
$this->addFilter('product_type', 'products.type');
$this->addFilter('attribute_family', 'attribute_families.name');

View File

@ -2,6 +2,7 @@
namespace Webkul\Admin\Http\Controllers\Customer;
use Webkul\Customer\Rules\VatIdRule;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Customer\Repositories\CustomerRepository as Customer;
use Webkul\Customer\Repositories\CustomerAddressRepository as CustomerAddress;
@ -20,25 +21,26 @@ class AddressController extends Controller
* @var array
*/
protected $_config;
/**
* Customer Repository object
*
* @var object
*/
*/
protected $customer;
/**
* CustomerAddress Repository object
*
* @var object
*/
*/
protected $customerAddress;
/**
* Create a new controller instance.
*
* @param Webkul\Customer\Repositories\CustomerAddressRepository $customerAddress
* @param Webkul\Customer\Repositories\CustomerAddressRepository $customerAddress
*
* @return void
*/
public function __construct(
@ -59,7 +61,7 @@ class AddressController extends Controller
* @return Mixed
*/
public function index($id)
{
{
$customer = $this->customer->find($id);
return view($this->_config['view'], compact('customer'));
@ -84,20 +86,24 @@ class AddressController extends Controller
*/
public function store()
{
request()->merge(['address1' => implode(PHP_EOL, array_filter(request()->input('address1')))]);
request()->merge([
'address1' => implode(PHP_EOL, array_filter(request()->input('address1'))),
]);
$data = collect(request()->input())->except('_token')->toArray();
$this->validate(request(), [
'address1' => 'string|required',
'country' => 'string|required',
'state' => 'string|required',
'city' => 'string|required',
'postcode' => 'required',
'phone' => 'required'
'company_name' => 'string',
'address1' => 'string|required',
'country' => 'string|required',
'state' => 'string|required',
'city' => 'string|required',
'postcode' => 'required',
'phone' => 'required',
'vat_id' => new VatIdRule(),
]);
if ( $this->customerAddress->create($data) ) {
if ($this->customerAddress->create($data)) {
session()->flash('success', trans('admin::app.customers.addresses.success-create'));
return redirect()->route('admin.customer.addresses.index', ['id' => $data['customer_id']]);
@ -131,19 +137,21 @@ class AddressController extends Controller
request()->merge(['address1' => implode(PHP_EOL, array_filter(request()->input('address1')))]);
$this->validate(request(), [
'address1' => 'string|required',
'country' => 'string|required',
'state' => 'string|required',
'city' => 'string|required',
'postcode' => 'required',
'phone' => 'required'
'company_name' => 'string',
'address1' => 'string|required',
'country' => 'string|required',
'state' => 'string|required',
'city' => 'string|required',
'postcode' => 'required',
'phone' => 'required',
'vat_id' => new VatIdRule(),
]);
$data = collect(request()->input())->except('_token')->toArray();
$address = $this->customerAddress->find($id);
if ( $address ) {
if ($address) {
$this->customerAddress->update($data, $id);
@ -157,7 +165,8 @@ class AddressController extends Controller
/**
* Remove the specified resource from storage.
*
* @param int $id
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function destroy($id)

View File

@ -31,14 +31,14 @@ class CustomerController extends Controller
*/
protected $customerRepository;
/**
/**
* CustomerGroupRepository object
*
* @var array
*/
protected $customerGroupRepository;
/**
/**
* ChannelRepository object
*
* @var array
@ -74,13 +74,13 @@ class CustomerController extends Controller
* Display a listing of the resource.
*
* @return \Illuminate\View\View
*/
*/
public function index()
{
return view($this->_config['view']);
}
/**
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\View\View
@ -91,10 +91,10 @@ class CustomerController extends Controller
$channelName = $this->channelRepository->all();
return view($this->_config['view'], compact('customerGroup','channelName'));
return view($this->_config['view'], compact('customerGroup', 'channelName'));
}
/**
/**
* Store a newly created resource in storage.
*
* @return \Illuminate\Http\Response
@ -102,16 +102,16 @@ class CustomerController extends Controller
public function store()
{
$this->validate(request(), [
'first_name' => 'string|required',
'last_name' => 'string|required',
'gender' => 'required',
'email' => 'required|unique:customers,email',
'date_of_birth' => 'date|before:today'
'first_name' => 'string|required',
'last_name' => 'string|required',
'gender' => 'required',
'email' => 'required|unique:customers,email',
'date_of_birth' => 'date|before:today',
]);
$data = request()->all();
$password = rand(100000,10000000);
$password = rand(100000, 10000000);
$data['password'] = bcrypt($password);
@ -122,7 +122,7 @@ class CustomerController extends Controller
try {
Mail::queue(new NewCustomerNotification($customer, $password));
} catch (\Exception $e) {
report($e);
}
session()->flash('success', trans('admin::app.response.create-success', ['name' => 'Customer']));
@ -133,7 +133,8 @@ class CustomerController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param int $id
* @param int $id
*
* @return \Illuminate\View\View
*/
public function edit($id)
@ -147,20 +148,21 @@ class CustomerController extends Controller
return view($this->_config['view'], compact('customer', 'customerGroup', 'channelName'));
}
/**
/**
* Update the specified resource in storage.
*
* @param int $id
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function update($id)
{
$this->validate(request(), [
'first_name' => 'string|required',
'last_name' => 'string|required',
'gender' => 'required',
'email' => 'required|unique:customers,email,'. $id,
'date_of_birth' => 'date|before:today'
'first_name' => 'string|required',
'last_name' => 'string|required',
'gender' => 'required',
'email' => 'required|unique:customers,email,' . $id,
'date_of_birth' => 'date|before:today',
]);
$this->customerRepository->update(request()->all(), $id);
@ -173,7 +175,8 @@ class CustomerController extends Controller
/**
* Remove the specified resource from storage.
*
* @param int $id
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function destroy($id)
@ -186,7 +189,7 @@ class CustomerController extends Controller
session()->flash('success', trans('admin::app.response.delete-success', ['name' => 'Customer']));
return response()->json(['message' => true], 200);
} catch(\Exception $e) {
} catch (\Exception $e) {
session()->flash('error', trans('admin::app.response.delete-failed', ['name' => 'Customer']));
}
@ -213,13 +216,13 @@ class CustomerController extends Controller
public function storeNote()
{
$this->validate(request(), [
'notes' => 'string|nullable'
'notes' => 'string|nullable',
]);
$customer = $this->customerRepository->find(request()->input('_customer'));
$noteTaken = $customer->update([
'notes' => request()->input('notes')
'notes' => request()->input('notes'),
]);
if ($noteTaken) {
@ -245,7 +248,7 @@ class CustomerController extends Controller
$customer = $this->customerRepository->find($customerId);
$customer->update([
'status' => $updateOption
'status' => $updateOption,
]);
}
@ -265,7 +268,7 @@ class CustomerController extends Controller
foreach ($customerIds as $customerId) {
$this->customerRepository->deleteWhere([
'id' => $customerId
'id' => $customerId,
]);
}

View File

@ -10,13 +10,15 @@ use Webkul\Admin\Mail\NewShipmentNotification;
use Webkul\Admin\Mail\NewInventorySourceNotification;
use Webkul\Admin\Mail\CancelOrderNotification;
use Webkul\Admin\Mail\NewRefundNotification;
/**
* Order event handler
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class Order {
class Order
{
/**
* @param mixed $order
@ -30,7 +32,7 @@ class Order {
Mail::queue(new NewAdminNotification($order));
} catch (\Exception $e) {
report($e);
}
}
@ -42,12 +44,13 @@ class Order {
public function sendNewInvoiceMail($invoice)
{
try {
if ($invoice->email_sent)
if ($invoice->email_sent) {
return;
}
Mail::queue(new NewInvoiceNotification($invoice));
} catch (\Exception $e) {
report($e);
}
}
@ -61,7 +64,7 @@ class Order {
try {
Mail::queue(new NewRefundNotification($refund));
} catch (\Exception $e) {
report($e);
}
}
@ -73,25 +76,28 @@ class Order {
public function sendNewShipmentMail($shipment)
{
try {
if ($shipment->email_sent)
if ($shipment->email_sent) {
return;
}
Mail::queue(new NewShipmentNotification($shipment));
Mail::queue(new NewInventorySourceNotification($shipment));
} catch (\Exception $e) {
report($e);
}
}
/*
/**
* @param mixed $order
* */
public function sendCancelOrderMail($order){
try{
*
*/
public function sendCancelOrderMail($order)
{
try {
Mail::queue(new CancelOrderNotification($order));
}catch (\Exception $e){
\Log::error('Error occured when sending email '.$e->getMessage());
} catch (\Exception $e) {
report($e);
}
}
}

View File

@ -760,10 +760,19 @@ return [
'state' => 'State',
'select-state' => 'اختر منطقة أو ولاية أو مقاطعة.',
'country' => 'Country',
'other' => 'Other',
'male' => 'Male',
'female' => 'Female',
'phone' => 'Phone',
'group-default' => 'لا يستطيع حذف افتراضي المجموعة.',
'edit-help-title' => 'Edit Customer',
'delete-help-title' => 'Delete Customer',
'addresses' => 'Addresses',
'mass-destroy-success' => 'Customers deleted successfully',
'mass-update-success' => 'Customers updated successfully',
'status' => 'Status',
'active' => 'Active',
'in-active' => 'Inactive'
],
'reviews' => [
'title' => 'باء-الاستعراضات',

View File

@ -334,7 +334,7 @@ return [
'item-status' => 'Item Status',
'item-ordered' => 'Ordered (:qty_ordered)',
'item-invoice' => 'Invoiced (:qty_invoiced)',
'item-shipped' => 'shipped (:qty_shipped)',
'item-shipped' => 'Shipped (:qty_shipped)',
'item-canceled' => 'Canceled (:qty_canceled)',
'item-refunded' => 'Refunded (:qty_refunded)',
'price' => 'Price',
@ -868,6 +868,7 @@ return [
'addresses' => [
'title' => ':customer_name\'s Addresses List',
'vat_id' => 'Vat id',
'create-title' => 'Create Customer\'s Address',
'edit-title' => 'Update Customer\'s Address',
'title-orders' => ':customer_name\'s Orders List',
@ -918,12 +919,14 @@ return [
'state' => 'State',
'select-state' => 'Select a region, state or province.',
'country' => 'Country',
'other' => 'Other',
'male' => 'Male',
'female' => 'Female',
'phone' => 'Phone',
'group-default' => 'Cannot delete the default group.',
'edit-help-title' => 'Edit Customer',
'delete-help-title' => 'Delete Customer',
'addresses' => 'Addresses',
'mass-destroy-success' => 'Customers deleted successfully',
'mass-update-success' => 'Customers updated successfully',
'status' => 'Status',

View File

@ -802,11 +802,13 @@ return [
'state' => 'استان',
'select-state' => 'یک منطقه ، ایالت یا استان را انتخاب کنید.',
'country' => 'کشور',
'other' => 'Other',
'male' => 'مرد',
'female' => 'زن',
'group-default' => 'نمی توان گروه پیش فرض را حذف کرد.',
'edit-help-title' => 'ویرایش مشتری',
'delete-help-title' => 'حذف مشتری',
'addresses' => 'Addresses',
'mass-destroy-success' => 'مشتریان با موفقیت حذف شدند',
'mass-update-success' => 'مشتریان با موفقیت به روز شدند',
'status' => 'وضعیت',

View File

@ -791,12 +791,14 @@ return [
'state' => 'Estado',
'select-state' => 'Selecione uma região, estado ou província.',
'country' => 'País',
'other' => 'Other',
'male' => 'Masculino',
'female' => 'Feminino',
'phone' => 'Telefone',
'group-default' => 'Não possível excluir o grupo de cliente.',
'edit-help-title' => 'Editar cliente',
'delete-help-title' => 'Excluir cliente',
'addresses' => 'Addresses',
'mass-destroy-success' => 'Clientes excluídos com sucesso',
'mass-update-success' => 'Clientes atualizados com sucesso',
'status' => 'Status',

View File

@ -39,7 +39,7 @@
<div class="control-group" :class="[errors.has('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="name" value="{{ old('name') }}" data-vv-as="&quot;{{ __('admin::app.catalog.categories.name') }}&quot;"/>
<input type="text" v-validate="'required'" class="control" id="name" name="name" value="{{ old('name') }}" data-vv-as="&quot;{{ __('admin::app.catalog.categories.name') }}&quot;" v-slugify-target="'slug'"/>
<span class="control-error" v-if="errors.has('name')">@{{ errors.first('name') }}</span>
</div>
@ -96,7 +96,7 @@
<description></description>
<div class="control-group {!! $errors->has('image.*') ? 'has-error' : '' !!}">
<label>{{ __('admin::app.catalog.categories.image') }}
<label>{{ __('admin::app.catalog.categories.image') }}</label>
<image-wrapper :button-label="'{{ __('admin::app.catalog.products.add-image-btn-title') }}'" input-name="image" :multiple="false"></image-wrapper>

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;" v-slugify-target="'slug'"/>
<span class="control-error" v-if="errors.has('{{$locale}}[name]')">@{{ errors.first('{!!$locale!!}[name]') }}</span>
</div>
@ -109,7 +109,7 @@
<description></description>
<div class="control-group {!! $errors->has('image.*') ? 'has-error' : '' !!}">
<label>{{ __('admin::app.catalog.categories.image') }}
<label>{{ __('admin::app.catalog.categories.image') }}</label>
<image-wrapper :button-label="'{{ __('admin::app.catalog.products.add-image-btn-title') }}'" input-name="image" :multiple="false" :images='"{{ $category->image_url }}"'></image-wrapper>

View File

@ -304,6 +304,7 @@
this.bundle_option_products.push({
product: item,
qty: 0,
is_default: 0,
sort_order: 0
});
}
@ -348,9 +349,9 @@
this.bundle_option_products.forEach(function(product) {
if (this_this.bundleOption.type == 'radio' || this_this.bundleOption.type == 'select') {
product.is_default = product.id == productId ? 1 : 0;
product.is_default = product.product.id == productId ? 1 : 0;
} else {
if (product.id == productId)
if (product.product.id == productId)
product.is_default = product.is_default ? 0 : 1;
}
});
@ -381,7 +382,7 @@
},
checkProduct: function($event) {
this.$emit('onCheckProduct', this.product.id)
this.$emit('onCheckProduct', this.product.product.id)
}
}
});

View File

@ -1 +1,4 @@
<input type="text" v-validate="'{{$validations}}'" class="control" id="{{ $attribute->code }}" name="{{ $attribute->code }}" value="{{ old($attribute->code) ?: $product[$attribute->code] }}" data-vv-as="&quot;{{ $attribute->admin_name }}&quot;" {{ in_array($attribute->code, ['sku', 'url_key']) ? 'v-slugify' : '' }}/>
<input type="text" v-validate="'{{$validations}}'" class="control" id="{{ $attribute->code }}" name="{{ $attribute->code }}" value="{{ old($attribute->code) ?: $product[$attribute->code] }}" {{ in_array($attribute->code, ['sku', 'url_key']) ? 'v-slugify' : '' }} data-vv-as="&quot;{{ $attribute->admin_name }}&quot;" {{ $attribute->code == 'name' ? 'v-slugify-target=\'url_key\'' : '' }} />

View File

@ -301,6 +301,8 @@
@push('scripts')
<?php if ($field['type'] == 'country'): ?>
<script type="text/x-template" id="country-template">
<div>
@ -404,6 +406,8 @@
});
</script>
<?php endif; ?>
<script type="text/x-template" id="depends-template">
<div class="control-group" :class="[errors.has(name) ? 'has-error' : '']" v-if="this.isVisible">

View File

@ -36,6 +36,18 @@
<accordian :title="'{{ __('admin::app.customers.addresses.general') }}'" :active="true">
<div slot="body">
<div class="control-group" :class="[errors.has('company_name') ? 'has-error' : '']">
<label for="company_name">{{ __('shop::app.customer.account.address.create.company_name') }}</label>
<input type="text" class="control" name="company_name" value="{{ old('company_name') }}" data-vv-as="&quot;{{ __('shop::app.customer.account.address.create.company_name') }}&quot;">
<span class="control-error" v-if="errors.has('company_name')">@{{ errors.first('company_name') }}</span>
</div>
<div class="control-group" :class="[errors.has('vat_id') ? 'has-error' : '']">
<label for="vat_id">{{ __('shop::app.customer.account.address.create.vat_id') }}</label>
<input type="text" class="control" name="vat_id" value="{{ old('vat_id') }}" data-vv-as="&quot;{{ __('shop::app.customer.account.address.create.vat_id') }}&quot;">
<span class="control-error" v-if="errors.has('vat_id')">@{{ errors.first('vat_id') }}</span>
</div>
<div class="control-group" :class="[errors.has('address1[]') ? 'has-error' : '']">
<label for="address_0" class="required">{{ __('shop::app.customer.account.address.edit.street-address') }}</label>
<input type="text" class="control" name="address1[]" id="address_0" v-validate="'required'" value="{{ old('address1') }}" data-vv-as="&quot;{{ __('shop::app.customer.account.address.create.street-address') }}&quot;">
@ -56,7 +68,7 @@
<span class="control-error" v-if="errors.has('city')">@{{ errors.first('city') }}</span>
</div>
@include ('shop::customers.account.address.country-state', ['countryCode' => old('country') ?? '', 'stateCode' => old('state') ?? ''])
@include ('admin::customers.country-state', ['countryCode' => old('country') ?? config('app.default_country'), 'stateCode' => old('state') ?? ''])
<div class="control-group" :class="[errors.has('postcode') ? 'has-error' : '']">
<label for="postcode" class="required">{{ __('shop::app.customer.account.address.create.postcode') }}</label>

View File

@ -15,17 +15,17 @@
<div class="page-title">
<h1>{{ __('admin::app.customers.addresses.edit-title') }}</h1>
</div>
<div class="page-action">
<button type="submit" class="btn btn-primary btn-lg">
{{ __('admin::app.customers.addresses.save-btn-title') }}
</button>
</div>
</div>
<div class="page-content">
@csrf()
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="customer_id" value="{{ $address->customer_id }}">
@ -35,6 +35,18 @@
<?php $addresses = explode(PHP_EOL, $address->address1); ?>
<div class="control-group" :class="[errors.has('company_name') ? 'has-error' : '']">
<label for="company_name">{{ __('shop::app.customer.account.address.create.company_name') }}</label>
<input type="text" class="control" name="company_name" value="{{ $address->company_name }}" data-vv-as="&quot;{{ __('shop::app.customer.account.address.create.company_name') }}&quot;">
<span class="control-error" v-if="errors.has('company_name')">@{{ errors.first('company_name') }}</span>
</div>
<div class="control-group" :class="[errors.has('vat_id') ? 'has-error' : '']">
<label for="vat_id">{{ __('shop::app.customer.account.address.create.vat_id') }}</label>
<input type="text" class="control" name="vat_id" value="{{ $address->vat_id }}" data-vv-as="&quot;{{ __('shop::app.customer.account.address.create.vat_id') }}&quot;">
<span class="control-error" v-if="errors.has('vat_id')">@{{ errors.first('vat_id') }}</span>
</div>
<div class="control-group" :class="[errors.has('address1[]') ? 'has-error' : '']">
<label for="address_0" class="required">{{ __('shop::app.customer.account.address.edit.street-address') }}</label>
<input type="text" class="control" name="address1[]" id="address_0" v-validate="'required'" value="{{ isset($addresses[0]) ? $addresses[0] : '' }}" data-vv-as="&quot;{{ __('shop::app.customer.account.address.create.street-address') }}&quot;">
@ -51,11 +63,11 @@
<div class="control-group" :class="[errors.has('city') ? 'has-error' : '']">
<label for="city" class="required">{{ __('shop::app.customer.account.address.create.city') }}</label>
<input type="text" class="control" name="city" v-validate="'required|alpha_spaces'" value="{{ $address->city }}" data-vv-as="&quot;{{ __('shop::app.customer.account.address.create.city') }}&quot;">
<input type="text" class="control" name="city" v-validate="'required'" value="{{ $address->city }}" data-vv-as="&quot;{{ __('shop::app.customer.account.address.create.city') }}&quot;">
<span class="control-error" v-if="errors.has('city')">@{{ errors.first('city') }}</span>
</div>
@include ('shop::customers.account.address.country-state', ['countryCode' => old('country') ?? $address->country, 'stateCode' => old('state') ?? $address->state])
@include ('admin::customers.country-state', ['countryCode' => old('country') ?? $address->country, 'stateCode' => old('state') ?? $address->state])
<div class="control-group" :class="[errors.has('postcode') ? 'has-error' : '']">
<label for="postcode" class="required">{{ __('shop::app.customer.account.address.create.postcode') }}</label>

View File

@ -52,6 +52,7 @@
<div class="control-group" :class="[errors.has('gender') ? 'has-error' : '']">
<label for="gender" class="required">{{ __('admin::app.customers.customers.gender') }}</label>
<select name="gender" class="control" v-validate="'required'" data-vv-as="&quot;{{ __('shop::app.customers.customers.gender') }}&quot;">
<option value="Other">{{ __('admin::app.customers.customers.other') }}</option>
<option value="Male">{{ __('admin::app.customers.customers.male') }}</option>
<option value="Female">{{ __('admin::app.customers.customers.female') }}</option>
</select>

View File

@ -58,6 +58,7 @@
<div class="control-group">
<label for="gender" class="required">{{ __('admin::app.customers.customers.gender') }}</label>
<select name="gender" class="control" value="{{ $customer->gender }}" v-validate="'required'" data-vv-as="&quot;{{ __('shop::app.customers.customers.gender') }}&quot;">
<option value="Other" {{ $customer->gender == "Other" ? 'selected' : '' }}>{{ __('admin::app.customers.customers.other') }}</option>
<option value="Male" {{ $customer->gender == "Male" ? 'selected' : '' }}>{{ __('admin::app.customers.customers.male') }}</option>
<option value="Female" {{ $customer->gender == "Female" ? 'selected' : '' }}>{{ __('admin::app.customers.customers.female') }}</option>
</select>

View File

@ -2,6 +2,9 @@
<thead>
<tr>
@foreach ($columns as $key => $value)
<?php
$value == 'increment_id' ? $value : 'order_id';
?>
<th>{{ $value }}</th>
@endforeach
</tr>

View File

@ -160,13 +160,13 @@
</div>
<div class="control-group">
<label>{{ __('admin::app.settings.channels.logo') }}
<label>{{ __('admin::app.settings.channels.logo') }}</label>
<image-wrapper :button-label="'{{ __('admin::app.catalog.products.add-image-btn-title') }}'" input-name="logo" :multiple="false"></image-wrapper>
</div>
<div class="control-group">
<label>{{ __('admin::app.settings.channels.favicon') }}
<label>{{ __('admin::app.settings.channels.favicon') }}</label>
<image-wrapper :button-label="'{{ __('admin::app.catalog.products.add-image-btn-title') }}'" input-name="logo" :multiple="false"></image-wrapper>
</div>

View File

@ -171,13 +171,13 @@
</div>
<div class="control-group">
<label>{{ __('admin::app.settings.channels.logo') }}
<label>{{ __('admin::app.settings.channels.logo') }}</label>
<image-wrapper :button-label="'{{ __('admin::app.catalog.products.add-image-btn-title') }}'" input-name="logo" :multiple="false" :images='"{{ $channel->logo_url }}"'></image-wrapper>
</div>
<div class="control-group">
<label>{{ __('admin::app.settings.channels.favicon') }}
<label>{{ __('admin::app.settings.channels.favicon') }}</label>
<image-wrapper :button-label="'{{ __('admin::app.catalog.products.add-image-btn-title') }}'" input-name="favicon" :multiple="false" :images='"{{ $channel->favicon_url }}"'></image-wrapper>
</div>

View File

@ -47,8 +47,8 @@
<div class="control-group" :class="[errors.has('direction') ? 'has-error' : '']">
<label for="direction" class="required">{{ __('admin::app.settings.locales.direction') }}</label>
<select v-validate="'required'" class="control" id="direction" name="direction" data-vv-as="&quot;{{ __('admin::app.settings.locales.direction') }}&quot;">
<option value="ltr" selected title="Text direction left to right">ltr</option>
<option value="rtl" title="Text direction right to left">rtl</option>
<option value="ltr" selected title="Text direction left to right">LTR</option>
<option value="rtl" title="Text direction right to left">RTL</option>
</select>
<span class="control-error" v-if="errors.has('direction')">@{{ errors.first('direction') }}</span>
</div>

View File

@ -51,8 +51,8 @@
<div class="control-group" :class="[errors.has('direction') ? 'has-error' : '']">
<label for="direction" class="required">{{ __('admin::app.settings.locales.direction') }}</label>
<select v-validate="'required'" class="control" id="direction" name="direction" data-vv-as="&quot;{{ __('admin::app.settings.locales.direction') }}&quot;">
<option value="ltr" {{ old('direction') == 'ltr' ? 'selected' : '' }} title="Text direction left to right">ltr</option>
<option value="rtl" {{ old('direction') == 'rtl' ? 'selected' : '' }} title="Text direction right to left">rtl</option>
<option value="ltr" {{ (old('direction') ?: $locale->direction) == 'ltr' ? 'selected' : '' }}>LTR</option>
<option value="rtl" {{ (old('direction') ?: $locale->direction) == 'rtl' ? 'selected' : '' }}>RTL</option>
</select>
<span class="control-error" v-if="errors.has('direction')">@{{ errors.first('direction') }}</span>
</div>

View File

@ -0,0 +1,15 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Attribute\Models\AttributeFamily;
$factory->define(AttributeFamily::class, function (Faker $faker) {
return [
'name' => $faker->word(),
'code' => $faker->word(),
'is_user_defined' => rand(0, 1),
'status' => 0,
];
});

View File

@ -167,6 +167,7 @@ class AttributeController extends Controller
$this->attributeRepository->delete($value);
}
} catch (\Exception $e) {
report($e);
$suppressFlash = true;
continue;

View File

@ -154,7 +154,8 @@ class AttributeFamilyController extends Controller
return response()->json(['message' => true], 200);
} catch (\Exception $e) {
session()->flash('error', trans( 'admin::app.response.delete-failed', ['name' => 'Family']));
report($e);
session()->flash('error', trans('admin::app.response.delete-failed', ['name' => 'Family']));
}
}
@ -177,16 +178,18 @@ class AttributeFamilyController extends Controller
try {
$this->attributeFamilyRepository->delete($value);
} catch (\Exception $e) {
report($e);
$suppressFlash = true;
continue;
}
}
if (! $suppressFlash)
if (!$suppressFlash) {
session()->flash('success', ('admin::app.datagrid.mass-ops.delete-success'));
else
} else {
session()->flash('info', trans('admin::app.datagrid.mass-ops.partial-action', ['resource' => 'Attribute Family']));
}
return redirect()->back();
} else {

View File

@ -144,7 +144,7 @@ class AttributeRepository extends Repository
$data['value_per_channel'] = $data['value_per_locale'] = 0;
}
if (! in_array($data['type'], ['select', 'multiselect', 'price'])) {
if (! in_array($data['type'], ['select', 'multiselect', 'price', 'checkbox'])) {
$data['is_filterable'] = 0;
}

View File

@ -65,7 +65,7 @@ class CatalogRuleIndex
$this->catalogRuleProductPriceHelper->indexRuleProductPrice(1000);
} catch (\Exception $e) {
report($e);
}
}
@ -80,7 +80,7 @@ class CatalogRuleIndex
try {
if (! $product->getTypeInstance()->priceRuleCanBeApplied())
return;
$productIds = $product->getTypeInstance()->isComposite()
? $product->getTypeInstance()->getChildrenIds()
: [$product->id];
@ -93,7 +93,7 @@ class CatalogRuleIndex
$this->catalogRuleProductPriceHelper->indexRuleProductPrice(1000, $product);
} catch (\Exception $e) {
report($e);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Core\Models\SubscribersList;
$factory->define(SubscribersList::class, function (Faker $faker) {
return [
'email' => $faker->safeEmail,
'is_subscribed' => $faker->boolean,
'channel_id' => 1,
];
});

View File

@ -0,0 +1,52 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddApiTokenColumns extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// @see https://laravel.com/docs/6.x/api-authentication#database-preparation
Schema::table('customers', function ($table) {
$table
->string('api_token', 80)
->after('password')
->unique()
->nullable()
->default(null);
});
Schema::table('admins', function ($table) {
$table
->string('api_token', 80)
->after('password')
->unique()
->nullable()
->default(null);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('customers', function (Blueprint $table) {
$table->dropColumn('api_token');
});
Schema::table('admins', function (Blueprint $table) {
$table->dropColumn('api_token');
});
}
}

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AlterNameColumnInCountryStateTranslationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('country_state_translations', function (Blueprint $table) {
$table->renameColumn('name', 'default_name');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('country_state_translations', function (Blueprint $table) {
$table->renameColumn('default_name', 'name');
});
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Webkul\Core\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Arr;
use DB;
class CountryStateTranslationSeeder extends Seeder
{
public function run()
{
foreach (['ar', 'fa', 'pt_BR'] as $code) {
DB::table('country_translations')->where('locale', $code)->delete();
DB::table('country_state_translations')->where('locale', $code)->delete();
$countryTranslations = json_decode(file_get_contents(__DIR__ . '/../../Data/country_state_translation/countries_' . $code . '.json'), true);
data_fill($countryTranslations, '*.locale', $code);
$stateTranslations = json_decode(file_get_contents(__DIR__ . '/../../Data/country_state_translation/states_' . $code . '.json'), true);
data_fill($stateTranslations, '*.locale', $code);
DB::table('country_translations')->insert($countryTranslations);
DB::table('country_state_translations')->insert($stateTranslations);
}
}
}

View File

@ -17,6 +17,7 @@ class DatabaseSeeder extends Seeder
$this->call(CurrencyTableSeeder::class);
$this->call(CountriesTableSeeder::class);
$this->call(StatesTableSeeder::class);
$this->call(CountryStateTranslationSeeder::class);
$this->call(ChannelTableSeeder::class);
$this->call(ConfigTableSeeder::class);
}

View File

@ -144,6 +144,7 @@ class CurrencyController extends Controller
return response()->json(['message' => true], 200);
} catch (\Exception $e) {
report($e);
session()->flash('error', trans('admin::app.response.delete-failed', ['name' => 'Currency']));
}
}

View File

@ -200,6 +200,7 @@ class ExchangeRateController extends Controller
return response()->json(['message' => true], 200);
} catch (\Exception $e) {
report($e);
session()->flash('error', trans('admin::app.response.delete-error', ['name' => 'Exchange rate']));
}
}

View File

@ -104,6 +104,7 @@ class SubscriptionController extends Controller
return response()->json(['message' => true], 200);
} catch (\Exception $e) {
report($e);
session()->flash('error', trans('admin::app.response.delete-failed', ['name' => 'Subscriber']));
}

View File

@ -5,12 +5,23 @@ namespace Webkul\Core\Models;
use Webkul\Core\Eloquent\TranslatableModel;
use Webkul\Core\Contracts\CountryState as CountryStateContract;
class CountryState extends TranslatableModel implements CountryStateContract
{
public $timestamps = false;
public $translatedAttributes = ['name'];
public $translatedAttributes = ['default_name'];
protected $with = ['translations'];
/**
* @return array
*/
public function toArray()
{
$array = parent::toArray();
$array['default_name'] = $this->default_name;
return $array;
}
}

View File

@ -9,5 +9,5 @@ class CountryStateTranslation extends Model implements CountryStateTranslationCo
{
public $timestamps = false;
protected $fillable = ['name'];
protected $fillable = ['default_name'];
}

View File

@ -24,6 +24,8 @@ class CoreServiceProvider extends ServiceProvider
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
$this->registerEloquentFactoriesFrom(__DIR__ . '/../Database/Factories');
$this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'core');
Validator::extend('slug', 'Webkul\Core\Contracts\Validations\Slug@passes');
@ -37,8 +39,6 @@ class CoreServiceProvider extends ServiceProvider
]);
SliderProxy::observe(SliderObserver::class);
$this->registerEloquentFactoriesFrom(__DIR__ . '/../Database/Factories');
}
/**
@ -50,6 +50,7 @@ class CoreServiceProvider extends ServiceProvider
{
$this->registerFacades();
}
/**
* Register Bouncer as a singleton.
*
@ -68,11 +69,12 @@ class CoreServiceProvider extends ServiceProvider
/**
* Register factories.
*
* @param string $path
* @param string $path
*
* @return void
*/
protected function registerEloquentFactoriesFrom($path): void
{
$this->app->make(EloquentFactory::class)->load($path);
}
}
}

View File

@ -0,0 +1,36 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Customer\Models\Customer;
use Webkul\Customer\Models\CustomerAddress;
$factory->define(CustomerAddress::class, function (Faker $faker) {
$now = date("Y-m-d H:i:s");
// use an locale from a country in europe so the vat id can be generated
$fakerIt = \Faker\Factory::create('it_IT');
return [
'customer_id' => function () {
return factory(Customer::class)->create()->id;
},
'company_name' => $faker->company,
'vat_id' => $fakerIt->vatId(),
'first_name' => $faker->firstName,
'last_name' => $faker->lastName,
'address1' => $faker->streetAddress,
'country' => $faker->countryCode,
'state' => $faker->state,
'city' => $faker->city,
'postcode' => $faker->postcode,
'phone' => $faker->e164PhoneNumber,
'default_address' => array_random([0, 1]),
'created_at' => $now,
'updated_at' => $now,
];
});

View File

@ -0,0 +1,34 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Illuminate\Support\Facades\Hash;
use Webkul\Customer\Models\Customer;
$factory->define(Customer::class, function (Faker $faker) {
$now = date("Y-m-d H:i:s");
$gender = array_rand(['male', 'female', 'other']);
$password = $faker->password;
return [
'first_name' => $faker->firstName($gender),
'last_name' => $faker->lastName,
'gender' => ucfirst($gender),
'email' => $faker->email,
'status' => 1,
'password' => Hash::make($password),
'customer_group_id' => 2,
'is_verified' => 1,
'created_at' => $now,
'updated_at' => $now,
'notes' => json_encode(array('plain_password' => $password)),
];
});
$factory->state(Customer::class, 'male', [
'gender' => 'Male',
]);
$factory->state(Customer::class, 'female', [
'gender' => 'Female',
]);

View File

@ -0,0 +1,15 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Customer\Models\CustomerGroup;
$factory->define(CustomerGroup::class, function (Faker $faker) {
$name = ucfirst($faker->word);
return [
'name' => $name,
'is_user_defined' => $faker->boolean,
'code' => lcfirst($name),
];
});

View File

@ -0,0 +1,44 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CustomerAddressImprovements extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('customer_addresses', function (Blueprint $table) {
$table->string('company_name')->nullable()->after('customer_id');
$table->string('vat_id')->nullable()->after('company_name');
});
Schema::table('orders', function (Blueprint $table) {
$table->string('customer_company_name')->nullable()->after('customer_last_name');
$table->string('customer_vat_id')->nullable()->after('customer_company_name');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('customer_addresses', function (Blueprint $table) {
$table->dropColumn('company_name');
$table->dropColumn('vat_id');
});
Schema::table('orders', function (Blueprint $table) {
$table->dropColumn('customer_company_name');
$table->dropColumn('customer_vat_id');
});
}
}

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddNewColumnAndRenameNameColumnInCustomerAddressesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('customer_addresses', function (Blueprint $table) {
$table->renameColumn('name', 'first_name')->nullable();
});
Schema::table('customer_addresses', function (Blueprint $table) {
$table->string('last_name')->after('first_name')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('customer_addresses', function (Blueprint $table) {
//
});
}
}

View File

@ -5,6 +5,7 @@ namespace Webkul\Customer\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Webkul\Customer\Repositories\CustomerAddressRepository;
use Webkul\Customer\Rules\VatIdRule;
use Auth;
/**
@ -77,15 +78,19 @@ class AddressController extends Controller
$data = collect(request()->input())->except('_token')->toArray();
$this->validate(request(), [
'address1' => 'string|required',
'country' => 'string|required',
'state' => 'string|required',
'city' => 'string|required',
'postcode' => 'required',
'phone' => 'required',
'company_name' => 'string',
'address1' => 'string|required',
'country' => 'string|required',
'state' => 'string|required',
'city' => 'string|required',
'postcode' => 'required',
'phone' => 'required',
'vat_id' => new VatIdRule(),
]);
$cust_id['customer_id'] = $this->customer->id;
$cust_id['first_name'] = $this->customer->first_name;
$cust_id['last_name'] = $this->customer->last_name;
$data = array_merge($cust_id, $data);
if ($this->customer->addresses->count() == 0) {
@ -136,12 +141,14 @@ class AddressController extends Controller
request()->merge(['address1' => implode(PHP_EOL, array_filter(request()->input('address1')))]);
$this->validate(request(), [
'address1' => 'string|required',
'country' => 'string|required',
'state' => 'string|required',
'city' => 'string|required',
'postcode' => 'required',
'phone' => 'required',
'company_name' => 'string',
'address1' => 'string|required',
'country' => 'string|required',
'state' => 'string|required',
'city' => 'string|required',
'postcode' => 'required',
'phone' => 'required',
'vat_id' => new VatIdRule(),
]);
$data = collect(request()->input())->except('_token')->toArray();

View File

@ -10,7 +10,7 @@ use Webkul\Product\Repositories\ProductReviewRepository;
* Customer controlller for the customer basically for the tasks of customers which will be
* done after customer authentication.
*
* @author Prashant Singh <prashant.singh852@webkul.com>
* @author Prashant Singh <prashant.singh852@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CustomerController extends Controller
@ -26,23 +26,24 @@ class CustomerController extends Controller
* CustomerRepository object
*
* @var Object
*/
*/
protected $customerRepository;
/**
* ProductReviewRepository object
*
* @var array
*/
*/
protected $productReviewRepository;
/**
* Create a new controller instance.
*
* @param \Webkul\Customer\Repositories\CustomerRepository $customer
* @param \Webkul\Product\Repositories\ProductReviewRepository $productReview
* @param \Webkul\Customer\Repositories\CustomerRepository $customer
* @param \Webkul\Product\Repositories\ProductReviewRepository $productReview
*
* @return void
*/
*/
public function __construct(CustomerRepository $customerRepository, ProductReviewRepository $productReviewRepository)
{
$this->middleware('customer');
@ -88,31 +89,34 @@ class CustomerController extends Controller
$id = auth()->guard('customer')->user()->id;
$this->validate(request(), [
'first_name' => 'string',
'last_name' => 'string',
'gender' => 'required',
'date_of_birth' => 'date|before:today',
'email' => 'email|unique:customers,email,'.$id,
'password' => 'confirmed|min:6|required_with:oldpassword',
'oldpassword' => 'required_with:password',
'first_name' => 'string',
'last_name' => 'string',
'gender' => 'required',
'date_of_birth' => 'date|before:today',
'email' => 'email|unique:customers,email,' . $id,
'password' => 'confirmed|min:6|required_with:oldpassword',
'oldpassword' => 'required_with:password',
'password_confirmation' => 'required_with:password',
]);
$data = collect(request()->input())->except('_token')->toArray();
if ($data['date_of_birth'] == "")
if (isset ($data['date_of_birth']) && $data['date_of_birth'] == "") {
unset($data['date_of_birth']);
}
if ($data['oldpassword'] != "" || $data['oldpassword'] != null) {
if(Hash::check($data['oldpassword'], auth()->guard('customer')->user()->password)) {
$data['password'] = bcrypt($data['password']);
if (isset ($data['oldpassword'])) {
if ($data['oldpassword'] != "" || $data['oldpassword'] != null) {
if (Hash::check($data['oldpassword'], auth()->guard('customer')->user()->password)) {
$data['password'] = bcrypt($data['password']);
} else {
session()->flash('warning', trans('shop::app.customer.account.profile.unmatch'));
return redirect()->back();
}
} else {
session()->flash('warning', trans('shop::app.customer.account.profile.unmatch'));
return redirect()->back();
unset($data['password']);
}
} else {
unset($data['password']);
}
if ($this->customerRepository->update($data, $id)) {
@ -129,7 +133,8 @@ class CustomerController extends Controller
/**
* Remove the specified resource from storage.
*
* @param int $id
* @param int $id
*
* @return \Illuminate\Http\Response
*/
public function destroy($id)
@ -160,7 +165,7 @@ class CustomerController extends Controller
return redirect()->back();
}
} catch(\Exception $e) {
} catch (\Exception $e) {
session()->flash('error', trans('admin::app.response.delete-failed', ['name' => 'Customer']));
return redirect()->route($this->_config['redirect']);

View File

@ -71,6 +71,7 @@ class ForgotPasswordController extends Controller
['email' => trans($response)]
);
} catch (\Exception $e) {
report($e);
session()->flash('error', trans($e->getMessage()));
return redirect()->back();

View File

@ -2,6 +2,8 @@
namespace Webkul\Customer\Http\Controllers;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Mail;
use Webkul\Customer\Mail\RegistrationEmail;
@ -85,6 +87,7 @@ class RegistrationController extends Controller
$data = request()->input();
$data['password'] = bcrypt($data['password']);
$data['api_token'] = Str::random(80);
if (core()->getConfigData('customer.settings.email.verification')) {
$data['is_verified'] = 0;
@ -111,18 +114,19 @@ class RegistrationController extends Controller
session()->flash('success', trans('shop::app.customer.signup-form.success-verify'));
} catch (\Exception $e) {
report($e);
session()->flash('info', trans('shop::app.customer.signup-form.success-verify-email-unsent'));
}
} else {
try {
try {
Mail::queue(new RegistrationEmail(request()->all()));
session()->flash('success', trans('shop::app.customer.signup-form.success-verify')); //customer registered successfully
} catch (\Exception $e) {
report($e);
session()->flash('info', trans('shop::app.customer.signup-form.success-verify-email-unsent'));
}
session()->flash('success', trans('shop::app.customer.signup-form.success'));
}
@ -174,6 +178,7 @@ class RegistrationController extends Controller
\Cookie::queue(\Cookie::forget('email-for-resend'));
}
} catch (\Exception $e) {
report($e);
session()->flash('error', trans('shop::app.customer.signup-form.verification-not-sent'));
return redirect()->back();

View File

@ -172,6 +172,7 @@ class WishlistController extends Controller
return redirect()->back();
} catch (\Exception $e) {
report($e);
session()->flash('warning', $e->getMessage());
return redirect()->route('shop.productOrCategory.index', $wishlistItem->product->url_key);

View File

@ -17,9 +17,9 @@ class Customer extends Authenticatable implements CustomerContract, JWTSubject
protected $table = 'customers';
protected $fillable = ['first_name', 'last_name', 'gender', 'date_of_birth', 'email', 'phone', 'password', 'customer_group_id', 'subscribed_to_news_letter', 'is_verified', 'token', 'notes', 'status'];
protected $fillable = ['first_name', 'last_name', 'gender', 'date_of_birth', 'email', 'phone', 'password', 'api_token', 'customer_group_id', 'subscribed_to_news_letter', 'is_verified', 'token', 'notes', 'status'];
protected $hidden = ['password', 'remember_token'];
protected $hidden = ['password', 'api_token', 'remember_token'];
/**
* Get the customer full name.

View File

@ -1,4 +1,5 @@
<?php
namespace Webkul\Customer\Models;
use Illuminate\Database\Eloquent\Model;
@ -8,5 +9,27 @@ class CustomerAddress extends Model implements CustomerAddressContract
{
protected $table = 'customer_addresses';
protected $fillable = ['customer_id' ,'address1', 'country', 'state', 'city', 'postcode', 'phone', 'default_address'];
protected $fillable = [
'customer_id',
'company_name',
'vat_id',
'address1',
'address2',
'country',
'state',
'city',
'postcode',
'phone',
'default_address',
'first_name',
'last_name',
];
/**
* Get the customer address full name.
*/
public function getNameAttribute()
{
return $this->first_name . ' ' . $this->last_name;
}
}

View File

@ -2,6 +2,7 @@
namespace Webkul\Customer\Providers;
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
use Illuminate\Support\ServiceProvider;
use Illuminate\Routing\Router;
use Webkul\Customer\Http\Middleware\RedirectIfNotCustomer;
@ -16,4 +17,28 @@ class CustomerServiceProvider extends ServiceProvider
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
}
/**
* Register services.
*
* @return void
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function register()
{
$this->registerEloquentFactoriesFrom(__DIR__ . '/../Database/Factories');
}
/**
* Register factories.
*
* @param string $path
*
* @return void
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function registerEloquentFactoriesFrom($path): void
{
$this->app->make(EloquentFactory::class)->load($path);
}
}

View File

@ -6,12 +6,11 @@ use Webkul\Core\Eloquent\Repository;
use Illuminate\Support\Facades\Event;
/**
* Customer Reposotory
* Customer Repository
*
* @author Prashant Singh <prashant.singh852@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class CustomerAddressRepository extends Repository
{
/**
@ -27,21 +26,20 @@ class CustomerAddressRepository extends Repository
/**
* @param array $data
*
* @return mixed
*/
public function create(array $data)
{
Event::dispatch('customer.addresses.create.before');
if ( isset($data['default_address']) ) {
$data['default_address'] = 1;
} else {
$data['default_address'] = 0;
}
$data['default_address'] = isset($data['default_address']) ? 1 : 0;
$default_address = $this->findWhere(['customer_id' => $data['customer_id'], 'default_address' => 1])->first();
$default_address = $this
->findWhere(['customer_id' => $data['customer_id'], 'default_address' => 1])
->first();
if ( isset($default_address->id) && $data['default_address'] ) {
if (isset($default_address->id) && $data['default_address']) {
$default_address->update(['default_address' => 0]);
}
@ -54,7 +52,8 @@ class CustomerAddressRepository extends Repository
/**
* @param array $data
* @param $id
* @param $id
*
* @return mixed
*/
public function update(array $data, $id)
@ -63,16 +62,14 @@ class CustomerAddressRepository extends Repository
Event::dispatch('customer.addresses.update.before', $id);
if (isset($data['default_address']) ) {
$data['default_address'] = 1;
} else {
$data['default_address'] = 0;
}
$data['default_address'] = isset($data['default_address']) ? 1 : 0;
$default_address = $this->findWhere(['customer_id' => $address->customer_id, 'default_address' => 1])->first();
$default_address = $this
->findWhere(['customer_id' => $address->customer_id, 'default_address' => 1])
->first();
if ( isset($default_address->id) && $data['default_address'] ) {
if ( $default_address->id != $address->id ) {
if (isset($default_address->id) && $data['default_address']) {
if ($default_address->id != $address->id) {
$default_address->update(['default_address' => 0]);
}
$address->update($data);

View File

@ -0,0 +1,42 @@
<?php
namespace Webkul\Customer\Rules;
use Illuminate\Contracts\Validation\Rule;
use Webkul\Customer\Rules\VatValidator;
/**
* Class VatIdRule
*
* @see https://laravel.com/docs/5.8/validation#using-rule-objects
* @package App\Rules
*/
class VatIdRule implements Rule
{
/**
* Determine if the validation rule passes.
*
* The rules are borrowed from:
* @see https://raw.githubusercontent.com/danielebarbaro/laravel-vat-eu-validator/master/src/VatValidator.php
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$validator = new VatValidator();
return $validator->validate($value);
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return trans('shop::app.invalid_vat_format');
}
}

View File

@ -0,0 +1,95 @@
<?php
namespace Webkul\Customer\Rules;
/**
* Class VatValidator
*
* This class is borrowed from:
*
* @see https://raw.githubusercontent.com/danielebarbaro/laravel-vat-eu-validator
*
* @package Danielebarbaro\LaravelVatEuValidator
*/
class VatValidator
{
/**
* Regular expression patterns per country code
*
* @var array
* @link http://ec.europa.eu/taxation_customs/vies/faq.html?locale=en#item_11
*/
protected static $pattern_expression = array(
'AT' => 'U[A-Z\d]{8}',
'BE' => '(0\d{9}|\d{10})',
'BG' => '\d{9,10}',
'CY' => '\d{8}[A-Z]',
'CZ' => '\d{8,10}',
'DE' => '\d{9}',
'DK' => '(\d{2} ?){3}\d{2}',
'EE' => '\d{9}',
'EL' => '\d{9}',
'ES' => '[A-Z]\d{7}[A-Z]|\d{8}[A-Z]|[A-Z]\d{8}',
'FI' => '\d{8}',
'FR' => '([A-Z]{2}|\d{2})\d{9}',
'GB' => '\d{9}|\d{12}|(GD|HA)\d{3}',
'HR' => '\d{11}',
'HU' => '\d{8}',
'IE' => '[A-Z\d]{8}|[A-Z\d]{9}',
'IT' => '\d{11}',
'LT' => '(\d{9}|\d{12})',
'LU' => '\d{8}',
'LV' => '\d{11}',
'MT' => '\d{8}',
'NL' => '\d{9}B\d{2}',
'PL' => '\d{10}',
'PT' => '\d{9}',
'RO' => '\d{2,10}',
'SE' => '\d{12}',
'SI' => '\d{8}',
'SK' => '\d{10}',
);
/**
* Validate a VAT number format.
*
* @param string $vatNumber
*
* @return boolean
*/
public function validate(string $vatNumber): bool
{
$vatNumber = $this->vatCleaner($vatNumber);
list($country, $number) = $this->splitVat($vatNumber);
if (! isset(self::$pattern_expression[$country])) {
return false;
}
return preg_match('/^' . self::$pattern_expression[$country] . '$/', $number) > 0;
}
/**
* @param string $vatNumber
*
* @return string
*/
private function vatCleaner(string $vatNumber): string
{
$vatNumber_no_spaces = trim($vatNumber);
return strtoupper($vatNumber_no_spaces);
}
/**
* @param string $vatNumber
*
* @return array
*/
private function splitVat(string $vatNumber): array
{
return [
substr($vatNumber, 0, 2),
substr($vatNumber, 2),
];
}
}

View File

@ -168,6 +168,7 @@ class InventorySourceController extends Controller
return response()->json(['message' => true], 200);
} catch (\Exception $e) {
report($e);
session()->flash('error', trans('admin::app.response.delete-failed', ['name' => 'Inventory source']));
}
}

View File

@ -3,7 +3,7 @@ return [
'cashondelivery' => [
'code' => 'cashondelivery',
'title' => 'Cash On Delivery',
'description' => 'shop::app.checkout.onepage.cash-desc',
'description' => 'Cash On Delivery',
'class' => 'Webkul\Payment\Payment\CashOnDelivery',
'active' => true,
'sort' => 1
@ -12,7 +12,7 @@ return [
'moneytransfer' => [
'code' => 'moneytransfer',
'title' => 'Money Transfer',
'description' => 'shop::app.checkout.onepage.money-desc',
'description' => 'Money Transfer',
'class' => 'Webkul\Payment\Payment\MoneyTransfer',
'active' => true,
'sort' => 2
@ -21,7 +21,7 @@ return [
'paypal_standard' => [
'code' => 'paypal_standard',
'title' => 'Paypal Standard',
'description' => 'shop::app.checkout.onepage.paypal-desc',
'description' => 'Paypal Standard',
'class' => 'Webkul\Paypal\Payment\Standard',
'sandbox' => true,
'active' => true,

View File

@ -0,0 +1,20 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Product\Models\ProductReview;
$factory->define(ProductReview::class, function (Faker $faker, array $attributes) {
if (!array_key_exists('product_id', $attributes)) {
throw new InvalidArgumentException('product_id must be provided. You may use $I->haveProduct() to generate a product');
}
return [
'title' => $faker->words(5, true),
'rating' => $faker->numberBetween(0, 10),
'status' => 1,
'comment' => $faker->sentence(20),
'product_id' => $attributes['product_id'],
];
});

View File

@ -45,6 +45,14 @@ class BundleOption extends AbstractProduct
$options[$option->id] = $this->getOptionItemData($option);
}
usort ($options, function($a, $b) {
if ($a['sort_order'] == $b['sort_order']) {
return 0;
}
return ($a['sort_order'] < $b['sort_order']) ? -1 : 1;
});
return $options;
}
@ -68,7 +76,7 @@ class BundleOption extends AbstractProduct
/**
* Get formed data from bundle option product
*
*
* @param ProductBundleOption $option
* @return array
*/
@ -83,10 +91,19 @@ class BundleOption extends AbstractProduct
'price' => $bundleOptionProduct->product->getTypeInstance()->getProductPrices(),
'name' => $bundleOptionProduct->product->name,
'product_id' => $bundleOptionProduct->product_id,
'is_default' => $bundleOptionProduct->is_default
'is_default' => $bundleOptionProduct->is_default,
'sort_order' => $bundleOptionProduct->sort_order
];
}
usort ($products, function($a, $b) {
if ($a['sort_order'] == $b['sort_order']) {
return 0;
}
return ($a['sort_order'] < $b['sort_order']) ? -1 : 1;
});
return $products;
}
}

View File

@ -191,7 +191,7 @@ class ConfigurableOption extends AbstractProduct
if (isset($options[$attribute->id][$optionId])) {
$attributeOptionsData[] = [
'id' => $optionId,
'label' => $attributeOption->label,
'label' => $attributeOption->label ? $attributeOption->label : $attributeOption->admin_name,
'swatch_value' => $attribute->swatch_type == 'image' ? $attributeOption->swatch_value_url : $attributeOption->swatch_value,
'products' => $options[$attribute->id][$optionId]
];

View File

@ -245,6 +245,7 @@ class ProductController extends Controller
return response()->json(['message' => true], 200);
} catch (\Exception $e) {
report($e);
session()->flash('error', trans('admin::app.response.delete-failed', ['name' => 'Product']));
}

View File

@ -105,6 +105,7 @@ class ReviewController extends Controller
return response()->json(['message' => true], 200);
} catch (\Exception $e) {
report($e);
session()->flash('success', trans('admin::app.response.delete-failed', ['name' => 'Review']));
}

View File

@ -74,7 +74,8 @@ class ProductForm extends FormRequest
'sku' => ['required', 'unique:products,sku,' . $this->id, new \Webkul\Core\Contracts\Validations\Slug],
'images.*' => 'mimes:jpeg,jpg,bmp,png',
'special_price_from' => 'nullable|date',
'special_price_to' => 'nullable|date|after_or_equal:special_price_from'
'special_price_to' => 'nullable|date|after_or_equal:special_price_from',
'special_price' => ['nullable', new \Webkul\Core\Contracts\Validations\Decimal, 'lt:price']
]);
foreach ($product->getEditableAttributes() as $attribute) {

View File

@ -68,7 +68,7 @@ class ProductBundleOptionProductRepository extends Repository
}
}
if (! $haveIsDefaulFlag)
if (! $haveIsDefaulFlag && $data['is_required'])
$data['products'][key($data['products'])]['is_default'] = 1;
}
}

View File

@ -213,7 +213,7 @@ class Bundle extends AbstractType
}
}
if (! $haveRequiredOptions)
if (! $haveRequiredOptions && count($minPrices))
$minPrice = min($minPrices);
return $minPrice;
@ -523,6 +523,8 @@ class Bundle extends AbstractType
*/
public function getAdditionalOptions($data)
{
$bundleOptionQuantities = $data['bundle_option_qty'] ?? [];
foreach ($data['bundle_options'] as $optionId => $optionProductIds) {
$option = $this->productBundleOptionRepository->find($optionId);
@ -537,7 +539,7 @@ class Bundle extends AbstractType
$qty = $data['bundle_option_qty'][$optionId] ?? $optionProduct->qty;
if (! isset($data['bundle_option_qty'][$optionId]))
$data['bundle_option_qty'][$optionId] = $qty;
$bundleOptionQuantities[$optionId] = $qty;
$labels[] = $qty . ' x ' . $optionProduct->product->name . ' ' . core()->currency($optionProduct->product->getTypeInstance()->getMinimalPrice());
}
@ -551,6 +553,8 @@ class Bundle extends AbstractType
}
}
$data['bundle_option_qty'] = $bundleOptionQuantities;
return $data;
}

View File

@ -471,7 +471,7 @@ class Configurable extends AbstractType
$data['attributes'][$attribute->code] = [
'attribute_name' => $attribute->name ? $attribute->name : $attribute->admin_name,
'option_id' => $option->id,
'option_label' => $option->label,
'option_label' => $option->label ? $option->label : $option->admin_name,
];
}

View File

@ -0,0 +1,25 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Inventory\Models\InventorySource;
$factory->define(InventorySource::class, function (Faker $faker) {
$code = $faker->unique()->word;
return [
'code' => $faker->unique()->word,
'name' => $code,
'description' => $faker->sentence,
'contact_name' => $faker->name,
'contact_email' => $faker->safeEmail,
'contact_number' => $faker->phoneNumber,
'country' => $faker->countryCode,
'state' => $faker->state,
'city' => $faker->city,
'street' => $faker->streetAddress,
'postcode' => $faker->postcode,
'priority' => 0,
'status' => 1,
];
});

View File

@ -0,0 +1,61 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Sales\Models\Invoice;
use Webkul\Sales\Models\Order;
use Webkul\Sales\Models\OrderAddress;
$factory->define(Invoice::class, function (Faker $faker, array $attributes) {
$subTotal = $faker->randomFloat(2);
$shippingAmount = $faker->randomFloat(2);
$taxAmount = $faker->randomFloat(2);
if (!$attributes['order_id']) {
$attributes['order_id'] = function () {
return factory(Order::class)->create()->id;
};
}
if (!$attributes['order_address_id']) {
$attributes['order_address_id'] = function () use ($attributes) {
return factory(OrderAddress::class)
->create(['order_id' => $attributes['order_id']])
->id;
};
}
return [
'email_sent' => 0,
'total_qty' => $faker->randomNumber(),
'base_currency_code' => 'EUR',
'channel_currency_code' => 'EUR',
'order_currency_code' => 'EUR',
'sub_total' => $subTotal,
'base_sub_total' => $subTotal,
'grand_total' => $subTotal,
'base_grand_total' => $subTotal,
'shipping_amount' => $shippingAmount,
'base_shipping_amount' => $shippingAmount,
'tax_amount' => $taxAmount,
'base_tax_amount' => $taxAmount,
'discount_amount' => 0,
'base_discount_amount' => 0,
'order_id' => $attributes['order_id'],
'order_address_id' => $attributes['order_address_id'],
];
});
$factory->state(Invoice::class, 'pending', [
'status' => 'pending',
]);
$factory->state(Invoice::class, 'paid', [
'status' => 'paid',
]);
$factory->state(Invoice::class, 'refunded', [
'status' => 'refunded',
]);

View File

@ -0,0 +1,34 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Customer\Models\Customer;
use Webkul\Customer\Models\CustomerAddress;
use Webkul\Sales\Models\Order;
use Webkul\Sales\Models\OrderAddress;
$factory->define(OrderAddress::class, function (Faker $faker) {
$customer = factory(Customer::class)->create();
$customerAddress = factory(CustomerAddress::class)->create();
return [
'first_name' => $customer->first_name,
'last_name' => $customer->last_name,
'email' => $customer->email,
'address1' => $customerAddress->address1,
'country' => $customerAddress->country,
'state' => $customerAddress->state,
'city' => $customerAddress->city,
'postcode' => $customerAddress->postcode,
'phone' => $customerAddress->phone,
'address_type' => 'billing',
'order_id' => function () {
return factory(Order::class)->create()->id;
},
];
});
$factory->state(OrderAddress::class, 'shipping', [
'address_type' => 'shipping',
]);

View File

@ -0,0 +1,65 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Illuminate\Support\Facades\DB;
use Webkul\Core\Models\Channel;
use Webkul\Customer\Models\Customer;
use Webkul\Sales\Models\Order;
$factory->define(Order::class, function (Faker $faker) {
$lastOrder = DB::table('orders')
->orderBy('id', 'desc')
->select('id')
->first()
->id ?? 0;
$customer = factory(Customer::class)->create();
return [
'increment_id' => $lastOrder + 1,
'status' => 'pending',
'channel_name' => 'Default',
'is_guest' => 0,
'customer_id' => $customer->id,
'customer_email' => $customer->email,
'customer_first_name' => $customer->first_name,
'customer_last_name' => $customer->last_name,
'is_gift' => 0,
'total_item_count' => 1,
'total_qty_ordered' => 1,
'base_currency_code' => 'EUR',
'channel_currency_code' => 'EUR',
'order_currency_code' => 'EUR',
'grand_total' => 0.0000,
'base_grand_total' => 0.0000,
'grand_total_invoiced' => 0.0000,
'base_grand_total_invoiced' => 0.0000,
'grand_total_refunded' => 0.0000,
'base_grand_total_refunded' => 0.0000,
'sub_total' => 0.0000,
'base_sub_total' => 0.0000,
'sub_total_invoiced' => 0.0000,
'base_sub_total_invoiced' => 0.0000,
'sub_total_refunded' => 0.0000,
'base_sub_total_refunded' => 0.0000,
'customer_type' => Customer::class,
'channel_id' => 1,
'channel_type' => Channel::class,
'cart_id' => 0,
];
});
$factory->state(Order::class, 'pending', [
'status' => 'pending',
]);
$factory->state(Order::class, 'completed', [
'status' => 'completed',
]);
$factory->state(Order::class, 'closed', [
'status' => 'closed',
]);

View File

@ -0,0 +1,16 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Sales\Models\Order;
use Webkul\Sales\Models\Refund;
$factory->define(Refund::class, function (Faker $faker, array $attributes) {
return [
'order_id' => function () {
return factory(Order::class)->create()->id;
},
];
});

View File

@ -0,0 +1,22 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Inventory\Models\InventorySource;
use Webkul\Sales\Models\OrderAddress;
use Webkul\Sales\Models\Shipment;
$factory->define(Shipment::class, function (Faker $faker) {
$address = factory(OrderAddress::class)->create();
return [
'total_qty' => $faker->numberBetween(1, 20),
'order_id' => $address->order_id,
'order_address_id' => $address->id,
'inventory_source_id' => function () {
return factory(InventorySource::class)->create()->id;
},
];
});

View File

@ -7,16 +7,26 @@ use Webkul\Sales\Contracts\Order as OrderContract;
class Order extends Model implements OrderContract
{
protected $guarded = ['id', 'items', 'shipping_address', 'billing_address', 'customer', 'channel', 'payment', 'created_at', 'updated_at'];
protected $guarded = [
'id',
'items',
'shipping_address',
'billing_address',
'customer',
'channel',
'payment',
'created_at',
'updated_at',
];
protected $statusLabel = [
'pending' => 'Pending',
'pending' => 'Pending',
'pending_payment' => 'Pending Payment',
'processing' => 'Processing',
'completed' => 'Completed',
'canceled' => 'Canceled',
'closed' => 'Closed',
'fraud' => 'Fraud'
'processing' => 'Processing',
'completed' => 'Completed',
'canceled' => 'Canceled',
'closed' => 'Closed',
'fraud' => 'Fraud',
];
/**
@ -90,7 +100,7 @@ class Order extends Model implements OrderContract
{
return $this->hasMany(RefundProxy::modelClass());
}
/**
* Get the customer record associated with the order.
*/
@ -193,12 +203,12 @@ class Order extends Model implements OrderContract
{
if ($this->status == 'fraud')
return false;
foreach ($this->items as $item) {
if ($item->canInvoice())
return true;
}
return false;
}
@ -209,7 +219,7 @@ class Order extends Model implements OrderContract
{
if ($this->status == 'fraud')
return false;
foreach ($this->items as $item) {
if ($item->canCancel())
return true;
@ -225,7 +235,7 @@ class Order extends Model implements OrderContract
{
if ($this->status == 'fraud')
return false;
foreach ($this->items as $item) {
if ($item->qty_to_refund > 0)
return true;

View File

@ -2,6 +2,7 @@
namespace Webkul\Sales\Providers;
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
use Illuminate\Support\ServiceProvider;
class SalesServiceProvider extends ServiceProvider
@ -15,11 +16,27 @@ class SalesServiceProvider extends ServiceProvider
* Register services.
*
* @return void
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
public function register()
{
$this->registerEloquentFactoriesFrom(__DIR__ . '/../Database/Factories');
$this->mergeConfigFrom(
dirname(__DIR__) . '/Config/system.php', 'core'
);
}
/**
* Register factories.
*
* @param string $path
*
* @return void
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function registerEloquentFactoriesFrom($path): void
{
$this->app->make(EloquentFactory::class)->load($path);
}
}

View File

@ -6,7 +6,6 @@ use Illuminate\Container\Container as App;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Webkul\Core\Eloquent\Repository;
use Webkul\Core\Models\CoreConfig;
use Webkul\Sales\Contracts\Order;
use Webkul\Sales\Models\Order as OrderModel;
@ -196,22 +195,20 @@ class OrderRepository extends Repository
*/
public function generateIncrementId()
{
$config = new CoreConfig();
foreach ([ 'Prefix' => 'prefix',
'Length' => 'length',
'Suffix' => 'suffix', ] as
$varSuffix => $confKey)
{
$var = "invoiceNumber{$varSuffix}";
$$var = $config->where('code', '=', "sales.orderSettings.order_number.order_number_{$confKey}")->first() ?: false;
$$var = core()->getConfigData('sales.orderSettings.order_number.order_number_'.$confKey) ?: false;
}
$lastOrder = $this->model->orderBy('id', 'desc')->limit(1)->first();
$lastId = $lastOrder ? $lastOrder->id : 0;
if ($invoiceNumberLength && ($invoiceNumberPrefix || $invoiceNumberSuffix)) {
$invoiceNumber = ($invoiceNumberPrefix->value) . sprintf("%0{$invoiceNumberLength->value}d", 0) . ($lastId + 1) . ($invoiceNumberSuffix->value);
$invoiceNumber = ($invoiceNumberPrefix) . sprintf("%0{$invoiceNumberLength}d", 0) . ($lastId + 1) . ($invoiceNumberSuffix);
} else {
$invoiceNumber = $lastId + 1;
}

View File

@ -263,7 +263,7 @@ class RefundRepository extends Repository
$summary['tax']['price'] += ($orderItem->tax_amount / $orderItem->qty_ordered) * $qty;
}
$summary['shipping']['price'] += $order->base_shipping_invoiced - $order->base_shipping_refunded;
$summary['shipping']['price'] += $order->base_shipping_invoiced - $order->base_shipping_refunded - $order->base_shipping_discount_amount;
$summary['grand_total']['price'] += $summary['subtotal']['price'] + $summary['tax']['price'] + $summary['shipping']['price'] - $summary['discount']['price'];

View File

@ -25,6 +25,6 @@
"ez-plus": "^1.2.1",
"vee-validate": "^2.2.15",
"vue-flatpickr": "^2.3.0",
"vue-slider-component": "^3.0.44"
"vue-slider-component": "^2.7.5"
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -39,9 +39,3 @@
* (c) 2014-2019 Evan You
* Released under the MIT License.
*/
/**
* vue-class-component v7.0.1
* (c) 2015-present Evan You
* @license MIT
*/

View File

@ -1,4 +1,4 @@
{
"/js/shop.js": "/js/shop.js?id=8fc3af6f1d9024329021",
"/css/shop.css": "/css/shop.css?id=617c680f279cb4a73734"
"/js/shop.js": "/js/shop.js?id=d0a3f597b439d75f4a4e",
"/css/shop.css": "/css/shop.css?id=dbbd9009872174a61946"
}

View File

@ -12,6 +12,8 @@ class AddStoredFunctionToGetUrlPathOfCategory extends Migration
*/
public function up()
{
$dbPrefix = DB::getTablePrefix();
$functionSQL = <<< SQL
DROP FUNCTION IF EXISTS `get_url_path_of_category`;
CREATE FUNCTION get_url_path_of_category(
@ -21,7 +23,7 @@ class AddStoredFunctionToGetUrlPathOfCategory extends Migration
RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE urlPath VARCHAR(255);
-- Category with id 1 is root by default
IF categoryId <> 1
@ -29,9 +31,9 @@ class AddStoredFunctionToGetUrlPathOfCategory extends Migration
SELECT
GROUP_CONCAT(parent_translations.slug SEPARATOR '/') INTO urlPath
FROM
categories AS node,
categories AS parent
JOIN category_translations AS parent_translations ON parent.id = parent_translations.category_id
${dbPrefix}categories AS node,
${dbPrefix}categories AS parent
JOIN ${dbPrefix}category_translations AS parent_translations ON parent.id = parent_translations.category_id
WHERE
node._lft >= parent._lft
AND node._rgt <= parent._rgt
@ -40,15 +42,15 @@ class AddStoredFunctionToGetUrlPathOfCategory extends Migration
AND parent_translations.locale = localeCode
GROUP BY
node.id;
IF urlPath IS NULL
THEN
SET urlPath = (SELECT slug FROM category_translations WHERE category_translations.category_id = categoryId);
SET urlPath = (SELECT slug FROM ${dbPrefix}category_translations WHERE ${dbPrefix}category_translations.category_id = categoryId);
END IF;
ELSE
SET urlPath = '';
END IF;
RETURN urlPath;
END;
SQL;

View File

@ -2,6 +2,7 @@
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Migrations\Migration;
use Webkul\Category\Models\CategoryTranslation;
class AddTriggerToCategoryTranslations extends Migration
{
@ -15,11 +16,13 @@ class AddTriggerToCategoryTranslations extends Migration
*/
public function up()
{
$dbPrefix = DB::getTablePrefix();
$triggerBody = $this->getTriggerBody();
$insertTrigger = <<< SQL
CREATE TRIGGER %s
BEFORE INSERT ON category_translations
FOR EACH ROW
BEFORE INSERT ON ${dbPrefix}category_translations
FOR EACH ROW
BEGIN
$triggerBody
END;
@ -27,8 +30,8 @@ SQL;
$updateTrigger = <<< SQL
CREATE TRIGGER %s
BEFORE UPDATE ON category_translations
FOR EACH ROW
BEFORE UPDATE ON ${dbPrefix}category_translations
FOR EACH ROW
BEGIN
$triggerBody
END;
@ -59,20 +62,22 @@ SQL;
*/
private function getTriggerBody()
{
$dbPrefix = DB::getTablePrefix();
return <<<SQL
DECLARE parentUrlPath varchar(255);
DECLARE urlPath varchar(255);
-- Category with id 1 is root by default
IF NEW.category_id <> 1
THEN
SELECT
GROUP_CONCAT(parent_translations.slug SEPARATOR '/') INTO parentUrlPath
FROM
categories AS node,
categories AS parent
JOIN category_translations AS parent_translations ON parent.id = parent_translations.category_id
${dbPrefix}categories AS node,
${dbPrefix}categories AS parent
JOIN ${dbPrefix}category_translations AS parent_translations ON parent.id = parent_translations.category_id
WHERE
node._lft >= parent._lft
AND node._rgt <= parent._rgt
@ -81,16 +86,16 @@ SQL;
AND parent_translations.locale = NEW.locale
GROUP BY
node.id;
IF parentUrlPath IS NULL
IF parentUrlPath IS NULL
THEN
SET urlPath = NEW.slug;
ELSE
SET urlPath = concat(parentUrlPath, '/', NEW.slug);
END IF;
SET NEW.url_path = urlPath;
END IF;
SQL;

View File

@ -16,10 +16,11 @@ class AddTriggerToCategories extends Migration
public function up()
{
$triggerBody = $this->getTriggerBody();
$dbPrefix = DB::getTablePrefix();
$insertTrigger = <<< SQL
CREATE TRIGGER %s
AFTER INSERT ON categories
CREATE TRIGGER %s
AFTER INSERT ON ${dbPrefix}categories
FOR EACH ROW
BEGIN
$triggerBody
@ -27,8 +28,8 @@ class AddTriggerToCategories extends Migration
SQL;
$updateTrigger = <<< SQL
CREATE TRIGGER %s
AFTER UPDATE ON categories
CREATE TRIGGER %s
AFTER UPDATE ON ${dbPrefix}categories
FOR EACH ROW
BEGIN
$triggerBody
@ -60,39 +61,41 @@ SQL;
*/
private function getTriggerBody(): string
{
$dbPrefix = DB::getTablePrefix();
return <<< SQL
DECLARE urlPath VARCHAR(255);
DECLARE localeCode VARCHAR(255);
DECLARE done INT;
DECLARE curs CURSOR FOR (SELECT category_translations.locale
FROM category_translations
DECLARE curs CURSOR FOR (SELECT ${dbPrefix}category_translations.locale
FROM ${dbPrefix}category_translations
WHERE category_id = NEW.id);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
IF EXISTS (
SELECT *
FROM category_translations
FROM ${dbPrefix}category_translations
WHERE category_id = NEW.id
)
THEN
OPEN curs;
SET done = 0;
REPEAT
REPEAT
FETCH curs INTO localeCode;
SELECT get_url_path_of_category(NEW.id, localeCode) INTO urlPath;
UPDATE category_translations
SET url_path = urlPath
WHERE category_translations.category_id = NEW.id;
UPDATE ${dbPrefix}category_translations
SET url_path = urlPath
WHERE ${dbPrefix}category_translations.category_id = NEW.id;
UNTIL done END REPEAT;
CLOSE curs;
END IF;
SQL;
}

View File

@ -16,10 +16,12 @@ class AlterTriggerCategoryTranslations extends Migration
public function up()
{
$triggerBody = $this->getTriggerBody();
$dbPrefix = DB::getTablePrefix();
$insertTrigger = <<< SQL
CREATE TRIGGER %s
BEFORE INSERT ON category_translations
FOR EACH ROW
BEFORE INSERT ON ${dbPrefix}category_translations
FOR EACH ROW
BEGIN
$triggerBody
END;
@ -27,8 +29,8 @@ SQL;
$updateTrigger = <<< SQL
CREATE TRIGGER %s
BEFORE UPDATE ON category_translations
FOR EACH ROW
BEFORE UPDATE ON ${dbPrefix}category_translations
FOR EACH ROW
BEGIN
$triggerBody
END;
@ -66,44 +68,46 @@ SQL;
*/
private function getTriggerBody()
{
$dbPrefix = DB::getTablePrefix();
return <<<SQL
DECLARE parentUrlPath varchar(255);
DECLARE urlPath varchar(255);
IF NOT EXISTS (
SELECT id
FROM categories
FROM ${dbPrefix}categories
WHERE
id = NEW.category_id
AND parent_id IS NULL
)
THEN
SELECT
GROUP_CONCAT(parent_translations.slug SEPARATOR '/') INTO parentUrlPath
FROM
categories AS node,
categories AS parent
JOIN category_translations AS parent_translations ON parent.id = parent_translations.category_id
${dbPrefix}categories AS node,
${dbPrefix}categories AS parent
JOIN ${dbPrefix}category_translations AS parent_translations ON parent.id = parent_translations.category_id
WHERE
node._lft >= parent._lft
AND node._rgt <= parent._rgt
AND node.id = (SELECT parent_id FROM categories WHERE id = NEW.category_id)
AND node.parent_id IS NOT NULL
AND node.id = (SELECT parent_id FROM ${dbPrefix}categories WHERE id = NEW.category_id)
AND node.parent_id IS NOT NULL
AND parent.parent_id IS NOT NULL
AND parent_translations.locale = NEW.locale
GROUP BY
node.id;
IF parentUrlPath IS NULL
IF parentUrlPath IS NULL
THEN
SET urlPath = NEW.slug;
ELSE
SET urlPath = concat(parentUrlPath, '/', NEW.slug);
END IF;
SET NEW.url_path = urlPath;
END IF;
SQL;

View File

@ -12,6 +12,8 @@ class AlterStoredFunctionUrlPathCategory extends Migration
*/
public function up()
{
$dbPrefix = DB::getTablePrefix();
$functionSQL = <<< SQL
DROP FUNCTION IF EXISTS `get_url_path_of_category`;
CREATE FUNCTION get_url_path_of_category(
@ -21,12 +23,12 @@ class AlterStoredFunctionUrlPathCategory extends Migration
RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE urlPath VARCHAR(255);
IF NOT EXISTS (
SELECT id
FROM categories
SELECT id
FROM ${dbPrefix}categories
WHERE
id = categoryId
AND parent_id IS NULL
@ -35,9 +37,9 @@ class AlterStoredFunctionUrlPathCategory extends Migration
SELECT
GROUP_CONCAT(parent_translations.slug SEPARATOR '/') INTO urlPath
FROM
categories AS node,
categories AS parent
JOIN category_translations AS parent_translations ON parent.id = parent_translations.category_id
${dbPrefix}categories AS node,
${dbPrefix}categories AS parent
JOIN ${dbPrefix}category_translations AS parent_translations ON parent.id = parent_translations.category_id
WHERE
node._lft >= parent._lft
AND node._rgt <= parent._rgt
@ -47,15 +49,15 @@ class AlterStoredFunctionUrlPathCategory extends Migration
AND parent_translations.locale = localeCode
GROUP BY
node.id;
IF urlPath IS NULL
THEN
SET urlPath = (SELECT slug FROM category_translations WHERE category_translations.category_id = categoryId);
SET urlPath = (SELECT slug FROM ${dbPrefix}category_translations WHERE ${dbPrefix}category_translations.category_id = categoryId);
END IF;
ELSE
SET urlPath = '';
END IF;
RETURN urlPath;
END;
SQL;

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