diff --git a/Controller/BackendController.php b/Controller/BackendController.php
index 9764e1e..01aef4f 100755
--- a/Controller/BackendController.php
+++ b/Controller/BackendController.php
@@ -25,6 +25,7 @@ use phpOMS\Message\RequestAbstract;
use phpOMS\Message\ResponseAbstract;
use phpOMS\Stdlib\Base\SmartDateTime;
use phpOMS\Views\View;
+use phpOMS\Localization\ISO3166NameEnum;
/**
* ClientManagement class.
@@ -54,10 +55,9 @@ final class BackendController extends Controller
$view->setTemplate('/Modules/ClientManagement/Theme/Backend/client-list');
$view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1003102001, $request, $response));
- $client = ClientMapper
- ::with('notes', models: null)
+ $client = ClientMapper::with('notes', models: null)
::with('contactElements', models: null)
- ::with('type', 'backend_image', models: [Media::class]) // @todo: it would be nicer if I coult say files:type or files/type and remove the models parameter?
+ //::with('type', 'backend_image', models: [Media::class]) // @todo: it would be nicer if I coult say files:type or files/type and remove the models parameter? @todo: uncommented for now because the type is also part of client and therefore bug. that's the problem with a mix of black/whitelisting in the datamapper with the "with" feature. make it whitelist only for belongsTo, ownsMany, hasOne, ....
::getAfterPivot(0, null, 25);
$view->addData('client', $client);
@@ -163,4 +163,161 @@ final class BackendController extends Controller
return $view;
}
+
+ /**
+ * Routing end-point for application behaviour.
+ *
+ * @param RequestAbstract $request Request
+ * @param ResponseAbstract $response Response
+ * @param mixed $data Generic data
+ *
+ * @return RenderableInterface
+ *
+ * @since 1.0.0
+ * @codeCoverageIgnore
+ */
+ public function viewClientAnalysis(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
+ {
+ $head = $response->get('Content')->getData('head');
+ $head->addAsset(AssetType::CSS, 'Resources/chartjs/Chartjs/chart.css');
+ $head->addAsset(AssetType::JSLATE, 'Resources/chartjs/Chartjs/chart.js');
+ $head->addAsset(AssetType::JSLATE, 'Modules/ClientManagement/Controller.js', ['type' => 'module']);
+
+ $view = new View($this->app->l11nManager, $request, $response);
+ $view->setTemplate('/Modules/ClientManagement/Theme/Backend/client-analysis');
+ $view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1001602001, $request, $response));
+
+ //
+ $monthlySalesCosts = [];
+ for ($i = 1; $i < 13; ++$i) {
+ $monthlySalesCosts[] = [
+ 'net_sales' => $sales = \mt_rand(1200000000, 2000000000),
+ 'net_costs' => (int) ($sales * \mt_rand(25, 55) / 100),
+ 'year' => 2020,
+ 'month' => $i,
+ ];
+ }
+
+ $view->addData('monthlySalesCosts', $monthlySalesCosts);
+
+ //
+ $salesCustomer = [];
+ for ($i = 1; $i < 13; ++$i) {
+ $salesCustomer[] = [
+ 'net_sales' => $sales = \mt_rand(1200000000, 2000000000),
+ 'customers' => \mt_rand(200, 400),
+ 'year' => 2020,
+ 'month' => $i,
+ ];
+ }
+
+ $view->addData('salesCustomer', $salesCustomer);
+
+ //
+ $customerRetention = [];
+ for ($i = 1; $i < 10; ++$i) {
+ $customerRetention[] = [
+ 'customers' => \mt_rand(200, 400),
+ 'year' => \date('y') - 9 + $i,
+ ];
+ }
+
+ $view->addData('customerRetention', $customerRetention);
+
+ //
+ $customerRegion = [
+ '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->addData('customerRegion', $customerRegion);
+
+ //
+ $customersRep = [];
+ for ($i = 1; $i < 13; ++$i) {
+ $customersRep['Rep ' . $i] = [
+ 'customers' => (int) (\mt_rand(200, 400) / 12),
+ ];
+ }
+
+ \uasort($customersRep, function($a, $b) { return $b['customers'] <=> $a['customers']; });
+
+ $view->addData('customersRep', $customersRep);
+
+ //
+ $customersCountry = [];
+ for ($i = 1; $i < 13; ++$i) {
+ $country = ISO3166NameEnum::getRandom();
+ $customersCountry[\substr($country, 0, 20)] = [
+ 'customers' => (int) (\mt_rand(200, 400) / 12),
+ ];
+ }
+
+ \uasort($customersCountry, function($a, $b) { return $b['customers'] <=> $a['customers']; });
+
+ $view->addData('customersCountry', $customersCountry);
+
+ //
+ $customerGroups = [];
+ for ($i = 1; $i < 7; ++$i) {
+ $customerGroups['Group ' . $i] = [
+ 'customers' => (int) (\mt_rand(200, 400) / 12),
+ ];
+ }
+
+ $view->addData('customerGroups', $customerGroups);
+
+ //
+ $salesRegion = [
+ 'Europe' => (int) (\mt_rand(1200000000, 2000000000) / 4),
+ 'America' => (int) (\mt_rand(1200000000, 2000000000) / 4),
+ 'Asia' => (int) (\mt_rand(1200000000, 2000000000) / 4),
+ 'Africa' => (int) (\mt_rand(1200000000, 2000000000) / 4),
+ 'CIS' => (int) (\mt_rand(1200000000, 2000000000) / 4),
+ 'Other' => (int) (\mt_rand(1200000000, 2000000000) / 4),
+ ];
+
+ $view->addData('salesRegion', $salesRegion);
+
+ //
+ $salesRep = [];
+ for ($i = 1; $i < 13; ++$i) {
+ $salesRep['Rep ' . $i] = [
+ 'net_sales' => (int) (\mt_rand(1200000000, 2000000000) / 12),
+ ];
+ }
+
+ \uasort($salesRep, function($a, $b) { return $b['net_sales'] <=> $a['net_sales']; });
+
+ $view->addData('salesRep', $salesRep);
+
+ //
+ $salesCountry = [];
+ for ($i = 1; $i < 13; ++$i) {
+ $country = ISO3166NameEnum::getRandom();
+ $salesCountry[\substr($country, 0, 20)] = [
+ 'net_sales' => (int) (\mt_rand(1200000000, 2000000000) / 12),
+ ];
+ }
+
+ \uasort($salesCountry, function($a, $b) { return $b['net_sales'] <=> $a['net_sales']; });
+
+ $view->addData('salesCountry', $salesCountry);
+
+ //
+ $salesGroups = [];
+ for ($i = 1; $i < 7; ++$i) {
+ $salesGroups['Group ' . $i] = [
+ 'net_sales' => (int) (\mt_rand(1200000000, 2000000000) / 12),
+ ];
+ }
+
+ $view->addData('salesGroups', $salesGroups);
+
+ return $view;
+ }
}
diff --git a/Theme/Backend/client-analysis.tpl.php b/Theme/Backend/client-analysis.tpl.php
new file mode 100644
index 0000000..05f953d
--- /dev/null
+++ b/Theme/Backend/client-analysis.tpl.php
@@ -0,0 +1,882 @@
+getData('nav')->render();
+?>
+
+
+
+
+ = $this->getHtml('Analysis'); ?>
+ = $this->getHtml('Customers'); ?>
+ = $this->getHtml('NewCustomers'); ?>
+ = $this->getHtml('LostCustomers'); ?>
+ = $this->getHtml('Margins'); ?>
+
+
+
+
request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>>
+
+
+
+
+ = $this->getHtml('Filter'); ?>
+
+
+
+
+
+
+
request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>>
+
+
+
+
+
+ Sales / Customers
+
+
+ getData('salesCustomer'); ?>
+
+
+ = '"' . \implode('", "', $temp) . '"'; ?>
+ ],
+ "datasets": [
+ {
+ "label": "= $this->getHtml('Customers'); ?>",
+ "type": "line",
+ "data": [
+
+ = \implode(',', $temp); ?>
+ ],
+ "yAxisID": "axis-2",
+ "fill": false,
+ "borderColor": "rgb(255, 99, 132)",
+ "backgroundColor": "rgb(255, 99, 132)",
+ "tension": 0.0
+ },
+ {
+ "label": "= $this->getHtml('Sales'); ?>",
+ "type": "bar",
+ "data": [
+
+ = \implode(',', $temp); ?>
+ ],
+ "yAxisID": "axis-1",
+ "fill": false,
+ "borderColor": "rgb(54, 162, 235)",
+ "backgroundColor": "rgb(54, 162, 235)",
+ "tension": 0.0
+ }
+ ]
+ },
+ "options": {
+ "title": {
+ "display": false,
+ "text": "Sales / Customers"
+ },
+ "scales": {
+ "yAxes": [
+ {
+ "id": "axis-1",
+ "display": true,
+ "position": "left"
+ },
+ {
+ "id": "axis-2",
+ "display": true,
+ "position": "right",
+ "scaleLabel": {
+ "display": true,
+ "labelString": "= $this->getHtml('Customers'); ?>"
+ },
+ "gridLines": {
+ "display": false
+ }
+ }
+ ]
+ }
+ }
+ }'>
+
+
+
+ Data
+
+
+
+
+
+
+ Month
+ Sales
+ Customer count
+
+
+
+ = $values['month'] . '/' . \substr((string) $values['year'], -2) ?>
+ = (new Money(((int) $values['net_sales']) / 1000))->getCurrency(); ?>
+ = ((int) $values['customers']); ?>
+
+
+ Total
+ = (new Money($sum1))->getCurrency(); ?>
+ = (int) ($sum2 / 12); ?>
+
+
+
+
+
+
+
+
+
+ getData('customerRetention'); ?>
+
+
+ = '"' . \implode('", "', $temp) . '"'; ?>
+ ],
+ "datasets": [
+ {
+ "label": "= $this->getHtml('Retention'); ?>",
+ "type": "line",
+ "data": [
+
+ = \implode(',', $temp); ?>
+ ],
+ "yAxisID": "axis-1",
+ "fill": false,
+ "borderColor": "rgb(54, 162, 235)",
+ "backgroundColor": "rgb(54, 162, 235)",
+ "tension": 0.0
+ }
+ ]
+ },
+ "options": {
+ "title": {
+ "display": true,
+ "text": "Customer retention"
+ },
+ "scales": {
+ "yAxes": [
+ {
+ "id": "axis-1",
+ "display": true,
+ "position": "left"
+ }
+ ]
+ }
+ }
+ }'>
+
+
+
+
+ Year
+ Retention
+
+
+
+ = \substr((string) $values['year'], -2) ?>
+ = ((int) $values['customers']); ?>
+
+
+ Avg.
+ = $sum1 / 12; ?>
+
+
+
+
+
+
+
+ getData('customerRegion'); ?>
+
+
,
+ = (int) ($customerRegion['America'] ?? 0); ?>,
+ = (int) ($customerRegion['Asia'] ?? 0); ?>,
+ = (int) ($customerRegion['Africa'] ?? 0); ?>,
+ = (int) ($customerRegion['CIS'] ?? 0); ?>,
+ = (int) ($customerRegion['Other'] ?? 0); ?>
+ ],
+ "backgroundColor": [
+ "rgb(255, 99, 132)",
+ "rgb(255, 159, 64)",
+ "rgb(255, 205, 86)",
+ "rgb(75, 192, 192)",
+ "rgb(54, 162, 235)",
+ "rgb(153, 102, 255)"
+ ]
+ }]
+ },
+ "options": {
+ "title": {
+ "display": true,
+ "text": "Customers per region"
+ }
+ }
+ }'>
+
+
+
+ Region
+ Customer count
+
+ $values) : $sum += $values; ?>
+
+ = $region; ?>
+ = $values; ?>
+
+
+ Total
+ = $sum; ?>
+
+
+
+
+
+
+
+ getData('customersRep'); ?>
+
+
+ ],
+ "datasets": [
+ {
+ "label": "= $this->getHtml('Customers'); ?>",
+ "type": "horizontalBar",
+ "data": [
+
+ = \implode(',', $temp); ?>
+ ],
+ "fill": false,
+ "borderColor": "rgb(54, 162, 235)",
+ "backgroundColor": "rgb(54, 162, 235)",
+ "tension": 0.0
+ }
+ ]
+ },
+ "options": {
+ "title": {
+ "display": true,
+ "text": "Customers per rep"
+ }
+ }
+ }'>
+
+
+
+ Rep
+ Customer count
+
+ $values) : $sum += $values['customers']; ?>
+
+ = $rep; ?>
+ = $values['customers']; ?>
+
+
+ Total
+ = $sum; ?>
+
+
+
+
+
+
+
+ getData('customersCountry'); ?>
+
+
+ ],
+ "datasets": [
+ {
+ "label": "= $this->getHtml('Customers'); ?>",
+ "type": "horizontalBar",
+ "data": [
+
+ = \implode(',', $temp); ?>
+ ],
+ "fill": false,
+ "borderColor": "rgb(54, 162, 235)",
+ "backgroundColor": "rgb(54, 162, 235)",
+ "tension": 0.0
+ }
+ ]
+ },
+ "options": {
+ "title": {
+ "display": true,
+ "text": "Customers per country"
+ }
+ }
+ }'>
+
+
+
+
+ Country
+ Customer count
+
+ $values) : $sum += $values['customers']; ?>
+
+ = $country; ?>
+ = $values['customers']; ?>
+
+
+ Total
+ = $sum; ?>
+
+
+
+
+
+
+
+ getData('customerGroups'); ?>
+
+
+ ],
+ "datasets": [{
+ "data": [
+
+ = \implode(',', $temp); ?>
+ ],
+ "backgroundColor": [
+ "rgb(255, 99, 132)",
+ "rgb(255, 159, 64)",
+ "rgb(255, 205, 86)",
+ "rgb(75, 192, 192)",
+ "rgb(54, 162, 235)",
+ "rgb(153, 102, 255)"
+ ]
+ }]
+ },
+ "options": {
+ "title": {
+ "display": true,
+ "text": "Customers per group"
+ }
+ }
+ }'>
+
+
+
+
+ Groups
+ Customer count
+
+ $values) : $sum += $values['customers']; ?>
+
+ = $groups; ?>
+ = $values['customers']; ?>
+
+
+ Total
+ = $sum; ?>
+
+
+
+
+
+
+
+ getData('salesRegion'); ?>
+
+ ,
+ = (int) ($salesRegion['America'] ?? 0); ?>,
+ = (int) ($salesRegion['Asia'] ?? 0); ?>,
+ = (int) ($salesRegion['Africa'] ?? 0); ?>,
+ = (int) ($salesRegion['CIS'] ?? 0); ?>,
+ = (int) ($salesRegion['Other'] ?? 0); ?>
+ ],
+ "backgroundColor": [
+ "rgb(255, 99, 132)",
+ "rgb(255, 159, 64)",
+ "rgb(255, 205, 86)",
+ "rgb(75, 192, 192)",
+ "rgb(54, 162, 235)",
+ "rgb(153, 102, 255)"
+ ]
+ }]
+ },
+ "options": {
+ "title": {
+ "display": true,
+ "text": "Sales per region"
+ }
+ }
+ }'>
+
+
+
+
+
+
+ getData('salesCountry'); ?>
+
+
+ ],
+ "datasets": [
+ {
+ "label": "= $this->getHtml('Sales'); ?>",
+ "type": "horizontalBar",
+ "data": [
+
+ = \implode(',', $temp); ?>
+ ],
+ "fill": false,
+ "borderColor": "rgb(54, 162, 235)",
+ "backgroundColor": "rgb(54, 162, 235)",
+ "tension": 0.0
+ }
+ ]
+ },
+ "options": {
+ "title": {
+ "display": true,
+ "text": "Sales per country"
+ }
+ }
+ }'>
+
+
+
+
+
+
+ getData('salesGroups'); ?>
+
+
+ ],
+ "datasets": [{
+ "data": [
+
+ = \implode(',', $temp); ?>
+ ],
+ "backgroundColor": [
+ "rgb(255, 99, 132)",
+ "rgb(255, 159, 64)",
+ "rgb(255, 205, 86)",
+ "rgb(75, 192, 192)",
+ "rgb(54, 162, 235)",
+ "rgb(153, 102, 255)"
+ ]
+ }]
+ },
+ "options": {
+ "title": {
+ "display": true,
+ "text": "Sales per group"
+ }
+ }
+ }'>
+
+
+
+
+
+
+
request->uri->fragment === 'c-tab-3' ? ' checked' : ''; ?>>
+
+
+
+
+ New customers
+
+ Shows new customers and their sales
+
+
+
+
+
+
+ New customers per region
+
+
+
+
+
+
+
+ New customers per sales rep
+
+
+
+
+
+
+
+ New customers per sales group
+
+
+
+
+
+
+
+ New customers per customer group
+
+
+
+
+
+
+
+ New customers sales per customer group
+
+
+
+
+
+
+
+
request->uri->fragment === 'c-tab-4' ? ' checked' : ''; ?>>
+
+
+
+
+ Lost customers
+
+ Shows lost customers and their sales
+
+
+
+
+
+
+ Lost customers per region
+
+
+
+
+
+
+
+ Lost customers per sales rep
+
+
+
+
+
+
+
+ Lost customers per sales group
+
+
+
+
+
+
+
+ Lost customers per customer group
+
+
+
+
+
+
+
+ Lost customers sales per customer group
+
+
+
+
+
+
+
+
request->uri->fragment === 'c-tab-5' ? ' checked' : ''; ?>>
+
+
+
+
+ getData('monthlySalesCosts'); ?>
+
+
+ = '"' . \implode('", "', $temp) . '"'; ?>
+ ],
+ "datasets": [
+ {
+ "label": "= $this->getHtml('Margin'); ?>",
+ "type": "line",
+ "data": [
+
+ = \implode(',', $temp); ?>
+ ],
+ "yAxisID": "axis-2",
+ "fill": false,
+ "borderColor": "rgb(255, 99, 132)",
+ "backgroundColor": "rgb(255, 99, 132)",
+ "tension": 0.0
+ },
+ {
+ "label": "= $this->getHtml('Sales'); ?>",
+ "type": "bar",
+ "data": [
+
+ = \implode(',', $temp); ?>
+ ],
+ "yAxisID": "axis-1",
+ "backgroundColor": "rgb(54, 162, 235)"
+ }
+ ]
+ },
+ "options": {
+ "title": {
+ "display": true,
+ "text": "Sales / Margin"
+ },
+ "scales": {
+ "yAxes": [
+ {
+ "id": "axis-1",
+ "display": true,
+ "position": "left"
+ },
+ {
+ "id": "axis-2",
+ "display": true,
+ "position": "right",
+ "scaleLabel": {
+ "display": true,
+ "labelString": "= $this->getHtml('Margin'); ?> %"
+ },
+ "gridLines": {
+ "display": false
+ },
+ "beginAtZero": true,
+ "ticks": {
+ "min": 0,
+ "max": 100,
+ "stepSize": 10
+ }
+ }
+ ]
+ }
+ }
+ }'>
+
+
+
+
+
+
+ Margins per region
+
+
+
+
+
+
+
+ Margins per sales rep
+
+
+
+
+
+
+
+ Margins per sales group
+
+
+
+
+
+
+
+ Margins per customer group
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Theme/Backend/client-list.tpl.php b/Theme/Backend/client-list.tpl.php
index d4de24c..eae2b9e 100755
--- a/Theme/Backend/client-list.tpl.php
+++ b/Theme/Backend/client-list.tpl.php
@@ -23,7 +23,7 @@ echo $this->getData('nav')->render(); ?>
= $this->getHtml('Clients'); ?>
-