diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 57a311f..7489e74 100755 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -19,6 +19,7 @@ use Modules\SalesAnalysis\Models\ClientMapper; use Modules\SalesAnalysis\Models\GeneralMapper; use Modules\SalesAnalysis\Models\ItemMapper; use Modules\SalesAnalysis\Models\RegionMapper; +use Modules\SalesAnalysis\Models\SalesRepMapper; use phpOMS\Asset\AssetType; use phpOMS\Contract\RenderableInterface; use phpOMS\Localization\ISO3166CharEnum; @@ -37,6 +38,12 @@ use phpOMS\Views\View; * @license OMS License 1.0 * @link https://jingga.app * @since 1.0.0 + * + * @feature Create a map of sales (maybe as data points or as heat maps) + * https://github.com/Karaka-Management/oms-ClientManagement/issues/14 + * + * @feature Create a map of all customers (maybe as data points or as heat maps) + * https://github.com/Karaka-Management/oms-ClientManagement/issues/15 */ final class BackendController extends Controller { @@ -348,7 +355,7 @@ final class BackendController extends Controller * @since 1.0.0 * @codeCoverageIgnore */ - public function viewBillAnalysis(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface + public function viewSalesRepAnalysis(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface { $head = $response->data['Content']->head; $nonce = $this->app->appSettings->getOption('script-nonce'); @@ -358,9 +365,79 @@ final class BackendController extends Controller $head->addAsset(AssetType::JSLATE, 'Modules/SalesAnalysis/Controller/Controller.js?v=' . self::VERSION, ['nonce' => $nonce, 'type' => 'module']); $view = new View($this->app->l11nManager, $request, $response); - $view->setTemplate('/Modules/SalesAnalysis/Theme/Backend/analysis-bill'); + $view->setTemplate('/Modules/SalesAnalysis/Theme/Backend/analysis-rep'); $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1005401001, $request, $response); + $businessStart = 1; + $startOfYear = SmartDateTime::createFromDateTime(SmartDateTime::startOfYear($businessStart)); + $startCurrent = $request->getDataDateTime('startcurrent') ?? clone $startOfYear; + $endCurrent = $request->getDataDateTime('endcurrent') ?? SmartDateTime::endOfMonth(); + $endCurrentIndex = SmartDateTime::calculateMonthIndex((int) $endCurrent->format('m'), $businessStart); + $startComparison = $request->getDataDateTime('startcomparison') ?? SmartDateTime::createFromDateTime($startCurrent)->createModify(-1); + $endComparison = $request->getDataDateTime('endcomparison') ?? SmartDateTime::createFromDateTime(SmartDateTime::endOfYear($businessStart))->smartModify(-1); + $historyStart = $startOfYear->createModify(-9); + + $view->data['startCurrent'] = $startCurrent; + $view->data['endCurrent'] = $endCurrent; + $view->data['startComparison'] = $startComparison; + $view->data['endComparison'] = $endComparison; + $view->data['historyStart'] = $historyStart; + + $domestic = UnitMapper::get() + ->with('mainAddress') + ->where('id', $this->app->unitId) + ->execute(); + + [ + $mtdCurrent, + $ytdCurrent, + $monthlyCurrent + ] = SalesRepMapper::monthlySalesProfit( + $startCurrent, + $endCurrent, + $businessStart + ); + + [ + $mtdPY, + $ytdPY, + $monthlyPY + ] = SalesRepMapper::monthlySalesProfit( + $startComparison, + $endComparison, + $businessStart + ); + + [ + $view->data['mtdPYClientRep'], + $view->data['mtdAClientRep'], + $view->data['ytdPYClientRep'], + $view->data['ytdAClientRep'], + ] = SalesRepMapper::mtdYtdRep( + $startCurrent, + $endCurrent, + $startComparison, + $endComparison, + $businessStart + ); + + $annualRepSales = SalesRepMapper::salesProfitRep($historyStart, $startCurrent, $startCurrent, $endCurrent); + + [ + $view->data['mtdPYClientRepCount'], + $view->data['mtdAClientRepCount'], + $view->data['ytdPYClientRepCount'], + $view->data['ytdAClientRepCount'], + ] = SalesRepMapper::mtdYtdClientRep( + $startCurrent, + $endCurrent, + $startComparison, + $endComparison, + $businessStart + ); + + $annualRepCount = SalesRepMapper::annualCustomerRep(clone $historyStart, $endCurrent); + return $view; } @@ -376,7 +453,7 @@ final class BackendController extends Controller * @since 1.0.0 * @codeCoverageIgnore */ - public function viewSalesRepAnalysis(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface + public function viewBillAnalysis(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface { $head = $response->data['Content']->head; $nonce = $this->app->appSettings->getOption('script-nonce'); @@ -386,65 +463,9 @@ final class BackendController extends Controller $head->addAsset(AssetType::JSLATE, 'Modules/SalesAnalysis/Controller/Controller.js?v=' . self::VERSION, ['nonce' => $nonce, 'type' => 'module']); $view = new View($this->app->l11nManager, $request, $response); - $view->setTemplate('/Modules/SalesAnalysis/Theme/Backend/analysis-rep'); + $view->setTemplate('/Modules/SalesAnalysis/Theme/Backend/analysis-bill'); $view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1005401001, $request, $response); - ///// - $currentCustomerRegion = [ - 'Europe' => (int) (\mt_rand(200, 400) / 4), - 'America' => (int) (\mt_rand(200, 400) / 4), - 'Asia' => (int) (\mt_rand(200, 400) / 4), - 'Africa' => (int) (\mt_rand(200, 400) / 4), - 'CIS' => (int) (\mt_rand(200, 400) / 4), - 'Other' => (int) (\mt_rand(200, 400) / 4), - ]; - - $view->data['currentCustomerRegion'] = $currentCustomerRegion; - - $annualCustomerRegion = []; - for ($i = 1; $i < 11; ++$i) { - $annualCustomerRegion[] = [ - 'year' => 2020 - 10 + $i, - 'Europe' => $a = (int) (\mt_rand(200, 400) / 4), - 'America' => $b = (int) (\mt_rand(200, 400) / 4), - 'Asia' => $c = (int) (\mt_rand(200, 400) / 4), - 'Africa' => $d = (int) (\mt_rand(200, 400) / 4), - 'CIS' => $e = (int) (\mt_rand(200, 400) / 4), - 'Other' => $f = (int) (\mt_rand(200, 400) / 4), - 'Total' => $a + $b + $c + $d + $e + $f, - ]; - } - - $view->data['annualCustomerRegion'] = $annualCustomerRegion; - - ///// - $currentCustomersRep = []; - for ($i = 1; $i < 13; ++$i) { - $currentCustomersRep['Rep ' . $i] = [ - 'customers' => (int) (\mt_rand(200, 400) / 12), - ]; - } - - \uasort($currentCustomersRep, function($a, $b) { - return $b['customers'] <=> $a['customers']; - }); - - $view->data['currentCustomersRep'] = $currentCustomersRep; - - $annualCustomersRep = []; - for ($i = 1; $i < 13; ++$i) { - $annualCustomersRep['Rep ' . $i] = []; - - for ($j = 1; $j < 11; ++$j) { - $annualCustomersRep['Rep ' . $i][] = [ - 'customers' => (int) (\mt_rand(200, 400) / 12), - 'year' => 2020 - 10 + $j, - ]; - } - } - - $view->data['annualCustomersRep'] = $annualCustomersRep; - return $view; } diff --git a/Models/ClientMapper.php b/Models/ClientMapper.php index 9f34ca0..fbaa5bd 100755 --- a/Models/ClientMapper.php +++ b/Models/ClientMapper.php @@ -53,8 +53,8 @@ class ClientMapper extends DataMapperFactory clientmgmt_attr_value_l11n_title, YEAR(billing_bill_performance_date) as salesyear, MONTH(billing_bill_performance_date) as salesmonth, - SUM(billing_bill_netsales * billing_type_transfer_sign) as netsales, - SUM(billing_bill_netprofit * billing_type_transfer_sign) as netprofit + SUM(billing_bill_netsales * billing_type_sign) as netsales, + SUM(billing_bill_netprofit * billing_type_sign) as netprofit FROM billing_bill LEFT JOIN billing_type ON billing_bill_type = billing_type_id diff --git a/Models/GeneralMapper.php b/Models/GeneralMapper.php index 1213af3..1745ac4 100755 --- a/Models/GeneralMapper.php +++ b/Models/GeneralMapper.php @@ -47,8 +47,8 @@ class GeneralMapper extends DataMapperFactory 'SELECT YEAR(billing_bill_performance_date) as salesyear, MONTH(billing_bill_performance_date) as salesmonth, - SUM(billing_bill_netsales * billing_type_transfer_sign) as netsales, - SUM(billing_bill_netprofit * billing_type_transfer_sign) as netprofit + SUM(billing_bill_netsales * billing_type_sign) as netsales, + SUM(billing_bill_netprofit * billing_type_sign) as netprofit FROM billing_bill LEFT JOIN billing_type ON billing_bill_type = billing_type_id @@ -143,8 +143,8 @@ class GeneralMapper extends DataMapperFactory 'SELECT YEAR(billing_bill_performance_date) as salesyear, MONTH(billing_bill_performance_date) as salesmonth, - SUM(billing_bill_netsales * billing_type_transfer_sign) as netsales, - SUM(billing_bill_netprofit * billing_type_transfer_sign) as netprofit + SUM(billing_bill_netsales * billing_type_sign) as netsales, + SUM(billing_bill_netprofit * billing_type_sign) as netprofit FROM billing_bill LEFT JOIN billing_type ON billing_bill_type = billing_type_id diff --git a/Models/ItemMapper.php b/Models/ItemMapper.php index c861d4d..7f6ddb9 100755 --- a/Models/ItemMapper.php +++ b/Models/ItemMapper.php @@ -53,8 +53,8 @@ class ItemMapper extends DataMapperFactory itemmgmt_attr_value_l11n_title, YEAR(billing_bill_performance_date) as salesyear, MONTH(billing_bill_performance_date) as salesmonth, - SUM(billing_bill_element_total_netsalesprice * billing_type_transfer_sign) as netsales, - SUM(billing_bill_element_total_netprofit * billing_type_transfer_sign) as netprofit + SUM(billing_bill_element_total_netsalesprice * billing_type_sign) as netsales, + SUM(billing_bill_element_total_netprofit * billing_type_sign) as netprofit FROM billing_bill LEFT JOIN billing_type ON billing_bill_type = billing_type_id diff --git a/Models/RegionMapper.php b/Models/RegionMapper.php index 7eb0589..796855b 100755 --- a/Models/RegionMapper.php +++ b/Models/RegionMapper.php @@ -50,8 +50,8 @@ class RegionMapper extends DataMapperFactory address_country, YEAR(billing_bill_performance_date) as salesyear, MONTH(billing_bill_performance_date) as salesmonth, - SUM(billing_bill_netsales * billing_type_transfer_sign) as netsales, - SUM(billing_bill_netprofit * billing_type_transfer_sign) as netprofit + SUM(billing_bill_netsales * billing_type_sign) as netsales, + SUM(billing_bill_netprofit * billing_type_sign) as netprofit FROM billing_bill LEFT JOIN billing_type ON billing_bill_type = billing_type_id @@ -327,8 +327,8 @@ class RegionMapper extends DataMapperFactory address_country, YEAR(billing_bill_performance_date) as salesyear, MONTH(billing_bill_performance_date) as salesmonth, - SUM(billing_bill_netsales * billing_type_transfer_sign) as netsales, - SUM(billing_bill_netprofit * billing_type_transfer_sign) as netprofit + SUM(billing_bill_netsales * billing_type_sign) as netsales, + SUM(billing_bill_netprofit * billing_type_sign) as netprofit FROM billing_bill LEFT JOIN billing_type ON billing_bill_type = billing_type_id @@ -394,8 +394,8 @@ class RegionMapper extends DataMapperFactory address_country, YEAR(billing_bill_performance_date) as salesyear, MONTH(billing_bill_performance_date) as salesmonth, - SUM(billing_bill_netsales * billing_type_transfer_sign) as netsales, - SUM(billing_bill_netprofit * billing_type_transfer_sign) as netprofit + SUM(billing_bill_netsales * billing_type_sign) as netsales, + SUM(billing_bill_netprofit * billing_type_sign) as netprofit FROM billing_bill LEFT JOIN billing_type ON billing_bill_type = billing_type_id diff --git a/Models/SalesRepMapper.php b/Models/SalesRepMapper.php new file mode 100755 index 0000000..d2a2f19 --- /dev/null +++ b/Models/SalesRepMapper.php @@ -0,0 +1,490 @@ +format('m'), $businessStart); + + $query = new Builder(self::$db); + $query->raw( + 'SELECT + billing_bill_rep, + YEAR(billing_bill_performance_date) as salesyear, + MONTH(billing_bill_performance_date) as salesmonth, + SUM(billing_bill_netsales * billing_type_sign) as netsales, + SUM(billing_bill_netprofit * billing_type_sign) as netprofit + FROM billing_bill + LEFT JOIN billing_type + ON billing_bill_type = billing_type_id + LEFT JOIN clientmgmt_client + ON clientmgmt_client_id = billing_bill_client + LEFT JOIN address + ON clientmgmt_client_address = address_id + WHERE + billing_type_transfer_type = ' . BillTransferType::SALES . ' + AND billing_type_accounting = 1 + AND billing_bill_performance_date >= \'' . $start->format('Y-m-d') . '\' + AND billing_bill_performance_date <= \'' . $end->format('Y-m-d') . '\' + GROUP BY + billing_bill_rep, + YEAR(billing_bill_performance_date), + MONTH(billing_bill_performance_date) + ORDER BY + YEAR(billing_bill_performance_date) ASC, + MONTH(billing_bill_performance_date) ASC, + billing_bill_rep' + ); + + $results = $query->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? []; + + $monthlySales = []; + + $mtd = []; + $ytd = []; + + foreach ($results as $result) { + $monthIndex = SmartDateTime::calculateMonthIndex((int) $result['salesmonth'], $businessStart); + + if (!isset($monthlySales[$result['billing_bill_rep']])) { + $monthlySales[$result['billing_bill_rep']] = []; + + $mtdA[$result['billing_bill_rep']] = ['net_sales' => 0, 'net_profit' => 0]; + $mtdPY[$result['billing_bill_rep']] = ['net_sales' => 0, 'net_profit' => 0]; + + $ytdA[$result['billing_bill_rep']] = ['net_sales' => 0, 'net_profit' => 0]; + $ytdPY[$result['billing_bill_rep']] = ['net_sales' => 0, 'net_profit' => 0]; + + for ($i = 1; $i < 3; ++$i) { + $monthlySales[$result['billing_bill_rep']][$i] = \array_fill(1, 12, [ + 'net_sales' => null, + 'net_profit' => null, + ]); + } + } + + // indexed according to the fiscal year + $monthlySales[$result['billing_bill_rep']][$monthIndex] = [ + 'net_sales' => (int) $result['netsales'], + 'net_profit' => (int) $result['netprofit'], + ]; + + if ($monthIndex === $endCurrentIndex) { + $mtd[$result['billing_bill_rep']] = $monthlySales[$result['billing_bill_rep']][$monthIndex]; + } + + if ($monthIndex <= $endCurrentIndex) { + $ytd[$result['billing_bill_rep']]['net_sales'] += $monthlySales[$result['billing_bill_rep']][$monthIndex]['net_sales']; + $ytd[$result['billing_bill_rep']]['net_profit'] += $monthlySales[$result['billing_bill_rep']][$monthIndex]['net_profit']; + } + } + + return [$mtd, $ytd, $monthlySales]; + } + + /** + * @todo Re-implement, still in use? + */ + public static function annualCustomerRep( + SmartDateTime $historyStart, + \DateTime $endCurrent, + int $businessStart = 1 + ) : array { + $query = new Builder(self::$db); + $query->raw( + 'SELECT + billing_bill_rep, + YEAR(billing_bill_performance_date) as salesyear, + MONTH(billing_bill_performance_date) as salesmonth, + COUNT(billing_bill_netsales) as client_count + FROM billing_bill + LEFT JOIN billing_type + ON billing_bill_type = billing_type_id + LEFT JOIN clientmgmt_client + ON clientmgmt_client_id = billing_bill_client + LEFT JOIN address + ON clientmgmt_client_address = address_id + WHERE + billing_type_transfer_type = ' . BillTransferType::SALES . ' + AND billing_type_accounting = 1 + AND billing_bill_performance_date >= \'' . $historyStart->format('Y-m-d') . '\' + AND billing_bill_performance_date <= \'' . $endCurrent->format('Y-m-d') . '\' + GROUP BY + billing_bill_rep, + YEAR(billing_bill_performance_date), + MONTH(billing_bill_performance_date) + ORDER BY + YEAR(billing_bill_performance_date) ASC, + MONTH(billing_bill_performance_date) ASC, + billing_bill_rep' + ); + + $results = $query->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? []; + + $annualCustomer = []; + + $oldIndex = 1; + // @todo this calculation doesn't consider the start of the fiscal year + $period = (int) (((((int) $results[0]['salesyear']) - ((int) $historyStart->format('Y'))) * 12 + - ((int) $results[0]['salesmonth']) + ((int) $historyStart->format('m'))) / 12 + 1); + + foreach ($results as $result) { + $monthIndex = SmartDateTime::calculateMonthIndex((int) $result['salesmonth'], $businessStart); + if ($monthIndex < $oldIndex) { + $oldIndex = $monthIndex; + + ++$period; + } + + if ($period > 10) { + break; + } + + $oldIndex = $monthIndex; + + if (!isset($annualCustomer[$result['billing_bill_rep']])) { + for ($i = 1; $i < 11; ++$i) { + $annualCustomer[$result['billing_bill_rep']][$i] = [ + 'client_count' => 0, + ]; + + $historyStart->smartModify(1); + } + + $historyStart->smartModify(-10); + } + + // indexed according to the fiscal year + $annualCustomer[$result['billing_bill_rep']][$period]['client_count'] += (int) $result['client_count']; + } + + return $annualCustomer; + } + + /** + * @todo Re-implement, still in use? + */ + public static function mtdYtdClientRep( + \DateTime $startCurrent, + \DateTime $endCurrent, + \DateTime $startComparison, + \DateTime $endComparison, + int $businessStart = 1, + ) : array { + // @todo this cannot be correct since the same customer may buy something in two month (distinct is required over an actual period) + $endCurrentIndex = SmartDateTime::calculateMonthIndex((int) $endCurrent->format('m'), $businessStart); + + $query = new Builder(self::$db); + $query->raw( + 'SELECT + billing_bill_rep, + YEAR(billing_bill_performance_date) as salesyear, + MONTH(billing_bill_performance_date) as salesmonth, + COUNT(billing_bill_netsales) as client_count + FROM billing_bill + LEFT JOIN billing_type + ON billing_bill_type = billing_type_id + LEFT JOIN clientmgmt_client + ON clientmgmt_client_id = billing_bill_client + LEFT JOIN address + ON clientmgmt_client_address = address_id + WHERE + billing_type_transfer_type = ' . BillTransferType::SALES . ' + AND billing_type_accounting = 1 + AND billing_bill_performance_date >= \'' . $startComparison->format('Y-m-d') . '\' + AND billing_bill_performance_date <= \'' . $endCurrent->format('Y-m-d') . '\' + GROUP BY + billing_bill_rep, + YEAR(billing_bill_performance_date), + MONTH(billing_bill_performance_date) + ORDER BY + YEAR(billing_bill_performance_date) ASC, + MONTH(billing_bill_performance_date) ASC, + billing_bill_rep ASC' + ); + + $results = $query->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? []; + + $oldIndex = 1; + $period = 1; + + $mtdAClientCountry = []; + $mtdPYClientCountry = []; + + $ytdAClientCountry = []; + $ytdPYClientCountry = []; + + foreach ($results as $result) { + $monthIndex = SmartDateTime::calculateMonthIndex((int) $result['salesmonth'], $businessStart); + if ($monthIndex < $oldIndex) { + $oldIndex = $monthIndex; + + ++$period; + } + + if ($period > 2) { + break; + } + + $oldIndex = $monthIndex; + + // indexed according to the fiscal year + $temp = [ + 'client_count' => (int) $result['client_count'], + ]; + + if ($temp['client_count'] === 0) { + continue; + } + + if ($monthIndex === $endCurrentIndex) { + if ($period === 1) { + $mtdPYClientCountry[$result['billing_bill_rep']] = $temp; + } else { + $mtdAClientCountry[$result['billing_bill_rep']] = $temp; + } + } + + if ($monthIndex <= $endCurrentIndex) { + if (!isset($ytdPYClientCountry[$result['billing_bill_rep']])) { + $ytdPYClientCountry[$result['billing_bill_rep']] = [ + 'client_count' => 0, + ]; + + $ytdAClientCountry[$result['billing_bill_rep']] = [ + 'client_count' => 0, + ]; + } + + if ($period === 1) { + $ytdPYClientCountry[$result['billing_bill_rep']]['client_count'] += $temp['client_count']; + } else { + $ytdAClientCountry[$result['billing_bill_rep']]['client_count'] += $temp['client_count']; + } + } + } + + return [ + $mtdPYClientCountry, + $mtdAClientCountry, + $ytdPYClientCountry, + $ytdAClientCountry, + ]; + } + + // @todo remove businessStart, that should be baked into the historyStart + // Explanation: in the past I had to compare periods which weren't even business years!!! + /** + * @todo Re-implement, still in use? + */ + public static function salesProfitRep( + \DateTime $historyStart, + \DateTime $historyEnd, + \DateTime $currentStart, + \DateTime $currentEnd + ) : array { + $query = new Builder(self::$db); + $query->raw( + 'SELECT + billing_bill_rep, + YEAR(billing_bill_performance_date) as salesyear, + MONTH(billing_bill_performance_date) as salesmonth, + SUM(billing_bill_netsales * billing_type_sign) as netsales, + SUM(billing_bill_netprofit * billing_type_sign) as netprofit + FROM billing_bill + LEFT JOIN billing_type + ON billing_bill_type = billing_type_id + LEFT JOIN clientmgmt_client + ON clientmgmt_client_id = billing_bill_client + LEFT JOIN address + ON clientmgmt_client_address = address_id + WHERE + billing_type_transfer_type = ' . BillTransferType::SALES . ' + AND billing_type_accounting = 1 + AND billing_bill_performance_date >= \'' . $historyStart->format('Y-m-d') . '\' + AND billing_bill_performance_date <= \'' . $currentEnd->format('Y-m-d') . '\' + GROUP BY + billing_bill_rep, + YEAR(billing_bill_performance_date), + MONTH(billing_bill_performance_date) + ORDER BY + YEAR(billing_bill_performance_date) ASC, + MONTH(billing_bill_performance_date) ASC, + billing_bill_rep ASC' + ); + + $results = $query->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? []; + + $sales = []; + $period = 0; + + foreach ($results as $result) { + // @todo Handle fiscal year + $period = $result['salesyear'] - ((int) $historyStart->format('Y')) + 1; + if (!isset($sales[$result['billing_bill_rep']])) { + for ($i = 1; $i < 11; ++$i) { + $sales[$result['billing_bill_rep']][$i] = [ + 'net_sales' => 0, + 'net_profit' => 0, + 'year' => $period === 0 ? 'PY' : 'A', + ]; + } + } + + $sales[$result['billing_bill_rep']][$period]['net_sales'] += (int) $result['netsales']; + $sales[$result['billing_bill_rep']][$period]['net_profit'] += (int) $result['netprofit']; + } + + return $sales; + } + + /** + * @todo Re-implement, still in use? + */ + public static function mtdYtdRep( + \DateTime $startCurrent, + \DateTime $endCurrent, + \DateTime $startComparison, + \DateTime $endComparison, + int $businessStart = 1, + ) : array { + $endCurrentIndex = SmartDateTime::calculateMonthIndex((int) $endCurrent->format('m'), $businessStart); + + $query = new Builder(self::$db); + $query->raw( + 'SELECT + billing_bill_rep, + YEAR(billing_bill_performance_date) as salesyear, + MONTH(billing_bill_performance_date) as salesmonth, + SUM(billing_bill_netsales * billing_type_sign) as netsales, + SUM(billing_bill_netprofit * billing_type_sign) as netprofit + FROM billing_bill + LEFT JOIN billing_type + ON billing_bill_type = billing_type_id + LEFT JOIN clientmgmt_client + ON clientmgmt_client_id = billing_bill_client + LEFT JOIN address + ON clientmgmt_client_address = address_id + WHERE + billing_type_transfer_type = ' . BillTransferType::SALES . ' + AND billing_type_accounting = 1 + AND billing_bill_performance_date >= \'' . $startComparison->format('Y-m-d') . '\' + AND billing_bill_performance_date <= \'' . $endCurrent->format('Y-m-d') . '\' + GROUP BY + billing_bill_rep, + YEAR(billing_bill_performance_date), + MONTH(billing_bill_performance_date) + ORDER BY + YEAR(billing_bill_performance_date) ASC, + MONTH(billing_bill_performance_date) ASC, + billing_bill_rep ASC' + ); + + $results = $query->execute()?->fetchAll(\PDO::FETCH_ASSOC) ?? []; + + $oldIndex = 1; + $period = 1; + + $mtdAClientCountry = []; + $mtdPYClientCountry = []; + + $ytdAClientCountry = []; + $ytdPYClientCountry = []; + + foreach ($results as $result) { + $monthIndex = SmartDateTime::calculateMonthIndex((int) $result['salesmonth'], $businessStart); + if ($monthIndex < $oldIndex) { + $oldIndex = $monthIndex; + + ++$period; + } + + if ($period > 2) { + break; + } + + $oldIndex = $monthIndex; + + // indexed according to the fiscal year + $temp = [ + 'net_sales' => (int) $result['netsales'], + 'net_profit' => (int) $result['netprofit'], + ]; + + if (($temp['net_sales'] === 0 && $temp['net_profit'] === 0)) { + continue; + } + + if ($monthIndex === $endCurrentIndex) { + if ($period === 1) { + $mtdPYClientCountry[$result['billing_bill_rep']] = $temp; + } else { + $mtdAClientCountry[$result['billing_bill_rep']] = $temp; + } + } + + if ($monthIndex <= $endCurrentIndex) { + if (!isset($ytdPYClientCountry[$result['billing_bill_rep']])) { + $ytdPYClientCountry[$result['billing_bill_rep']] = [ + 'net_sales' => 0, + 'net_profit' => 0, + ]; + + $ytdAClientCountry[$result['billing_bill_rep']] = [ + 'net_sales' => 0, + 'net_profit' => 0, + ]; + } + + if ($period === 1) { + $ytdPYClientCountry[$result['billing_bill_rep']]['net_sales'] += $temp['net_sales']; + $ytdPYClientCountry[$result['billing_bill_rep']]['net_profit'] += $temp['net_profit']; + } else { + $ytdAClientCountry[$result['billing_bill_rep']]['net_sales'] += $temp['net_sales']; + $ytdAClientCountry[$result['billing_bill_rep']]['net_profit'] += $temp['net_profit']; + } + } + } + + return [ + $mtdPYClientCountry, + $mtdAClientCountry, + $ytdPYClientCountry, + $ytdAClientCountry, + ]; + } +} diff --git a/Theme/Backend/Lang/de.lang.php b/Theme/Backend/Lang/de.lang.php index fc5354b..2971838 100755 --- a/Theme/Backend/Lang/de.lang.php +++ b/Theme/Backend/Lang/de.lang.php @@ -68,4 +68,6 @@ return ['SalesAnalysis' => [ 'America' => 'Amerika', 'Oceania' => 'Ozeanien', 'Africa' => 'Afrika', + 'SalesRep' => 'Verkäufer', + 'SalesReps' => 'Verkäufer', ]]; diff --git a/Theme/Backend/Lang/en.lang.php b/Theme/Backend/Lang/en.lang.php index 3fde187..e463f22 100755 --- a/Theme/Backend/Lang/en.lang.php +++ b/Theme/Backend/Lang/en.lang.php @@ -68,4 +68,6 @@ return ['SalesAnalysis' => [ 'America' => 'America', 'Oceania' => 'Oceania', 'Africa' => 'Africa', + 'SalesRep' => 'SalesRep', + 'SalesReps' => 'SalesReps', ]]; diff --git a/Theme/Backend/analysis-rep.tpl.php b/Theme/Backend/analysis-rep.tpl.php index 8b8d0bf..cd59d3c 100755 --- a/Theme/Backend/analysis-rep.tpl.php +++ b/Theme/Backend/analysis-rep.tpl.php @@ -12,6 +12,10 @@ */ declare(strict_types=1); +use phpOMS\Localization\ISO3166CharEnum; +use phpOMS\Localization\ISO3166NameEnum; +use phpOMS\Stdlib\Base\FloatInt; + /** * @var \phpOMS\Views\View $this */ @@ -21,13 +25,1414 @@ echo $this->data['nav']->render();
request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>>
- +
+
+
+
+ getHtml('SalesRep'); ?> +
+
+ + + + + data['ytdAClientRep'] as $type => $values) : ?> + +
getHtml('SalesRep'); ?> + getHtml('SalesPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesA'); ?> (getHtml('YTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesPY'); ?> (getHtml('MTD'); ?>) + getHtml('SalesA'); ?> (getHtml('MTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('MTD'); ?>) +
printHtml(ISO3166NameEnum::getBy2Code($type)); ?> + getCurrency((int) ($this->data['ytdPYClientRep'][$type]['net_sales'] ?? 0), symbol: ''); ?> + getCurrency((int) ($this->data['ytdAClientRep'][$type]['net_sales'] ?? 0), symbol: ''); ?> + getCurrency( + ((int) ($this->data['ytdAClientRep'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['ytdPYClientRep'][$type]['net_sales'] ?? 0)), + symbol: '' + ); ?> + getCurrency((int) ($this->data['mtdPYClientRep'][$type]['net_sales'] ?? 0), symbol: ''); ?> + getCurrency((int) ($this->data['mtdAClientRep'][$type]['net_sales'] ?? 0), symbol: ''); ?> + getCurrency( + ((int) ($this->data['mtdAClientRep'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['mtdPYClientRep'][$type]['net_sales'] ?? 0)), + symbol: '' + ); ?> + +
+
+
+
+
+
+ + request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>> +
+ +
+
+
+
+ getHtml('SalesProfit'); ?> (getHtml('monthly'); ?>) - getHtml('Domestic'); ?> +
+ $this->data['monthlyDomesticExportPY'], 2 => $this->data['monthlyDomesticExportCurrent']]; ?> +
+ + ], + "datasets": [ + { + "label": "getHtml('Profit'); ?> PY", + "type": "line", + "data": [ + data['domestic']][$i]['net_sales']) || !isset($sales[1][$this->data['domestic']][$i]['net_profit'])) { + $temp[] = 'null'; + + continue; + } + + $temp[] = $sales[1][$this->data['domestic']][$i]['net_sales'] == 0 + ? 0 + : $sales[1][$this->data['domestic']][$i]['net_profit'] * 100 / $sales[1][$this->data['domestic']][$i]['net_sales']; + } + echo \implode(',', $temp); + ?> + ], + "yAxisID": "y1", + "fill": false, + "tension": 0.0, + "borderColor": "rgb(166, 193, 178)", + "backgroundColor": "rgb(166, 193, 178)" + }, + { + "label": "getHtml('Profit'); ?> A", + "type": "line", + "data": [ + data['domestic']][$i]['net_sales']) || !isset($sales[2][$this->data['domestic']][$i]['net_profit'])) { + $temp[] = 'null'; + + continue; + } + + $temp[] = $sales[2][$this->data['domestic']][$i]['net_sales'] == 0 + ? 0 + : $sales[2][$this->data['domestic']][$i]['net_profit'] * 100 / $sales[2][$this->data['domestic']][$i]['net_sales']; + } + echo \implode(',', $temp); + ?> + ], + "yAxisID": "y1", + "fill": false, + "tension": 0.0, + "borderColor": "rgb(46, 204, 113)", + "backgroundColor": "rgb(46, 204, 113)" + }, + { + "label": "getHtml('Sales'); ?> PY", + "type": "bar", + "data": [ + data['domestic']][$i]['net_sales'] ?? 0) / FloatInt::DIVISOR; + } + echo \implode(',', $temp); + ?> + ], + "yAxisID": "y", + "fill": false, + "tension": 0.0, + "backgroundColor": "rgb(177, 195, 206)" + }, + { + "label": "getHtml('Sales'); ?> A", + "type": "bar", + "data": [ + data['domestic']][$i]['net_sales'] ?? 0) / FloatInt::DIVISOR; + } + echo \implode(',', $temp); + ?> + ], + "yAxisID": "y", + "fill": false, + "tension": 0.0, + "backgroundColor": "rgb(54, 162, 235)" + } + ] + }, + "options": { + "responsive": true, + "scales": { + "x": { + "title": { + "display": true, + "text": "getHtml('Months'); ?>" + } + }, + "y": { + "title": { + "display": true, + "text": "getHtml('Sales'); ?>" + }, + "display": true, + "position": "left" + }, + "y1": { + "title": { + "display": true, + "text": "getHtml('Profit'); ?> %" + }, + "display": true, + "position": "right", + "scaleLabel": { + "display": true, + "labelString": "getHtml('Profit'); ?>" + }, + "grid": { + "drawOnChartArea": false + } + } + } + } + }'> +
+ + +
+ + + + + data['domestic']][$i]['net_sales'] ?? 0); + $sum2 += (int) ($sales[2][$this->data['domestic']][$i]['net_sales'] ?? 0); + $sum3 += (int) ($sales[1][$this->data['domestic']][$i]['net_profit'] ?? 0); + $sum4 += (int) ($sales[2][$this->data['domestic']][$i]['net_profit'] ?? 0); + ?> + + +
getHtml('Month'); ?> + getHtml('SalesPY'); ?> + getHtml('SalesA'); ?> + getHtml('ProfitPY'); ?> + getHtml('ProfitA'); ?> +
+ getCurrency((int) ($sales[1][$this->data['domestic']][$i]['net_sales'] ?? 0), symbol: ''); ?> + getCurrency((int) ($sales[2][$this->data['domestic']][$i]['net_sales'] ?? 0), symbol: ''); ?> + data['domestic']][$i]['net_sales'] ?? 0) == 0 ? 0 : $sales[1][$this->data['domestic']][$i]['net_profit'] * 100 / $sales[1][$this->data['domestic']][$i]['net_sales']); ?> % + data['domestic']][$i]['net_sales'] ?? 0) == 0 ? 0 : $sales[2][$this->data['domestic']][$i]['net_profit'] * 100 / $sales[2][$this->data['domestic']][$i]['net_sales']); ?> % + +
getHtml('Total'); ?> + getCurrency($sum1, symbol: ''); ?> + getCurrency($sum2, symbol: ''); ?> + % + % +
+
+
+
+
+
+ +
+
+
+ getHtml('SalesProfit'); ?> (getHtml('monthly'); ?>) - getHtml('Export'); ?> +
+ $this->data['monthlyDomesticExportPY'], 2 => $this->data['monthlyDomesticExportCurrent']]; ?> +
+ + ], + "datasets": [ + { + "label": "getHtml('Profit'); ?> PY", + "type": "line", + "data": [ + + ], + "yAxisID": "y1", + "fill": false, + "tension": 0.0, + "borderColor": "rgb(166, 193, 178)", + "backgroundColor": "rgb(166, 193, 178)" + }, + { + "label": "getHtml('Profit'); ?> A", + "type": "line", + "data": [ + + ], + "yAxisID": "y1", + "fill": false, + "tension": 0.0, + "borderColor": "rgb(46, 204, 113)", + "backgroundColor": "rgb(46, 204, 113)" + }, + { + "label": "getHtml('Sales'); ?> PY", + "type": "bar", + "data": [ + + ], + "yAxisID": "y", + "fill": false, + "tension": 0.0, + "backgroundColor": "rgb(177, 195, 206)" + }, + { + "label": "getHtml('Sales'); ?> A", + "type": "bar", + "data": [ + + ], + "yAxisID": "y", + "fill": false, + "tension": 0.0, + "backgroundColor": "rgb(54, 162, 235)" + } + ] + }, + "options": { + "responsive": true, + "scales": { + "x": { + "title": { + "display": true, + "text": "getHtml('Months'); ?>" + } + }, + "y": { + "title": { + "display": true, + "text": "getHtml('Sales'); ?>" + }, + "display": true, + "position": "left" + }, + "y1": { + "title": { + "display": true, + "text": "getHtml('Profit'); ?> %" + }, + "display": true, + "position": "right", + "scaleLabel": { + "display": true, + "labelString": "getHtml('Profit'); ?>" + }, + "grid": { + "drawOnChartArea": false + } + } + } + } + }'> +
+ + +
+ + + + + + + +
getHtml('Month'); ?> + getHtml('SalesPY'); ?> + getHtml('SalesA'); ?> + getHtml('ProfitPY'); ?> + getHtml('ProfitA'); ?> +
+ getCurrency((int) ($sales[1]['Other'][$i]['net_sales'] ?? 0), symbol: ''); ?> + getCurrency((int) ($sales[2]['Other'][$i]['net_sales'] ?? 0), symbol: ''); ?> + % + % + +
getHtml('Total'); ?> + getCurrency($sum1, symbol: ''); ?> + getCurrency($sum2, symbol: ''); ?> + % + % +
+
+
+
+
+
+
+ +
+
+
+
getHtml('Sales'); ?> (getHtml('YTD'); ?>) - getHtml('DomesticExport'); ?>
+
+ data['ytdADomesticExport'])); ?>" + ], + "datasets": [{ + "data": [ + data['ytdADomesticExport'] as $values) { + $temp[] = $values['net_sales'] / FloatInt::DIVISOR; + } + echo \implode(',', $temp); + ?> + ] + }] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['ytdADomesticExport'] as $region => $values) : $sum += $values['net_sales']; ?> + + +
getHtml('Region'); ?> + getHtml('Sales'); ?> +
+ getCurrency($values['net_sales'], symbol: ''); ?> + +
getHtml('Total'); ?> + getCurrency($sum, symbol: ''); ?> +
+
+
+
+
+
+ +
+
+
getHtml('Sales'); ?> (getHtml('annually'); ?>) - getHtml('DomesticExport'); ?>
+
+ format('Y')) + $i; + } + echo \implode(',', $temp); + ?> + ], + "datasets": [ + data['annualDomesticExport'] as $region => $values) : + echo($first ? '' : ','); + $first = false; + ?>{ + "label": "printHtml($region); ?>", + "type": "line", + "data": [ + + ], + "fill": false, + "tension": 0.0 + } + + ] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['annualDomesticExport'] as $region => $values) : ?> + + +
getHtml('Region'); ?> + + data['historyStart']->format('Y')) + $i; ?> + +
printHtml($region); ?> + $annual) : + $sum[$idx] = ($sum[$idx] ?? 0) + ($annual['net_sales'] ?? 0); + ?> + getCurrency($annual['net_sales'] ?? 0, symbol: '', format: 'short', divide: 1000); ?> + +
getHtml('Total'); ?> + + getCurrency($value, symbol: '', format: 'short', divide: 1000); ?> + +
+
+
+
+
+
+
+ +
+
+
+
getHtml('Profit'); ?> (getHtml('YTD'); ?>) - getHtml('DomesticExport'); ?>
+
+ data['ytdADomesticExport'])); ?>" + ], + "datasets": [{ + "data": [ + data['ytdADomesticExport'] as $values) { + $temp[] = $values['net_profit'] / FloatInt::DIVISOR; + } + echo \implode(',', $temp); + ?> + ] + }] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['ytdADomesticExport'] as $region => $values) : $sum += $values['net_profit']; ?> + + +
getHtml('Region'); ?> + getHtml('Profit'); ?> +
+ getCurrency($values['net_profit'], symbol: ''); ?> + +
getHtml('Total'); ?> + getCurrency($sum, symbol: ''); ?> +
+
+
+
+
+
+ +
+
+
getHtml('Profit'); ?> (getHtml('annually'); ?>) - getHtml('DomesticExport'); ?>
+
+ format('Y')) + $i; + } + echo \implode(',', $temp); + ?> + ], + "datasets": [ + data['annualDomesticExport'] as $region => $values) : + echo($first ? '' : ','); + $first = false; + ?>{ + "label": "printHtml($region); ?>", + "type": "line", + "data": [ + + ], + "fill": false, + "tension": 0.0 + } + + ] + }, + "options": { + "responsive": true, + "scales": { + "y": { + "title": { + "display": true, + "text": "getHtml('Profit'); ?> %" + }, + "display": true, + "position": "left" + } + } + } + }'> + +
+ + +
+ + + + + data['annualDomesticExport'] as $region => $values) : ?> + + +
getHtml('Region'); ?> + + data['historyStart']->format('Y')) + $i; ?> + +
printHtml($region); ?> + $annual) : + $sum[$idx] = ($sum[$idx] ?? 0) + ($annual['net_profit'] ?? 0); + ?> + getCurrency($annual['net_profit'] ?? 0, symbol: '', format: 'short', divide: 1000); ?> + +
getHtml('Total'); ?> + + getCurrency($value, symbol: '', format: 'short', divide: 1000); ?> + +
+
+
+
+
+
+
+ +
+
+
+
getHtml('Clients'); ?> (getHtml('YTD'); ?>) - getHtml('DomesticExport'); ?>
+
+ data['ytdADomesticExportCount'])); ?>" + ], + "datasets": [{ + "data": [ + data['ytdADomesticExportCount'] as $values) { + $temp[] = $values['client_count']; + } + echo \implode(',', $temp); + ?> + ] + }] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['ytdADomesticExportCount'] as $region => $values) : $sum += $values['client_count']; ?> + + +
getHtml('Region'); ?> + getHtml('Clients'); ?> +
+ getNumeric($values['client_count'], format: 'very_short'); ?> + +
getHtml('Total'); ?> + getNumeric($sum, format: 'very_short'); ?> +
+
+
+
+
+
+ +
+
+
getHtml('Clients'); ?> (getHtml('annually'); ?>) - getHtml('DomesticExport'); ?>
+
+ format('Y')) + $i; + } + echo \implode(',', $temp); + ?> + ], + "datasets": [ + data['annualDomesticExportCount'] as $region => $values) : + echo($first ? '' : ','); + $first = false; + ?>{ + "label": "printHtml($region); ?>", + "type": "line", + "data": [ + + ], + "fill": false, + "tension": 0.0 + } + + ] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['annualDomesticExportCount'] as $region => $values) : ?> + + +
getHtml('Region'); ?> + + data['historyStart']->format('Y')) + $i; ?> + +
printHtml($region); ?> + $annual) : + $sum[$idx] = ($sum[$idx] ?? 0) + ($annual['client_count'] ?? 0); + ?> + getNumeric($annual['client_count'] ?? 0, format: 'very_short'); ?> + +
getHtml('Total'); ?> + + getNumeric($value, format: 'very_short'); ?> + +
+
+
+
+
+
+
+
+ + request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>> +
+
+
+
+
getHtml('Sales'); ?> (getHtml('YTD'); ?>) - getHtml('Continent'); ?>
+
+ data['ytdAContinent'])); ?>" + ], + "datasets": [{ + "data": [ + data['ytdAContinent'] as $values) { + $temp[] = $values['net_sales'] / FloatInt::DIVISOR; + } + echo \implode(',', $temp); + ?> + ] + }] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['ytdAContinent'] as $region => $values) : $sum += $values['net_sales']; ?> + + +
getHtml('Region'); ?> + getHtml('Sales'); ?> +
+ getCurrency($values['net_sales'], symbol: ''); ?> + +
getHtml('Total'); ?> + getCurrency($sum, symbol: ''); ?> +
+
+
+
+
+
+ +
+
+
getHtml('Sales'); ?> (getHtml('annually'); ?>) - getHtml('Continent'); ?>
+
+ format('Y')) + $i; + } + echo \implode(',', $temp); + ?> + ], + "datasets": [ + data['annualContinent'] as $region => $values) : + echo($first ? '' : ','); + $first = false; + ?>{ + "label": "getHtml($region); ?>", + "type": "line", + "data": [ + + ], + "fill": false, + "tension": 0.0 + } + + ] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['annualContinent'] as $region => $values) : ?> + + +
getHtml('Region'); ?> + + data['historyStart']->format('Y')) + $i; ?> + +
getHtml($region); ?> + $annual) : + $sum[$idx] = ($sum[$idx] ?? 0) + ($annual['net_sales'] ?? 0); + ?> + getCurrency($annual['net_sales'] ?? 0, symbol: '', format: 'short', divide: 1000); ?> + +
getHtml('Total'); ?> + + getCurrency($value, symbol: '', format: 'short', divide: 1000); ?> + +
+
+
+
+
+
+
+ +
+
+
+
getHtml('Profit'); ?> (getHtml('YTD'); ?>) - getHtml('Continent'); ?>
+
+ data['ytdAContinent'])); ?>" + ], + "datasets": [{ + "data": [ + data['ytdAContinent'] as $values) { + $temp[] = $values['net_profit'] / FloatInt::DIVISOR; + } + echo \implode(',', $temp); + ?> + ] + }] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['ytdAContinent'] as $region => $values) : $sum += $values['net_profit']; ?> + + +
getHtml('Region'); ?> + getHtml('Profit'); ?> +
+ getCurrency($values['net_profit']); ?> + +
getHtml('Total'); ?> + getCurrency($sum, symbol: ''); ?> +
+
+
+
+
+
+ +
+
+
getHtml('Profit'); ?> (getHtml('annually'); ?>) - getHtml('Continent'); ?>
+
+ format('Y')) + $i; + } + echo \implode(',', $temp); + ?> + ], + "datasets": [ + data['annualContinent'] as $region => $values) : + echo($first ? '' : ','); + $first = false; + ?>{ + "label": "getHtml($region); ?>", + "type": "line", + "data": [ + + ], + "fill": false, + "tension": 0.0 + } + + ] + }, + "options": { + "responsive": true, + "scales": { + "y": { + "title": { + "display": true, + "text": "getHtml('Profit'); ?> %" + }, + "display": true, + "position": "left" + } + } + } + }'> + +
+ + +
+ + + + + data['annualContinent'] as $region => $values) : ?> + + +
getHtml('Region'); ?> + + data['historyStart']->format('Y')) + $i; ?> + +
getHtml($region); ?> + $annual) : + $sum[$idx] = ($sum[$idx] ?? 0) + ($annual['net_profit'] ?? 0); + ?> + getCurrency($annual['net_profit'] ?? 0, symbol: '', format: 'short', divide: 1000); ?> + +
getHtml('Total'); ?> + + getCurrency($value, symbol: '', format: 'short', divide: 1000); ?> + +
+
+
+
+
+
+
+ +
+
+
+
getHtml('Clients'); ?> (getHtml('YTD'); ?>) - getHtml('Continent'); ?>
+
+ data['ytdAContinentCount'])); ?>" + ], + "datasets": [{ + "data": [ + data['ytdAContinentCount'] as $values) { + $temp[] = $values['client_count']; + } + echo \implode(',', $temp); + ?> + ] + }] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['ytdAContinentCount'] as $region => $values) : $sum += $values['client_count']; ?> + + +
getHtml('Region'); ?> + getHtml('Clients'); ?> +
+ getNumeric($values['client_count'], format: 'very_short'); ?> + +
getHtml('Total'); ?> + getNumeric($sum, format: 'very_short'); ?> +
+
+
+
+
+
+ +
+
+
getHtml('Clients'); ?> (getHtml('annually'); ?>) - getHtml('Continent'); ?>
+
+ format('Y')) + $i; + } + echo \implode(',', $temp); + ?> + ], + "datasets": [ + data['annualContinentCount'] as $region => $values) : + echo($first ? '' : ','); + $first = false; + ?>{ + "label": "printHtml($region); ?>", + "type": "line", + "data": [ + + ], + "fill": false, + "tension": 0.0 + } + + ] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['annualContinentCount'] as $region => $values) : ?> + + +
getHtml('Region'); ?> + + data['historyStart']->format('Y')) + $i; ?> + +
printHtml($region); ?> + $annual) : + $sum[$idx] = ($sum[$idx] ?? 0) + ($annual['client_count'] ?? 0); + ?> + getNumeric($annual['client_count'] ?? 0, format: 'very_short'); ?> + +
getHtml('Total'); ?> + + getNumeric($value, format: 'very_short'); ?> + +
+
+
+
+
+
+
+
+ + request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>> +
+
+
+
+
getHtml('Sales'); ?>
+
+ + + + + data['ytdARegions'] as $type => $values) : ?> + +
getHtml('Region'); ?> + getHtml('SalesPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesA'); ?> (getHtml('YTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesPY'); ?> (getHtml('MTD'); ?>) + getHtml('SalesA'); ?> (getHtml('MTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('MTD'); ?>) +
printHtml($type); ?> + getCurrency((int) ($this->data['ytdPYRegions'][$type]['net_sales'] ?? 0), symbol: ''); ?> + getCurrency((int) ($this->data['ytdARegions'][$type]['net_sales'] ?? 0), symbol: ''); ?> + getCurrency( + ((int) ($this->data['ytdARegions'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['ytdPYRegions'][$type]['net_sales'] ?? 0)), + symbol: '' + ); ?> + getCurrency((int) ($this->data['mtdPYRegions'][$type]['net_sales'] ?? 0), symbol: ''); ?> + getCurrency((int) ($this->data['mtdARegions'][$type]['net_sales'] ?? 0), symbol: ''); ?> + getCurrency( + ((int) ($this->data['mtdARegions'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['mtdPYRegions'][$type]['net_sales'] ?? 0)), + symbol: '' + ); ?> + +
+
+
+
+
+
+ + request->uri->fragment === 'c-tab-3' ? ' checked' : ''; ?>> +
+
+
+
+
+
getHtml('Filter'); ?>
+
+
+ + +
+ +
+
+ + +
+ +
+ + +
+
+ +
+
+ + +
+ +
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+
+
+
\ No newline at end of file