diff --git a/controllers/admin/AdminCategoriesController.php b/controllers/admin/AdminCategoriesController.php index 24f250fdb..4dd277177 100644 --- a/controllers/admin/AdminCategoriesController.php +++ b/controllers/admin/AdminCategoriesController.php @@ -350,9 +350,9 @@ class AdminCategoriesControllerCore extends AdminController $helper->icon = 'icon-off'; $helper->color = 'color1'; $helper->title = $this->l('Disabled Categories'); - if (ConfigurationKPI::get('DISABLED_CATS') !== false) - $helper->value = ConfigurationKPI::get('DISABLED_CATS'); - if (ConfigurationKPI::get('DISABLED_CATS_EXPIRE') < $time) + if (ConfigurationKPI::get('DISABLED_CATEGORIES') !== false) + $helper->value = ConfigurationKPI::get('DISABLED_CATEGORIES'); + if (ConfigurationKPI::get('DISABLED_CATEGORIES_EXPIRE') < $time) $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=disabled_categories'; $kpis[] = $helper->generate(); @@ -361,9 +361,9 @@ class AdminCategoriesControllerCore extends AdminController $helper->icon = 'icon-bookmark-empty'; $helper->color = 'color2'; $helper->title = $this->l('Empty Categories'); - if (ConfigurationKPI::get('EMPTY_CATS') !== false) - $helper->value = ConfigurationKPI::get('EMPTY_CATS'); - if (ConfigurationKPI::get('EMPTY_CATS_EXPIRE') < $time) + if (ConfigurationKPI::get('EMPTY_CATEGORIES') !== false) + $helper->value = ConfigurationKPI::get('EMPTY_CATEGORIES'); + if (ConfigurationKPI::get('EMPTY_CATEGORIES_EXPIRE') < $time) $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=empty_categories'; $kpis[] = $helper->generate(); diff --git a/controllers/admin/AdminProductsController.php b/controllers/admin/AdminProductsController.php index 830c8cbe3..0361a0eb0 100644 --- a/controllers/admin/AdminProductsController.php +++ b/controllers/admin/AdminProductsController.php @@ -2296,26 +2296,52 @@ class AdminProductsControllerCore extends AdminController /* The data generation is located in AdminStatsControllerCore */ - $helper = new HelperKpi(); - $helper->id = 'box-products-stock'; - $helper->icon = 'icon-archive'; - $helper->color = 'color1'; - $helper->title = $this->l('Products in Stock'); - if (ConfigurationKPI::get('PRODUCTS_STOCK') !== false) - $helper->value = ConfigurationKPI::get('PRODUCTS_STOCK'); - if (ConfigurationKPI::get('PRODUCTS_STOCK_EXPIRE') < $time) - $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=products_stock'; - $kpis[] = $helper->generate(); + if (Configuration::get('PS_STOCK_MANAGEMENT')) + { + $helper = new HelperKpi(); + $helper->id = 'box-products-stock'; + $helper->icon = 'icon-archive'; + $helper->color = 'color1'; + $helper->title = $this->l('Items in Stock'); + if (ConfigurationKPI::get('PERCENT_PRODUCT_STOCK') !== false) + $helper->value = ConfigurationKPI::get('PERCENT_PRODUCT_STOCK'); + if (ConfigurationKPI::get('PERCENT_PRODUCT_STOCK_EXPIRE') < $time) + $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=percent_product_stock'; + $kpis[] = $helper->generate(); + } $helper = new HelperKpi(); $helper->id = 'box-avg-gross-margin'; $helper->icon = 'icon-tags'; $helper->color = 'color2'; $helper->title = $this->l('Average Gross Margin'); - if (ConfigurationKPI::get('AVG_GROSS_MARGIN') !== false) - $helper->value = ConfigurationKPI::get('AVG_GROSS_MARGIN'); - if (ConfigurationKPI::get('AVG_GROSS_MARGIN_EXPIRE') < $time) - $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=avg_gross_margin'; + if (ConfigurationKPI::get('PRODUCT_AVG_GROSS_MARGIN') !== false) + $helper->value = ConfigurationKPI::get('PRODUCT_AVG_GROSS_MARGIN'); + if (ConfigurationKPI::get('PRODUCT_AVG_GROSS_MARGIN_EXPIRE') < $time) + $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=product_avg_gross_margin'; + $kpis[] = $helper->generate(); + + $helper = new HelperKpi(); + $helper->id = 'box-8020-sales-catalog'; + $helper->icon = 'icon-beaker'; + $helper->color = 'color3'; + $helper->title = $this->l('80% of your sales'); + $helper->subtitle = $this->l('30 days'); + if (ConfigurationKPI::get('8020_SALES_CATALOG') !== false) + $helper->value = ConfigurationKPI::get('8020_SALES_CATALOG'); + if (ConfigurationKPI::get('8020_SALES_CATALOG_EXPIRE') < $time) + $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=8020_sales_catalog'; + $kpis[] = $helper->generate(); + + $helper = new HelperKpi(); + $helper->id = 'box-disabled-products'; + $helper->icon = 'icon-off'; + $helper->color = 'color4'; + $helper->title = $this->l('Disabled Products'); + if (ConfigurationKPI::get('DISABLED_PRODUCTS') !== false) + $helper->value = ConfigurationKPI::get('DISABLED_PRODUCTS'); + if (ConfigurationKPI::get('DISABLED_PRODUCTS_EXPIRE') < $time) + $helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=disabled_products'; $kpis[] = $helper->generate(); $helper = new HelperKpiRow(); diff --git a/controllers/admin/AdminStatsController.php b/controllers/admin/AdminStatsController.php index e3e9df6bc..6758b7f8f 100644 --- a/controllers/admin/AdminStatsController.php +++ b/controllers/admin/AdminStatsController.php @@ -26,51 +26,184 @@ class AdminStatsControllerCore extends AdminStatsTabController { + public static function getUniqueVisitors($date_from, $date_to, $granularity = false) + { + $visitors = array(); + $gapi = Module::isInstalled('gapi') ? Module::getInstanceByName('gapi') : false; + if (Validate::isLoadedObject($gapi) && $gapi->isConfigured()) + { + if ($result = $gapi->requestReportData($granularity ? 'ga:date' : '', 'ga:visitors', $date_from, $date_to, null, null, 1, 30)) + foreach ($result as $row) + if ($granularity == 'day') + $visitors[strtotime(preg_replace('/^([0-9]{4})([0-9]{2})([0-9]{2})$/', '$1-$2-$3', $row['dimensions']['date']))] = $row['metrics']['visitors']; + elseif ($granularity == false) + $visitors = $row['metrics']['visitors']; + } + else + { + if ($granularity == 'day') + { + $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS(' + SELECT LEFT(`date_add`, 10) as date, COUNT(DISTINCT id_guest) as visitors + FROM `'._DB_PREFIX_.'connections` + WHERE `date_add` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" + '.Shop::addSqlRestriction(false).' + GROUP BY LEFT(`date_add`, 10)'); + foreach ($result as $row) + $visitors[strtotime($row['date'])] = $row['visitors']; + } + else + $visitors = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' + SELECT COUNT(DISTINCT id_guest) as visitors + FROM `'._DB_PREFIX_.'connections` + WHERE `date_add` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" + '.Shop::addSqlRestriction(false)); + } + return $visitors; + } + + public static function getAbandonedCarts($date_from, $date_to, $granularity = false) + { + return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' + SELECT COUNT(*) + FROM `'._DB_PREFIX_.'cart` + WHERE `date_add` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" + AND id_cart NOT IN (SELECT id_cart FROM `'._DB_PREFIX_.'orders`) + '.Shop::addSqlRestriction(Shop::SHARE_ORDER)); + } + + public static function getPercentProductStock() + { + $row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(' + SELECT SUM(IF(IFNULL(stock.quantity, 0) > 0, 1, 0)) as with_stock, COUNT(*) as products + FROM `'._DB_PREFIX_.'product` p + '.Shop::addSqlAssociation('product', 'p').' + LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON p.id_product = pa.id_product + '.Product::sqlStock('p', 'pa')); + return round($row['products'] ? 100 * $row['with_stock'] / $row['products'] : 0, 2).'%'; + } + + public static function getProductAverageGrossMargin() + { + $value = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' + SELECT AVG((IFNULL(product_attribute_shop.price, product_shop.price) - IFNULL(product_attribute_shop.wholesale_price, product_shop.wholesale_price)) / IFNULL(product_attribute_shop.price, product_shop.price)) + FROM `'._DB_PREFIX_.'product` p + '.Shop::addSqlAssociation('product', 'p').' + LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON p.id_product = pa.id_product + '.Shop::addSqlAssociation('product_attribute', 'pa')); + return round(100 * $value, 2).'%'; + } + + public static function getDisabledCategories() + { + return (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' + SELECT COUNT(*) + FROM `'._DB_PREFIX_.'category` c + '.Shop::addSqlAssociation('category', 'c').' + WHERE c.active = 0'); + } + + public static function getDisabledProducts() + { + return (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' + SELECT COUNT(*) + FROM `'._DB_PREFIX_.'product` p + '.Shop::addSqlAssociation('product', 'p').' + WHERE product_shop.active = 0'); + } + + public static function get8020SalesCatalog($date_from, $date_to) + { + $total_products = (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' + SELECT COUNT(*) + FROM `'._DB_PREFIX_.'product` p + '.Shop::addSqlAssociation('product', 'p')); + $total_sales = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT SUM(total_price_tax_excl / o.conversion_rate) as product_sales + FROM `'._DB_PREFIX_.'orders` o + LEFT JOIN `'._DB_PREFIX_.'order_detail` od ON o.id_order = od.id_order + WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" + '.Shop::addSqlRestriction(Shop::SHARE_ORDER, 'o')); + $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' + SELECT SUM(total_price_tax_excl / o.conversion_rate) as product_sales + FROM `'._DB_PREFIX_.'orders` o + LEFT JOIN `'._DB_PREFIX_.'order_detail` od ON o.id_order = od.id_order + WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" + '.Shop::addSqlRestriction(Shop::SHARE_ORDER, 'o').' + GROUP BY od.product_id, od.product_attribute_id + ORDER BY SUM(total_price_tax_excl) DESC'); + $products = 0; + $products_sales = 0; + foreach ($result as $row) + { + ++$products; + $products_sales += $row['product_sales']; + if ($products_sales > $total_sales) + break; + } + return round(100 * $products / $total_products).'%'; + } + + public static function getOrders($date_from, $date_to, $granularity = false) + { + if ($granularity == 'day') + { + $orders = array(); + $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS(' + SELECT LEFT(`invoice_date`, 10) as date, COUNT(*) as orders + FROM `'._DB_PREFIX_.'orders` + WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" + '.Shop::addSqlRestriction(Shop::SHARE_ORDER).' + GROUP BY LEFT(`invoice_date`, 10)'); + foreach ($result as $row) + $orders[strtotime($row['date'])] = $row['orders']; + } + else + $orders = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' + SELECT COUNT(*) as orders + FROM `'._DB_PREFIX_.'orders` + WHERE `invoice_date` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59" + '.Shop::addSqlRestriction(Shop::SHARE_ORDER)); + return $orders; + } + + public static function getEmptyCategories() + { + $total = (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' + SELECT COUNT(*) + FROM `'._DB_PREFIX_.'category` c + '.Shop::addSqlAssociation('category', 'c').' + AND c.active = 1 + AND c.nright = c.nleft + 1'); + $used = (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' + SELECT COUNT(DISTINCT cp.id_category) + FROM `'._DB_PREFIX_.'category` c + LEFT JOIN `'._DB_PREFIX_.'category_product` cp ON c.id_category = cp.id_category + '.Shop::addSqlAssociation('category', 'cp').' + AND c.active = 1 + AND c.nright = c.nleft + 1'); + return intval($total - $used); + } + public function displayAjaxGetKpi() { $currency = new Currency(Configuration::get('PS_CURRENCY_DEFAULT')); switch (Tools::getValue('kpi')) { case 'conversion_rate': - $visits = array(); - $gapi = Module::isInstalled('gapi') ? Module::getInstanceByName('gapi') : false; - if (Validate::isLoadedObject($gapi) && $gapi->isConfigured()) - { - if ($result = $gapi->requestReportData('ga:date', 'ga:visits', date('Y-m-d', strtotime('-31 day')), date('Y-m-d', strtotime('-1 day')), null, null, 1, 30)) - foreach ($result as $row) - $visits[strtotime(preg_replace('/^([0-9]{4})([0-9]{2})([0-9]{2})$/', '$1-$2-$3', $row['dimensions']['date']))] = $row['metrics']['visits']; - } - else - { - $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS(' - SELECT LEFT(`date_add`, 10) as date, COUNT(*) as visits - FROM `'._DB_PREFIX_.'connections` - WHERE `date_add` BETWEEN "'.pSQL(date('Y-m-d', strtotime('-31 day'))).' 00:00:00" AND "'.pSQL(date('Y-m-d', strtotime('-1 day'))).' 23:59:59" - '.Shop::addSqlRestriction(false).' - GROUP BY LEFT(`date_add`, 10)'); - foreach ($result as $row) - $visits[strtotime($row['date'])] = $row['visits']; - } - $orders = array(); - $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS(' - SELECT LEFT(`invoice_date`, 10) as date, COUNT(*) as orders - FROM `'._DB_PREFIX_.'orders` - WHERE `invoice_date` BETWEEN "'.pSQL(date('Y-m-d', strtotime('-31 day'))).' 00:00:00" AND "'.pSQL(date('Y-m-d', strtotime('-1 day'))).' 23:59:59" - '.Shop::addSqlRestriction(Shop::SHARE_ORDER).' - GROUP BY LEFT(`invoice_date`, 10)'); - foreach ($result as $row) - $orders[strtotime($row['date'])] = $row['orders']; + $visitors = AdminStatsController::getUniqueVisitors(date('Y-m-d', strtotime('-31 day')), date('Y-m-d', strtotime('-1 day')), 'day'); + $orders = AdminStatsController::getOrders(date('Y-m-d', strtotime('-31 day')), date('Y-m-d', strtotime('-1 day')), 'day'); $data = array(); $from = strtotime(date('Y-m-d 00:00:00', strtotime('-31 day'))); $to = strtotime(date('Y-m-d 23:59:59', strtotime('-1 day'))); for ($date = $from; $date <= $to; $date = strtotime('+1 day', $date)) - if (isset($visits[$date]) && $visits[$date]) - $data[$date] = round(100 * ((isset($orders[$date]) && $orders[$date]) ? $orders[$date] : 0) / $visits[$date], 2); + if (isset($visitors[$date]) && $visitors[$date]) + $data[$date] = round(100 * ((isset($orders[$date]) && $orders[$date]) ? $orders[$date] : 0) / $visitors[$date], 2); else $data[$date] = 0; - $visits_sum = array_sum($visits); + $visits_sum = array_sum($visitors); $orders_sum = array_sum($orders); if ($visits_sum) $value = round(100 * $orders_sum / $visits_sum, 2); @@ -86,59 +219,46 @@ class AdminStatsControllerCore extends AdminStatsTabController break; case 'abandoned_cart': - $value = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' - SELECT COUNT(*) - FROM `'._DB_PREFIX_.'cart` - WHERE `date_add` BETWEEN "'.pSQL(date('Y-m-d')).' 00:00:00" AND "'.pSQL(date('Y-m-d')).' 23:59:59" - AND id_cart NOT IN (SELECT id_cart FROM `'._DB_PREFIX_.'orders`) - '.Shop::addSqlRestriction(Shop::SHARE_ORDER)); + $value = AdminStatsController::getAbandonedCarts(date('Y-m-d'), date('Y-m-d')); ConfigurationKPI::updateValue('ABANDONED_CARTS', $value); ConfigurationKPI::updateValue('ABANDONED_CARTS_EXPIRE', strtotime('+10 min')); break; - case 'products_stock': - $row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(' - SELECT SUM(IF(IFNULL(stock.quantity, 0) > 0, 1, 0)) as stock, COUNT(*) as total - FROM `'._DB_PREFIX_.'product` p - '.Shop::addSqlAssociation('product', 'p').' - '.Product::sqlStock('p')); - $value = round($row['total'] ? $row['stock'] / $row['total'] : 0, 2).'%'; - ConfigurationKPI::updateValue('PRODUCTS_STOCK', $value); - ConfigurationKPI::updateValue('PRODUCTS_STOCK_EXPIRE', strtotime('+10 min')); + case 'percent_product_stock': + $value = AdminStatsController::getPercentProductStock(); + ConfigurationKPI::updateValue('PERCENT_PRODUCT_STOCK', $value); + ConfigurationKPI::updateValue('PERCENT_PRODUCT_STOCK_EXPIRE', strtotime('+4 hour')); break; - case 'avg_gross_margin': - $value = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' - SELECT AVG((product_shop.price - product_shop.wholesale_price) / product_shop.price) - FROM `'._DB_PREFIX_.'product` p - '.Shop::addSqlAssociation('product', 'p')); - $value = round(100 * $value, 2).'%'; - ConfigurationKPI::updateValue('AVG_GROSS_MARGIN', $value); - ConfigurationKPI::updateValue('AVG_GROSS_MARGIN_EXPIRE', strtotime('+6 hour')); + case 'product_avg_gross_margin': + $value = AdminStatsController::getProductAverageGrossMargin(); + ConfigurationKPI::updateValue('PRODUCT_AVG_GROSS_MARGIN', $value); + ConfigurationKPI::updateValue('PRODUCT_AVG_GROSS_MARGIN_EXPIRE', strtotime('+6 hour')); break; case 'disabled_categories': - $value = (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' - SELECT COUNT(*) - FROM `'._DB_PREFIX_.'category` c - '.Shop::addSqlAssociation('category', 'c').' - WHERE c.active = 0'); - ConfigurationKPI::updateValue('DISABLED_CATS', $value); - ConfigurationKPI::updateValue('DISABLED_CATS_EXPIRE', strtotime('+2 hour')); + $value = AdminStatsController::getDisabledCategories(); + ConfigurationKPI::updateValue('DISABLED_CATEGORIES', $value); + ConfigurationKPI::updateValue('DISABLED_CATEGORIES_EXPIRE', strtotime('+2 hour')); + break; + + case 'disabled_products': + $value = AdminStatsController::getDisabledProducts(); + ConfigurationKPI::updateValue('DISABLED_PRODUCTS', $value); + ConfigurationKPI::updateValue('DISABLED_PRODUCTS_EXPIRE', strtotime('+2 hour')); + break; + + case '8020_sales_catalog': + $value = AdminStatsController::get8020SalesCatalog(date('Y-m-d', strtotime('-31 day')), date('Y-m-d', strtotime('-1 day'))); + $value .= ' '.$this->l('of your Catalog'); + ConfigurationKPI::updateValue('8020_SALES_CATALOG', $value); + ConfigurationKPI::updateValue('8020_SALES_CATALOG_EXPIRE', strtotime('+12 hour')); break; case 'empty_categories': - $total = (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' - SELECT COUNT(*) - FROM `'._DB_PREFIX_.'category` c - '.Shop::addSqlAssociation('category', 'c')); - $used = (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(' - SELECT COUNT(DISTINCT cp.id_category) - FROM `'._DB_PREFIX_.'category_product` cp - '.Shop::addSqlAssociation('category', 'cp')); - $value = intval($total - $used); - ConfigurationKPI::updateValue('EMPTY_CATS', $value); - ConfigurationKPI::updateValue('EMPTY_CATS_EXPIRE', strtotime('+2 hour')); + $value = AdminStatsController::getEmptyCategories(); + ConfigurationKPI::updateValue('EMPTY_CATEGORIES', $value); + ConfigurationKPI::updateValue('EMPTY_CATEGORIES_EXPIRE', strtotime('+2 hour')); break; case 'customer_main_gender':