diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 05992519f..6ca0cbdf5 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -14,12 +14,10 @@ class Kernel extends HttpKernel * @var array */ protected $middleware = [ - \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, - \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, - \App\Http\Middleware\TrimStrings::class, - \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, - \App\Http\Middleware\TrustProxies::class, - // \Illuminate\Session\Middleware\StartSession::class, + \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, + \App\Http\Middleware\EncryptCookies::class, + \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, + \Illuminate\Session\Middleware\StartSession::class, ]; /** @@ -29,9 +27,6 @@ class Kernel extends HttpKernel */ protected $middlewareGroups = [ 'web' => [ - \App\Http\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, diff --git a/config/database.php b/config/database.php index e486bd20d..71280b1b1 100644 --- a/config/database.php +++ b/config/database.php @@ -50,7 +50,7 @@ return [ 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', - 'strict' => true, + 'strict' => false, 'engine' => 'InnoDB ROW_FORMAT=DYNAMIC', ], diff --git a/packages/Webkul/Admin/src/DataGrids/ExchangeRatesDataGrid.php b/packages/Webkul/Admin/src/DataGrids/ExchangeRatesDataGrid.php index 9d4f085e0..9171064dd 100644 --- a/packages/Webkul/Admin/src/DataGrids/ExchangeRatesDataGrid.php +++ b/packages/Webkul/Admin/src/DataGrids/ExchangeRatesDataGrid.php @@ -27,10 +27,10 @@ class ExchangeRatesDataGrid return DataGrid::make([ 'name' => 'Exchange Rates', - 'table' => 'currency_exchange_rates', - 'select' => 'id', + 'table' => 'currency_exchange_rates as cer', + 'select' => 'cer.id', 'perpage' => 5, - 'aliased' => false, //use this with false as default and true in case of joins + 'aliased' => true, //use this with false as default and true in case of joins 'massoperations' =>[ [ @@ -56,13 +56,13 @@ class ExchangeRatesDataGrid ], 'join' => [ - // [ - // 'join' => 'leftjoin', - // 'table' => 'roles as r', - // 'primaryKey' => 'u.role_id', - // 'condition' => '=', - // 'secondaryKey' => 'r.id', - // ] + [ + 'join' => 'leftjoin', + 'table' => 'currencies as curr', + 'primaryKey' => 'cer.target_currency', + 'condition' => '=', + 'secondaryKey' => 'curr.id', + ] ], //use aliasing on secodary columns if join is performed @@ -70,39 +70,38 @@ class ExchangeRatesDataGrid 'columns' => [ [ - 'name' => 'id', + 'name' => 'cer.id', 'alias' => 'exchID', 'type' => 'number', 'label' => 'Rate ID', 'sortable' => true, ], [ - 'name' => 'target_currency', - 'alias' => 'exchTargetCurrency', + 'name' => 'curr.name', + 'alias' => 'currencyname', 'type' => 'string', - 'label' => 'Target Currency', + 'label' => 'Currency Name', 'sortable' => true, ], [ - 'name' => 'rate', + 'name' => 'cer.rate', 'alias' => 'exchRate', 'type' => 'string', 'label' => 'Exchange Rate', ], - ], //don't use aliasing in case of filters 'filterable' => [ [ - 'column' => 'id', + 'column' => 'cer.id', 'alias' => 'exchId', 'type' => 'number', 'label' => 'Rate ID', ], [ - 'column' => 'target_currency', + 'column' => 'curr.name', 'alias' => 'exchTargetCurrency', 'type' => 'string', 'label' => 'Target Currency', diff --git a/packages/Webkul/Admin/src/DataGrids/OrderDataGrid.php b/packages/Webkul/Admin/src/DataGrids/OrderDataGrid.php index 5324637a4..477d5dde3 100644 --- a/packages/Webkul/Admin/src/DataGrids/OrderDataGrid.php +++ b/packages/Webkul/Admin/src/DataGrids/OrderDataGrid.php @@ -6,7 +6,7 @@ use Illuminate\View\View; use Webkul\Ui\DataGrid\Facades\DataGrid; /** - * Order DataGrid + * orderDataGrid * * @author Prashant Singh @prashant-webkul * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) @@ -17,19 +17,19 @@ class OrderDataGrid /** * The Data Grid implementation. * - * @var AttributeDataGrid - * for countries + * @var orderDataGrid + * for orders */ - - public function createCategoryDataGrid() + public function createOrderDataGrid() { return DataGrid::make([ - 'name' => 'Orders', - 'table' => 'orders as ord', - 'select' => 'ord.id', + 'name' => 'orders', + 'table' => 'orders as or', + 'select' => 'or.id', 'perpage' => 5, - 'aliased' => true, //use this with false as default and true in case of joins + 'aliased' => false, + //True in case of joins else aliasing key required on all cases 'massoperations' =>[ [ @@ -40,118 +40,89 @@ class OrderDataGrid ], ], - 'actions' => [ - [ - 'type' => 'Edit', - 'route' => route('admin.datagrid.delete'), - 'confirm_text' => 'Do you really want to do this?', - 'icon' => 'icon pencil-lg-icon', - ], [ - 'type' => 'Delete', - 'route' => route('admin.datagrid.delete'), - 'confirm_text' => 'Do you really want to do this?', - 'icon' => 'icon trash-icon', - ], - ], + 'actions' => [ ], - 'join' => [ - [ - 'join' => 'leftjoin', - 'table' => 'category_translations as ct', - 'primaryKey' => 'cat.id', - 'condition' => '=', - 'secondaryKey' => 'ct.category_id', - ], [ - 'join' => 'leftjoin', - 'table' => 'category_translations as cta', - 'primaryKey' => 'cat.parent_id', - 'condition' => '=', - 'secondaryKey' => 'cta.category_id', - ], - ], + 'join' => [], //use aliasing on secodary columns if join is performed 'columns' => [ [ - 'name' => 'cat.id', - 'alias' => 'catID', + 'name' => 'or.id', + 'alias' => 'orderid', 'type' => 'number', - 'label' => 'Category ID', + 'label' => 'ID', 'sortable' => true, ], [ - 'name' => 'ct.name', - 'alias' => 'catName', + 'name' => 'or.customer_first_name', + 'alias' => 'oafirstname', 'type' => 'string', - 'label' => 'Category Name', + 'label' => 'Billed To', 'sortable' => false, ], [ - 'name' => 'cat.position', - 'alias' => 'catPosition', + 'name' => 'or.base_grand_total', + 'alias' => 'orbasegrandtotal', 'type' => 'string', - 'label' => 'Category Position', - 'sortable' => false, - ], [ - 'name' => 'cta.name', - 'alias' => 'parentName', - 'type' => 'string', - 'label' => 'Parent Name', - 'sortable' => true, - ], [ - 'name' => 'cat.status', - 'alias' => 'catStatus', - 'type' => 'string', - 'label' => 'Visible in Menu', + 'label' => 'Base Total', 'sortable' => true, 'wrapper' => function ($value) { - if($value == 0) - return "False"; - else - return "True"; + return core()->currency($value); + } + ], [ + 'name' => 'or.grand_total', + 'alias' => 'oagrandtotal', + 'type' => 'string', + 'label' => 'Grand Total', + 'sortable' => false, + 'wrapper' => function ($value) { + return core()->currency($value); + } + ], [ + 'name' => 'or.status', + 'alias' => 'orstatus', + 'type' => 'string', + 'label' => 'Status', + 'sortable' => true, + 'wrapper' => function ($value) { + if($value == 'completed') + return 'Completed'; + else if($value == "cancelled") + return 'Completed'; + else if($value == "closed") + return 'Completed'; + else if($value == "pending") + return 'Pending'; + }, ], - ], 'filterable' => [ [ - 'column' => 'cat.id', - 'alias' => 'catID', + 'column' => 'or.id', + 'alias' => 'orderid', 'type' => 'number', - 'label' => 'Category ID', + 'label' => 'ID', ], [ - 'column' => 'ct.name', - 'alias' => 'catName', + 'name' => 'or.status', + 'alias' => 'orstatus', 'type' => 'string', - 'label' => 'Category Name', - ], [ - 'column' => 'cta.name', - 'alias' => 'parentName', - 'type' => 'string', - 'label' => 'Parent Name', - ], [ - 'column' => 'cat.status', - 'alias' => 'catStatus', - 'type' => 'string', - 'label' => 'Visible in Menu', - ], + 'label' => 'Status' + ] ], - //don't use aliasing in case of searchables 'searchable' => [ [ - 'column' => 'cat.id', + 'column' => 'or.id', + 'alias' => 'orderid', 'type' => 'number', - 'label' => 'Category ID', + 'label' => 'ID', ], [ - 'column' => 'ct.name', + 'name' => 'or.status', + 'alias' => 'orstatus', 'type' => 'string', - 'label' => 'Category Name', - ], [ - 'column' => 'cat.status', - 'type' => 'string', - 'label' => 'Visible in Menu', + 'label' => 'Status' ] ], @@ -173,7 +144,7 @@ class OrderDataGrid public function render() { - return $this->createCategoryDataGrid()->render(); + return $this->createOrderDataGrid()->render(); } } \ No newline at end of file diff --git a/packages/Webkul/Admin/src/DataGrids/ProductDataGrid.php b/packages/Webkul/Admin/src/DataGrids/ProductDataGrid.php index 74e236511..3f81843f0 100644 --- a/packages/Webkul/Admin/src/DataGrids/ProductDataGrid.php +++ b/packages/Webkul/Admin/src/DataGrids/ProductDataGrid.php @@ -3,7 +3,7 @@ namespace Webkul\Admin\DataGrids; use Illuminate\View\View; -use Webkul\Ui\DataGrid\Facades\ProductGrid; +use Webkul\Ui\DataGrid\Facades\DataGrid; use Webkul\Channel\Repositories\ChannelRepository; use Webkul\Product\Repositories\ProductRepository; /** @@ -16,27 +16,21 @@ use Webkul\Product\Repositories\ProductRepository; class ProductDataGrid { /** - * The Data Grid implementation. - * @var ProductDataGrid + * The Data Grid implementation @var ProductDataGrid * for Products */ public function createProductDataGrid() { - return ProductGrid::make([ + return DataGrid::make([ 'name' => 'Products', - 'table' => 'products as prods', - 'select' => 'prods.id', - 'perpage' => 10, + 'table' => 'products_grid as prods', + 'select' => 'prods.product_id', + 'perpage' => 5, 'aliased' => true, //use this with false as default and true in case of joins 'massoperations' =>[ - // [ - // 'route' => route('admin.datagrid.delete'), - // 'method' => 'DELETE', - // 'label' => 'Delete', - // 'type' => 'button', - // ], + //check other grid for configuration and make of your own ], 'actions' => [ @@ -53,43 +47,7 @@ class ProductDataGrid ], ], - 'attributeColumns' => [ - 'name', 'price' - ], - 'join' => [ - [ - 'join' => 'leftjoin', - 'table' => 'attribute_families as attfam', - 'primaryKey' => 'prods.attribute_family_id', - 'condition' => '=', - 'secondaryKey' => 'attfam.id', - ], - - //for getting the attribute values. - // [ - // 'join' => 'leftjoin', - // 'table' => 'product_attribute_values as pav', - // 'primaryKey' => 'prods.id', - // 'condition' => '=', - // 'secondaryKey' => 'pav.product_id', - // 'withAttributes' => [ - // 'condition' => [ - // 'attribute_id' => 2, - // 'select' => 'name', - // ] - // ] - // ], - - // for getting the inventory quantity of a product - [ - 'join' => 'leftjoin', - 'table' => 'product_inventories as pi', - 'primaryKey' => 'prods.id', - 'condition' => '=', - 'secondaryKey' => 'pi.product_id', - ], - ], //use aliasing on secodary columns if join is performed @@ -97,28 +55,52 @@ class ProductDataGrid 'columns' => [ //name, alias, type, label, sortable [ - 'name' => 'prods.id', - 'alias' => 'productID', - 'type' => 'number', + 'name' => 'prods.product_id', + 'alias' => 'id', + 'type' => 'string', 'label' => 'ID', 'sortable' => true, - ], - [ + ], [ 'name' => 'prods.sku', - 'alias' => 'productCode', + 'alias' => 'productSku', 'type' => 'string', 'label' => 'SKU', 'sortable' => true, - ], - [ - 'name' => 'attfam.name', - 'alias' => 'FamilyName', + ], [ + 'name' => 'prods.name', + 'alias' => 'ProductName', 'type' => 'string', - 'label' => 'Family Name', - 'sortable' => true, - ], - [ - 'name' => 'pi.qty', + 'label' => 'Product Name', + 'sortable' => false, + ], [ + 'name' => 'prods.type', + 'alias' => 'ProductType', + 'type' => 'string', + 'label' => 'Product Type', + 'sortable' => false, + ], [ + 'name' => 'prods.status', + 'alias' => 'ProductStatus', + 'type' => 'string', + 'label' => 'Product Status', + 'sortable' => false, + 'wrapper' => function ($value) { + if($value == 1) + return 'Active'; + else + return 'Inactive'; + }, + ], [ + 'name' => 'prods.price', + 'alias' => 'ProductPrice', + 'type' => 'string', + 'label' => 'Product Price', + 'sortable' => false, + 'wrapper' => function ($value) { + return core()->formatBasePrice($value); + }, + ], [ + 'name' => 'prods.quantity', 'alias' => 'ProductQuantity', 'type' => 'string', 'label' => 'Product Quantity', @@ -127,57 +109,50 @@ class ProductDataGrid ], 'filterable' => [ - //column, type, and label + //column, alias, type, and label [ - 'column' => 'prods.id', + 'column' => 'prods.product_id', 'alias' => 'productID', 'type' => 'number', 'label' => 'ID', - ], - [ + ], [ 'column' => 'prods.sku', - 'alias' => 'productCode', + 'alias' => 'productSku', 'type' => 'string', 'label' => 'SKU', - ], - [ - 'column' => 'attfam.name', - 'alias' => 'FamilyName', + ], [ + 'column' => 'prods.name', + 'alias' => 'ProductName', 'type' => 'string', - 'label' => 'Family Name', - ], - [ - 'column' => 'pi.qty', - 'alias' => 'ProductQuantity', - 'type' => 'number', - 'label' => 'Product Quatity', - ], + 'label' => 'Product Name', + ], [ + 'column' => 'prods.type', + 'alias' => 'ProductType', + 'type' => 'string', + 'label' => 'Product Type', + ] ], - //don't use aliasing in case of searchables 'searchable' => [ //column, type and label [ - 'column' => 'prods.id', + 'column' => 'prods.product_id', 'type' => 'number', 'label' => 'ID', - ], - [ + ], [ 'column' => 'prods.sku', 'type' => 'string', 'label' => 'SKU', - ], - [ - 'column' => 'attfam.name', + ], [ + 'column' => 'prods.name', 'type' => 'string', - 'label' => 'Family Name', - ], - [ - 'column' => 'pi.qty', + 'label' => 'Product Name', + ], [ + 'column' => 'prods.type', 'type' => 'string', - 'label' => 'Product Quatity', - ], + 'label' => 'Product Type', + ] ], //list of viable operators that will be used diff --git a/packages/Webkul/Admin/src/DataGrids/RolesDataGrid.php b/packages/Webkul/Admin/src/DataGrids/RolesDataGrid.php index ab8d08b8b..7bfeb06cc 100644 --- a/packages/Webkul/Admin/src/DataGrids/RolesDataGrid.php +++ b/packages/Webkul/Admin/src/DataGrids/RolesDataGrid.php @@ -68,29 +68,25 @@ class RolesDataGrid //use aliasing on secodary columns if join is performed 'columns' => [ - [ 'name' => 'id', 'alias' => 'roleId', 'type' => 'number', 'label' => 'ID', 'sortable' => true, - ], - [ + ], [ 'name' => 'name', 'alias' => 'roleName', 'type' => 'string', 'label' => 'Name', 'sortable' => true, - ], - [ + ], [ 'name' => 'permission_type', 'alias' => 'rolePermissionType', 'type' => 'string', 'label' => 'Permission Type', 'sortable' => true, ], - ], //don't use aliasing in case of filters @@ -101,14 +97,12 @@ class RolesDataGrid 'alias' => 'roleId', 'type' => 'number', 'label' => 'ID', - ], - [ + ], [ 'column' => 'name', 'alias' => 'roleName', 'type' => 'string', 'label' => 'Name', - ], - [ + ], [ 'column' => 'permission_type', 'alias' => 'rolePermissionType', 'type' => 'string', @@ -123,8 +117,7 @@ class RolesDataGrid 'column' => 'name', 'type' => 'string', 'label' => 'Name', - ], - [ + ], [ 'column' => 'permission_type', 'type' => 'string', 'label' => 'Permission Type', diff --git a/packages/Webkul/Admin/src/DataGrids/UserDataGrid.php b/packages/Webkul/Admin/src/DataGrids/UserDataGrid.php index 85432930a..0543693aa 100644 --- a/packages/Webkul/Admin/src/DataGrids/UserDataGrid.php +++ b/packages/Webkul/Admin/src/DataGrids/UserDataGrid.php @@ -111,13 +111,7 @@ class UserDataGrid 'label' => 'Role Name', 'sortable' => true, ], - [ - 'name' => 'r.id', - 'alias' => 'xc', - 'type' => 'string', - 'label' => 'Role ID', - 'sortable' => true, - ], + ], //don't use aliasing in case of filters 'filterable' => [ @@ -133,12 +127,7 @@ class UserDataGrid 'type' => 'number', 'label' => 'Admin ID' ], - [ - 'column' => 'r.id', - 'alias' => 'xc', - 'type' => 'number', - 'label' => 'Role ID' - ] + ], //don't use aliasing in case of searchables 'searchable' => [ @@ -146,8 +135,7 @@ class UserDataGrid 'column' => 'u.email', 'type' => 'string', 'label' => 'E-Mail' - ], - [ + ], [ 'column' => 'u.name', 'type' => 'string', 'label' => 'Name' diff --git a/packages/Webkul/Admin/src/Http/Controllers/DashboardController.php b/packages/Webkul/Admin/src/Http/Controllers/DashboardController.php index 5bee6cde8..bee17a769 100644 --- a/packages/Webkul/Admin/src/Http/Controllers/DashboardController.php +++ b/packages/Webkul/Admin/src/Http/Controllers/DashboardController.php @@ -5,6 +5,12 @@ namespace Webkul\Admin\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\DB; +use Carbon\Carbon; +use Webkul\Sales\Repositories\OrderRepository as Order; +use Webkul\Sales\Repositories\OrderItemRepository as OrderItem; +use Webkul\Customer\Repositories\CustomerRepository as Customer; +use Webkul\Product\Repositories\ProductInventoryRepository as ProductInventory; /** * Dashboard controller @@ -21,14 +27,275 @@ class DashboardController extends Controller */ protected $_config; - public function __construct() + /** + * OrderRepository object + * + * @var array + */ + protected $order; + + /** + * OrderItemRepository object + * + * @var array + */ + protected $orderItem; + + /** + * CustomerRepository object + * + * @var array + */ + protected $customer; + + /** + * ProductInventoryRepository object + * + * @var array + */ + protected $productInventory; + + /** + * string object + * + * @var array + */ + protected $startDate; + + /** + * string object + * + * @var array + */ + protected $lastStartDate; + + /** + * string object + * + * @var array + */ + protected $endDate; + + /** + * string object + * + * @var array + */ + protected $lastEndDate; + + /** + * Create a new controller instance. + * + * @param Webkul\Sales\Repositories\OrderRepository $order + * @param Webkul\Sales\Repositories\OrderItemRepository $orderItem + * @param Webkul\Customer\Repositories\CustomerRepository $customer + * @param Webkul\Product\Repositories\ProductInventoryRepository $productInventory + * @return void + */ + public function __construct( + Order $order, + OrderItem $orderItem, + Customer $customer, + ProductInventory $productInventory + ) { $this->_config = request('_config'); $this->middleware('admin'); + + $this->order = $order; + + $this->orderItem = $orderItem; + + $this->customer = $customer; + + $this->productInventory = $productInventory; } + + public function getPercentageChange($previous, $current) + { + if(!$previous) + return $current ? 100 : 0; + + return ($current - $previous) / $previous * 100; + } + + /** + * Display a listing of the resource. + * + * @return \Illuminate\Http\Response + */ public function index() { - return view('admin::dashboard.index'); + $this->setStartEndDate(); + + $statistics = [ + 'total_customers' => [ + 'previous' => $previous = $this->customer->scopeQuery(function($query) { + return $query->where('customers.created_at', '>=', $this->lastStartDate) + ->where('customers.created_at', '<=', $this->lastEndDate); + })->count(), + 'current' => $current = $this->customer->scopeQuery(function($query) { + return $query->where('customers.created_at', '>=', $this->startDate) + ->where('customers.created_at', '<=', $this->endDate); + })->count(), + 'progress' => $this->getPercentageChange($previous, $current) + ], + 'total_orders' => [ + 'previous' => $previous = $this->order->scopeQuery(function($query) { + return $query->where('orders.created_at', '>=', $this->lastStartDate) + ->where('orders.created_at', '<=', $this->lastEndDate); + })->count(), + 'current' => $current = $this->order->scopeQuery(function($query) { + return $query->where('orders.created_at', '>=', $this->startDate) + ->where('orders.created_at', '<=', $this->endDate); + })->count(), + 'progress' => $this->getPercentageChange($previous, $current) + ], + 'total_sales' => [ + 'previous' => $previous = $this->order->scopeQuery(function($query) { + return $query->where('orders.created_at', '>=', $this->lastStartDate) + ->where('orders.created_at', '<=', $this->lastEndDate); + })->sum('base_grand_total'), + 'current' => $current = $this->order->scopeQuery(function($query) { + return $query->where('orders.created_at', '>=', $this->startDate) + ->where('orders.created_at', '<=', $this->endDate); + })->sum('base_grand_total'), + 'progress' => $this->getPercentageChange($previous, $current) + ], + 'avg_sales' => [ + 'previous' => $previous = $this->order->scopeQuery(function($query) { + return $query->where('orders.created_at', '>=', $this->lastStartDate) + ->where('orders.created_at', '<=', $this->lastEndDate); + })->avg('base_grand_total'), + 'current' => $current = $this->order->scopeQuery(function($query) { + return $query->where('orders.created_at', '>=', $this->startDate) + ->where('orders.created_at', '<=', $this->endDate); + })->avg('base_grand_total'), + 'progress' => $this->getPercentageChange($previous, $current) + ], + 'top_selling_categories' => $this->getTopSellingCategories(), + 'top_selling_products' => $this->getTopSellingProducts(), + 'customer_with_most_sales' => $this->getCustomerWithMostSales(), + 'stock_threshold' => $this->getStockThreshold(), + ]; + + foreach (core()->getTimeInterval($this->startDate, $this->endDate) as $interval) { + $statistics['sale_graph']['label'][] = $interval['start']->format('d M'); + + $total = number_format($this->order->scopeQuery(function($query) use($interval) { + return $query->where('orders.created_at', '>=', $interval['start']) + ->where('orders.created_at', '<=', $interval['end']); + })->sum('base_grand_total'), 2); + + $statistics['sale_graph']['total'][] = $total; + $statistics['sale_graph']['formated_total'][] = core()->formatBasePrice($total); + } + + return view($this->_config['view'], compact('statistics'))->with(['startDate' => $this->startDate, 'endDate' => $this->endDate]); } -} + + /** + * Returns the list of top selling categories + * + * @return mixed + */ + public function getTopSellingCategories() + { + return $this->orderItem->getModel() + ->leftJoin('products', 'order_items.product_id', 'products.id') + ->leftJoin('product_categories', 'products.id', 'product_categories.product_id') + ->leftJoin('categories', 'product_categories.category_id', 'categories.id') + ->leftJoin('category_translations', 'categories.id', 'category_translations.category_id') + ->where('category_translations.locale', app()->getLocale()) + ->where('order_items.created_at', '>=', $this->startDate) + ->where('order_items.created_at', '<=', $this->endDate) + ->addSelect(DB::raw('SUM(qty_ordered) as total_qty_ordered')) + ->addSelect(DB::raw('COUNT(products.id) as total_products')) + ->addSelect('order_items.id', 'categories.id as category_id', 'category_translations.name') + ->groupBy('category_id') + ->orderBy('total_qty_ordered', 'DESC') + ->limit(5) + ->get(); + } + + /** + * Return stock threshold. + * + * @return mixed + */ + public function getStockThreshold() + { + return $this->productInventory->getModel() + ->leftJoin('products', 'product_inventories.product_id', 'products.id') + ->select(DB::raw('SUM(qty) as total_qty')) + ->addSelect('product_inventories.product_id') + ->where('products.type', '!=', 'configurable') + ->groupBy('product_id') + ->orderBy('total_qty', 'ASC') + ->limit(5) + ->get(); + } + + /** + * Returns top selling products + * @return mixed + */ + public function getTopSellingProducts() + { + return $this->orderItem->getModel() + ->select(DB::raw('SUM(qty_ordered) as total_qty_ordered')) + ->addSelect('id', 'product_id', 'product_type', 'name') + ->where('order_items.created_at', '>=', $this->startDate) + ->where('order_items.created_at', '<=', $this->endDate) + ->whereNull('parent_id') + ->groupBy('product_id') + ->orderBy('total_qty_ordered', 'DESC') + ->limit(5) + ->get(); + } + + /** + * Returns top selling products + * + * @return mixed + */ + public function getCustomerWithMostSales() + { + return $this->order->getModel() + ->select(DB::raw('SUM(base_grand_total) as total_base_grand_total')) + ->addSelect(DB::raw('COUNT(id) as total_orders')) + ->addSelect('id', 'customer_id', 'customer_email', 'customer_first_name', 'customer_last_name') + ->where('orders.created_at', '>=', $this->startDate) + ->where('orders.created_at', '<=', $this->endDate) + ->groupBy('customer_email') + ->orderBy('total_base_grand_total', 'DESC') + ->limit(5) + ->get(); + } + + /** + * Sets start and end date + * + * @return void + */ + public function setStartEndDate() + { + $this->startDate = request()->get('start') + ? Carbon::createFromTimeString(request()->get('start') . " 00:00:01") + : Carbon::createFromTimeString(Carbon::now()->subDays(30)->format('Y-m-d') . " 00:00:01"); + + $this->endDate = request()->get('end') + ? Carbon::createFromTimeString(request()->get('end') . " 23:59:59") + : Carbon::now(); + + if($this->endDate > Carbon::now()) + $this->endDate = Carbon::now(); + + $this->lastStartDate = clone $this->startDate; + $this->lastEndDate = clone $this->endDate; + + $this->lastStartDate->subDays($this->lastStartDate->diffInDays($this->lastEndDate)); + $this->lastEndDate->subDays($this->lastStartDate->diffInDays($this->lastEndDate)); + } +} \ No newline at end of file diff --git a/packages/Webkul/Admin/src/Http/routes.php b/packages/Webkul/Admin/src/Http/routes.php index dccbe614a..15ca6dacc 100644 --- a/packages/Webkul/Admin/src/Http/routes.php +++ b/packages/Webkul/Admin/src/Http/routes.php @@ -2,6 +2,8 @@ Route::group(['middleware' => ['web']], function () { Route::prefix('admin')->group(function () { + Route::get('/grid', 'Webkul\Product\Http\Controllers\ProductController@test'); + // Login Routes Route::get('/login', 'Webkul\User\Http\Controllers\SessionController@create')->defaults('_config', [ 'view' => 'admin::users.sessions.create' @@ -37,17 +39,15 @@ Route::group(['middleware' => ['web']], function () { ])->name('admin.session.destroy'); // Dashboard Route - Route::get('dashboard', 'Webkul\Admin\Http\Controllers\DashboardController@index')->name('admin.dashboard.index'); + Route::get('dashboard', 'Webkul\Admin\Http\Controllers\DashboardController@index')->defaults('_config', [ + 'view' => 'admin::dashboard.index' + ])->name('admin.dashboard.index'); //Customers Management Routes Route::get('customers', 'Webkul\Admin\Http\Controllers\Customer\CustomerController@index')->defaults('_config', [ 'view' => 'admin::customers.index' ])->name('admin.customer.index'); - Route::get('customers/orders', 'Webkul\Admin\Http\Controllers\Customer\CustomerController@index')->defaults('_config',[ - 'view' => 'admin::customers.orders.index' - ])->name('admin.customer.orders.index'); - Route::get('customers/create', 'Webkul\Admin\Http\Controllers\Customer\CustomerController@create')->defaults('_config',[ 'view' => 'admin::customers.create' ])->name('admin.customer.create'); @@ -158,6 +158,8 @@ Route::group(['middleware' => ['web']], function () { // Catalog Routes Route::prefix('catalog')->group(function () { + Route::get('/sync', 'Webkul\Product\Http\Controllers\ProductController@sync'); + // Catalog Product Routes Route::get('/products', 'Webkul\Product\Http\Controllers\ProductController@index')->defaults('_config', [ 'view' => 'admin::catalog.products.index' @@ -405,7 +407,6 @@ Route::group(['middleware' => ['web']], function () { Route::get('/inventory_sources/delete/{id}', 'Webkul\Inventory\Http\Controllers\InventorySourceController@destroy')->name('admin.inventory_sources.delete'); - // Channel Routes Route::get('/channels', 'Webkul\Core\Http\Controllers\ChannelController@index')->defaults('_config', [ 'view' => 'admin::settings.channels.index' @@ -491,7 +492,6 @@ Route::group(['middleware' => ['web']], function () { //tax category ends - //tax rate Route::get('tax-rates', 'Webkul\Tax\Http\Controllers\TaxRateController@index')->defaults('_config', [ 'view' => 'admin::tax.tax-rates.index' diff --git a/packages/Webkul/Admin/src/Listeners/Product.php b/packages/Webkul/Admin/src/Listeners/Product.php new file mode 100644 index 000000000..59648f121 --- /dev/null +++ b/packages/Webkul/Admin/src/Listeners/Product.php @@ -0,0 +1,143 @@ + @prashant-webkul + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class Product { + + /** + * Product Repository Object + */ + protected $product; + + /** + * Price Object + * + * @var array + */ + Protected $price; + + + /** + * Product Grid Repository Object + */ + protected $productGrid; + + public function __construct(ProductRepository $product, ProductGridRepository $productGrid, Price $price) + { + $this->product = $product; + + $this->productGrid = $productGrid; + + $this->price = $price; + } + + /** + * Creates a new entry in the product grid whenever a new product is created. + * + * @return boolean + */ + public function afterProductCreated($product) { + $gridObject = []; + + $gridObject = [ + 'product_id' => $product->id, + 'sku' => $product->sku, + 'type' => $product->type, + 'attribute_family_name' => $product->toArray()['attribute_family']['name'], + ]; + + if($this->productGrid->create($gridObject)) { + return true; + } else { + return false; + } + } + + /** + * Event before the product update + * + * @return boolean + */ + public function beforeProductUpdate($productId) { + return true; + } + + /** + * Event after the product update + * + * @var collection product + * + * return boolean + */ + public function afterProductUpdate($product) { + //update product grid here + $this->productGrid->updateWhere($product); + + return true; + } + + /** + * Event after deletion of the product + * + * @return boolean + */ + public function afterProductDelete($productId) { + return true; + } + + /** + * Fill attributes for that product after the creation + * + * @return boolean + */ + public function fillAttribute() { + + } + + public function sync() { + $gridObject = []; + + foreach($this->product->all() as $product) { + $gridObject = [ + 'product_id' => $product->id, + 'sku' => $product->sku, + 'type' => $product->type, + 'name' => $product->name, + 'attribute_family_name' => $product->toArray()['attribute_family']['name'], + 'price' => $this->price->getMinimalPrice($product), + 'status' => $product->status + ]; + + if($product->type == 'configurable') { + $gridObject['quantity'] = 0; + } else { + $qty = 0; + + foreach($product->toArray()['inventories'] as $inventorySource) { + $qty = $qty + $inventorySource['qty']; + } + + $gridObject['quantity'] = $qty; + + $qty = 0; + } + $this->productGrid->create($gridObject); + + $gridObject = []; + } + + return true; + } +} \ No newline at end of file diff --git a/packages/Webkul/Admin/src/Mail/NewInvoiceNotification.php b/packages/Webkul/Admin/src/Mail/NewInvoiceNotification.php index 9f760be4e..c1f2078ef 100644 --- a/packages/Webkul/Admin/src/Mail/NewInvoiceNotification.php +++ b/packages/Webkul/Admin/src/Mail/NewInvoiceNotification.php @@ -46,6 +46,6 @@ class NewInvoiceNotification extends Mailable return $this->to($order->customer_email, $order->customer_full_name) ->subject(trans('admin::app.mail.invoice.subject', ['order_id' => $order->id])) - ->view('admin::emails.sales.new-invoice'); + ->view('shop::emails.sales.new-invoice'); } } diff --git a/packages/Webkul/Admin/src/Mail/NewOrderNotification.php b/packages/Webkul/Admin/src/Mail/NewOrderNotification.php index 51940d1a8..daa5c7a2d 100644 --- a/packages/Webkul/Admin/src/Mail/NewOrderNotification.php +++ b/packages/Webkul/Admin/src/Mail/NewOrderNotification.php @@ -43,6 +43,6 @@ class NewOrderNotification extends Mailable { return $this->to($this->order->customer_email, $this->order->customer_full_name) ->subject(trans('admin::app.mail.order.subject')) - ->view('admin::emails.sales.new-order'); + ->view('shop::emails.sales.new-order'); } } diff --git a/packages/Webkul/Admin/src/Mail/NewShipmentNotification.php b/packages/Webkul/Admin/src/Mail/NewShipmentNotification.php index 5c8717581..34c89da2d 100644 --- a/packages/Webkul/Admin/src/Mail/NewShipmentNotification.php +++ b/packages/Webkul/Admin/src/Mail/NewShipmentNotification.php @@ -46,6 +46,6 @@ class NewShipmentNotification extends Mailable return $this->to($order->customer_email, $order->customer_full_name) ->subject(trans('admin::app.mail.shipment.subject', ['order_id' => $order->id])) - ->view('admin::emails.sales.new-shipment'); + ->view('shop::emails.sales.new-shipment'); } } diff --git a/packages/Webkul/Admin/src/Providers/AdminServiceProvider.php b/packages/Webkul/Admin/src/Providers/AdminServiceProvider.php index e9bbe7479..2d2f410ff 100644 --- a/packages/Webkul/Admin/src/Providers/AdminServiceProvider.php +++ b/packages/Webkul/Admin/src/Providers/AdminServiceProvider.php @@ -67,17 +67,17 @@ class AdminServiceProvider extends ServiceProvider $subMenus = $tabs = []; if (count($keys) > 1) { $subMenus = [ - 'items' => $menu->sortItems(array_get($menu->items, current($keys) . '.children')), - 'current' => $menu->current, - 'currentKey' => $menu->currentKey - ]; + 'items' => $menu->sortItems(array_get($menu->items, current($keys) . '.children')), + 'current' => $menu->current, + 'currentKey' => $menu->currentKey + ]; if (count($keys) > 2) { $tabs = [ - 'items' => $menu->sortItems(array_get($menu->items, implode('.children.', array_slice($keys, 0, 2)) . '.children')), - 'current' => $menu->current, - 'currentKey' => $menu->currentKey - ]; + 'items' => $menu->sortItems(array_get($menu->items, implode('.children.', array_slice($keys, 0, 2)) . '.children')), + 'current' => $menu->current, + 'currentKey' => $menu->currentKey + ]; } } diff --git a/packages/Webkul/Admin/src/Providers/EventServiceProvider.php b/packages/Webkul/Admin/src/Providers/EventServiceProvider.php index 53cb77851..a9608eded 100644 --- a/packages/Webkul/Admin/src/Providers/EventServiceProvider.php +++ b/packages/Webkul/Admin/src/Providers/EventServiceProvider.php @@ -24,6 +24,26 @@ class EventServiceProvider extends ServiceProvider 'sort' => 2, 'icon-class' => 'sales-icon', ], [ + 'key' => 'sales.orders', + 'name' => 'Orders', + 'route' => 'admin.sales.orders.index', + 'sort' => 1, + 'icon-class' => '', + ], + // [ + // 'key' => 'sales.shipments', + // 'name' => 'Shipments', + // 'route' => 'admin.sales.orders.index', + // 'sort' => 2, + // 'icon-class' => '', + // ], [ + // 'key' => 'sales.invoices', + // 'name' => 'Invoices', + // 'route' => 'admin.sales.orders.index', + // 'sort' => 3, + // 'icon-class' => '', + // ], + [ 'key' => 'catalog', 'name' => 'Catalog', 'route' => 'admin.catalog.products.index', diff --git a/packages/Webkul/Admin/src/Resources/assets/js/components/date.vue b/packages/Webkul/Admin/src/Resources/assets/js/components/date.vue index 36c254218..25924a3d7 100644 --- a/packages/Webkul/Admin/src/Resources/assets/js/components/date.vue +++ b/packages/Webkul/Admin/src/Resources/assets/js/components/date.vue @@ -22,12 +22,16 @@ }, mounted () { + var this_this = this; + var element = this.$el.getElementsByTagName('input')[0]; this.datepicker = new Flatpickr( element, { - allowInput: true, - altFormat: 'Y-m-d H:i:s', - dateFormat: 'Y-m-d H:i:s' + altFormat: 'Y-m-d', + dateFormat: 'Y-m-d', + onChange: function(selectedDates, dateStr, instance) { + this_this.$emit('onChange', dateStr) + }, }); } }; diff --git a/packages/Webkul/Admin/src/Resources/assets/js/components/datetime.vue b/packages/Webkul/Admin/src/Resources/assets/js/components/datetime.vue index 772f04d09..d4e3f8f99 100644 --- a/packages/Webkul/Admin/src/Resources/assets/js/components/datetime.vue +++ b/packages/Webkul/Admin/src/Resources/assets/js/components/datetime.vue @@ -10,25 +10,30 @@ import Flatpickr from "flatpickr"; export default { - props: { - name: String, - value: String - }, + props: { + name: String, + value: String + }, - data() { - return { - datepicker: null - }; - }, + data() { + return { + datepicker: null + }; + }, - mounted() { - var element = this.$el.getElementsByTagName("input")[0]; - this.datepicker = new Flatpickr(element, { - allowInput: true, - altFormat: "Y-m-d H:i:s", - dateFormat: "Y-m-d H:i:s", - enableTime: true - }); - } + mounted() { + var this_this = this; + + var element = this.$el.getElementsByTagName("input")[0]; + this.datepicker = new Flatpickr(element, { + allowInput: true, + altFormat: "Y-m-d H:i:s", + dateFormat: "Y-m-d H:i:s", + enableTime: true, + onChange: function(selectedDates, dateStr, instance) { + this_this.$emit('onChange', dateStr) + }, + }); + } }; \ No newline at end of file diff --git a/packages/Webkul/Admin/src/Resources/assets/sass/app.scss b/packages/Webkul/Admin/src/Resources/assets/sass/app.scss index 43653f4a7..50054105a 100644 --- a/packages/Webkul/Admin/src/Resources/assets/sass/app.scss +++ b/packages/Webkul/Admin/src/Resources/assets/sass/app.scss @@ -258,273 +258,174 @@ body { // admin dashboard css -.dashboard-content { +.dashboard { - .dashboard-stats { - display: flex; - + .page-header { + margin-bottom: 0 !important; + padding-bottom: 15px; + border-bottom: 1px solid $border-color; } - .graph-category { - display: flex; - margin-top: 25px; + .page-content { + margin-top: 15px; + } + + .card { + height: 445px; + background: #FFFFFF; + border: 1px solid #E7E7E7; + box-shadow: 0 5px 10px 2px rgba(0,0,0,0.08); + border-radius: 2px; + padding: 20px 0px 0px 20px; + + .card-title { + font-size: 14px; + color: #A2A2A2; + letter-spacing: -0.26px; + text-transform: uppercase; + } + + .card-info { + width: 100%; + height: 100%; + display: inline-block; + + &.center { + text-align: center; + } + + ul { + li { + border-bottom: 1px solid $border-color; + width: 100%; + display: inline-block; + padding: 10px 0; + position: relative; + + .image { + height: 60px; + width: 60px; + float: left; + margin-right: 15px; + + &.product { + background: #F2F2F2; + } + + img { + width: 100%; + } + } + + .description { + margin-top: 10px; + + .name { + color: #0041FF; + } + + .info { + color: #3A3A3A; + margin-top: 5px; + } + } + + .icon.angle-right-icon { + position: absolute; + right: 30px; + top: 50%; + margin-top: -8px; + } + + &:last-child { + border-bottom: 0; + } + } + } + + .no-result-found { + margin-top: 146px; + + p { + margin: 0; + color: #A2A2A2; + } + } + } + } + + .dashboard-stats { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + grid-auto-rows: auto; + grid-column-gap: 30px; + grid-row-gap: 15px; + + .dashboard-card { + height: 100px; + background: #FFFFFF; + border: 1px solid #E7E7E7; + box-shadow: 0 5px 10px 2px rgba(0,0,0,0.08); + border-radius: 5px; + position: relative; + padding: 15px; + + + .title { + font-size: 14px; + color: #A2A2A2; + text-transform: uppercase; + } + + .data { + padding-top: 3px; + font-size: 32px; + color: #0041FF; + + .progress { + font-size: 14px; + color: #8E8E8E; + float: right; + margin-top: 9px; + + .icon { + vertical-align: middle; + } + } + } + } + } + + .graph-stats { + margin-top: 30px; + width: 100%; + display: inline-block; + + .left-card-container { + float: left; + width: 75%; + padding-right: 9px; + } + + .right-card-container { + float: left; + width: 25%; + padding-left: 21px; + } } .sale-stock { - display: flex; - margin-top: 25px; + width: 100%; + display: inline-block; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(435px, 1fr)); + grid-auto-rows: auto; + grid-column-gap: 30px; + grid-row-gap: 15px; + margin-top: 30px; } - } - // admin dashboard css ends here - - -// admin dashboard component -.dashboard-card { - height: 100px; - width: 22%; - background: #FFFFFF; - border: 1px solid #E7E7E7; - box-shadow: 0 5px 10px 2px rgba(0,0,0,0.08); - border-radius: 5px; - - .visitor-content { - padding: 15px; - - .title { - - span { - padding-top:10px; - font-size: 14px; - color: #A2A2A2; - letter-spacing: -0.26px; - } - } - - .data { - padding-top: 3px; - - span { - font-size: 32px; - color: #0041FF; - letter-spacing: -0.6px; - - img { - margin-left: 75px; - height: 24px; - width:24px; - } - } - - span.right { - padding-top: 12px; - float: right; - font-size: 14px; - color: #8E8E8E; - letter-spacing: -0.7px; - } - } - } -} - -.dashboard-card:nth-last-child(1) { - margin-left: 30px; -} - -.dashboard-card:nth-last-child(2) { - margin-left: 30px; -} - -.dashboard-card:nth-last-child(3) { - margin-left: 30px; -} - - -.dashboard-graph { - height: 413px; - width: 70.5%; - background: #FFFFFF; - border: 1px solid #E7E7E7; - box-shadow: 0 5px 10px 2px rgba(0,0,0,0.08); - border-radius: 2px; -} - -.sale { - height: 465px; - width: 30.1%; - background: #FFFFFF; - border: 1px solid #E7E7E7; - box-shadow: 0 5px 10px 2px rgba(0,0,0,0.08); - border-radius: 2px; - - .top-sale { - margin-left: 20px; - margin-top: 27px; - - .title { - - span { - font-size: 14px; - color: #A2A2A2; - letter-spacing: -0.26px; - } - } - - .sale-info { - - ul { - - li { - - .pro-attribute{ - display: flex; - margin-top: 10px; - - .pro-img { - height: 60px; - width: 60px; - border: 1px solid green; - } - - .product-description { - margin-left: 15px; - margin-top: 8px; - width: 75%; - - .product-name { - - span { - font-size: 14px; - color: #0041FF; - letter-spacing: -0.26px; - } - - .right-side { - float: right; - margin-top: 10px; - height: 20px; - width: 20px; - } - } - - .product-info { - - span { - font-size: 14px; - color: #3A3A3A; - letter-spacing: -0.26px; - } - } - } - } - - .horizontal-rule { - border: .5px solid #A2A2A2; - opacity: 0.2; - margin-top: 10px; - } - } - - li:last-child { - .horizontal-rule { - display: none; - } - } - } - } - } -} - -.sale:nth-last-child(1) { - margin-left: 31px; -} - -.sale:nth-last-child(2) { - margin-left: 31px; -} -// admin dashboard css ends here - -.performing-category { - height: 413px; - width: 22%; - margin-left: 30px; - background: #FFFFFF; - border: 1px solid #E7E7E7; - box-shadow: 0 5px 10px 2px rgba(0,0,0,0.08); - border-radius: 2px; - - .category { - margin-left: 20px; - margin-top: 20px; - - .title { - - span { - font-size: 14px; - color: #A2A2A2; - letter-spacing: -0.26px; - } - } - - .category-info { - margin-top: 30px; - - - ul { - - li { - - .category-list { - margin-top: 10px; - - .cat-name { - - span { - font-size: 16px; - color: #0041FF; - letter-spacing: -0.3px; - - } - - .right-side { - float: right; - margin-right: 12px; - margin-top: 10px; - height: 20px; - width: 20px; - } - } - - .product-info { - margin-top: 5px; - - span { - font-size: 16px; - color: #3A3A3A; - letter-spacing: -0.3px; - } - } - - .horizon-rule { - margin-top: 8px; - border: .7px solid #D8D8D8; - } - } - } - - li:last-child { - - .category-list { - .horizon-rule { - display: none; - } - } - } - } - } - } -} // customer oder css for admin start here .sale-container { diff --git a/packages/Webkul/Admin/src/Resources/lang/en/app.php b/packages/Webkul/Admin/src/Resources/lang/en/app.php index d8a9eb0ff..6a66e69ad 100644 --- a/packages/Webkul/Admin/src/Resources/lang/en/app.php +++ b/packages/Webkul/Admin/src/Resources/lang/en/app.php @@ -7,7 +7,25 @@ return [ 'state' => 'State' ], 'dashboard' => [ - 'title' => 'Dashboard' + 'title' => 'Dashboard', + 'from' => 'From', + 'to' => 'To', + 'total-customers' => 'Total Customers', + 'total-orders' => 'Total Orders', + 'total-sale' => 'Total Sale', + 'average-sale' => 'Average Order Sale', + 'increased' => ':progress% Increased', + 'decreased' => ':progress% Decreased', + 'sales' => 'Sales', + 'top-performing-categories' => 'Top Performing Categories', + 'product-count' => ':count Products', + 'top-selling-products' => 'Top Selling Products', + 'sale-count' => ':count Sales', + 'customer-with-most-sales' => 'Customer With Most Sales', + 'order-count' => ':count Orders', + 'revenue' => 'Revenue :total', + 'stock-threshold' => 'Stock Threshold', + 'qty-left' => ':qty Left', ], 'account' => [ 'title' => 'My Account', @@ -32,6 +50,8 @@ return [ 'title' => 'Reset Password', 'title' => 'Reset Password', 'email' => 'Registered Email', + 'password' => 'Password', + 'confirm-password' => 'Confirm Password', 'back-link-title' => 'Back to Sign In', 'submit-btn-title' => 'Reset Password' ], @@ -471,6 +491,13 @@ return [ 'summary' => 'Summary of Shipment', 'carrier' => 'Carrier', 'tracking-number' => 'Tracking Number' + ], + 'forget-password' => [ + 'dear' => 'Dear :name', + 'info' => 'You are receiving this email because we received a password reset request for your account.', + 'reset-password' => 'Reset Password', + 'final-summary' => 'If you did not request a password reset, no further action is required.', + 'thanks' => 'Thanks!' ] ], 'error' => [ diff --git a/packages/Webkul/Admin/src/Resources/views/dashboard/index.blade.php b/packages/Webkul/Admin/src/Resources/views/dashboard/index.blade.php index 59ae4e5ba..60be3a496 100644 --- a/packages/Webkul/Admin/src/Resources/views/dashboard/index.blade.php +++ b/packages/Webkul/Admin/src/Resources/views/dashboard/index.blade.php @@ -5,497 +5,452 @@ @stop @section('content-wrapper') -
-

Dashboard

- -
+ +
+ + +
-
-
- NEW VISITORS -
-
- 450 - - - - - 12.5% Increased - -
+
+ {{ __('admin::app.dashboard.total-customers') }} +
+ +
+ {{ $statistics['total_customers']['current'] }} + + + @if ($statistics['total_customers']['progress'] < 0) + + {{ __('admin::app.dashboard.decreased', [ + 'progress' => -number_format($statistics['total_customers']['progress'], 1) + ]) + }} + @else + + {{ __('admin::app.dashboard.increased', [ + 'progress' => number_format($statistics['total_customers']['progress'], 1) + ]) + }} + @endif +
-
-
- NEW VISITORS -
-
- 450 - - - - - 12.5% Increased - -
+
+ {{ __('admin::app.dashboard.total-orders') }} +
+ +
+ {{ $statistics['total_orders']['current'] }} + + + @if ($statistics['total_orders']['progress'] < 0) + + {{ __('admin::app.dashboard.decreased', [ + 'progress' => -number_format($statistics['total_orders']['progress'], 1) + ]) + }} + @else + + {{ __('admin::app.dashboard.increased', [ + 'progress' => number_format($statistics['total_orders']['progress'], 1) + ]) + }} + @endif +
-
-
- NEW VISITORS -
-
- 450 - - - - - 12.5% Increased - -
+
+ {{ __('admin::app.dashboard.total-sale') }} +
+ +
+ {{ core()->formatBasePrice($statistics['total_sales']['current']) }} + + + @if ($statistics['total_sales']['progress'] < 0) + + {{ __('admin::app.dashboard.decreased', [ + 'progress' => -number_format($statistics['total_sales']['progress'], 1) + ]) + }} + @else + + {{ __('admin::app.dashboard.increased', [ + 'progress' => number_format($statistics['total_sales']['progress'], 1) + ]) + }} + @endif +
-
-
- NEW VISITORS -
-
- 450 - - - - - 12.5% Increased - -
+
+ {{ __('admin::app.dashboard.average-sale') }} +
+ +
+ {{ core()->formatBasePrice($statistics['avg_sales']['current']) }} + + + @if ($statistics['avg_sales']['progress'] < 0) + + {{ __('admin::app.dashboard.decreased', [ + 'progress' => -number_format($statistics['avg_sales']['progress'], 1) + ]) + }} + @else + + {{ __('admin::app.dashboard.increased', [ + 'progress' => number_format($statistics['avg_sales']['progress'], 1) + ]) + }} + @endif +
- - - -
-
-
-
+
-
-
-
- TOP PERFORMING CATEGORIES +
+
+
+ {{ __('admin::app.dashboard.sales') }}
-
- -
    +
    -
  • -
    -
    - Men - -
    -
    - 250 Products . 290 Sales -
    -
    -
    -
    -
  • - -
  • -
    -
    - Women - -
    -
    - 375 Products . 350 Sales -
    -
    -
    -
    -
  • - -
  • -
    -
    - Electronics - -
    -
    - 200 Products . 214 Sales -
    -
    -
    -
    -
  • - -
  • -
    -
    - Accessories - -
    -
    - 180 Products . 180 Sales -
    -
    -
    -
    -
  • -
+ +
+ +
+
+
+ {{ __('admin::app.dashboard.top-performing-categories') }} +
+ +
+ + + @if (!count($statistics['top_selling_categories'])) + +
+ + +

{{ __('admin::app.common.no-result-found') }}

+ +
+ + @endif +
+
+
+
+ @inject ('productImageHelper', 'Webkul\Product\Helpers\ProductImage') +
-
-
-
- TOP SELLING PRODUCTS -
+
+
+ {{ __('admin::app.dashboard.top-selling-products') }} +
-
- + + @if (!count($statistics['top_selling_products'])) + +
+ + +

{{ __('admin::app.common.no-result-found') }}

+ +
+ + @endif
-
-
-
- CUSTOMERS WITH MOST SALES -
- -
-
    -
  • -
    -
    - -
    -
    -
    - Emma Wagner - -
    -
    - 24 Orders . Revenue $450.00 -
    -
    -
    -
    -
    -
  • - -
  • -
    -
    - -
    -
    -
    - Emma Wagner - -
    -
    - 24 Orders . Revenue $450.00 -
    -
    -
    -
    -
    -
  • - -
  • -
    -
    - -
    -
    -
    - Emma Wagner - -
    -
    - 24 Orders . Revenue $450.00 -
    -
    -
    -
    -
    -
  • - -
  • -
    -
    - -
    -
    -
    - Emma Wagner - -
    -
    - 24 Orders . Revenue $450.00 -
    -
    - -
    -
    -
    -
  • - -
  • -
    -
    - -
    -
    -
    - Emma Wagner - -
    -
    - 24 Orders . Revenue $450.00 -
    -
    - -
    -
    -
    -
  • -
-
+
+
+ {{ __('admin::app.dashboard.customer-with-most-sales') }}
+ +
+ + + @if (!count($statistics['customer_with_most_sales'])) + +
+ + +

{{ __('admin::app.common.no-result-found') }}

+ +
+ + @endif +
+
-
-
-
- TOP SELLING PRODUCTS -
- -
-
    -
  • -
    -
    - -
    -
    -
    - Men's Olive Denim Jacket - -
    -
    - 7 left -
    -
    -
    -
    -
    -
  • - -
  • -
    -
    - -
    -
    -
    - Apple iPhone 8 Plus - 64GB - - -
    -
    - 250 Sales . In Stock - 500 -
    -
    -
    -
    -
    -
  • - -
  • -
    -
    - -
    -
    -
    - Long Lenngth Printed Shrug - -
    -
    - 250 Products . In Stock - 500 -
    -
    - -
    -
    -
    -
  • - -
  • -
    -
    - -
    -
    -
    - Black Round Neck T-Shirt for Men - - -
    -
    - 250 Products . In Stock - 500 -
    -
    - -
    -
    -
    -
  • - -
  • -
    -
    - -
    -
    -
    - Men's Linnen Shirt -Regular Fit - -
    -
    - 250 Products . In Stock - 500 -
    -
    - -
    -
    -
    -
  • -
-
+
+
+ {{ __('admin::app.dashboard.stock-threshold') }}
+ +
+ + + @if (!count($statistics['stock_threshold'])) + +
+ + +

{{ __('admin::app.common.no-result-found') }}

+ +
+ + @endif +
+
- - -
- - @stop + +@push('scripts') + + + + + + + +@endpush \ No newline at end of file diff --git a/packages/Webkul/Admin/src/Resources/views/layouts/anonymous-master.blade.php b/packages/Webkul/Admin/src/Resources/views/layouts/anonymous-master.blade.php index 07e0c7e52..9fef717d7 100644 --- a/packages/Webkul/Admin/src/Resources/views/layouts/anonymous-master.blade.php +++ b/packages/Webkul/Admin/src/Resources/views/layouts/anonymous-master.blade.php @@ -7,6 +7,8 @@ + + diff --git a/packages/Webkul/Admin/src/Resources/views/sales/orders/index.blade.php b/packages/Webkul/Admin/src/Resources/views/sales/orders/index.blade.php index 77b45387e..696d9c3c7 100644 --- a/packages/Webkul/Admin/src/Resources/views/sales/orders/index.blade.php +++ b/packages/Webkul/Admin/src/Resources/views/sales/orders/index.blade.php @@ -1,10 +1,10 @@ -@extends('admin::layouts.master') +@extends('admin::layouts.content') @section('page_title') {{ __('admin::app.sales.orders.title') }} @stop -@section('content-wrapper') +@section('content')
@stop \ No newline at end of file diff --git a/packages/Webkul/Checkout/src/Cart.php b/packages/Webkul/Checkout/src/Cart.php index ae14b98ee..0f1761bdb 100644 --- a/packages/Webkul/Checkout/src/Cart.php +++ b/packages/Webkul/Checkout/src/Cart.php @@ -159,9 +159,14 @@ class Cart { * * @return void */ - public function add($id, $data) + public function add($id, $data, $prepared = false, $preparedData = []) { - $itemData = $this->prepareItemData($id, $data); + if($prepared == false) { + $itemData = $this->prepareItemData($id, $data); + } + else { + $itemData = $preparedData; + } if(!$itemData) { return false; @@ -172,7 +177,7 @@ class Cart { $cartItems = $cart->items; - //check the isset conditions as collection empty object will mislead the condition and errorhandling case. + //check the isset conditions as collection empty object will mislead the condition and error handling case. if(isset($cartItems) && $cartItems->count()) { foreach($cartItems as $cartItem) { if($product->type == "simple") { @@ -202,6 +207,12 @@ class Cart { } else if($product->type == "configurable") { if($cartItem->type == "configurable") { $temp = $this->cartItem->findOneByField('parent_id', $cartItem->id); + + if($prepared == true) { + $data['selected_configurable_option'] = $preparedData['parent']['product_id']; + } + + if($temp->product_id == $data['selected_configurable_option']) { $child = $temp->child; @@ -252,11 +263,23 @@ class Cart { if(isset($cart)) { $this->cart->delete($cart->id); } else { - return $this->createNewCart($id, $data); + if($prepared == false) { + return $this->createNewCart($id, $data); + } + else { + return $this->createNewCart($id, $data, true, $preparedData); + } + } } } else { - return $this->createNewCart($id, $data); + // return $this->createNewCart($id, $data); + if($prepared == false) { + return $this->createNewCart($id, $data); + } + else { + return $this->createNewCart($id, $data, true, $preparedData); + } } } @@ -268,9 +291,12 @@ class Cart { * * @return Booleans */ - public function createNewCart($id, $data) + public function createNewCart($id, $data, $prepared = false, $preparedData = []) { - $itemData = $this->prepareItemData($id, $data); + if($prepared == false) + $itemData = $this->prepareItemData($id, $data); + else + $itemData = $preparedData; //if the item data is not valid to be processed it will be returning false if($itemData == false) { @@ -297,7 +323,13 @@ class Cart { $cartData['cart_currency_code'] = core()->getCurrentCurrencyCode(); //set the cart items and quantity $cartData['items_count'] = 1; - $cartData['items_qty'] = $data['quantity']; + + if($prepared == false) { + $cartData['items_qty'] = $data['quantity']; + } else { + $cartData['items_qty'] = 1; + } + //create the cart instance in the database if($cart = $this->cart->create($cartData)) { @@ -306,7 +338,11 @@ class Cart { if ($product->type == "configurable") { //parent item entry - $itemData['parent']['additional'] = json_encode($data); + if($prepared == false) { + $itemData['parent']['additional'] = json_encode($data); + } else { + $itemData['parent']['additional'] = json_encode($preparedData); + } if($parent = $this->cartItem->create($itemData['parent'])) { //child item entry @@ -953,17 +989,78 @@ class Cart { * Move a wishlist item to cart */ public function moveToCart($productId) { - $data = [ - 'product' => $productId, - 'quantity' => 1, - ]; + $product = $this->product->find($productId); - $result = $this->add($productId, $data); + if($product->parent_id == null ||$product->parent_id == 'null') { + $data = [ + 'product' => $productId, + 'quantity' => 1, + ]; - if($result instanceof Collection || $result == true) { - return true; + $result = $this->add($productId, $data); + + if($result instanceof Collection || $result == true) { + return true; + } else { + return false; + } } else { - return false; + //in case the product added is a configurable product. + $result = $this->moveConfigurableFromWishlistToCart($product->parent_id, $product->id); + + if(is_array($result)) { + $data['quantity'] = 1; + + $data['selected_configurable_option'] = $product->parent_id; + + $moved = $this->add($data['selected_configurable_option'], $data, true, $result); + + if($moved) { + return true; + } else { + return false; + } + } } } + + /** + * Move a configurable product from wishlist to cart. + * + * @return mixed + */ + public function moveConfigurableFromWishlistToCart($configurableproductId, $productId) { + $product = $this->product->find($configurableproductId); + + $child = $childData = null; + if($product->type == 'configurable') { + $child = $this->product->findOneByField('id', $productId); + + $childData = [ + 'product_id' => $configurableproductId, + 'sku' => $child->sku, + 'name' => $child->name, + 'type' => 'simple' + ]; + } + + $price = ($product->type == 'configurable' ? $child->price : $product->price); + + $parentData = [ + 'sku' => $product->sku, + 'product_id' => $productId, + 'quantity' => 1, + 'type' => $product->type, + 'name' => $product->name, + 'price' => core()->convertPrice($price), + 'base_price' => $price, + 'total' => core()->convertPrice($price), + 'base_total' => $price, + 'weight' => $weight = ($product->type == 'configurable' ? $child->weight : $product->weight), + 'total_weight' => $weight, + 'base_total_weight' => $weight + ]; + + return ['parent' => $parentData, 'child' => $childData]; + } } \ No newline at end of file diff --git a/packages/Webkul/Core/src/Core.php b/packages/Webkul/Core/src/Core.php index 26126db19..e4e0b21cb 100644 --- a/packages/Webkul/Core/src/Core.php +++ b/packages/Webkul/Core/src/Core.php @@ -512,4 +512,77 @@ class Core return $collection; } + + /** + * Returns time intervals + * + * @return array + */ + public function getTimeInterval($startDate, $endDate) { + $timeIntervals = []; + + $totalDays = $startDate->diffInDays($endDate); + $totalMonths = $startDate->diffInMonths($endDate); + + $startWeekDay = Carbon::createFromTimeString($this->xWeekRange($startDate, 0) . ' 00:00:01'); + $endWeekDay = Carbon::createFromTimeString($this->xWeekRange($endDate, 1) . ' 23:59:59'); + $totalWeeks = $startWeekDay->diffInWeeks($endWeekDay); + + if($totalMonths > 5) { + for ($i = 0; $i < $totalMonths; $i++) { + $date = clone $startDate; + $date->addMonths($i); + + $start = Carbon::createFromTimeString($date->format('Y-m-d') . ' 00:00:01'); + $end = $totalMonths - 1 == $i + ? $endDate + : Carbon::createFromTimeString($date->format('Y-m-d') . ' 23:59:59'); + + $timeIntervals[] = ['start' => $start, 'end' => $end, 'formatedDate' => $date->format('M')]; + } + } elseif($totalWeeks > 6) { + for ($i = 0; $i < $totalWeeks; $i++) { + $date = clone $startDate; + $date->addWeeks($i); + + $start = $i == 0 + ? $startDate + : Carbon::createFromTimeString($this->xWeekRange($date, 0) . ' 00:00:01'); + $end = $totalWeeks - 1 == $i + ? $endDate + : Carbon::createFromTimeString($this->xWeekRange($date, 1) . ' 23:59:59'); + + $timeIntervals[] = ['start' => $start, 'end' => $end, 'formatedDate' => $date->format('d M')]; + } + } else { + for ($i = 0; $i < $totalDays; $i++) { + $date = clone $startDate; + $date->addDays($i); + + $start = Carbon::createFromTimeString($date->format('Y-m-d') . ' 00:00:01'); + $end = Carbon::createFromTimeString($date->format('Y-m-d') . ' 23:59:59'); + + $timeIntervals[] = ['start' => $start, 'end' => $end, 'formatedDate' => $date->format('d M')]; + } + } + + return $timeIntervals; + } + + /** + * @return string + */ + public function xWeekRange($date, $day) { + $ts = strtotime($date); + + if(!$day) { + $start = (date('D', $ts) == 'Sun') ? $ts : strtotime('last sunday', $ts); + + return date('Y-m-d', $start); + } else { + $end = (date('D', $ts) == 'Sat') ? $ts : strtotime('next saturday', $ts); + + return date('Y-m-d', $end); + } + } } \ No newline at end of file diff --git a/packages/Webkul/Core/src/Database/Migrations/2018_07_20_054542_create_currency_exchange_rates_table.php b/packages/Webkul/Core/src/Database/Migrations/2018_07_20_054542_create_currency_exchange_rates_table.php index 83764e3a9..e68558cd6 100644 --- a/packages/Webkul/Core/src/Database/Migrations/2018_07_20_054542_create_currency_exchange_rates_table.php +++ b/packages/Webkul/Core/src/Database/Migrations/2018_07_20_054542_create_currency_exchange_rates_table.php @@ -16,7 +16,6 @@ class CreateCurrencyExchangeRatesTable extends Migration Schema::create('currency_exchange_rates', function (Blueprint $table) { $table->increments('id'); $table->decimal('rate', 10, 5); - $table->integer('target_currency')->unique()->unsigned(); $table->foreign('target_currency')->references('id')->on('currencies')->onDelete('cascade'); $table->timestamps(); diff --git a/packages/Webkul/Core/src/Eloquent/Repository.php b/packages/Webkul/Core/src/Eloquent/Repository.php index 07d2d7401..30add72fa 100644 --- a/packages/Webkul/Core/src/Eloquent/Repository.php +++ b/packages/Webkul/Core/src/Eloquent/Repository.php @@ -87,7 +87,41 @@ abstract class Repository extends BaseRepository { */ public function count() { - return $this->model->count(); + $this->applyCriteria(); + $this->applyScope(); + + $total = $this->model->count(); + $this->resetModel(); + + return $total; + } + + /** + * @return mixed + */ + public function sum($columns) + { + $this->applyCriteria(); + $this->applyScope(); + + $sum = $this->model->sum($columns); + $this->resetModel(); + + return $sum; + } + + /** + * @return mixed + */ + public function avg($columns) + { + $this->applyCriteria(); + $this->applyScope(); + + $avg = $this->model->avg($columns); + $this->resetModel(); + + return $avg; } /** diff --git a/packages/Webkul/Customer/src/Http/Controllers/RegistrationController.php b/packages/Webkul/Customer/src/Http/Controllers/RegistrationController.php index fa9ac1f22..53b389bd1 100644 --- a/packages/Webkul/Customer/src/Http/Controllers/RegistrationController.php +++ b/packages/Webkul/Customer/src/Http/Controllers/RegistrationController.php @@ -51,20 +51,20 @@ class RegistrationController extends Controller */ public function create(Request $request) { - // dd(request()->input()); $request->validate([ - 'first_name' => 'string|required', 'last_name' => 'string|required', 'email' => 'email|required', 'password' => 'confirmed|min:6|required', - 'agreement' => 'confirmed' + 'agreement' => 'required' ]); $data = request()->input(); $data['password'] = bcrypt($data['password']); + $data['channel_id'] = core()->getCurrentChannel()->id; + if ($this->customer->create($data)) { session()->flash('success', 'Account created successfully.'); @@ -72,11 +72,9 @@ class RegistrationController extends Controller return redirect()->route($this->_config['redirect']); } else { - session()->flash('error', 'Cannot Create Your Account.'); return redirect()->back(); - } } } diff --git a/packages/Webkul/Customer/src/Http/Controllers/WishlistController.php b/packages/Webkul/Customer/src/Http/Controllers/WishlistController.php index 600431d22..646230d1c 100644 --- a/packages/Webkul/Customer/src/Http/Controllers/WishlistController.php +++ b/packages/Webkul/Customer/src/Http/Controllers/WishlistController.php @@ -133,7 +133,6 @@ class WishlistController extends Controller * @return response */ public function addconfigurable($urlkey) { - dd($urlkey); session()->flash('warning', trans('Select options before adding to wishlist')); return redirect()->route('shop.products.index', $urlkey); } @@ -155,16 +154,24 @@ class WishlistController extends Controller public function move($productId) { $result = Cart::moveToCart($productId); - $wishlist = $this->wishlist->findWhere(['customer_id' => auth()->guard('customer')->user()->id, 'product_id' => $productId]); + if($result) { + $wishlist = $this->wishlist->findWhere(['customer_id' => auth()->guard('customer')->user()->id, 'product_id' => $productId]); - if($this->wishlist->delete($wishlist[0]->id)) { - session()->flash('success', 'Item Moved To Cart Successfully'); + if($this->wishlist->delete($wishlist[0]->id)) { + Cart::collectTotals(); - return redirect()->back(); + session()->flash('success', 'Item Moved To Cart Successfully'); + + return redirect()->back(); + } else { + session()->flash('error', 'Item Cannot Be Moved To Cart Successfully'); + + return redirect()->back(); + } } else { - session()->flash('error', 'Item Cannot Be Moved To Cart Successfully'); + Session('error', 'Cannot Add The Product To Wishlist Due To Unknown Problems, Please Checkback Later'); return redirect()->back(); } } -} +} \ No newline at end of file diff --git a/packages/Webkul/Customer/src/Models/Customer.php b/packages/Webkul/Customer/src/Models/Customer.php index 9e31965d3..912086883 100644 --- a/packages/Webkul/Customer/src/Models/Customer.php +++ b/packages/Webkul/Customer/src/Models/Customer.php @@ -18,9 +18,9 @@ class Customer extends Authenticatable protected $table = 'customers'; - protected $fillable = ['channel_id', 'first_name', 'last_name', 'gender', 'date_of_birth','phone','email','password','customer_group_id','subscribed_to_news_letter']; + protected $fillable = ['first_name', 'channel_id', 'last_name', 'gender', 'date_of_birth', 'phone', 'email', 'password', 'customer_group_id', 'subscribed_to_news_letter']; - protected $hidden = ['password','remember_token']; + protected $hidden = ['password', 'remember_token']; protected $with = ['customerGroup']; diff --git a/packages/Webkul/Customer/src/Notifications/CustomerResetPassword.php b/packages/Webkul/Customer/src/Notifications/CustomerResetPassword.php index 3c1517058..9db9e27ae 100644 --- a/packages/Webkul/Customer/src/Notifications/CustomerResetPassword.php +++ b/packages/Webkul/Customer/src/Notifications/CustomerResetPassword.php @@ -21,8 +21,9 @@ class CustomerResetPassword extends ResetPassword } return (new MailMessage) - ->line('You are receiving this email because we received a password reset request for your account.') - ->action('Reset Password', route('customer.reset-password.create', $this->token)) - ->line('If you did not request a password reset, no further action is required.'); + ->view('shop::emails.customer.forget-password')->with([ + 'user_name' => $notifiable->name, + 'token' => $this->token + ]); } } diff --git a/packages/Webkul/Customer/src/Resources/lang/en/app.php b/packages/Webkul/Customer/src/Resources/lang/en/app.php index 30f59002c..cbd32f946 100644 --- a/packages/Webkul/Customer/src/Resources/lang/en/app.php +++ b/packages/Webkul/Customer/src/Resources/lang/en/app.php @@ -10,4 +10,7 @@ return [ 'empty' => 'You Don\'t Have Any Items In Your Wishlist', 'select-options' => 'Need To Select Options Before Adding To Wishlist' ], + 'reviews' => [ + 'empty' => 'You Haven\'t Reviewed Any Product Yet' + ] ]; \ No newline at end of file diff --git a/packages/Webkul/Product/src/Database/Migrations/2018_10_22_111807_create_products_grid.php b/packages/Webkul/Product/src/Database/Migrations/2018_10_22_111807_create_products_grid.php new file mode 100644 index 000000000..46130304f --- /dev/null +++ b/packages/Webkul/Product/src/Database/Migrations/2018_10_22_111807_create_products_grid.php @@ -0,0 +1,40 @@ +increments('id'); + $table->integer('product_id')->unsigned(); + $table->foreign('product_id')->references('id')->on('products')->onDelete('cascade'); + $table->string('sku')->nullable(); + $table->string('type')->nullable(); + $table->string('name')->nullable(); + $table->string('quantity')->default(0); + $table->string('cost')->default(0); + $table->string('price')->nullable(); + $table->string('status')->nullable(); + $table->string('attribute_family_name')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('products_grid'); + } +} diff --git a/packages/Webkul/Product/src/Http/Controllers/ProductController.php b/packages/Webkul/Product/src/Http/Controllers/ProductController.php index 47e3fb0a1..44a88fd5b 100644 --- a/packages/Webkul/Product/src/Http/Controllers/ProductController.php +++ b/packages/Webkul/Product/src/Http/Controllers/ProductController.php @@ -6,9 +6,11 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; use Webkul\Product\Http\Requests\ProductForm; use Webkul\Product\Repositories\ProductRepository as Product; +use Webkul\Product\Repositories\ProductGridRepository as ProductGrid; use Webkul\Attribute\Repositories\AttributeFamilyRepository as AttributeFamily; use Webkul\Category\Repositories\CategoryRepository as Category; use Webkul\Inventory\Repositories\InventorySourceRepository as InventorySource; +use Event; /** * Product controller @@ -53,6 +55,13 @@ class ProductController extends Controller */ protected $product; + /** + * ProductGrid Repository object + * + * @var array + */ + protected $productGrid; + /** * Create a new controller instance. * @@ -66,7 +75,8 @@ class ProductController extends Controller AttributeFamily $attributeFamily, Category $category, InventorySource $inventorySource, - Product $product) + Product $product, + ProductGrid $productGrid) { $this->attributeFamily = $attributeFamily; @@ -76,6 +86,8 @@ class ProductController extends Controller $this->product = $product; + $this->productGrid = $productGrid; + $this->_config = request('_config'); } @@ -112,6 +124,9 @@ class ProductController extends Controller */ public function store() { + //before store of the product + // Event::fire('product.save.before', false); + if(!request()->get('family') && request()->input('type') == 'configurable' && request()->input('sku') != '') { return redirect(url()->current() . '?family=' . request()->input('attribute_family_id') . '&sku=' . request()->input('sku')); } @@ -130,6 +145,9 @@ class ProductController extends Controller $product = $this->product->create(request()->all()); + //after store of the product + Event::fire('product.save.after', $product); + session()->flash('success', 'Product created successfully.'); return redirect()->route($this->_config['redirect'], ['id' => $product->id]); @@ -161,8 +179,14 @@ class ProductController extends Controller */ public function update(ProductForm $request, $id) { + // before update of product + // Event::fire('product.update.before', $id); + $this->product->update(request()->all(), $id); + //after update of product + Event::fire('product.update.after', $this->product->find($id)); + session()->flash('success', 'Product updated successfully.'); return redirect()->route($this->_config['redirect']); @@ -178,8 +202,15 @@ class ProductController extends Controller { $this->product->delete($id); + //before update of product + Event::fire('product.delete.after', $id); + session()->flash('success', 'Product deleted successfully.'); return redirect()->back(); } + + public function sync() { + Event::fire('products.datagrid.create', true); + } } \ No newline at end of file diff --git a/packages/Webkul/Product/src/Models/ProductGrid.php b/packages/Webkul/Product/src/Models/ProductGrid.php new file mode 100644 index 000000000..4b5779ba8 --- /dev/null +++ b/packages/Webkul/Product/src/Models/ProductGrid.php @@ -0,0 +1,19 @@ +belongsTo(Product::class, 'product_id'); + } +} \ No newline at end of file diff --git a/packages/Webkul/Product/src/Models/ProductInventory.php b/packages/Webkul/Product/src/Models/ProductInventory.php index 1bf30ebca..f76ba89a5 100644 --- a/packages/Webkul/Product/src/Models/ProductInventory.php +++ b/packages/Webkul/Product/src/Models/ProductInventory.php @@ -10,19 +10,7 @@ class ProductInventory extends Model public $timestamps = false; protected $fillable = ['qty', 'product_id', 'inventory_source_id']; - - /** - * Use by cart for - * checking the - * inventory source - * status - * - * @return Collection - */ - // public function checkInventoryStatus() { - // return $this->leftjoin('inventory_sources', 'inventory_sources.id', 'inventory_source_id')->select('status')->where('status', '=','1'); - // } - + /** * Get the product attribute family that owns the product. */ @@ -30,4 +18,12 @@ class ProductInventory extends Model { return $this->belongsTo(InventorySource::class); } + + /** + * Get the product that owns the product inventory. + */ + public function product() + { + return $this->belongsTo(Product::class); + } } \ No newline at end of file diff --git a/packages/Webkul/Product/src/Providers/ProductServiceProvider.php b/packages/Webkul/Product/src/Providers/ProductServiceProvider.php index 5d9b4b613..73c99bb38 100644 --- a/packages/Webkul/Product/src/Providers/ProductServiceProvider.php +++ b/packages/Webkul/Product/src/Providers/ProductServiceProvider.php @@ -6,6 +6,7 @@ use Illuminate\Support\ServiceProvider; use Illuminate\Routing\Router; use Webkul\Product\Models\Product; use Webkul\Product\Observers\ProductObserver; +use Event; class ProductServiceProvider extends ServiceProvider { @@ -18,6 +19,18 @@ class ProductServiceProvider extends ServiceProvider { $this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations'); + Event::listen('products.datagrid.create', 'Webkul\Admin\Listeners\Product@sync'); + + Event::listen('product.save.after', 'Webkul\Admin\Listeners\Product@afterProductCreated'); + + Event::listen('product.update.before', + 'Webkul\Admin\Listeners\Product@beforeProductUpdate'); + + Event::listen('product.update.after', + 'Webkul\Admin\Listeners\Product@afterProductUpdate'); + + Event::listen('product.delete.after', 'Webkul\Admin\Listeners\Product@afterProductDelete'); + Product::observe(ProductObserver::class); } @@ -28,6 +41,6 @@ class ProductServiceProvider extends ServiceProvider */ public function register() { - + } } \ No newline at end of file diff --git a/packages/Webkul/Product/src/Repositories/ProductGridRepository.php b/packages/Webkul/Product/src/Repositories/ProductGridRepository.php new file mode 100644 index 000000000..c11180504 --- /dev/null +++ b/packages/Webkul/Product/src/Repositories/ProductGridRepository.php @@ -0,0 +1,114 @@ + @prashant-webkul + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class ProductGridRepository extends Repository +{ + protected $product; + + /** + * Price Object + * + * @var array + */ + Protected $price; + + + public function __construct( + Product $product, + Price $price, + App $app + ) + { + $this->product = $product; + + $this->price = $price; + + parent::__construct($app); + } + + public function model() { + return 'Webkul\Product\Models\ProductGrid'; + } + + public function updateWhere($product) { + if($product->type == "simple") { + $gridObject = [ + 'sku' => $product->sku, + 'name' => $product->name, + 'attribute_family_name' => $product->toArray()['attribute_family']['name'], + 'price' => $this->price->getMinimalPrice($product), + 'status' => $product->status + ]; + + $qty = 0; + + if($product->parent_id == 'null') { + $gridObject['type'] = $product->type; + } + + foreach($product->toArray()['inventories'] as $inventorySource) { + $qty = $qty + $inventorySource['qty']; + } + + $gridObject['quantity'] = $qty; + + return $this->getModel()->where('product_id', $product->id)->update($gridObject); + + } else if($product->type == "configurable") { + $gridObject = [ + 'sku' => $product->sku, + 'type' => $product->type, + 'name' => $product->name, + 'attribute_family_name' => $product->toArray()['attribute_family']['name'], + 'price' => $this->price->getMinimalPrice($product), + 'status' => $product->status + ]; + $qty = 0; + + $gridObject['quantity'] = $qty; + + $this->getModel()->where('product_id', $product->id)->update($gridObject); + + $variants = $product->variants; + + foreach($variants as $variant) { + $gridObject = []; + + $gridObject = [ + 'sku' => $variant->sku, + 'name' => $variant->name, + 'attribute_family_name' => $variant->toArray()['attribute_family']['name'], + 'price' => $this->price->getMinimalPrice($variant), + 'status' => $variant->status + ]; + + if($variant->type == 'configurable') { + $gridObject['quantity'] = 0; + } else { + $qty = 0; + + foreach($variant->toArray()['inventories'] as $inventorySource) { + $qty = $qty + $inventorySource['qty']; + } + + $gridObject['quantity'] = $qty; + } + + return $this->getModel()->where('product_id', $product->id)->update($gridObject); + } + } + return false; + } +} \ No newline at end of file diff --git a/packages/Webkul/Sales/src/Models/OrderItem.php b/packages/Webkul/Sales/src/Models/OrderItem.php index 08ad131cb..ee4ca55b2 100644 --- a/packages/Webkul/Sales/src/Models/OrderItem.php +++ b/packages/Webkul/Sales/src/Models/OrderItem.php @@ -4,6 +4,7 @@ namespace Webkul\Sales\Models; use Illuminate\Database\Eloquent\Model; use Webkul\Sales\Contracts\OrderItem as OrderItemContract; +use Webkul\Product\Models\Product; class OrderItem extends Model implements OrderItemContract { diff --git a/packages/Webkul/Shop/src/Http/routes.php b/packages/Webkul/Shop/src/Http/routes.php index 2cce2c032..fb5d31694 100644 --- a/packages/Webkul/Shop/src/Http/routes.php +++ b/packages/Webkul/Shop/src/Http/routes.php @@ -76,7 +76,7 @@ Route::group(['middleware' => ['web', 'theme', 'locale', 'currency']], function //Reset Password create Route::get('/reset-password/{token}', 'Webkul\Customer\Http\Controllers\ResetPasswordController@create')->defaults('_config', [ 'view' => 'shop::customers.signup.reset-password' - ])->name('password.reset'); + ])->name('customer.reset-password.create'); Route::post('/reset-password', 'Webkul\Customer\Http\Controllers\ResetPasswordController@store')->defaults('_config', [ 'redirect' => 'customer.session.index' diff --git a/packages/Webkul/Shop/src/Resources/assets/sass/app.scss b/packages/Webkul/Shop/src/Resources/assets/sass/app.scss index 6e34f1eb0..a8c5a7371 100644 --- a/packages/Webkul/Shop/src/Resources/assets/sass/app.scss +++ b/packages/Webkul/Shop/src/Resources/assets/sass/app.scss @@ -586,7 +586,7 @@ section.slider-block { } .header-bottom { - height: 48px; + height: 47px; margin-left: auto; margin-right: auto; border-top: 1px solid $border-color; @@ -596,7 +596,7 @@ section.slider-block { ul.nav { display: block; font-size:16px; - height: 48px; + // height: 48px; max-width: 100%; width: auto; margin-left: auto; diff --git a/packages/Webkul/Shop/src/Resources/assets/sass/icons.scss b/packages/Webkul/Shop/src/Resources/assets/sass/icons.scss index 0ad81d721..a2af8910f 100644 --- a/packages/Webkul/Shop/src/Resources/assets/sass/icons.scss +++ b/packages/Webkul/Shop/src/Resources/assets/sass/icons.scss @@ -7,7 +7,7 @@ background-image:URL('../images/icon-dropdown-left.svg'); width: 8px; height: 8px; - margin-left:auto; + margin-left: auto; margin-bottom: 2px; } @@ -15,7 +15,14 @@ background-image:URL('../images/icon-menu-close.svg'); width: 24px; height: 24px; - margin-left:auto; + margin-left: auto; +} + +.icon-menu-close-adj { + background-image:URL('../images/cross-icon-adj.svg'); + width: 32px; + height: 32px; + margin-left: auto; } .grid-view-icon { diff --git a/packages/Webkul/Shop/src/Resources/lang/en/app.php b/packages/Webkul/Shop/src/Resources/lang/en/app.php index bbed35b6b..7af6c6919 100644 --- a/packages/Webkul/Shop/src/Resources/lang/en/app.php +++ b/packages/Webkul/Shop/src/Resources/lang/en/app.php @@ -17,7 +17,6 @@ return [ 'add-review-page-title' => 'Add Review', 'write-review' => 'Write a review', 'review-title' => 'Give Your Review a Title', - 'empty' => 'You Haven\'t Reviewed Any Product Yet' ], 'customer' => [ diff --git a/packages/Webkul/Shop/src/Resources/views/customers/account/reviews/index.blade.php b/packages/Webkul/Shop/src/Resources/views/customers/account/reviews/index.blade.php index d3c8bc759..66a87afbe 100644 --- a/packages/Webkul/Shop/src/Resources/views/customers/account/reviews/index.blade.php +++ b/packages/Webkul/Shop/src/Resources/views/customers/account/reviews/index.blade.php @@ -21,35 +21,33 @@