Merged with master

This commit is contained in:
jitendra 2019-12-21 10:58:46 +05:30
commit 2380c1b597
155 changed files with 2180 additions and 786 deletions

View File

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

45
.env.testing Normal file
View File

@ -0,0 +1,45 @@
APP_NAME=Bagisto
APP_ENV=local
APP_VERSION=0.1.8
APP_KEY=base64:NFtGjjFAqET6RlX3PVC/gFpzHb4jK1OxDc3cuU5Asz4=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=bagisto_test
DB_USERNAME=root
DB_PASSWORD=root
BROADCAST_DRIVER=log
CACHE_DRIVER=file
SESSION_DRIVER=file
SESSION_LIFETIME=20
QUEUE_DRIVER=sync
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
SHOP_MAIL_FROM=
ADMIN_MAIL_TO=
fixer_api_key=
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

View File

@ -1,5 +1,5 @@
---
name: "🧐 Support Question"
name: " Support Question"
about: 'This repository is only for reporting bugs or problems. If you need help, see:
https://github.com/bagisto/bagisto#documentation'
---
@ -8,4 +8,4 @@ This repository is only for reporting bugs or issues. If you need support, pleas
1. Create support ticket on https://bagisto.uvdesk.com
Thanks!
Thanks!

4
.gitignore vendored
View File

@ -5,7 +5,6 @@
/public/js
/public/vendor
/public/themes
/storage/*.key
/vendor
/.idea
/.vscode
@ -20,3 +19,6 @@ composer.lock
yarn.lock
package-lock.json
yarn.lock
.php_cs.cache
storage/
storage/*.key

11
codeception.yml Normal file
View File

@ -0,0 +1,11 @@
paths:
tests: tests
output: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
actor_suffix: Tester
extensions:
enabled:
- Codeception\Extension\RunFailed

View File

@ -2,9 +2,10 @@
"name": "bagisto/bagisto",
"description": "Bagisto Laravel ECommerce",
"keywords": [
"framework",
"laravel"
"framework",
"laravel"
],
"license": "MIT",
"type": "project",
"require": {
@ -33,36 +34,40 @@
"prettus/l5-repository": "2.6.32",
"tymon/jwt-auth": "1.0.0-rc.4"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.1",
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.4",
"laravel/dusk": "^4.0",
"mockery/mockery": "^1.0",
"nunomaduro/collision": "^2.0",
"phpunit/phpunit": "^7.0"
"codeception/codeception": "3.1.*",
"barryvdh/laravel-debugbar": "^3.1",
"filp/whoops": "^2.0",
"fzaninotto/faker": "^1.4",
"laravel/dusk": "^4.0",
"mockery/mockery": "^1.0",
"nunomaduro/collision": "^2.0",
"phpunit/phpunit": "^7.0"
},
"replace": {
"bagisto/laravel-user": "v0.1.0",
"bagisto/laravel-admin": "v0.1.0",
"bagisto/laravel-ui": "v0.1.0",
"bagisto/laravel-core": "v0.1.0",
"bagisto/laravel-attribute": "v0.1.0",
"bagisto/laravel-checkout": "v0.1.0",
"bagisto/laravel-customer": "v0.1.0",
"bagisto/laravel-inventory": "v0.1.0",
"bagisto/laravel-category": "v0.1.0",
"bagisto/laravel-product": "v0.1.0",
"bagisto/laravel-shop": "v0.1.0",
"bagisto/laravel-theme": "v0.1.0",
"bagisto/laravel-shipping": "v0.1.0",
"bagisto/laravel-payment": "v0.1.0",
"bagisto/laravel-sales": "v0.1.0",
"bagisto/laravel-tax": "v0.1.0",
"bagisto/laravel-api": "v0.1.0",
"bagisto/laravel-paypal": "v0.1.0",
"bagisto/laravel-discount": "v0.1.0"
"bagisto/laravel-user": "v0.1.0",
"bagisto/laravel-admin": "v0.1.0",
"bagisto/laravel-ui": "v0.1.0",
"bagisto/laravel-core": "v0.1.0",
"bagisto/laravel-attribute": "v0.1.0",
"bagisto/laravel-checkout": "v0.1.0",
"bagisto/laravel-customer": "v0.1.0",
"bagisto/laravel-inventory": "v0.1.0",
"bagisto/laravel-category": "v0.1.0",
"bagisto/laravel-product": "v0.1.0",
"bagisto/laravel-shop": "v0.1.0",
"bagisto/laravel-theme": "v0.1.0",
"bagisto/laravel-shipping": "v0.1.0",
"bagisto/laravel-payment": "v0.1.0",
"bagisto/laravel-sales": "v0.1.0",
"bagisto/laravel-tax": "v0.1.0",
"bagisto/laravel-api": "v0.1.0",
"bagisto/laravel-paypal": "v0.1.0",
"bagisto/laravel-discount": "v0.1.0"
},
"autoload": {
"classmap": [
"database/seeds",
@ -94,35 +99,48 @@
"Webkul\\CMS\\": "packages/Webkul/CMS/src"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
"psr-4": {
"Tests\\": "tests/"
}
},
"extra": {
"laravel": {
"dont-discover": [
"barryvdh/laravel-debugbar",
"laravel/dusk"
]
}
"laravel": {
"dont-discover": [
"barryvdh/laravel-debugbar",
"laravel/dusk"
]
}
},
"scripts": {
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover"
]
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"@php artisan key:generate",
"Webkul\\Core\\Events\\ComposerEvents::postCreateProject"
],
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover"
],
"test": [
"set -e",
"@php artisan migrate:fresh --env=testing",
"vendor/bin/codecept run unit",
"vendor/bin/codecept run functional"
]
},
"config": {
"preferred-install": "stable",
"sort-packages": true,
"optimize-autoloader": true
"preferred-install": "stable",
"sort-packages": true,
"optimize-autoloader": true
},
"minimum-stability": "dev"
}
"minimum-stability": "dev",
"prefer-stable": true
}

View File

@ -36,7 +36,7 @@ return [
'sqlite' => [
'driver' => 'sqlite',
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'prefix' => env('DB_PREFIX'),
],
'mysql' => [
@ -49,7 +49,7 @@ return [
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix' => env('DB_PREFIX'),
'strict' => false,
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
],
@ -62,7 +62,7 @@ return [
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'prefix' => '',
'prefix' => env('DB_PREFIX'),
'schema' => 'public',
'sslmode' => 'prefer',
],

View File

@ -60,6 +60,20 @@ return [
'name' => env('MAIL_FROM_NAME')
],
/*
|--------------------------------------------------------------------------
| Global "Admin" Address
|--------------------------------------------------------------------------
|
| General admin related admins, such as order notifications.
|
*/
'admin' => [
'address' => env('ADMIN_MAIL_TO'),
'name' => env('ADMIN_MAIL_NAME', 'Admin')
],
/*
|--------------------------------------------------------------------------
| E-Mail Encryption Protocol

View File

@ -3,6 +3,7 @@
namespace Webkul\API\Http\Resources\Catalog;
use Illuminate\Http\Resources\Json\JsonResource;
use Webkul\Product\Helpers\ProductType;
class Product extends JsonResource
{
@ -44,7 +45,7 @@ class Product extends JsonResource
'base_image' => $this->productImageHelper->getProductBaseImage($product),
'variants' => Self::collection($this->variants),
'in_stock' => $product->haveSufficientQuantity(1),
$this->mergeWhen($product->type == 'configurable', [
$this->mergeWhen($product->getTypeInstance()->isComposite(), [
'super_attributes' => Attribute::collection($product->super_attributes),
]),
'special_price' => $this->when(

View File

@ -182,6 +182,12 @@ return [
'sort' => 2,
'icon-class' => ''
], [
'key' => 'settings.development',
'name' => 'admin::app.settings.development.title',
'route' => 'admin.development.index',
'sort' => 8,
'icon-class' => ''
],[
'key' => 'promotions',
'name' => 'admin::app.layouts.promotions',
'route' => 'admin.catalog-rules.index',

View File

@ -28,19 +28,19 @@ class AddressDataGrid extends DataGrid
* @var object
*/
protected $customer;
/**
* Create a new controller instance.
*
* @param Webkul\Customer\Repositories\CustomerRepository $customer
* @return void
*/
public function __construct(
public function __construct(
Customer $customer
)
{
parent::__construct();
$this->customer = $customer;
}
@ -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('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('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('country_states.default_name'));
$this->addFilter('country_name', DB::raw('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

@ -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 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(customers.first_name, " ", 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(customers.first_name, " ", 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(order_address_billing.first_name, " ", order_address_billing.last_name) as billed_to'))
->addSelect(DB::raw('CONCAT(order_address_shipping.first_name, " ", 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(order_address_billing.first_name, " ", order_address_billing.last_name)'));
$this->addFilter('shipped_to', DB::raw('CONCAT(order_address_shipping.first_name, " ", 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(order_address_billing.first_name, " ", 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(order_address_billing.first_name, " ", 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

@ -27,7 +27,7 @@ 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(order_address_shipping.first_name, " ", order_address_shipping.last_name) as shipped_to'));
->addSelect(DB::raw('CONCAT('.DB::getTablePrefix().'order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name) as shipped_to'));
$this->addFilter('shipment_id', 'shipments.id');
$this->addFilter('shipment_order_id', 'ors.increment_id');
@ -35,7 +35,7 @@ class OrderShipmentsDataGrid extends DataGrid
$this->addFilter('inventory_source_name', 'is.name');
$this->addFilter('order_date', 'ors.created_at');
$this->addFilter('shipment_created_at', 'shipments.created_at');
$this->addFilter('shipped_to', DB::raw('CONCAT(order_address_shipping.first_name, " ", order_address_shipping.last_name)'));
$this->addFilter('shipped_to', DB::raw(''.DB::getTablePrefix().'CONCAT(order_address_shipping.first_name, " ", '.DB::getTablePrefix().'order_address_shipping.last_name)'));
$this->setQueryBuilder($queryBuilder);
}

View File

@ -17,7 +17,7 @@ class ProductDataGrid extends DataGrid
protected $index = 'product_id';
protected $itemsPerPage = 20;
protected $itemsPerPage = 10;
public function prepareQueryBuilder()
{
@ -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(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');
@ -152,7 +152,7 @@ class ProductDataGrid extends DataGrid
public function prepareMassActions() {
$this->addMassAction([
'type' => 'delete',
'label' => 'Delete',
'label' => 'Delete',
'action' => route('admin.catalog.products.massdelete'),
'method' => 'DELETE'
]);

View File

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

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(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

@ -0,0 +1,24 @@
<?php
namespace Webkul\Admin\Http\Controllers\Development;
use Webkul\Admin\Http\Controllers\Controller;
/**
* Dashboard controller
*
* @author Alexey Khachatryan <info@khachatryan.org>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class DashboardController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\View\View
*/
public function index()
{
return view('admin::settings.development.dashboard');
}
}

View File

@ -278,7 +278,7 @@ Route::group(['middleware' => ['web']], function () {
Route::post('/products/upload-file/{id}', 'Webkul\Product\Http\Controllers\ProductController@uploadLink')->name('admin.catalog.products.upload_link');
Route::post('/products/upload-sample/{id}', 'Webkul\Product\Http\Controllers\ProductController@uploadSample')->name('admin.catalog.products.upload_sample');
//product delete
Route::post('/products/delete/{id}', 'Webkul\Product\Http\Controllers\ProductController@destroy')->name('admin.catalog.products.delete');
@ -756,6 +756,12 @@ Route::group(['middleware' => ['web']], function () {
// 'redirect' => 'admin.cms.index'
// ])->name('admin.cms.delete');
});
// Development settings
Route::prefix('development')->group(function () {
Route::get('/', 'Webkul\Admin\Http\Controllers\Development\DashboardController@index')
->name('admin.development.index');
});
});
});
});
});

View File

@ -42,7 +42,7 @@ class NewAdminNotification extends Mailable
*/
public function build()
{
return $this->to(env('ADMIN_MAIL_TO'))
return $this->to(config('mail.admin.address'))
->subject(trans('shop::app.mail.order.subject'))
->view('shop::emails.sales.new-admin-order');
}

View File

@ -45,7 +45,7 @@ class NewInvoiceNotification extends Mailable
$order = $this->invoice->order;
return $this->to($order->customer_email, $order->customer_full_name)
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.invoice.subject', ['order_id' => $order->increment_id]))
->view('shop::emails.sales.new-invoice');
}

View File

@ -42,7 +42,7 @@ class NewOrderNotification extends Mailable
public function build()
{
return $this->to($this->order->customer_email, $this->order->customer_full_name)
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.order.subject'))
->view('shop::emails.sales.new-order');
}

View File

@ -45,7 +45,7 @@ class NewRefundNotification extends Mailable
$order = $this->refund->order;
return $this->to($order->customer_email, $order->customer_full_name)
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.refund.subject', ['order_id' => $order->increment_id]))
->view('shop::emails.sales.new-refund');
}

View File

@ -45,7 +45,7 @@ class NewShipmentNotification extends Mailable
$order = $this->shipment->order;
return $this->to($order->customer_email, $order->customer_full_name)
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.shipment.subject', ['order_id' => $order->increment_id]))
->view('shop::emails.sales.new-shipment');
}

View File

@ -726,6 +726,9 @@ return [
'update-error' => 'خطأ! معدل الضرائب لا يمكن تحديثه',
'delete' => 'معدل الضريبة محذوف بنجاح',
'atleast-one' => 'لا يستطيع حذف الأخير ضريبة معدل'
],
'development' => [
'title' => 'تطوير',
]
],
@ -910,6 +913,7 @@ return [
'order-number-length' => 'Order Number Length',
'order-number-suffix' => 'Order Number Suffix',
'default' => 'Default',
'sandbox' => 'Sandbox'
]
]
];

View File

@ -845,6 +845,9 @@ return [
'update-error' => 'Error! Tax Rate Cannot Be Updated',
'delete' => 'Tax Rate Deleted Successfully',
'atleast-one' => 'Cannot Delete Last Tax Rate'
],
'development' => [
'title' => 'Development',
]
],
@ -1253,6 +1256,7 @@ return [
'order-number-length' => 'Order Number Length',
'order-number-suffix' => 'Order Number Suffix',
'default' => 'Default',
'sandbox' => 'Sandbox'
]
]
];

View File

@ -761,6 +761,9 @@ return [
'update-error' => 'خطا! نرخ مالیات نمی تواند به روز شود',
'delete' => 'نرخ مالیات با موفقیت حذف شد',
'atleast-one' => 'آخرین نرخ مالیات نمی تواند حذف شود'
],
'development' => [
'title' => 'توسعه',
]
],
@ -1048,7 +1051,8 @@ return [
'logo-image' => 'تصویر لوگو',
'credit-max' => 'اعتبار مشتری حداکثر',
'credit-max-value' => 'حداکثر میزان اعتبار',
'use-credit-max' => 'استفاده از حداکثر اعتبار'
'use-credit-max' => 'استفاده از حداکثر اعتبار',
'sandbox' => 'Sandbox'
]
]
];

View File

@ -584,7 +584,6 @@ return [
'title' => 'Idiomas',
'add-title' => 'Add Idioma',
'edit-title' => 'Editar Idioma',
'add-title' => 'Add Idioma',
'save-btn-title' => 'Salvar Idioma',
'general' => 'Geral',
'code' => 'Código',
@ -753,6 +752,9 @@ return [
'update-error' => 'Erro! Taxas de Impostos Não Poderam ser Atualizadas',
'delete' => 'Taxas de Impostos Excluídas com Sucesso',
'atleast-one' => 'Não é Possível Excluir a Última Taxa de Impostos'
],
'development' => [
'title' => 'Desenvolvimento',
]
],
'customers' => [
@ -1033,6 +1035,7 @@ return [
'order-number-prefix' => 'Order Number Prefix',
'order-number-length' => 'Order Number Length',
'order-number-suffix' => 'Order Number Suffix',
'sandbox' => 'Sandbox'
]
]
];

View File

@ -77,7 +77,7 @@
<option value="file" {{ $selectedOption == 'file' ? 'selected' : '' }}>
{{ __('admin::app.catalog.attributes.file') }}
</option>
<option value="file" {{ $selectedOption == 'checkbox' ? 'selected' : '' }}>
<option value="checkbox" {{ $selectedOption == 'checkbox' ? 'selected' : '' }}>
{{ __('admin::app.catalog.attributes.checkbox') }}
</option>
</select>
@ -461,4 +461,4 @@
}
});
</script>
@endpush
@endpush

View File

@ -64,7 +64,7 @@
</select>
@if ($familyId)
<input type="hidden" name="type" value="configurable"/>
<input type="hidden" name="type" value="{{ app('request')->input('type') }}"/>
@endif
<span class="control-error" v-if="errors.has('type')">@{{ errors.first('type') }}</span>
</div>

View File

@ -17,7 +17,8 @@
<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>
<i class="icon angle-left-icon back-link"
onclick="history.length > 1 ? history.go(-1) : window.location = '{{ url('/admin/dashboard') }}';"></i>
{{ __('admin::app.catalog.products.edit-title') }}
</h1>
@ -26,7 +27,8 @@
<select class="control" id="channel-switcher" name="channel">
@foreach (core()->getAllChannels() as $channelModel)
<option value="{{ $channelModel->code }}" {{ ($channelModel->code) == $channel ? 'selected' : '' }}>
<option
value="{{ $channelModel->code }}" {{ ($channelModel->code) == $channel ? 'selected' : '' }}>
{{ $channelModel->name }}
</option>
@ -38,7 +40,8 @@
<select class="control" id="locale-switcher" name="locale">
@foreach (core()->getAllLocales() as $localeModel)
<option value="{{ $localeModel->code }}" {{ ($localeModel->code) == $locale ? 'selected' : '' }}>
<option
value="{{ $localeModel->code }}" {{ ($localeModel->code) == $locale ? 'selected' : '' }}>
{{ $localeModel->name }}
</option>
@ -66,31 +69,37 @@
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.' . $attributeGroup->name . '.before', ['product' => $product]) !!}
<accordian :title="'{{ __($attributeGroup->name) }}'" :active="{{$index == 0 ? 'true' : 'false'}}">
<accordian :title="'{{ __($attributeGroup->name) }}'"
:active="{{$index == 0 ? 'true' : 'false'}}">
<div slot="body">
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.' . $attributeGroup->name . '.controls.before', ['product' => $product]) !!}
@foreach ($customAttributes as $attribute)
<?php
$validations = [];
$validations = [];
if ($attribute->is_required)
array_push($validations, 'required');
if ($attribute->is_required) {
array_push($validations, 'required');
}
if ($attribute->type == 'price')
array_push($validations, 'decimal');
if ($attribute->type == 'price') {
array_push($validations, 'decimal');
}
array_push($validations, $attribute->validation);
array_push($validations, $attribute->validation);
$validations = implode('|', array_filter($validations));
$validations = implode('|', array_filter($validations));
?>
@if (view()->exists($typeView = 'admin::catalog.products.field-types.' . $attribute->type))
<div class="control-group {{ $attribute->type }}" @if ($attribute->type == 'multiselect') :class="[errors.has('{{ $attribute->code }}[]') ? 'has-error' : '']" @else :class="[errors.has('{{ $attribute->code }}') ? 'has-error' : '']" @endif>
<div class="control-group {{ $attribute->type }}"
@if ($attribute->type == 'multiselect') :class="[errors.has('{{ $attribute->code }}[]') ? 'has-error' : '']"
@else :class="[errors.has('{{ $attribute->code }}') ? 'has-error' : '']" @endif>
<label for="{{ $attribute->code }}" {{ $attribute->is_required ? 'class=required' : '' }}>
<label
for="{{ $attribute->code }}" {{ $attribute->is_required ? 'class=required' : '' }}>
{{ $attribute->admin_name }}
@if ($attribute->type == 'price')
@ -98,13 +107,15 @@
@endif
<?php
$channel_locale = [];
$channel_locale = [];
if ($attribute->value_per_channel)
array_push($channel_locale, $channel);
if ($attribute->value_per_channel) {
array_push($channel_locale, $channel);
}
if ($attribute->value_per_locale)
array_push($channel_locale, $locale);
if ($attribute->value_per_locale) {
array_push($channel_locale, $locale);
}
?>
@if (count($channel_locale))
@ -114,7 +125,9 @@
@include ($typeView)
<span class="control-error" @if ($attribute->type == 'multiselect') v-if="errors.has('{{ $attribute->code }}[]')" @else v-if="errors.has('{{ $attribute->code }}')" @endif>
<span class="control-error"
@if ($attribute->type == 'multiselect') v-if="errors.has('{{ $attribute->code }}[]')"
@else v-if="errors.has('{{ $attribute->code }}')" @endif>
@if ($attribute->type == 'multiselect')
@{{ errors.first('{!! $attribute->code !!}[]') }}
@else
@ -137,12 +150,20 @@
@endforeach
{!! view_render_event(
'bagisto.admin.catalog.product.edit_form_accordian.additional_views.before',
['product' => $product])
!!}
@foreach ($product->getTypeInstance()->getAdditionalViews() as $view)
@include ($view)
@endforeach
{!! view_render_event(
'bagisto.admin.catalog.product.edit_form_accordian.additional_views.after',
['product' => $product])
!!}
</div>
</form>
@ -173,4 +194,4 @@
});
});
</script>
@endpush
@endpush

View File

@ -274,7 +274,7 @@
{{ __('admin::app.dashboard.order-count', ['count' => $item->total_orders]) }}
&nbsp;.&nbsp;
{{ __('admin::app.dashboard.revenue', [
'total' => core()->formatBasePrice($item->total_base_grand_total_invoiced)
'total' => core()->formatBasePrice($item->total_base_grand_total)
])
}}
</div>

View File

@ -48,6 +48,8 @@
<div class="page-content">
<tabs>
{!! view_render_event('sales.order.tabs.before', ['order' => $order]) !!}
<tab name="{{ __('admin::app.sales.orders.info') }}" :selected="true">
<div class="sale-container">
@ -506,8 +508,10 @@
</div>
</tab>
{!! view_render_event('sales.order.tabs.after', ['order' => $order]) !!}
</tabs>
</div>
</div>
@stop
@stop

View File

@ -241,14 +241,14 @@
<tbody>
@foreach ($order->items as $item)
<tr>
<td>{{ $item->type == 'configurable' ? $item->child->sku : $item->sku }}</td>
<td>{{ Webkul\Product\Helpers\ProductType::hasVariants($item->type) ? $item->child->sku : $item->sku }}</td>
<td>
{{ $item->name }}
@if (isset($item->additional['attributes']))
<div class="item-options">
@foreach ($item->additional['attributes'] as $attribute)
<b>{{ $attribute['attribute_name'] }} : </b>{{ $attribute['option_label'] }}</br>
@endforeach
@ -417,7 +417,7 @@
if (! response.data) {
window.flashMessages = [{
'type': 'alert-error',
'message': "{{ __('admin::app.sales.refunds.invalid-qty') }}"
'message': "{{ __('admin::app.sales.refunds.invalid-qty') }}"
}];
this_this.$root.addFlashMessages()

View File

@ -0,0 +1,19 @@
@extends('admin::layouts.content')
@section('page_title')
{{ __('admin::app.settings.development.title') }}
@stop
@section('content')
<div class="content">
<div class="page-header">
<div class="page-title">
<h1>{{ __('admin::app.settings.development.title') }}</h1>
</div>
</div>
<div class="page-content">
</div>
</div>
@stop

View File

@ -52,7 +52,7 @@
<div class="control-group {!! $errors->has('image.*') ? 'has-error' : '' !!}">
<label class="required">{{ __('admin::app.catalog.categories.image') }}</label>
<image-wrapper :button-label="'{{ __('admin::app.settings.sliders.image') }}'" input-name="image" :multiple="false" :images='"{{ url('storage/'.$slider->path) }}"' ></image-wrapper>
<image-wrapper :button-label="'{{ __('admin::app.settings.sliders.image') }}'" input-name="image" :multiple="false" :images='"{{ Storage::url($slider->path) }}"'></image-wrapper>
<span class="control-error" v-if="{!! $errors->has('image.*') !!}">
@foreach ($errors->get('image.*') as $key => $message)

View File

@ -0,0 +1,6 @@
<?php
return [
'name' => 'Webkul Bagisto Attributes',
'version' => '0.0.1'
];

View File

@ -40,7 +40,9 @@ use Webkul\Core\Repositories\LocaleRepository as Locale;
/**
* Pass the class instance through admin middleware
*/
$this->middleware('auth:admin');
// $this->middleware('auth:admin');
$this->middleware('admin');
/**
* Channel repository instance

View File

@ -0,0 +1,6 @@
<?php
return [
'name' => 'Webkul Bagisto Cms',
'version' => '0.0.1'
];

View File

@ -0,0 +1,19 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Category\Models\Category;
$factory->define(Category::class, function (Faker $faker, array $attributes) {
return [
'status' => 1,
'position' => $faker->randomDigit,
'parent_id' => 1,
];
});
$factory->state(Category::class, 'inactive', [
'status' => 0,
]);

View File

@ -8,6 +8,13 @@ use Illuminate\Support\Facades\Storage;
use Webkul\Category\Contracts\Category as CategoryContract;
use Webkul\Attribute\Models\AttributeProxy;
/**
* Class Category
*
* @package Webkul\Category\Models
*
* @property-read string $url_path maintained by database triggers
*/
class Category extends TranslatableModel implements CategoryContract
{
use NodeTrait;

View File

@ -5,6 +5,13 @@ namespace Webkul\Category\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Category\Contracts\CategoryTranslation as CategoryTranslationContract;
/**
* Class CategoryTranslation
*
* @package Webkul\Category\Models
*
* @property-read string $url_path maintained by database triggers
*/
class CategoryTranslation extends Model implements CategoryTranslationContract
{
public $timestamps = false;

View File

@ -3,6 +3,8 @@
namespace Webkul\Category\Observers;
use Illuminate\Support\Facades\Storage;
use Webkul\Category\Models\Category;
use Carbon\Carbon;
class CategoryObserver
{
@ -16,4 +18,16 @@ class CategoryObserver
{
Storage::deleteDirectory('category/' . $category->id);
}
/**
* Handle the Category "saved" event.
*
* @param Category $category
*/
public function saved($category)
{
foreach ($category->children as $child) {
$child->touch();
}
}
}

View File

@ -2,6 +2,7 @@
namespace Webkul\Category\Providers;
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
use Illuminate\Support\ServiceProvider;
use Webkul\Category\Models\CategoryProxy;
use Webkul\Category\Observers\CategoryObserver;
@ -18,6 +19,8 @@ class CategoryServiceProvider extends ServiceProvider
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
CategoryProxy::observe(CategoryObserver::class);
$this->registerEloquentFactoriesFrom(__DIR__ . '/../Database/Factories');
}
/**
@ -29,4 +32,15 @@ class CategoryServiceProvider extends ServiceProvider
{
}
/**
* Register factories.
*
* @param string $path
* @return void
*/
protected function registerEloquentFactoriesFrom($path): void
{
$this->app->make(EloquentFactory::class)->load($path);
}
}

View File

@ -151,6 +151,16 @@ class CategoryRepository extends Repository
);
}
/**
* @param string $urlPath
*
* @return mixed
*/
public function findByPath(string $urlPath)
{
return $this->model->whereTranslation('url_path', $urlPath)->first();
}
/**
* @param array $data
* @param $id

View File

@ -0,0 +1,6 @@
<?php
return [
'name' => 'Webkul Bagisto Categories',
'version' => '0.0.1'
];

View File

@ -0,0 +1,6 @@
<?php
return [
'name' => 'Webkul Bagisto Checkout',
'version' => '0.0.1'
];

View File

@ -211,7 +211,13 @@ class Core
if ($locale)
return $locale;
return $locale = $this->localeRepository->findOneByField('code', app()->getLocale());
$locale = $this->localeRepository->findOneByField('code', app()->getLocale());
if(!$locale) {
$locale = $this->localeRepository->findOneByField('code', config('app.fallback_locale'));
}
return $locale;
}
/**

View File

@ -0,0 +1,19 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use Faker\Generator as Faker;
use Webkul\Core\Models\Locale;
$factory->define(Locale::class, function (Faker $faker, array $attributes) {
return [
'code' => $faker->languageCode,
'name' => $faker->country,
'direction' => 'ltr',
];
});
$factory->state(Category::class, 'rtl', [
'direction' => 'rtl',
]);

View File

@ -0,0 +1,15 @@
<?php
namespace Webkul\Core\Events;
use Symfony\Component\Console\Output\ConsoleOutput;
class ComposerEvents
{
public static function postCreateProject()
{
$output = new ConsoleOutput();
$output->writeln(file_get_contents(__DIR__ . '/../Templates/on-boarding.php'));
}
}

View File

@ -2,6 +2,7 @@
namespace Webkul\Core\Providers;
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\AliasLoader;
@ -36,6 +37,8 @@ class CoreServiceProvider extends ServiceProvider
]);
SliderProxy::observe(SliderObserver::class);
$this->registerEloquentFactoriesFrom(__DIR__ . '/../Database/Factories');
}
/**
@ -61,4 +64,15 @@ class CoreServiceProvider extends ServiceProvider
return app()->make(Core::class);
});
}
/**
* Register factories.
*
* @param string $path
* @return void
*/
protected function registerEloquentFactoriesFrom($path): void
{
$this->app->make(EloquentFactory::class)->load($path);
}
}

View File

@ -0,0 +1,14 @@
____ _ _
| __ ) __ _ __ _(_)___| |_ ___
| _ \ / _` |/ _` | / __| __/ _ \
| |_) | (_| | (_| | \__ \ || (_) |
|____/ \__,_|\__, |_|___/\__\___/
|___/
</>
Welcome to the <info>Bagisto</info> project! Bagisto Community is an <comment>open-source e-commerce ecosystem </comment>
which is built on top of Laravel and Vue.js.
Made with 💖 by the Bagisto Team. Happy helping :)

View File

@ -0,0 +1,6 @@
<?php
return [
'name' => 'Webkul Bagisto Core',
'version' => '0.0.1'
];

View File

@ -93,8 +93,9 @@ class CustomerController extends Controller
'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' => 'confirmed|min:6'
'password_confirmation' => 'required_with:password',
]);
$data = collect(request()->input())->except('_token')->toArray();
@ -139,27 +140,26 @@ class CustomerController extends Controller
$customerRepository = $this->customerRepository->findorFail($id);
$orders = $customerRepository->all_orders->whereIn('status', ['pending', 'processing'])->first();
if ( $orders ) {
session()->flash('error', trans('admin::app.response.order-pending'));
return redirect()->route($this->_config['redirect']);
}
try {
if ( Hash::check($data['password'], $customerRepository->password) ) {
if (Hash::check($data['password'], $customerRepository->password)) {
$orders = $customerRepository->all_orders->whereIn('status', ['pending', 'processing'])->first();
$this->customerRepository->delete($id);
if ($orders) {
session()->flash('error', trans('admin::app.response.order-pending'));
session()->flash('success', trans('admin::app.response.delete-success', ['name' => 'Customer']));
return redirect()->route($this->_config['redirect']);
} else {
$this->customerRepository->delete($id);
return redirect()->route('customer.session.index');
session()->flash('success', trans('admin::app.response.delete-success', ['name' => 'Customer']));
return redirect()->route('customer.session.index');
}
} else {
session()->flash('error', trans('shop::app.customer.account.address.delete.wrong-password'));
}
return redirect()->route('customer.session.index');
return redirect()->back();
}
} catch(\Exception $e) {
session()->flash('error', trans('admin::app.response.delete-failed', ['name' => 'Customer']));

View File

@ -59,15 +59,12 @@ class WishlistController extends Controller
/**
* Displays the listing resources if the customer having items in wishlist.
*
*
* @return \Illuminate\View\View
*/
public function index()
{
$wishlistItems = $this->wishlistRepository->findWhere([
'channel_id' => core()->getCurrentChannel()->id,
'customer_id' => auth()->guard('customer')->user()->id]
);
$wishlistItems = $this->wishlistRepository->getCustomerWhishlist();
return view($this->_config['view'])->with('items', $wishlistItems);
}
@ -166,14 +163,14 @@ class WishlistController extends Controller
} else {
session()->flash('info', trans('shop::app.wishlist.option-missing'));
return redirect()->route('shop.products.index', $wishlistItem->product->url_key);
return redirect()->route('shop.productOrCategory.index', $wishlistItem->product->url_key);
}
return redirect()->back();
} catch (\Exception $e) {
session()->flash('warning', $e->getMessage());
return redirect()->route('shop.products.index', ['slug' => $wishlistItem->product->url_key]);
return redirect()->route('shop.productOrCategory.index', ['slug' => $wishlistItem->product->url_key]);
}
}

View File

@ -31,7 +31,7 @@ class RegistrationEmail extends Mailable
public function build()
{
return $this->to($this->data['email'])
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.customer.registration.customer-registration'))
->view('shop::emails.customer.registration')->with('data', $this->data);
}

View File

@ -31,7 +31,7 @@ class VerificationEmail extends Mailable
public function build()
{
return $this->to($this->verificationData['email'])
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.customer.verification.subject'))
->view('shop::emails.customer.verification-email')->with('data', ['email' => $this->verificationData['email'], 'token' => $this->verificationData['token']]);
}

View File

@ -21,7 +21,7 @@ class CustomerResetPassword extends ResetPassword
}
return (new MailMessage)
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(__('shop::app.mail.forget-password.subject') )
->view('shop::emails.customer.forget-password', [
'user_name' => $notifiable->name,

View File

@ -62,8 +62,7 @@ class WishlistRepository extends Repository
return $this->model->find($id)->item_wishlist;
}
/**
/**
* get customer wishlist Items.
*
* @return mixed

View File

@ -0,0 +1,6 @@
<?php
return [
'name' => 'Webkul Bagisto Customer',
'version' => '0.0.1'
];

View File

@ -0,0 +1,6 @@
<?php
return [
'name' => 'Webkul Bagisto Inventory',
'version' => '0.0.1'
];

View File

@ -159,7 +159,23 @@ return [
'validation' => 'required',
'channel_based' => false,
'locale_based' => true
], [
], [
'name' => 'sandbox',
'title' => 'admin::app.admin.system.sandbox',
'type' => 'select',
'options' => [
[
'title' => 'Active',
'value' => true
], [
'title' => 'Inactive',
'value' => false
]
],
'validation' => 'required',
'channel_based' => false,
'locale_based' => true
], [
'name' => 'sort',
'title' => 'admin::app.admin.system.sort_order',
'type' => 'select',

View File

@ -0,0 +1,39 @@
<?php
namespace Webkul\Product\Helpers;
use Webkul\Product\Type\AbstractType;
class ProductType extends AbstractProduct
{
/**
* Checks if a ProductType may have variants
*
* @param string $typeKey as defined in config('product_types)
*
* @return bool whether ProductType is able to have variants
*/
public static function hasVariants(string $typeKey): bool
{
/** @var AbstractType $type */
$type = app(config('product_types.' . $typeKey . '.class'));
return $type->hasVariants();
}
/**
* Get all ProductTypes that are allowed to have variants
*
* @return array of product_types->keys
*/
public static function getAllTypesHavingVariants(): array
{
foreach (config('product_types') as $type) {
if (self::hasVariants($type['key'])) {
array_push($havingVariants, $type['key']);
}
}
return $havingVariants;
}
}

View File

@ -40,7 +40,7 @@ class Review extends AbstractProduct
if(array_key_exists($product->id, $avgRating))
return $avgRating[$product->id];
return $avgRating[$product->id] = number_format(round($product->reviews()->where('status', 'approved')->average('rating'), 2), 1);
return $avgRating[$product->id] = number_format(round($product->reviews()->where('status', 'approved')->avg('rating'), 2), 1);
}
/**

View File

@ -4,6 +4,7 @@ namespace Webkul\Product\Http\Controllers;
use Illuminate\Support\Facades\Event;
use Webkul\Product\Http\Requests\ProductForm;
use Webkul\Product\Helpers\ProductType;
use Webkul\Category\Repositories\CategoryRepository;
use Webkul\Product\Repositories\ProductRepository;
use Webkul\Product\Repositories\ProductDownloadableLinkRepository;
@ -140,13 +141,13 @@ class ProductController extends Controller
public function store()
{
if (! request()->get('family')
&& request()->input('type') == 'configurable'
&& ProductType::hasVariants(request()->input('type'))
&& request()->input('sku') != '') {
return redirect(url()->current() . '?type=' . request()->input('type') . '&family=' . request()->input('attribute_family_id') . '&sku=' . request()->input('sku'));
}
if (request()->input('type') == 'configurable'
if (ProductType::hasVariants(request()->input('type'))
&& (! request()->has('super_attributes')
|| ! count(request()->get('super_attributes')))) {

View File

@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Attribute\Repositories\AttributeOptionRepository;
use Webkul\Product\Helpers\ProductType;
use Webkul\Product\Repositories\ProductFlatRepository;
use Webkul\Product\Repositories\ProductAttributeValueRepository;
use Webkul\Product\Models\ProductAttributeValue;
@ -148,7 +149,7 @@ class ProductFlat
{
$this->createFlat($product);
if ($product->type == 'configurable') {
if (ProductType::hasVariants($product->type)) {
foreach ($product->variants()->get() as $variant) {
$this->createFlat($variant, $product);
}

View File

@ -227,6 +227,22 @@ class ProductRepository extends Repository
return $product;
}
/**
* Retrieve product from slug without throwing an exception (might return null)
*
* @param $slug
*
* @return mixed
*/
public function findBySlug($slug)
{
return app('Webkul\Product\Repositories\ProductFlatRepository')->findOneWhere([
'url_key' => $slug,
'locale' => app()->getLocale(),
'channel' => core()->getCurrentChannelCode(),
]);
}
/**
* Returns newly added product
*

View File

@ -3,6 +3,7 @@
namespace Webkul\Product\Type;
use Illuminate\Support\Facades\Storage;
use phpDocumentor\Reflection\Types\Boolean;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Product\Repositories\ProductRepository;
use Webkul\Product\Repositories\ProductAttributeValueRepository;
@ -57,7 +58,7 @@ abstract class AbstractType
/**
* Product Image helper instance
*
*
* @var ProductImage
*/
protected $productImageHelper;
@ -104,6 +105,13 @@ abstract class AbstractType
*/
protected $canBeMovedFromWishlistToCart = true;
/**
* Has child products aka variants
*
* @var boolean
*/
protected $hasVariants = false;
/**
* Create a new product type instance.
*
@ -159,17 +167,21 @@ abstract class AbstractType
$product->update($data);
foreach ($product->attribute_family->custom_attributes as $attribute) {
if ($attribute->type == 'boolean')
if ($attribute->type == 'boolean') {
$data[$attribute->code] = isset($data[$attribute->code]) && $data[$attribute->code] ? 1 : 0;
}
if (! isset($data[$attribute->code]))
if (! isset($data[$attribute->code])) {
continue;
}
if ($attribute->type == 'date' && $data[$attribute->code] == '')
if ($attribute->type == 'date' && $data[$attribute->code] == '') {
$data[$attribute->code] = null;
}
if ($attribute->type == 'multiselect' || $attribute->type == 'checkbox')
if ($attribute->type == 'multiselect' || $attribute->type == 'checkbox') {
$data[$attribute->code] = implode(",", $data[$attribute->code]);
}
if ($attribute->type == 'image' || $attribute->type == 'file') {
$data[$attribute->code] = gettype($data[$attribute->code]) == 'object'
@ -198,16 +210,18 @@ abstract class AbstractType
], $attributeValue->id
);
if ($attribute->type == 'image' || $attribute->type == 'file')
if ($attribute->type == 'image' || $attribute->type == 'file') {
Storage::delete($attributeValue->text_value);
}
}
}
$route = request()->route() ? request()->route()->getName() : "";
$route = request()->route() ? request()->route()->getName() : "";
if ($route != 'admin.catalog.products.massupdate') {
if (isset($data['categories']))
if (isset($data['categories'])) {
$product->categories()->sync($data['categories']);
}
$product->up_sells()->sync($data['up_sell'] ?? []);
@ -243,9 +257,10 @@ abstract class AbstractType
*/
public function isSaleable()
{
if (! $this->product->status)
if (! $this->product->status) {
return false;
}
return true;
}
@ -260,7 +275,7 @@ abstract class AbstractType
}
/**
* Return true if this product can have inventory
* Return true if this product can be composite
*
* @return boolean
*/
@ -269,6 +284,16 @@ abstract class AbstractType
return $this->isComposite;
}
/**
* Return true if this product can have variants
*
* @return bool
*/
public function hasVariants(): bool
{
return $this->hasVariants;
}
/**
* @param integer $qty
* @return bool
@ -277,7 +302,7 @@ abstract class AbstractType
{
return $this->haveSufficientQuantity;
}
/**
* Return true if this product can have inventory
*
@ -310,8 +335,9 @@ abstract class AbstractType
->pluck('id');
foreach ($this->product->inventories as $inventory) {
if (is_numeric($index = $channelInventorySourceIds->search($inventory->inventory_source_id)))
if (is_numeric($index = $channelInventorySourceIds->search($inventory->inventory_source_id))) {
$total += $inventory->qty;
}
}
$orderedInventory = $this->product->ordered_inventories()
@ -344,11 +370,13 @@ abstract class AbstractType
*/
public function getEditableAttributes($group = null, $skipSuperAttribute = true)
{
if ($skipSuperAttribute)
if ($skipSuperAttribute) {
$this->skipAttributes = array_merge($this->product->super_attributes->pluck('code')->toArray(), $this->skipAttributes);
}
if (! $group)
if (! $group) {
return $this->product->attribute_family->custom_attributes()->whereNotIn('attributes.code', $this->skipAttributes)->get();
}
return $group->custom_attributes()->whereNotIn('code', $this->skipAttributes)->get();
}
@ -380,8 +408,9 @@ abstract class AbstractType
*/
public function getMinimalPrice()
{
if ($this->haveSpecialPrice())
if ($this->haveSpecialPrice()) {
return $this->product->special_price;
}
return $this->product->price;
}
@ -421,11 +450,13 @@ abstract class AbstractType
*/
public function haveSpecialPrice()
{
if (is_null($this->product->special_price) || ! (float) $this->product->special_price)
if (is_null($this->product->special_price) || ! (float) $this->product->special_price) {
return false;
}
if (core()->isChannelDateInInterval($this->product->special_price_from, $this->product->special_price_to))
if (core()->isChannelDateInInterval($this->product->special_price_from, $this->product->special_price_to)) {
return true;
}
return false;
}
@ -479,8 +510,9 @@ abstract class AbstractType
$data = $this->getQtyRequest($data);
if (! $this->haveSufficientQuantity($data['quantity']))
if (! $this->haveSufficientQuantity($data['quantity'])) {
return trans('shop::app.checkout.cart.quantity.inventory_warning');
}
$price = $this->getFinalPrice();
@ -514,12 +546,13 @@ abstract class AbstractType
*/
public function getQtyRequest($data)
{
if ($item = Cart::getItemByProduct(['additional' => $data]))
if ($item = Cart::getItemByProduct(['additional' => $data])) {
$data['quantity'] += $item->quantity;
}
return $data;
}
/**
*
* @param array $options1
@ -546,7 +579,7 @@ abstract class AbstractType
return true;
}
/**
* Returns additional information for items
*
@ -590,8 +623,9 @@ abstract class AbstractType
{
$price = $item->product->getTypeInstance()->getFinalPrice();
if ($price == $item->base_price)
if ($price == $item->base_price) {
return;
}
$item->base_price = $price;
$item->price = core()->convertPrice($price);

View File

@ -22,7 +22,7 @@ class Configurable extends AbstractType
/**
* These blade files will be included in product edit page
*
*
* @var array
*/
protected $additionalViews = [
@ -47,6 +47,13 @@ class Configurable extends AbstractType
*/
protected $showQuantityBox = true;
/**
* Has child products aka variants
*
* @var boolean
*/
protected $hasVariants = true;
/**
* @param array $data
* @return Product
@ -136,9 +143,15 @@ class Configurable extends AbstractType
];
}
$typeOfVariants = 'simple';
$productInstance = app(config('product_types.' . $product->type . '.class'));
if (isset($productInstance->variantsType) && ! in_array($productInstance->variantsType , ['bundle', 'configurable', 'grouped'])) {
$typeOfVariants = $productInstance->variantsType;
}
$variant = $this->productRepository->getModel()->create([
'parent_id' => $product->id,
'type' => 'simple',
'type' => $typeOfVariants,
'attribute_family_id' => $product->attribute_family_id,
'sku' => $data['sku'],
]);
@ -416,7 +429,7 @@ class Configurable extends AbstractType
return $products;
}
/**
*
* @param array $options1
@ -451,7 +464,7 @@ class Configurable extends AbstractType
];
}
return $data;
return $data;
}
/**

View File

@ -0,0 +1,6 @@
<?php
return [
'name' => 'Webkul Bagisto Product',
'version' => '0.0.1'
];

View File

@ -55,7 +55,7 @@ class DownloadableLinkPurchasedRepository extends Repository
*/
public function saveLinks($orderItem)
{
if ($orderItem->type != 'downloadable' || ! isset($orderItem->additional['links']))
if (stristr($orderItem->type,'downloadable') === false || ! isset($orderItem->additional['links']))
return;
foreach ($orderItem->additional['links'] as $linkId) {

View File

@ -3,6 +3,7 @@
namespace Webkul\Sales\Repositories;
use Illuminate\Container\Container as App;
use Illuminate\Support\Facades\Event;
use Webkul\Core\Eloquent\Repository;
use Webkul\Sales\Contracts\OrderItem;
@ -51,7 +52,7 @@ class OrderItemRepository extends Repository
$totalInvoiced = $baseTotalInvoiced = 0;
$taxInvoiced = $baseTaxInvoiced = 0;
$totalRefunded = $baseTotalRefunded = 0;
$taxRefunded = $baseTaxRefunded = 0;

View File

@ -3,15 +3,15 @@
namespace Webkul\Sales\Repositories;
use Illuminate\Container\Container as App;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Webkul\Core\Eloquent\Repository;
use Webkul\Sales\Contracts\Order;
use Webkul\Sales\Repositories\OrderItemRepository;
use Webkul\Core\Models\CoreConfig;
use Webkul\Sales\Contracts\Order;
use Webkul\Sales\Models\Order as OrderModel;
/**
* Order Reposotory
* Order Repository
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
@ -35,16 +35,16 @@ class OrderRepository extends Repository
/**
* Create a new repository instance.
*
* @param Webkul\Sales\Repositories\OrderItemRepository $orderItemRepository
* @param Webkul\Sales\Repositories\DownloadableLinkPurchasedRepository $downloadableLinkPurchasedRepository
* @param Webkul\Sales\Repositories\OrderItemRepository $orderItemRepository
* @param Webkul\Sales\Repositories\DownloadableLinkPurchasedRepository $downloadableLinkPurchasedRepository
*
* @return void
*/
public function __construct(
OrderItemRepository $orderItemRepository,
DownloadableLinkPurchasedRepository $downloadableLinkPurchasedRepository,
App $app
)
{
) {
$this->orderItemRepository = $orderItemRepository;
$this->downloadableLinkPurchasedRepository = $downloadableLinkPurchasedRepository;
@ -58,13 +58,14 @@ class OrderRepository extends Repository
* @return Mixed
*/
function model()
public function model()
{
return Order::class;
}
/**
* @param array $data
*
* @return mixed
*/
public function create(array $data)
@ -95,12 +96,15 @@ class OrderRepository extends Repository
$order->payment()->create($data['payment']);
if (isset($data['shipping_address']))
if (isset($data['shipping_address'])) {
$order->addresses()->create($data['shipping_address']);
}
$order->addresses()->create($data['billing_address']);
foreach ($data['items'] as $item) {
Event::fire('checkout.order.orderitem.save.before', $data);
$orderItem = $this->orderItemRepository->create(array_merge($item, ['order_id' => $order->id]));
if (isset($item['children']) && $item['children']) {
@ -112,6 +116,8 @@ class OrderRepository extends Repository
$this->orderItemRepository->manageInventory($orderItem);
$this->downloadableLinkPurchasedRepository->saveLinks($orderItem, 'available');
Event::fire('checkout.order.orderitem.save.after', $data);
}
Event::fire('checkout.order.save.after', $order);
@ -128,20 +134,23 @@ class OrderRepository extends Repository
/**
* @param int $orderId
*
* @return mixed
*/
public function cancel($orderId)
{
$order = $this->findOrFail($orderId);
if (! $order->canCancel())
if (! $order->canCancel()) {
return false;
}
Event::fire('sales.order.cancel.before', $order);
foreach ($order->items as $item) {
if (! $item->qty_to_cancel)
if (! $item->qty_to_cancel) {
continue;
}
$orderItems = [];
@ -152,11 +161,12 @@ class OrderRepository extends Repository
} else {
$orderItems[] = $item;
}
foreach ($orderItems as $orderItem) {
if ($orderItem->product)
if ($orderItem->product) {
$this->orderItemRepository->returnQtyToProductInventory($orderItem);
}
if ($orderItem->qty_ordered) {
$orderItem->qty_canceled += $orderItem->qty_to_cancel;
$orderItem->save();
@ -188,20 +198,20 @@ class OrderRepository extends Repository
{
$config = new CoreConfig();
$invoiceNumberPrefix = $config->where('code','=',"sales.orderSettings.order_number.order_number_prefix")->first()
? $config->where('code','=',"sales.orderSettings.order_number.order_number_prefix")->first()->value : false;
$invoiceNumberLength = $config->where('code','=',"sales.orderSettings.order_number.order_number_length")->first()
? $config->where('code','=',"sales.orderSettings.order_number.order_number_length")->first()->value : false;
$invoiceNumberSuffix = $config->where('code','=',"sales.orderSettings.order_number.order_number_suffix")->first()
? $config->where('code','=',"sales.orderSettings.order_number.order_number_suffix")->first()->value: false;
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;
}
$lastOrder = $this->model->orderBy('id', 'desc')->limit(1)->first();
$lastId = $lastOrder ? $lastOrder->id : 0;
if ($invoiceNumberLength && ( $invoiceNumberPrefix || $invoiceNumberSuffix) ) {
$invoiceNumber = $invoiceNumberPrefix . sprintf("%0{$invoiceNumberLength}d", 0) . ($lastId + 1) . $invoiceNumberSuffix;
if ($invoiceNumberLength && ($invoiceNumberPrefix || $invoiceNumberSuffix)) {
$invoiceNumber = ($invoiceNumberPrefix->value) . sprintf("%0{$invoiceNumberLength->value}d", 0) . ($lastId + 1) . ($invoiceNumberSuffix->value);
} else {
$invoiceNumber = $lastId + 1;
}
@ -211,13 +221,14 @@ class OrderRepository extends Repository
/**
* @param mixed $order
*
* @return void
*/
public function isInCompletedState($order)
{
$totalQtyOrdered = $totalQtyInvoiced = $totalQtyShipped = $totalQtyRefunded = $totalQtyCanceled = 0;
foreach ($order->items()->get() as $item) {
foreach ($order->items()->get() as $item) {
$totalQtyOrdered += $item->qty_ordered;
$totalQtyInvoiced += $item->qty_invoiced;
@ -233,14 +244,16 @@ class OrderRepository extends Repository
if ($totalQtyOrdered != ($totalQtyRefunded + $totalQtyCanceled)
&& $totalQtyOrdered == $totalQtyInvoiced + $totalQtyCanceled
&& $totalQtyOrdered == $totalQtyShipped + $totalQtyRefunded + $totalQtyCanceled)
&& $totalQtyOrdered == $totalQtyShipped + $totalQtyRefunded + $totalQtyCanceled) {
return true;
}
return false;
}
/**
* @param mixed $order
*
* @return void
*/
public function isInCanceledState($order)
@ -257,13 +270,14 @@ class OrderRepository extends Repository
/**
* @param mixed $order
*
* @return void
*/
public function isInClosedState($order)
{
$totalQtyOrdered = $totalQtyRefunded = $totalQtyCanceled = 0;
foreach ($order->items()->get() as $item) {
foreach ($order->items()->get() as $item) {
$totalQtyOrdered += $item->qty_ordered;
$totalQtyRefunded += $item->qty_refunded;
$totalQtyCanceled += $item->qty_canceled;
@ -274,19 +288,22 @@ class OrderRepository extends Repository
/**
* @param mixed $order
*
* @return void
*/
public function updateOrderStatus($order)
{
$status = 'processing';
if ($this->isInCompletedState($order))
if ($this->isInCompletedState($order)) {
$status = 'completed';
}
if ($this->isInCanceledState($order))
if ($this->isInCanceledState($order)) {
$status = 'canceled';
else if ($this->isInClosedState($order))
} elseif ($this->isInClosedState($order)) {
$status = 'closed';
}
$order->status = $status;
$order->save();
@ -294,6 +311,7 @@ class OrderRepository extends Repository
/**
* @param mixed $order
*
* @return mixed
*/
public function collectTotals($order)
@ -352,4 +370,4 @@ class OrderRepository extends Repository
return $order;
}
}
}

View File

@ -0,0 +1,6 @@
<?php
return [
'name' => 'Webkul Bagisto Sales',
'version' => '0.0.1'
];

View File

@ -22,7 +22,7 @@ class DownloadableProductDataGrid extends DataGrid
$queryBuilder = DB::table('downloadable_link_purchased')
->leftJoin('orders', 'downloadable_link_purchased.order_id', '=', 'orders.id')
->addSelect('downloadable_link_purchased.*', 'orders.increment_id')
->addSelect(DB::raw('(downloadable_link_purchased.download_bought - downloadable_link_purchased.download_used) as remaining_downloads'))
->addSelect(DB::raw('('.DB::getTablePrefix().'downloadable_link_purchased.download_bought - '.DB::getTablePrefix().'downloadable_link_purchased.download_used) as remaining_downloads'))
->where('downloadable_link_purchased.customer_id', auth()->guard('customer')->user()->id);
$this->addFilter('status', 'downloadable_link_purchased.status');
@ -97,7 +97,7 @@ class DownloadableProductDataGrid extends DataGrid
'wrapper' => function ($value) {
if (! $value->download_bought)
return trans('shop::app.customer.account.downloadable_products.unlimited');
return $value->remaining_downloads;
},
]);

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class AddColumnUrlPathToCategoryTranslations extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('category_translations', function (Blueprint $table) {
$table->string('url_path', 2048)
->comment('maintained by database triggers');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('category_translations', function (Blueprint $table) {
$table->dropColumn('url_path');
});
}
}

View File

@ -0,0 +1,68 @@
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Migrations\Migration;
class AddStoredFunctionToGetUrlPathOfCategory extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$functionSQL = <<< SQL
DROP FUNCTION IF EXISTS `get_url_path_of_category`;
CREATE FUNCTION get_url_path_of_category(
categoryId INT,
localeCode VARCHAR(255)
)
RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE urlPath VARCHAR(255);
-- Category with id 1 is root by default
IF categoryId <> 1
THEN
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
WHERE
node._lft >= parent._lft
AND node._rgt <= parent._rgt
AND node.id = categoryId
AND parent.id <> 1
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);
END IF;
ELSE
SET urlPath = '';
END IF;
RETURN urlPath;
END;
SQL;
DB::unprepared($functionSQL);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
DB::unprepared('DROP FUNCTION IF EXISTS `get_url_path_of_category`;');
}
}

View File

@ -0,0 +1,98 @@
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Migrations\Migration;
class AddTriggerToCategoryTranslations extends Migration
{
private const TRIGGER_NAME_INSERT = 'trig_category_translations_insert';
private const TRIGGER_NAME_UPDATE = 'trig_category_translations_update';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$triggerBody = $this->getTriggerBody();
$insertTrigger = <<< SQL
CREATE TRIGGER %s
BEFORE INSERT ON category_translations
FOR EACH ROW
BEGIN
$triggerBody
END;
SQL;
$updateTrigger = <<< SQL
CREATE TRIGGER %s
BEFORE UPDATE ON category_translations
FOR EACH ROW
BEGIN
$triggerBody
END;
SQL;
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_INSERT));
DB::unprepared(sprintf($insertTrigger, self::TRIGGER_NAME_INSERT));
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_UPDATE));
DB::unprepared(sprintf($updateTrigger, self::TRIGGER_NAME_UPDATE));
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_INSERT));
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_UPDATE));
}
/**
* Returns trigger body as string
*
* @return string
*/
private function getTriggerBody()
{
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
WHERE
node._lft >= parent._lft
AND node._rgt <= parent._rgt
AND node.id = (SELECT parent_id FROM categories WHERE id = NEW.category_id)
AND parent.id <> 1
AND parent_translations.locale = NEW.locale
GROUP BY
node.id;
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

@ -0,0 +1,35 @@
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Migrations\Migration;
use Webkul\Category\Models\CategoryTranslation;
class AddUrlPathToExistingCategoryTranslations extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$sqlStoredFunction = <<< SQL
SELECT get_url_path_of_category(:category_id, :locale_code) AS url_path;
SQL;
$categoryTranslationsTableName = app(CategoryTranslation::class)->getTable();
foreach (DB::table($categoryTranslationsTableName)->get() as $categoryTranslation) {
$urlPathQueryResult = DB::selectOne($sqlStoredFunction, [
'category_id' => $categoryTranslation->category_id,
'locale_code' => $categoryTranslation->locale,
]);
$url_path = $urlPathQueryResult ? $urlPathQueryResult->url_path : '';
DB::table($categoryTranslationsTableName)
->where('id', $categoryTranslation->id)
->update(['url_path' => $url_path]);
}
}
}

View File

@ -0,0 +1,99 @@
<?php
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Migrations\Migration;
class AddTriggerToCategories extends Migration
{
private const TRIGGER_NAME_INSERT = 'trig_categories_insert';
private const TRIGGER_NAME_UPDATE = 'trig_categories_update';
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$triggerBody = $this->getTriggerBody();
$insertTrigger = <<< SQL
CREATE TRIGGER %s
AFTER INSERT ON categories
FOR EACH ROW
BEGIN
$triggerBody
END;
SQL;
$updateTrigger = <<< SQL
CREATE TRIGGER %s
AFTER UPDATE ON categories
FOR EACH ROW
BEGIN
$triggerBody
END;
SQL;
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_INSERT));
DB::unprepared(sprintf($insertTrigger, self::TRIGGER_NAME_INSERT));
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_UPDATE));
DB::unprepared(sprintf($updateTrigger, self::TRIGGER_NAME_UPDATE));
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_INSERT));
DB::unprepared(sprintf('DROP TRIGGER IF EXISTS %s;', self::TRIGGER_NAME_UPDATE));
}
/**
* Returns trigger body as string
*
* @return string
*/
private function getTriggerBody(): string
{
return <<< SQL
DECLARE urlPath VARCHAR(255);
DECLARE localeCode VARCHAR(255);
DECLARE done INT;
DECLARE curs CURSOR FOR (SELECT category_translations.locale
FROM category_translations
WHERE category_id = NEW.id);
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
IF EXISTS (
SELECT *
FROM category_translations
WHERE category_id = NEW.id
)
THEN
OPEN curs;
SET done = 0;
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;
UNTIL done END REPEAT;
CLOSE curs;
END IF;
SQL;
}
}

View File

@ -96,7 +96,7 @@ class CartController extends Controller
$product = $this->productRepository->find($id);
return redirect()->route('shop.products.index', ['slug' => $product->url_key]);
return redirect()->route('shop.productOrCategory.index', ['slug' => $product->url_key]);
}
return redirect()->back();

View File

@ -38,17 +38,4 @@ class CategoryController extends Controller
$this->_config = request('_config');
}
/**
* Display a listing of the resource.
*
* @param string $slug
* @return \Illuminate\View\View
*/
public function index($slug)
{
$category = $this->categoryRepository->findBySlugOrFail($slug);
return view($this->_config['view'], compact('category'));
}
}

View File

@ -60,7 +60,7 @@ class OrderController extends Controller
/**
* Display a listing of the resource.
*
* @return \Illuminate\View\View
* @return \Illuminate\View\View
*/
public function index()
{
@ -71,7 +71,7 @@ class OrderController extends Controller
* Show the view for the specified resource.
*
* @param int $id
* @return \Illuminate\View\View
* @return \Illuminate\View\View
*/
public function view($id)
{

View File

@ -79,21 +79,6 @@ class ProductController extends Controller
$this->_config = request('_config');
}
/**
* Display a listing of the resource.
*
* @param string $slug
* @return \Illuminate\View\View
*/
public function index($slug)
{
$product = $this->productRepository->findBySlugOrFail($slug);
$customer = auth()->guard('customer')->user();
return view($this->_config['view'], compact('product', 'customer'));
}
/**
* Download image or file
*

View File

@ -0,0 +1,78 @@
<?php
namespace Webkul\Shop\Http\Controllers;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Webkul\Category\Models\CategoryTranslation;
use Webkul\Product\Models\ProductFlat;
use Webkul\Category\Repositories\CategoryRepository;
use Webkul\Product\Repositories\ProductRepository;
class ProductsCategoriesProxyController extends Controller
{
/**
* Contains route related configuration
*
* @var array
*/
protected $_config;
/**
* CategoryRepository object
*
* @var CategoryRepository
*/
protected $categoryRepository;
/**
* ProductRepository object
*
* @var ProductRepository
*/
protected $productRepository;
/**
* Create a new controller instance.
*
* @param CategoryRepository $categoryRepository
* @param ProductRepository $productRepository
*
* @return void
*/
public function __construct(CategoryRepository $categoryRepository, ProductRepository $productRepository)
{
$this->categoryRepository = $categoryRepository;
$this->productRepository = $productRepository;
$this->_config = request('_config');
}
/**
* Display a listing of the resource which can be a category or a product.
*
*
* @param string $slugOrPath
*
* @return \Illuminate\View\View
*/
public function index(string $slugOrPath)
{
if ($category = $this->categoryRepository->findByPath($slugOrPath)) {
return view($this->_config['category_view'], compact('category'));
}
if ($product = $this->productRepository->findBySlug($slugOrPath)) {
$customer = auth()->guard('customer')->user();
return view($this->_config['product_view'], compact('product', 'customer'));
}
abort(404);
}
}

View File

@ -44,8 +44,7 @@ class ReviewController extends Controller
public function __construct(
ProductRepository $productRepository,
ProductReviewRepository $productReviewRepository
)
{
) {
$this->productRepository = $productRepository;
$this->productReviewRepository = $productReviewRepository;
@ -57,15 +56,17 @@ class ReviewController extends Controller
* Show the form for creating a new resource.
*
* @param string $slug
* @return \Illuminate\View\View
* @return \Illuminate\View\View
*/
public function create($slug)
{
$product = $this->productRepository->findBySlugOrFail($slug);
if (auth()->guard('customer')->check() || core()->getConfigData('catalog.products.review.guest_review')) {
$product = $this->productRepository->findBySlugOrFail($slug);
$guest_review = core()->getConfigData('catalog.products.review.guest_review');
return view($this->_config['view'], compact('product', 'guest_review'));
}
return view($this->_config['view'], compact('product', 'guest_review'));
abort(404);
}
/**
@ -103,7 +104,7 @@ class ReviewController extends Controller
* Display reviews of particular product.
*
* @param string $slug
* @return \Illuminate\View\View
* @return \Illuminate\View\View
*/
public function show($slug)
{

View File

@ -13,11 +13,6 @@ Route::group(['middleware' => ['web', 'locale', 'theme', 'currency']], function
//unsubscribe
Route::get('/unsubscribe/{token}', 'Webkul\Shop\Http\Controllers\SubscriptionController@unsubscribe')->name('shop.unsubscribe');
//Store front header nav-menu fetch
Route::get('/categories/{slug}', 'Webkul\Shop\Http\Controllers\CategoryController@index')->defaults('_config', [
'view' => 'shop::products.index'
])->name('shop.categories.index');
//Store front search
Route::get('/search', 'Webkul\Shop\Http\Controllers\SearchController@index')->defaults('_config', [
'view' => 'shop::search.search'
@ -89,11 +84,6 @@ Route::group(['middleware' => ['web', 'locale', 'theme', 'currency']], function
//Shop buynow button action
Route::get('move/wishlist/{id}', 'Webkul\Shop\Http\Controllers\CartController@moveToWishlist')->name('shop.movetowishlist');
//Show Product Details Page(For individually Viewable Product)
Route::get('/products/{slug}', 'Webkul\Shop\Http\Controllers\ProductController@index')->defaults('_config', [
'view' => 'shop::products.view'
])->name('shop.products.index');
Route::get('/downloadable/download-sample/{type}/{id}', 'Webkul\Shop\Http\Controllers\ProductController@downloadSample')->name('shop.downloadable.download_sample');
// Show Product Review Form
@ -307,5 +297,13 @@ Route::group(['middleware' => ['web', 'locale', 'theme', 'currency']], function
Route::get('page/{slug}', 'Webkul\CMS\Http\Controllers\Shop\PagePresenterController@presenter')->name('shop.cms.page');
Route::get('{slugOrPath}', \Webkul\Shop\Http\Controllers\ProductsCategoriesProxyController::class . '@index')
->defaults('_config', [
'product_view' => 'shop::products.view',
'category_view' => 'shop::products.index'
])
->where('slugOrPath', '^([a-z0-9-]+\/?)+$')
->name('shop.productOrCategory.index');
Route::fallback('Webkul\Shop\Http\Controllers\HomeController@notFound');
});

View File

@ -31,7 +31,7 @@ class SubscriptionEmail extends Mailable
public function build()
{
return $this->to($this->subscriptionData['email'])
->from(env('SHOP_MAIL_FROM'))
->from(config('mail.from'))
->subject(trans('shop::app.mail.customer.subscription.subject'))
->view('shop::emails.customer.subscription-email')->with('data', ['content' => 'You Are Subscribed', 'token' => $this->subscriptionData['token']]);
}

View File

@ -25,6 +25,8 @@ class ShopServiceProvider extends ServiceProvider
*/
public function boot(Router $router)
{
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
$this->loadRoutesFrom(__DIR__ . '/../Http/routes.php');
$this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'shop');

View File

@ -3,26 +3,26 @@ export const messages = {
required : (field) => 'حقل' + field + ' مطلوب',
alpha : (field) => field + ' يجب ان يحتوي على حروف فقط',
alpha_num : (field) => field + ' قد يحتوي فقط على حروف وارقام',
min : (field) => 'الحقل ' + field + ' يجب ان يحتوي على {length} حروف على الأقل',
min : (field, length) => 'الحقل ' + field + ' يجب ان يحتوي على ' + length + ' حروف على الأقل',
numeric : (field) => field + ' يمكن ان يحتوي فقط على ارقام',
oneOf : (field) => 'الحقل ' + field + 'يجب ان يكون قيمة صحيحة',
regex : (field) => 'الحقل' + field+ ' غير صحيح',
required_if : (field) => 'حقل' + field + ' مطلوب',
size : (field) => field + ' يجب ان يكون اقل من {size} كيلوبايت',
min_value : (field) => 'قيمة الحقل' + field + ' يجب ان تكون اكبر من {min} او تساويها',
size : (field, size) => field + ' يجب ان يكون اقل من ' + size + ' كيلوبايت',
min_value : (field, min) => 'قيمة الحقل' + field + ' يجب ان تكون اكبر من ' + min + ' او تساويها',
alpha_spaces : (field) => field + ' قد يحتوي فقط على حروف ومسافات',
between : (field) => 'قيمة ' +field+ ' يجب ان تكون ما بين {min} و {max}',
between : (field, min, max) => 'قيمة ' +field+ ' يجب ان تكون ما بين ' + min + ' و ' + max,
confirmed : (field) => field + ' لا يماثل التأكيد',
digits : (field) => field + ' يجب ان تحتوي فقط على ارقام والا يزيد عددها عن {length} رقم',
dimensions : (field) => field + ' يجب ان تكون بمقاس {width} بكسل في {height} بكسل',
digits : (field, length) => field + ' يجب ان تحتوي فقط على ارقام والا يزيد عددها عن ' + length + ' رقم',
dimensions : (field, width, height) => field + ' يجب ان تكون بمقاس ' + width + ' بكسل في ' + height + ' بكسل',
email : (field) => field + ' يجب ان يكون بريدا اليكتروني صحيح',
excluded : (field) => 'الحقل' + field +'غير صحيح',
ext : (field) =>'نوع مل'+ field + 'غير صحيح',
image : (field) => field + ' يجب ان تكون صورة',
integer : (field) => 'الحقل ' +field + ' يجب ان يكون عدداً صحيحاً',
length : (field) => 'حقل'+ field + ' يجب الا يزيد عن {length}',
max_value : (field) => 'قيمة الحقل '+ field + ' يجب ان تكون اصغر من {min} او تساويها',
max : (field) => 'الحقل' + field + 'يجب ان يحتوي على {length} حروف على الأكثر',
length : (field, length) => 'حقل'+ field + ' يجب الا يزيد عن ' + length,
max_value : (field, min) => 'قيمة الحقل '+ field + ' يجب ان تكون اصغر من ' + min + ' او تساويها',
max : (field, length) => 'الحقل' + field + 'يجب ان يحتوي على ' + length + ' حروف على الأكثر',
mimes : (field) => 'نوع ملف' + field + 'غير صحيح'
}
}

View File

@ -54,7 +54,7 @@
@if (isset($item->additional['attributes']))
<div class="item-options">
@foreach ($item->additional['attributes'] as $attribute)
<b>{{ $attribute['attribute_name'] }} : </b>{{ $attribute['option_label'] }}</br>
@endforeach

View File

@ -296,10 +296,11 @@
else
paymentHtml = Vue.compile(response.data.html)
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] + 1;
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] - 1;
this_this.current_step = this_this.step_numbers[response.data.jump_to_section];
shippingMethods = response.data.shippingMethods;
paymentMethods = response.data.paymentMethods;
this_this.getOrderSummary();
})
@ -320,7 +321,7 @@
this_this.disable_button = false;
paymentHtml = Vue.compile(response.data.html)
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] + 1;
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] - 1;
this_this.current_step = this_this.step_numbers[response.data.jump_to_section];
paymentMethods = response.data.paymentMethods;
@ -344,7 +345,7 @@
this_this.disable_button = false;
reviewHtml = Vue.compile(response.data.html)
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] + 1;
this_this.completed_step = this_this.step_numbers[response.data.jump_to_section] - 1;
this_this.current_step = this_this.step_numbers[response.data.jump_to_section];
this_this.getOrderSummary();
@ -608,7 +609,7 @@
methods: {
onSubmit: function() {
var this_this = this;
const emptyCouponErrorText = "Please enter a coupon code";
axios.post('{{ route('shop.checkout.check.coupons') }}', {code: this_this.coupon_code})
.then(function(response) {
this_this.$emit('onApplyCoupon');
@ -618,7 +619,10 @@
.catch(function(error) {
this_this.couponChanged = true;
this_this.error_message = error.response.data.message;
this_this.error_message = (error.response.data.message === "The given data was invalid.")?
emptyCouponErrorText :
(error.response.data.message === "Cannot Apply Coupon")?
"Sorry, this Coupon code is invalid":error.response.data.message;
});
},

View File

@ -19,12 +19,10 @@
<span class="checkmark"></span>
{{-- <label class="radio-view" for="{{ $payment['method'] }}"></label> --}}
<span class="payment-method method-label">
<b>{{ $payment['method_title'] }}</b>
</span>
</label>
<span class="payment-method method-label">
<b>{{ $payment['method_title'] }}</b>
</span>
</div>
<div class="line-two mt-5">

View File

@ -30,6 +30,9 @@
<div class="account-table-content" style="width: 50%;">
<table style="color: #5E5E5E;">
<tbody>
{!! view_render_event(
'bagisto.shop.customers.account.profile.view.table.before', ['customer' => $customer])
!!}
<tr>
<td>{{ __('shop::app.customer.account.profile.fname') }}</td>
<td>{{ $customer->first_name }}</td>
@ -54,6 +57,9 @@
<td>{{ __('shop::app.customer.account.profile.email') }}</td>
<td>{{ $customer->email }}</td>
</tr>
{!! view_render_event(
'bagisto.shop.customers.account.profile.view.table.after', ['customer' => $customer])
!!}
{{-- @if ($customer->subscribed_to_news_letter == 1)
<tr>

View File

@ -19,7 +19,7 @@
@if (count($reviews) > 1)
<div class="account-action">
<a href="{{ route('customer.review.deleteall') }}">{{ __('shop::app.wishlist.deleteall') }}</a>
<a href="{{ route('customer.review.deleteall') }}">{{ __('shop::app.customer.account.wishlist.deleteall') }}</a>
</div>
@endif

View File

@ -22,7 +22,7 @@
<a href="{{ route('customer.wishlist.removeall') }}">{{ __('shop::app.customer.account.wishlist.deleteall') }}</a>
</div>
@endif
<div class="horizontal-rule"></div>
</div>
@ -43,10 +43,10 @@
<div class="info">
<div class="product-name">
{{ $item->product->name }}
@if (isset($item->additional['attributes']))
<div class="item-options">
@foreach ($item->additional['attributes'] as $attribute)
<b>{{ $attribute['attribute_name'] }} : </b>{{ $attribute['option_label'] }}</br>
@endforeach
@ -76,6 +76,10 @@
<div class="horizontal-rule mb-10 mt-10"></div>
@endforeach
<div class="bottom-toolbar">
{{ $items->links() }}
</div>
@else
<div class="empty">
{{ __('customer::app.wishlist.empty') }}

View File

@ -189,7 +189,7 @@
<p style="font-size: 16px;color: #5E5E5E;line-height: 24px;">
{!!
__('shop::app.mail.order.help', [
'support_email' => '<a style="color:#0041FF" href="mailto:' . env('ADMIN_MAIL_TO') . '">' . env('ADMIN_MAIL_TO'). '</a>'
'support_email' => '<a style="color:#0041FF" href="mailto:' . config('mail.admin.address') . '">' . config('mail.admin.address') . '</a>'
])
!!}
</p>

View File

@ -18,7 +18,7 @@
<ul class="list-group">
@foreach ($categories as $key => $category)
<li>
<a href="{{ route('shop.categories.index', $category->slug) }}">{{ $category->name }}</a>
<a href="{{ route('shop.productOrCategory.index', $category->slug) }}">{{ $category->name }}</a>
</li>
@endforeach
</ul>

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