mirror of
https://github.com/Karaka-Management/oms-SalesAnalysis.git
synced 2026-01-11 11:28:41 +00:00
update
This commit is contained in:
parent
32fa739301
commit
ae44226b1f
|
|
@ -5,131 +5,101 @@
|
|||
"type": 2,
|
||||
"subtype": 1,
|
||||
"name": "Analysis",
|
||||
"uri": "{/prefix}sales/analysis/overview/dashboard?{?}",
|
||||
"uri": "{/base}/sales/analysis?{?}",
|
||||
"target": "self",
|
||||
"icon": null,
|
||||
"order": 15,
|
||||
"from": "Analysis",
|
||||
"permission": { "permission": 2, "type": null, "element": null },
|
||||
"from": "SalesAnalysis",
|
||||
"permission": { "permission": 2, "category": null, "element": null },
|
||||
"parent": 1001601001,
|
||||
"children": [
|
||||
{
|
||||
"id": 1005401002,
|
||||
"pid": "44cbe06dd734865b28c110fbbf91d78ccf8124ae",
|
||||
"id": 1001602001,
|
||||
"pid": "/sales/analysis",
|
||||
"type": 3,
|
||||
"subtype": 1,
|
||||
"name": "Overview",
|
||||
"uri": "{/prefix}sales/analysis/overview/dashboard?{?}",
|
||||
"name": "Dashboard",
|
||||
"uri": "{/base}/sales/analysis?{?}",
|
||||
"target": "self",
|
||||
"icon": "fa fa-bar-chart",
|
||||
"icon": null,
|
||||
"order": 1,
|
||||
"from": "Analysis",
|
||||
"permission": { "permission": 2, "type": null, "element": null },
|
||||
"from": "SalesAnalysis",
|
||||
"permission": { "permission": 2, "category": null, "element": null },
|
||||
"parent": 1005401001,
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": 1005401003,
|
||||
"pid": "44cbe06dd734865b28c110fbbf91d78ccf8124ae",
|
||||
"id": 1001603001,
|
||||
"pid": "/sales/analysis",
|
||||
"type": 3,
|
||||
"subtype": 1,
|
||||
"name": "Products",
|
||||
"uri": "{/prefix}sales/analysis/product/dashboard?{?}",
|
||||
"name": "Item",
|
||||
"uri": "{/base}/sales/analysis/item",
|
||||
"target": "self",
|
||||
"icon": "fa fa-tag",
|
||||
"order": 1,
|
||||
"from": "Analysis",
|
||||
"permission": { "permission": 2, "type": null, "element": null },
|
||||
"icon": null,
|
||||
"order": 5,
|
||||
"from": "SalesAnalysis",
|
||||
"permission": { "permission": 2, "category": null, "element": null },
|
||||
"parent": 1005401001,
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": 1005401004,
|
||||
"pid": "44cbe06dd734865b28c110fbbf91d78ccf8124ae",
|
||||
"id": 1005404001,
|
||||
"pid": "/sales/analysis",
|
||||
"type": 3,
|
||||
"subtype": 1,
|
||||
"name": "Customers",
|
||||
"uri": "{/prefix}sales/analysis/customer/dashboard?{?}",
|
||||
"name": "Client",
|
||||
"uri": "{/base}/sales/analysis/client?{?}",
|
||||
"target": "self",
|
||||
"icon": "fa fa-users",
|
||||
"order": 1,
|
||||
"from": "Analysis",
|
||||
"permission": { "permission": 2, "type": null, "element": null },
|
||||
"icon": null,
|
||||
"order": 10,
|
||||
"from": "SalesAnalysis",
|
||||
"permission": { "permission": 2, "category": null, "element": null },
|
||||
"parent": 1005401001,
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": 1005401005,
|
||||
"pid": "44cbe06dd734865b28c110fbbf91d78ccf8124ae",
|
||||
"id": 1005405001,
|
||||
"pid": "/sales/analysis",
|
||||
"type": 3,
|
||||
"subtype": 1,
|
||||
"name": "Regions",
|
||||
"uri": "{/prefix}sales/analysis/region/dashboard?{?}",
|
||||
"name": "Bill",
|
||||
"uri": "{/base}/sales/analysis/bill",
|
||||
"target": "self",
|
||||
"icon": "fa fa-globe",
|
||||
"order": 1,
|
||||
"from": "Analysis",
|
||||
"permission": { "permission": 2, "type": null, "element": null },
|
||||
"icon": null,
|
||||
"order": 15,
|
||||
"from": "SalesAnalysis",
|
||||
"permission": { "permission": 2, "category": null, "element": null },
|
||||
"parent": 1005401001,
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": 1005401006,
|
||||
"pid": "44cbe06dd734865b28c110fbbf91d78ccf8124ae",
|
||||
"id": 1005406001,
|
||||
"pid": "/sales/analysis",
|
||||
"type": 3,
|
||||
"subtype": 1,
|
||||
"name": "Marketing",
|
||||
"uri": "{/prefix}sales/analysis/marketing/dashboard?{?}",
|
||||
"name": "Region",
|
||||
"uri": "{/base}/sales/analysis/region?{?}",
|
||||
"target": "self",
|
||||
"icon": "fa fa-paint-brush",
|
||||
"order": 1,
|
||||
"from": "Analysis",
|
||||
"permission": { "permission": 2, "type": null, "element": null },
|
||||
"icon": null,
|
||||
"order": 20,
|
||||
"from": "SalesAnalysis",
|
||||
"permission": { "permission": 2, "category": null, "element": null },
|
||||
"parent": 1005401001,
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": 1005401007,
|
||||
"pid": "44cbe06dd734865b28c110fbbf91d78ccf8124ae",
|
||||
"id": 1005407001,
|
||||
"pid": "/sales/analysis",
|
||||
"type": 3,
|
||||
"subtype": 1,
|
||||
"name": "Employees",
|
||||
"uri": "{/prefix}sales/analysis/employees/dashboard?{?}",
|
||||
"name": "SalesRep",
|
||||
"uri": "{/base}/sales/analysis/rep?{?}",
|
||||
"target": "self",
|
||||
"icon": "fa fa-building-o",
|
||||
"order": 1,
|
||||
"from": "Analysis",
|
||||
"permission": { "permission": 2, "type": null, "element": null },
|
||||
"parent": 1005401001,
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": 1005401008,
|
||||
"pid": "44cbe06dd734865b28c110fbbf91d78ccf8124ae",
|
||||
"type": 3,
|
||||
"subtype": 1,
|
||||
"name": "Invoices",
|
||||
"uri": "{/prefix}sales/analysis/invoices/dashboard?{?}",
|
||||
"target": "self",
|
||||
"icon": "fa fa-envelope",
|
||||
"order": 1,
|
||||
"from": "Analysis",
|
||||
"permission": { "permission": 2, "type": null, "element": null },
|
||||
"parent": 1005401001,
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"id": 1005401009,
|
||||
"pid": "44cbe06dd734865b28c110fbbf91d78ccf8124ae",
|
||||
"type": 3,
|
||||
"subtype": 1,
|
||||
"name": "Database",
|
||||
"uri": "{/prefix}sales/analysis/invoices/dashboard?{?}",
|
||||
"target": "self",
|
||||
"icon": "fa fa-database",
|
||||
"order": 1,
|
||||
"from": "Analysis",
|
||||
"permission": { "permission": 2, "type": null, "element": null },
|
||||
"icon": null,
|
||||
"order": 25,
|
||||
"from": "SalesAnalysis",
|
||||
"permission": { "permission": 2, "category": null, "element": null },
|
||||
"parent": 1005401001,
|
||||
"children": []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,20 +8,20 @@
|
|||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\SalesAnalysis\Admin\Install;
|
||||
|
||||
use phpOMS\DataStorage\Database\DatabasePool;
|
||||
use phpOMS\Application\ApplicationAbstract;
|
||||
|
||||
/**
|
||||
* Navigation class.
|
||||
*
|
||||
* @package Modules\SalesAnalysis\Admin\Install
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Navigation
|
||||
|
|
@ -29,15 +29,15 @@ class Navigation
|
|||
/**
|
||||
* Install navigation providing
|
||||
*
|
||||
* @param ApplicationAbstract $app Application
|
||||
* @param string $path Module path
|
||||
* @param DatabasePool $dbPool Database pool for database interaction
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function install(string $path, DatabasePool $dbPool) : void
|
||||
public static function install(ApplicationAbstract $app, string $path) : void
|
||||
{
|
||||
\Modules\Navigation\Admin\Installer::installExternal($dbPool, ['path' => __DIR__ . '/Navigation.install.json']);
|
||||
\Modules\Navigation\Admin\Installer::installExternal($app, ['path' => __DIR__ . '/Navigation.install.json']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
|
|
@ -21,9 +21,16 @@ use phpOMS\Module\InstallerAbstract;
|
|||
*
|
||||
* @package Modules\SalesAnalysis\Admin
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class Installer extends InstallerAbstract
|
||||
{
|
||||
/**
|
||||
* Path of the file
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public const PATH = __DIR__;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,75 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
use Modules\SalesAnalysis\Controller\BackendController;
|
||||
use Modules\SalesAnalysis\Models\PermissionState;
|
||||
use Modules\SalesAnalysis\Models\PermissionCategory;
|
||||
use phpOMS\Account\PermissionType;
|
||||
use phpOMS\Router\RouteVerb;
|
||||
|
||||
return [
|
||||
'^.*/sales/analysis/dashboard.*$' => [
|
||||
'^.*/sales/analysis(\?.*|$)$' => [
|
||||
[
|
||||
'dest' => '\Modules\SalesAnalysis\Controller\BackendController:viewBackendDashboard',
|
||||
'dest' => '\Modules\SalesAnalysis\Controller\BackendController:viewDashboard',
|
||||
'verb' => RouteVerb::GET,
|
||||
'permission' => [
|
||||
'module' => BackendController::MODULE_NAME,
|
||||
'type' => PermissionType::READ,
|
||||
'state' => PermissionState::DASHBOARD,
|
||||
'module' => BackendController::NAME,
|
||||
'type' => PermissionType::CREATE,
|
||||
'state' => PermissionCategory::DASHBOARD,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'^.*/sales/analysis/overview/dashboard.*$' => [
|
||||
'^.*/sales/analysis/bill(\?.*|$)$' => [
|
||||
[
|
||||
'dest' => '\Modules\SalesAnalysis\Controller\BackendController:viewBackendOverviewDashboard',
|
||||
'dest' => '\Modules\SalesAnalysis\Controller\BackendController:viewBillAnalysis',
|
||||
'verb' => RouteVerb::GET,
|
||||
'permission' => [
|
||||
'module' => BackendController::MODULE_NAME,
|
||||
'module' => BackendController::NAME,
|
||||
'type' => PermissionType::READ,
|
||||
'state' => PermissionState::DASHBOARD,
|
||||
'state' => PermissionCategory::DASHBOARD,
|
||||
],
|
||||
],
|
||||
],
|
||||
'^.*/sales/analysis/rep(\?.*|$)$' => [
|
||||
[
|
||||
'dest' => '\Modules\SalesAnalysis\Controller\BackendController:viewSalesRepAnalysis',
|
||||
'verb' => RouteVerb::GET,
|
||||
'permission' => [
|
||||
'module' => BackendController::NAME,
|
||||
'type' => PermissionType::READ,
|
||||
'state' => PermissionCategory::DASHBOARD,
|
||||
],
|
||||
],
|
||||
],
|
||||
'^.*/sales/analysis/region(\?.*|$)$' => [
|
||||
[
|
||||
'dest' => '\Modules\SalesAnalysis\Controller\BackendController:viewRegionAnalysis',
|
||||
'verb' => RouteVerb::GET,
|
||||
'permission' => [
|
||||
'module' => BackendController::NAME,
|
||||
'type' => PermissionType::READ,
|
||||
'state' => PermissionCategory::DASHBOARD,
|
||||
],
|
||||
],
|
||||
],
|
||||
'^.*/sales/analysis/client(\?.*|$)$' => [
|
||||
[
|
||||
'dest' => '\Modules\SalesAnalysis\Controller\BackendController:viewClientAnalysis',
|
||||
'verb' => RouteVerb::GET,
|
||||
'permission' => [
|
||||
'module' => BackendController::NAME,
|
||||
'type' => PermissionType::READ,
|
||||
'state' => PermissionCategory::DASHBOARD,
|
||||
],
|
||||
],
|
||||
],
|
||||
'^.*/sales/analysis/item(\?.*|$)$' => [
|
||||
[
|
||||
'dest' => '\Modules\SalesAnalysis\Controller\BackendController:viewItemSalesAnalysis',
|
||||
'verb' => RouteVerb::GET,
|
||||
'permission' => [
|
||||
'module' => BackendController::NAME,
|
||||
'type' => PermissionType::READ,
|
||||
'state' => PermissionCategory::DASHBOARD,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
|
|
@ -21,9 +21,16 @@ use phpOMS\Module\StatusAbstract;
|
|||
*
|
||||
* @package Modules\SalesAnalysis\Admin
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class Status extends StatusAbstract
|
||||
{
|
||||
/**
|
||||
* Path of the file
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public const PATH = __DIR__;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
|
|
@ -21,9 +21,16 @@ use phpOMS\Module\UninstallerAbstract;
|
|||
*
|
||||
* @package Modules\SalesAnalysis\Admin
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class Uninstaller extends UninstallerAbstract
|
||||
{
|
||||
/**
|
||||
* Path of the file
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public const PATH = __DIR__;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
|
|
@ -21,9 +21,16 @@ use phpOMS\Module\UpdaterAbstract;
|
|||
*
|
||||
* @package Modules\SalesAnalysis\Admin
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class Updater extends UpdaterAbstract
|
||||
{
|
||||
/**
|
||||
* Path of the file
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public const PATH = __DIR__;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,15 +8,27 @@
|
|||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\SalesAnalysis\Controller;
|
||||
|
||||
use Modules\Organization\Models\UnitMapper;
|
||||
use Modules\SalesAnalysis\Models\ClientMapper;
|
||||
use Modules\SalesAnalysis\Models\GeneralMapper;
|
||||
use Modules\SalesAnalysis\Models\ItemMapper;
|
||||
use Modules\SalesAnalysis\Models\RegionMapper;
|
||||
use phpOMS\Asset\AssetType;
|
||||
use phpOMS\Contract\RenderableInterface;
|
||||
use phpOMS\DataStorage\Database\Query\Builder;
|
||||
use phpOMS\Localization\ISO3166CharEnum;
|
||||
use phpOMS\Localization\ISO3166NameEnum;
|
||||
use phpOMS\Localization\ISO3166TwoEnum;
|
||||
use phpOMS\Localization\RegionEnum;
|
||||
use phpOMS\Message\RequestAbstract;
|
||||
use phpOMS\Message\ResponseAbstract;
|
||||
use phpOMS\Stdlib\Base\SmartDateTime;
|
||||
use phpOMS\Views\View;
|
||||
|
||||
/**
|
||||
|
|
@ -24,28 +36,108 @@ use phpOMS\Views\View;
|
|||
*
|
||||
* @package Modules\SalesAnalysis
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class BackendController extends Controller
|
||||
{
|
||||
/**
|
||||
* Routing end-point for application behaviour.
|
||||
* Method which shows the sales dashboard
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
* @param ResponseAbstract $response Response
|
||||
* @param mixed $data Generic data
|
||||
* @param array $data Generic data
|
||||
*
|
||||
* @return RenderableInterface
|
||||
* @return RenderableInterface Response can be rendered
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function viewBackendDashboard(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
|
||||
public function viewDashboard(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface
|
||||
{
|
||||
$head = $response->data['Content']->head;
|
||||
$nonce = $this->app->appSettings->getOption('script-nonce');
|
||||
|
||||
$head->addAsset(AssetType::CSS, 'Resources/chartjs/chart.css');
|
||||
$head->addAsset(AssetType::JSLATE, 'Resources/chartjs/chart.js', ['nonce' => $nonce]);
|
||||
$head->addAsset(AssetType::JSLATE, 'Modules/SalesAnalysis/Controller/Controller.js', ['nonce' => $nonce, 'type' => 'module']);
|
||||
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
$view->setTemplate('/Modules/SalesAnalysis/Theme/Backend/analysis-dashboard');
|
||||
$view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationSplash(1005401001, $request, $response));
|
||||
$view->setTemplate('/Modules/SalesAnalysis/Theme/Backend/analysis-overview-dashboard');
|
||||
$view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1005401001, $request, $response);
|
||||
|
||||
// @todo: limit bill type (invoice/credit note) (customers only)
|
||||
// @todo: limit bill status
|
||||
|
||||
$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);
|
||||
|
||||
$view->data['startCurrent'] = $startCurrent;
|
||||
$view->data['endCurrent'] = $endCurrent;
|
||||
$view->data['startComparison'] = $startComparison;
|
||||
$view->data['endComparison'] = $endComparison;
|
||||
|
||||
[
|
||||
$view->data['mtdA'],
|
||||
$view->data['mtdPY'],
|
||||
$view->data['ytdA'],
|
||||
$view->data['ytdPY'],
|
||||
$view->data['monthlySales']
|
||||
] = GeneralMapper::monthlySalesProfit(
|
||||
$startCurrent,
|
||||
$endCurrent,
|
||||
$startComparison,
|
||||
$endComparison,
|
||||
$businessStart
|
||||
);
|
||||
|
||||
$historyStart = $startOfYear->createModify(-9);
|
||||
$view->data['annualSales'] = GeneralMapper::annualSalesProfit($historyStart, $endCurrent, $businessStart);
|
||||
|
||||
[
|
||||
$view->data['mtdAItemAttribute'],
|
||||
$view->data['mtdPYItemAttribute'],
|
||||
$view->data['ytdAItemAttribute'],
|
||||
$view->data['ytdPYItemAttribute']
|
||||
] = ItemMapper::mtdYtdItemAttribute(
|
||||
$startCurrent,
|
||||
$endCurrent,
|
||||
$startComparison,
|
||||
$endComparison,
|
||||
$businessStart,
|
||||
$request->header->l11n->language
|
||||
);
|
||||
|
||||
[
|
||||
$view->data['mtdAClientAttribute'],
|
||||
$view->data['mtdPYClientAttribute'],
|
||||
$view->data['ytdAClientAttribute'],
|
||||
$view->data['ytdPYClientAttribute']
|
||||
] = ClientMapper::mtdYtdClientAttribute(
|
||||
$startCurrent,
|
||||
$endCurrent,
|
||||
$startComparison,
|
||||
$endComparison,
|
||||
$businessStart,
|
||||
$request->header->l11n->language
|
||||
);
|
||||
|
||||
[
|
||||
$view->data['mtdAClientCountry'],
|
||||
$view->data['mtdPYClientCountry'],
|
||||
$view->data['ytdAClientCountry'],
|
||||
$view->data['ytdPYClientCountry']
|
||||
] = RegionMapper::mtdYtdCountry(
|
||||
$startCurrent,
|
||||
$endCurrent,
|
||||
$startComparison,
|
||||
$endComparison,
|
||||
$businessStart
|
||||
);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
|
@ -55,18 +147,528 @@ final class BackendController extends Controller
|
|||
*
|
||||
* @param RequestAbstract $request Request
|
||||
* @param ResponseAbstract $response Response
|
||||
* @param mixed $data Generic data
|
||||
* @param array $data Generic data
|
||||
*
|
||||
* @return RenderableInterface
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function viewBackendOverviewDashboard(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
|
||||
public function viewRegionAnalysis(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface
|
||||
{
|
||||
$head = $response->data['Content']->head;
|
||||
$nonce = $this->app->appSettings->getOption('script-nonce');
|
||||
|
||||
$head->addAsset(AssetType::CSS, 'Resources/chartjs/chart.css');
|
||||
$head->addAsset(AssetType::JSLATE, 'Resources/chartjs/chart.js', ['nonce' => $nonce]);
|
||||
$head->addAsset(AssetType::JSLATE, 'Resources/chartjs/plugins/chartjs-chart-geo.js', ['nonce' => $nonce]);
|
||||
$head->addAsset(AssetType::JSLATE, 'Modules/SalesAnalysis/Controller/Controller.js', ['nonce' => $nonce, 'type' => 'module']);
|
||||
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
$view->setTemplate('/Modules/SalesAnalysis/Theme/Backend/analysis-overview-dashboard');
|
||||
$view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1005401001, $request, $response));
|
||||
$view->setTemplate('/Modules/SalesAnalysis/Theme/Backend/analysis-region');
|
||||
$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();
|
||||
|
||||
$view->data['domestic'] = $domestic->mainAddress->country === ISO3166TwoEnum::_XXX ? 'US' : $domestic->mainAddress->country;
|
||||
|
||||
[
|
||||
$mtdCurrent,
|
||||
$ytdCurrent,
|
||||
$monthlyCurrent
|
||||
] = RegionMapper::monthlySalesProfit(
|
||||
$startCurrent,
|
||||
$endCurrent,
|
||||
$businessStart
|
||||
);
|
||||
|
||||
$view->data['monthlyDomesticExportCurrent'] = RegionMapper::countryIntervalToRegion(
|
||||
$monthlyCurrent,
|
||||
[$view->data['domestic']],
|
||||
['net_sales', 'net_profit']
|
||||
);
|
||||
|
||||
[
|
||||
$mtdPY,
|
||||
$ytdPY,
|
||||
$monthlyPY
|
||||
] = RegionMapper::monthlySalesProfit(
|
||||
$startComparison,
|
||||
$endComparison,
|
||||
$businessStart
|
||||
);
|
||||
|
||||
$view->data['monthlyDomesticExportPY'] = RegionMapper::countryIntervalToRegion(
|
||||
$monthlyPY,
|
||||
[$view->data['domestic']],
|
||||
['net_sales', 'net_profit']
|
||||
);
|
||||
|
||||
[
|
||||
$view->data['mtdPYClientCountry'],
|
||||
$view->data['mtdAClientCountry'],
|
||||
$view->data['ytdPYClientCountry'],
|
||||
$view->data['ytdAClientCountry'],
|
||||
] = RegionMapper::mtdYtdCountry(
|
||||
$startCurrent,
|
||||
$endCurrent,
|
||||
$startComparison,
|
||||
$endComparison,
|
||||
$businessStart
|
||||
);
|
||||
|
||||
$annualCountrySales = RegionMapper::annualSalesProfitCountry(clone $historyStart, $endCurrent);
|
||||
|
||||
$view->data['ytdADomesticExport'] = RegionMapper::countryToRegion(
|
||||
$view->data['ytdAClientCountry'],
|
||||
[$view->data['domestic']],
|
||||
['net_sales', 'net_profit']
|
||||
);
|
||||
|
||||
$view->data['annualDomesticExport'] = RegionMapper::countryIntervalToRegion(
|
||||
$annualCountrySales,
|
||||
[$view->data['domestic']],
|
||||
['net_sales', 'net_profit']
|
||||
);
|
||||
|
||||
[
|
||||
$view->data['mtdPYClientCountryCount'],
|
||||
$view->data['mtdAClientCountryCount'],
|
||||
$view->data['ytdPYClientCountryCount'],
|
||||
$view->data['ytdAClientCountryCount'],
|
||||
] = RegionMapper::mtdYtdClientCountry(
|
||||
$startCurrent,
|
||||
$endCurrent,
|
||||
$startComparison,
|
||||
$endComparison,
|
||||
$businessStart
|
||||
);
|
||||
|
||||
$annualCountryCount = RegionMapper::annualCustomerCountry(clone $historyStart, $endCurrent);
|
||||
|
||||
$view->data['ytdADomesticExportCount'] = RegionMapper::countryToRegion(
|
||||
$view->data['ytdAClientCountryCount'],
|
||||
[$view->data['domestic']],
|
||||
['client_count']
|
||||
);
|
||||
|
||||
$view->data['annualDomesticExportCount'] = RegionMapper::countryIntervalToRegion(
|
||||
$annualCountryCount,
|
||||
[$view->data['domestic']],
|
||||
['client_count']
|
||||
);
|
||||
|
||||
///
|
||||
|
||||
$view->data['ytdAContinent'] = RegionMapper::countryToRegion(
|
||||
$view->data['ytdAClientCountry'],
|
||||
ISO3166NameEnum::getSubregions('continents'),
|
||||
['net_sales', 'net_profit']
|
||||
);
|
||||
|
||||
$view->data['annualContinent'] = RegionMapper::countryIntervalToRegion(
|
||||
$annualCountrySales,
|
||||
ISO3166NameEnum::getSubregions('continents'),
|
||||
['net_sales', 'net_profit']
|
||||
);
|
||||
|
||||
$view->data['ytdAContinentCount'] = RegionMapper::countryToRegion(
|
||||
$view->data['ytdAClientCountryCount'],
|
||||
ISO3166NameEnum::getSubregions('continents'),
|
||||
['client_count']
|
||||
);
|
||||
|
||||
$view->data['annualContinentCount'] = RegionMapper::countryIntervalToRegion(
|
||||
$annualCountryCount,
|
||||
ISO3166NameEnum::getSubregions('continents'),
|
||||
['client_count']
|
||||
);
|
||||
|
||||
///
|
||||
|
||||
$view->data['ytdARegions'] = RegionMapper::countryToRegion(
|
||||
$view->data['ytdAClientCountry'],
|
||||
RegionEnum::getConstants(),
|
||||
['net_sales', 'net_profit']
|
||||
);
|
||||
|
||||
$view->data['ytdPYRegions'] = RegionMapper::countryToRegion(
|
||||
$view->data['ytdPYClientCountry'],
|
||||
RegionEnum::getConstants(),
|
||||
['net_sales', 'net_profit']
|
||||
);
|
||||
|
||||
$view->data['mtdARegions'] = RegionMapper::countryToRegion(
|
||||
$view->data['mtdAClientCountry'],
|
||||
RegionEnum::getConstants(),
|
||||
['net_sales', 'net_profit']
|
||||
);
|
||||
|
||||
$view->data['mtdPYRegions'] = RegionMapper::countryToRegion(
|
||||
$view->data['mtdPYClientCountry'],
|
||||
RegionEnum::getConstants(),
|
||||
['net_sales', 'net_profit']
|
||||
);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing end-point for application behaviour.
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
* @param ResponseAbstract $response Response
|
||||
* @param array $data Generic data
|
||||
*
|
||||
* @return RenderableInterface
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function viewBillAnalysis(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface
|
||||
{
|
||||
$head = $response->data['Content']->head;
|
||||
$nonce = $this->app->appSettings->getOption('script-nonce');
|
||||
|
||||
$head->addAsset(AssetType::CSS, 'Resources/chartjs/chart.css');
|
||||
$head->addAsset(AssetType::JSLATE, 'Resources/chartjs/chart.js', ['nonce' => $nonce]);
|
||||
$head->addAsset(AssetType::JSLATE, 'Modules/SalesAnalysis/Controller/Controller.js', ['nonce' => $nonce, 'type' => 'module']);
|
||||
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
$view->setTemplate('/Modules/SalesAnalysis/Theme/Backend/analysis-bill');
|
||||
$view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1005401001, $request, $response);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing end-point for application behaviour.
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
* @param ResponseAbstract $response Response
|
||||
* @param array $data Generic data
|
||||
*
|
||||
* @return RenderableInterface
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function viewSalesRepAnalysis(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface
|
||||
{
|
||||
$head = $response->data['Content']->head;
|
||||
$nonce = $this->app->appSettings->getOption('script-nonce');
|
||||
|
||||
$head->addAsset(AssetType::CSS, 'Resources/chartjs/chart.css');
|
||||
$head->addAsset(AssetType::JSLATE, 'Resources/chartjs/chart.js', ['nonce' => $nonce]);
|
||||
$head->addAsset(AssetType::JSLATE, 'Modules/SalesAnalysis/Controller/Controller.js', ['nonce' => $nonce, 'type' => 'module']);
|
||||
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
$view->setTemplate('/Modules/SalesAnalysis/Theme/Backend/analysis-rep');
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing end-point for application behaviour.
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
* @param ResponseAbstract $response Response
|
||||
* @param array $data Generic data
|
||||
*
|
||||
* @return RenderableInterface
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function viewClientAnalysis(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface
|
||||
{
|
||||
$head = $response->data['Content']->head;
|
||||
$nonce = $this->app->appSettings->getOption('script-nonce');
|
||||
|
||||
$head->addAsset(AssetType::CSS, 'Resources/chartjs/chart.css');
|
||||
$head->addAsset(AssetType::JSLATE, 'Resources/chartjs/chart.js', ['nonce' => $nonce]);
|
||||
$head->addAsset(AssetType::JSLATE, 'Resources/chartjs/plugins/chartjs-chart-geo.js', ['nonce' => $nonce]);
|
||||
$head->addAsset(AssetType::JSLATE, 'Modules/SalesAnalysis/Controller/Controller.js', ['nonce' => $nonce, 'type' => 'module']);
|
||||
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
$view->setTemplate('/Modules/SalesAnalysis/Theme/Backend/analysis-client');
|
||||
$view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1005401001, $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->data['monthlySalesCosts'] = $monthlySalesCosts;
|
||||
|
||||
/////
|
||||
$monthlySalesCustomer = [];
|
||||
for ($i = 1; $i < 13; ++$i) {
|
||||
$monthlySalesCustomer[] = [
|
||||
'net_sales' => $sales = \mt_rand(1200000000, 2000000000),
|
||||
'customers' => \mt_rand(200, 400),
|
||||
'year' => 2020,
|
||||
'month' => $i,
|
||||
];
|
||||
}
|
||||
|
||||
$view->data['monthlySalesCustomer'] = $monthlySalesCustomer;
|
||||
|
||||
$annualSalesCustomer = [];
|
||||
for ($i = 1; $i < 11; ++$i) {
|
||||
$annualSalesCustomer[] = [
|
||||
'net_sales' => $sales = \mt_rand(1200000000, 2000000000) * 12,
|
||||
'customers' => \mt_rand(200, 400) * 6,
|
||||
'year' => 2020 - 10 + $i,
|
||||
];
|
||||
}
|
||||
|
||||
$view->data['annualSalesCustomer'] = $annualSalesCustomer;
|
||||
|
||||
/////
|
||||
$monthlyCustomerRetention = [];
|
||||
for ($i = 1; $i < 10; ++$i) {
|
||||
$monthlyCustomerRetention[] = [
|
||||
'customers' => \mt_rand(200, 400),
|
||||
'year' => \date('y') - 9 + $i,
|
||||
];
|
||||
}
|
||||
|
||||
$view->data['monthlyCustomerRetention'] = $monthlyCustomerRetention;
|
||||
|
||||
/////
|
||||
$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;
|
||||
|
||||
/////
|
||||
$currentCustomersCountry = [];
|
||||
for ($i = 1; $i < 51; ++$i) {
|
||||
$country = (string) ISO3166NameEnum::getRandom();
|
||||
$currentCustomersCountry[\substr($country, 0, 20)] = [
|
||||
'customers' => (int) (\mt_rand(200, 400) / 12),
|
||||
];
|
||||
}
|
||||
|
||||
\uasort($currentCustomersCountry, function($a, $b) {
|
||||
return $b['customers'] <=> $a['customers'];
|
||||
});
|
||||
|
||||
$view->data['currentCustomersCountry'] = $currentCustomersCountry;
|
||||
|
||||
$annualCustomersCountry = [];
|
||||
for ($i = 1; $i < 51; ++$i) {
|
||||
$countryCode = ISO3166CharEnum::getRandom();
|
||||
$countryName = (string) ISO3166NameEnum::getByName('_' . $countryCode);
|
||||
$annualCustomersCountry[\substr($countryName, 0, 20)] = [];
|
||||
|
||||
for ($j = 1; $j < 11; ++$j) {
|
||||
$annualCustomersCountry[\substr($countryName, 0, 20)][] = [
|
||||
'customers' => (int) (\mt_rand(200, 400) / 12),
|
||||
'year' => 2020 - 10 + $j,
|
||||
'name' => $countryName,
|
||||
'code' => $countryCode,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$view->data['annualCustomersCountry'] = $annualCustomersCountry;
|
||||
|
||||
/////
|
||||
$customerGroups = [];
|
||||
for ($i = 1; $i < 7; ++$i) {
|
||||
$customerGroups['Group ' . $i] = [
|
||||
'customers' => (int) (\mt_rand(200, 400) / 12),
|
||||
];
|
||||
}
|
||||
|
||||
$view->data['customerGroups'] = $customerGroups;
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing end-point for application behaviour.
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
* @param ResponseAbstract $response Response
|
||||
* @param array $data Generic data
|
||||
*
|
||||
* @return RenderableInterface
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function viewItemSalesAnalysis(RequestAbstract $request, ResponseAbstract $response, array $data = []) : RenderableInterface
|
||||
{
|
||||
$head = $response->data['Content']->head;
|
||||
$nonce = $this->app->appSettings->getOption('script-nonce');
|
||||
|
||||
$head->addAsset(AssetType::CSS, 'Resources/chartjs/chart.css');
|
||||
$head->addAsset(AssetType::JSLATE, 'Resources/chartjs/chart.js', ['nonce' => $nonce]);
|
||||
$head->addAsset(AssetType::JSLATE, 'Modules/SalesAnalysis/Controller/Controller.js', ['nonce' => $nonce, 'type' => 'module']);
|
||||
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
$view->setTemplate('/Modules/SalesAnalysis/Theme/Backend/analysis-item');
|
||||
$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);
|
||||
|
||||
$view->data['startCurrent'] = $startCurrent;
|
||||
$view->data['endCurrent'] = $endCurrent;
|
||||
$view->data['startComparison'] = $startComparison;
|
||||
$view->data['endComparison'] = $endComparison;
|
||||
|
||||
[
|
||||
$view->data['mtdAItemAttribute'],
|
||||
$view->data['mtdPYItemAttribute'],
|
||||
$view->data['ytdAItemAttribute'],
|
||||
$view->data['ytdPYItemAttribute']
|
||||
] = ItemMapper::mtdYtdItemAttribute(
|
||||
$startCurrent,
|
||||
$endCurrent,
|
||||
$startComparison,
|
||||
$endComparison,
|
||||
$businessStart,
|
||||
$request->header->l11n->language
|
||||
);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
|
|
|||
73
Controller/Controller.js
Normal file
73
Controller/Controller.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import { jsOMS } from '../../../jsOMS/Utils/oLib.js';
|
||||
import { Autoloader } from '../../../jsOMS/Autoloader.js';
|
||||
|
||||
Autoloader.defineNamespace('omsApp.Modules');
|
||||
|
||||
omsApp.Modules.SalesAnalysis = class {
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
constructor (app)
|
||||
{
|
||||
this.app = app;
|
||||
};
|
||||
|
||||
bind (id)
|
||||
{
|
||||
const charts = typeof id === 'undefined' ? document.getElementsByTagName('canvas') : [document.getElementById(id)];
|
||||
let length = charts.length;
|
||||
|
||||
for (let i = 0; i < length; ++i) {
|
||||
if (charts[i].getAttribute('data-chart') === null
|
||||
&& charts[i].getAttribute('data-chart') !== 'undefined'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.bindChart(charts[i]);
|
||||
}
|
||||
};
|
||||
|
||||
bindChart (chart)
|
||||
{
|
||||
if (typeof chart === 'undefined' || !chart) {
|
||||
jsOMS.Log.Logger.instance.error('Invalid chart: ' + chart, 'ClientManagement');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const self = this;
|
||||
const data = JSON.parse(chart.getAttribute('data-chart'));
|
||||
|
||||
if (data.type === 'choropleth') {
|
||||
const parts = data.mapurl.split('/');
|
||||
const fileName = parts[parts.length - 1];
|
||||
const mapName = fileName.replace('.topo.json', '');
|
||||
|
||||
fetch(data.mapurl).then((r) => r.json()).then((d) => {
|
||||
const countries = ChartGeo.topojson.feature(d, d.objects[mapName]).features;
|
||||
|
||||
data.data.labels = countries.map((c) => c.properties.name);
|
||||
|
||||
const vals = {};
|
||||
const length = data.data.datasets[0].data.length;
|
||||
for (let i = 0; i < length; ++i) {
|
||||
vals[data.data.datasets[0].data[i].id] = data.data.datasets[0].data[i].value;
|
||||
}
|
||||
|
||||
data.data.datasets[0].data = countries.map((c) => (
|
||||
{feature: c, value: (vals.hasOwnProperty(c.id) ? vals[c.id] : null)}
|
||||
));
|
||||
|
||||
const myChart = new Chart(chart.getContext('2d'), data);
|
||||
});
|
||||
} else {
|
||||
const myChart = new Chart(chart.getContext('2d'), data);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
window.omsApp.moduleManager.get('SalesAnalysis').bind();
|
||||
|
|
@ -8,24 +8,23 @@
|
|||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\SalesAnalysis\Controller;
|
||||
|
||||
use phpOMS\Module\ModuleAbstract;
|
||||
use phpOMS\Module\WebInterface;
|
||||
|
||||
/**
|
||||
* Sales class.
|
||||
*
|
||||
* @package Modules\SalesAnalysis
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Controller extends ModuleAbstract implements WebInterface
|
||||
class Controller extends ModuleAbstract
|
||||
{
|
||||
/**
|
||||
* Module path.
|
||||
|
|
@ -33,7 +32,7 @@ class Controller extends ModuleAbstract implements WebInterface
|
|||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public const MODULE_PATH = __DIR__ . '/../';
|
||||
public const PATH = __DIR__ . '/../';
|
||||
|
||||
/**
|
||||
* Module version.
|
||||
|
|
@ -65,7 +64,7 @@ class Controller extends ModuleAbstract implements WebInterface
|
|||
* @var string[]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected static array $providing = [];
|
||||
public static array $providing = [];
|
||||
|
||||
/**
|
||||
* Dependencies.
|
||||
|
|
@ -73,5 +72,5 @@ class Controller extends ModuleAbstract implements WebInterface
|
|||
* @var string[]
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected static array $dependencies = [];
|
||||
public static array $dependencies = [];
|
||||
}
|
||||
|
|
|
|||
157
Models/ClientMapper.php
Normal file
157
Models/ClientMapper.php
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 7.4
|
||||
*
|
||||
* @package Modules\SalesAnalysis\Models
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\SalesAnalysis\Models;
|
||||
|
||||
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
|
||||
use phpOMS\DataStorage\Database\Query\Builder;
|
||||
use phpOMS\Stdlib\Base\SmartDateTime;
|
||||
|
||||
/**
|
||||
* Permision state enum.
|
||||
*
|
||||
* @package Modules\SalesAnalysis\Models
|
||||
* @license OMS License 1.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class ClientMapper extends DataMapperFactory
|
||||
{
|
||||
public static function mtdYtdClientAttribute(
|
||||
\DateTime $startCurrent,
|
||||
\DateTime $endCurrent,
|
||||
\DateTime $startComparison,
|
||||
\DateTime $endComparison,
|
||||
int $businessStart = 1,
|
||||
string $language = 'en'
|
||||
) {
|
||||
$endCurrentIndex = SmartDateTime::calculateMonthIndex((int) $endCurrent->format('m'), $businessStart);
|
||||
|
||||
// @todo: this query doesn't return clients that have not segment etc. defined.
|
||||
$query = new Builder(self::$db);
|
||||
$query->raw(
|
||||
'SELECT
|
||||
clientmgmt_attr_type_name,
|
||||
clientmgmt_attr_type_l11n_title,
|
||||
clientmgmt_attr_value_id,
|
||||
clientmgmt_attr_value_l11n_title,
|
||||
YEAR(billing_bill_performance_date) as salesyear,
|
||||
MONTH(billing_bill_performance_date) as salesmonth,
|
||||
SUM(billing_bill_netsales) as netsales,
|
||||
SUM(billing_bill_netprofit) as netprofit
|
||||
FROM billing_bill
|
||||
LEFT JOIN clientmgmt_client
|
||||
ON clientmgmt_client_id = billing_bill_client
|
||||
LEFT JOIN clientmgmt_client_attr
|
||||
ON clientmgmt_client_id = clientmgmt_client_attr_client
|
||||
LEFT JOIN clientmgmt_attr_type
|
||||
ON clientmgmt_client_attr_type = clientmgmt_attr_type_id
|
||||
LEFT JOIN clientmgmt_attr_type_l11n
|
||||
ON clientmgmt_attr_type_id = clientmgmt_attr_type_l11n_type AND clientmgmt_attr_type_l11n_lang = \'' . $language . '\'
|
||||
LEFT JOIN clientmgmt_attr_value
|
||||
ON clientmgmt_client_attr_value = clientmgmt_attr_value_id
|
||||
LEFT JOIN clientmgmt_attr_value_l11n
|
||||
ON clientmgmt_attr_value_id = clientmgmt_attr_value_l11n_value AND clientmgmt_attr_value_l11n_lang = \'' . $language . '\'
|
||||
WHERE
|
||||
billing_bill_performance_date >= \'' . $startComparison->format('Y-m-d') . '\'
|
||||
AND billing_bill_performance_date <= \'' . $endCurrent->format('Y-m-d') . '\'
|
||||
AND clientmgmt_attr_type_name IN (\'segment\', \'section\', \'client_group\', \'client_type\')
|
||||
GROUP BY
|
||||
clientmgmt_attr_type_name,
|
||||
clientmgmt_attr_type_l11n_title,
|
||||
clientmgmt_attr_value_id,
|
||||
clientmgmt_attr_value_l11n_title,
|
||||
YEAR(billing_bill_performance_date),
|
||||
MONTH(billing_bill_performance_date)
|
||||
ORDER BY
|
||||
YEAR(billing_bill_performance_date) ASC,
|
||||
MONTH(billing_bill_performance_date) ASC'
|
||||
);
|
||||
|
||||
$results = $query->execute()->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
$oldIndex = 1;
|
||||
$period = 1;
|
||||
|
||||
$mtdAClientAttribute = [];
|
||||
$mtdPYClientAttribute = [];
|
||||
|
||||
$ytdAClientAttribute = [];
|
||||
$ytdPYClientAttribute = [];
|
||||
|
||||
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) {
|
||||
$mtdPYClientAttribute[$result['clientmgmt_attr_type_name']] = $temp;
|
||||
} else {
|
||||
$mtdAClientAttribute[$result['clientmgmt_attr_type_name']] = $temp;
|
||||
}
|
||||
}
|
||||
|
||||
if ($monthIndex <= $endCurrentIndex) {
|
||||
if (!isset($ytdPYClientAttribute[$result['clientmgmt_attr_type_name']])) {
|
||||
$ytdPYClientAttribute[$result['clientmgmt_attr_type_name']] = [
|
||||
'net_sales' => 0,
|
||||
'net_profit' => 0,
|
||||
'value_l11n' => $result['clientmgmt_attr_value_l11n_title'],
|
||||
];
|
||||
|
||||
$ytdAClientAttribute[$result['clientmgmt_attr_type_name']] = [
|
||||
'net_sales' => 0,
|
||||
'net_profit' => 0,
|
||||
'value_l11n' => $result['clientmgmt_attr_value_l11n_title'],
|
||||
];
|
||||
}
|
||||
|
||||
if ($period === 1) {
|
||||
$ytdPYClientAttribute[$result['clientmgmt_attr_type_name']]['net_sales'] += $temp['net_sales'];
|
||||
$ytdPYClientAttribute[$result['clientmgmt_attr_type_name']]['net_profit'] += $temp['net_profit'];
|
||||
} else {
|
||||
$ytdAClientAttribute[$result['clientmgmt_attr_type_name']]['net_sales'] += $temp['net_sales'];
|
||||
$ytdAClientAttribute[$result['clientmgmt_attr_type_name']]['net_profit'] += $temp['net_profit'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
$mtdAClientAttribute,
|
||||
$mtdPYClientAttribute,
|
||||
$ytdAClientAttribute,
|
||||
$ytdPYClientAttribute,
|
||||
];
|
||||
}
|
||||
}
|
||||
193
Models/GeneralMapper.php
Normal file
193
Models/GeneralMapper.php
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 7.4
|
||||
*
|
||||
* @package Modules\SalesAnalysis\Models
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\SalesAnalysis\Models;
|
||||
|
||||
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
|
||||
use phpOMS\DataStorage\Database\Query\Builder;
|
||||
use phpOMS\Stdlib\Base\SmartDateTime;
|
||||
|
||||
/**
|
||||
* Permision state enum.
|
||||
*
|
||||
* @package Modules\SalesAnalysis\Models
|
||||
* @license OMS License 1.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class GeneralMapper extends DataMapperFactory
|
||||
{
|
||||
public static function monthlySalesProfit(
|
||||
\DateTime $startCurrent,
|
||||
\DateTime $endCurrent,
|
||||
\DateTime $startComparison,
|
||||
\DateTime $endComparison,
|
||||
int $businessStart = 1
|
||||
) {
|
||||
$endCurrentIndex = SmartDateTime::calculateMonthIndex((int) $endCurrent->format('m'), $businessStart);
|
||||
|
||||
$query = new Builder(self::$db);
|
||||
$query->raw(
|
||||
'SELECT
|
||||
YEAR(billing_bill_performance_date) as salesyear,
|
||||
MONTH(billing_bill_performance_date) as salesmonth,
|
||||
SUM(billing_bill_netsales) as netsales,
|
||||
SUM(billing_bill_netprofit) as netprofit
|
||||
FROM billing_bill
|
||||
WHERE
|
||||
billing_bill_performance_date >= \'' . $startComparison->format('Y-m-d') . '\'
|
||||
AND billing_bill_performance_date <= \'' . $endCurrent->format('Y-m-d') . '\'
|
||||
GROUP BY
|
||||
YEAR(billing_bill_performance_date),
|
||||
MONTH(billing_bill_performance_date)
|
||||
ORDER BY
|
||||
YEAR(billing_bill_performance_date) ASC,
|
||||
MONTH(billing_bill_performance_date) ASC'
|
||||
);
|
||||
|
||||
$results = $query->execute()->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
$oldIndex = 1;
|
||||
$period = 1;
|
||||
|
||||
$monthlySales = [];
|
||||
for ($i = 1; $i < 3; ++$i) {
|
||||
$monthlySales[$i] = \array_fill(0, 12, [
|
||||
'net_sales' => null,
|
||||
'net_profit' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
$mtdA = ['net_sales' => 0, 'net_profit' => 0];
|
||||
$mtdPY = ['net_sales' => 0, 'net_profit' => 0];
|
||||
|
||||
$ytdA = ['net_sales' => 0, 'net_profit' => 0];
|
||||
$ytdPY = ['net_sales' => 0, 'net_profit' => 0];
|
||||
|
||||
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
|
||||
$monthlySales[$period][$monthIndex - 1] = [
|
||||
'net_sales' => (int) $result['netsales'],
|
||||
'net_profit' => (int) $result['netprofit'],
|
||||
];
|
||||
|
||||
if ($monthIndex === $endCurrentIndex) {
|
||||
if ($period === 1) {
|
||||
$mtdPY = $monthlySales[$period][$monthIndex - 1];
|
||||
} else {
|
||||
$mtdA = $monthlySales[$period][$monthIndex - 1];
|
||||
}
|
||||
}
|
||||
|
||||
if ($monthIndex <= $endCurrentIndex) {
|
||||
if ($period === 1) {
|
||||
$ytdPY['net_sales'] += $monthlySales[$period][$monthIndex - 1]['net_sales'];
|
||||
$ytdPY['net_profit'] += $monthlySales[$period][$monthIndex - 1]['net_profit'];
|
||||
} else {
|
||||
$ytdA['net_sales'] += $monthlySales[$period][$monthIndex - 1]['net_sales'];
|
||||
$ytdA['net_profit'] += $monthlySales[$period][$monthIndex - 1]['net_profit'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
$mtdA, $mtdPY,
|
||||
$ytdA, $ytdPY,
|
||||
$monthlySales
|
||||
];
|
||||
}
|
||||
|
||||
public static function annualSalesProfit(
|
||||
SmartDateTime $historyStart,
|
||||
\DateTime $endCurrent,
|
||||
int $businessStart = 1
|
||||
) : array {
|
||||
|
||||
$query = new Builder(self::$db);
|
||||
$query->raw(
|
||||
'SELECT
|
||||
YEAR(billing_bill_performance_date) as salesyear,
|
||||
MONTH(billing_bill_performance_date) as salesmonth,
|
||||
SUM(billing_bill_netsales) as netsales,
|
||||
SUM(billing_bill_netprofit) as netprofit
|
||||
FROM billing_bill
|
||||
WHERE
|
||||
billing_bill_performance_date >= \'' . $historyStart->format('Y-m-d') . '\'
|
||||
AND billing_bill_performance_date <= \'' . $endCurrent->format('Y-m-d') . '\'
|
||||
GROUP BY
|
||||
YEAR(billing_bill_performance_date),
|
||||
MONTH(billing_bill_performance_date)
|
||||
ORDER BY
|
||||
YEAR(billing_bill_performance_date) ASC,
|
||||
MONTH(billing_bill_performance_date) ASC'
|
||||
);
|
||||
|
||||
$results = $query->execute()->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
$annualSales = [];
|
||||
for ($i = 1; $i < 11; ++$i) {
|
||||
$annualSales[$i] = [
|
||||
'net_sales' => null,
|
||||
'net_profit' => null,
|
||||
'year' => $historyStart->format('Y'),
|
||||
];
|
||||
|
||||
$historyStart->smartModify(1);
|
||||
}
|
||||
|
||||
$historyStart->smartModify(-10);
|
||||
|
||||
$oldIndex = 1;
|
||||
// @todo: this calculation doesn't consider the start of the fiscal year
|
||||
$period = ((((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;
|
||||
|
||||
// indexed according to the fiscal year
|
||||
$annualSales[$period]['net_sales'] ??= 0;
|
||||
$annualSales[$period]['net_profit'] ??= 0;
|
||||
|
||||
$annualSales[$period]['net_sales'] += (int) $result['netsales'];
|
||||
$annualSales[$period]['net_profit'] += (int) $result['netprofit'];
|
||||
}
|
||||
|
||||
return $annualSales;
|
||||
}
|
||||
}
|
||||
159
Models/ItemMapper.php
Normal file
159
Models/ItemMapper.php
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 7.4
|
||||
*
|
||||
* @package Modules\SalesAnalysis\Models
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\SalesAnalysis\Models;
|
||||
|
||||
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
|
||||
use phpOMS\DataStorage\Database\Query\Builder;
|
||||
use phpOMS\Stdlib\Base\SmartDateTime;
|
||||
|
||||
/**
|
||||
* Permision state enum.
|
||||
*
|
||||
* @package Modules\SalesAnalysis\Models
|
||||
* @license OMS License 1.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class ItemMapper extends DataMapperFactory
|
||||
{
|
||||
public static function mtdYtdItemAttribute(
|
||||
\DateTime $startCurrent,
|
||||
\DateTime $endCurrent,
|
||||
\DateTime $startComparison,
|
||||
\DateTime $endComparison,
|
||||
int $businessStart = 1,
|
||||
string $language = 'en'
|
||||
) {
|
||||
$endCurrentIndex = SmartDateTime::calculateMonthIndex((int) $endCurrent->format('m'), $businessStart);
|
||||
|
||||
// @todo: this query doesn't return clients that have not segment etc. defined.
|
||||
$query = new Builder(self::$db);
|
||||
$query->raw(
|
||||
'SELECT
|
||||
itemmgmt_attr_type_name,
|
||||
itemmgmt_attr_type_l11n_title,
|
||||
itemmgmt_attr_value_id,
|
||||
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) as netsales,
|
||||
SUM(billing_bill_element_total_netprofit) as netprofit
|
||||
FROM billing_bill
|
||||
LEFT JOIN billing_bill_element
|
||||
ON billing_bill_id = billing_bill_element_bill
|
||||
LEFT JOIN itemmgmt_item
|
||||
ON itemmgmt_item_id = billing_bill_element_item
|
||||
LEFT JOIN itemmgmt_item_attr
|
||||
ON itemmgmt_item_id = itemmgmt_item_attr_item
|
||||
LEFT JOIN itemmgmt_attr_type
|
||||
ON itemmgmt_item_attr_type = itemmgmt_attr_type_id
|
||||
LEFT JOIN itemmgmt_attr_type_l11n
|
||||
ON itemmgmt_attr_type_id = itemmgmt_attr_type_l11n_type AND itemmgmt_attr_type_l11n_lang = \'' . $language . '\'
|
||||
LEFT JOIN itemmgmt_attr_value
|
||||
ON itemmgmt_item_attr_value = itemmgmt_attr_value_id
|
||||
LEFT JOIN itemmgmt_attr_value_l11n
|
||||
ON itemmgmt_attr_value_id = itemmgmt_attr_value_l11n_value AND itemmgmt_attr_value_l11n_lang = \'' . $language . '\'
|
||||
WHERE
|
||||
billing_bill_performance_date >= \'' . $startComparison->format('Y-m-d') . '\'
|
||||
AND billing_bill_performance_date <= \'' . $endCurrent->format('Y-m-d') . '\'
|
||||
AND itemmgmt_attr_type_name IN (\'segment\', \'section\', \'product_group\', \'product_type\')
|
||||
GROUP BY
|
||||
itemmgmt_attr_type_name,
|
||||
itemmgmt_attr_type_l11n_title,
|
||||
itemmgmt_attr_value_id,
|
||||
itemmgmt_attr_value_l11n_title,
|
||||
YEAR(billing_bill_performance_date),
|
||||
MONTH(billing_bill_performance_date)
|
||||
ORDER BY
|
||||
YEAR(billing_bill_performance_date) ASC,
|
||||
MONTH(billing_bill_performance_date) ASC'
|
||||
);
|
||||
|
||||
$results = $query->execute()->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
$oldIndex = 1;
|
||||
$period = 1;
|
||||
|
||||
$mtdAItemAttribute = [];
|
||||
$mtdPYItemAttribute = [];
|
||||
|
||||
$ytdAItemAttribute = [];
|
||||
$ytdPYItemAttribute = [];
|
||||
|
||||
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) {
|
||||
$mtdPYItemAttribute[$result['itemmgmt_attr_type_name']] = $temp;
|
||||
} else {
|
||||
$mtdAItemAttribute[$result['itemmgmt_attr_type_name']] = $temp;
|
||||
}
|
||||
}
|
||||
|
||||
if ($monthIndex <= $endCurrentIndex) {
|
||||
if (!isset($ytdPYItemAttribute[$result['itemmgmt_attr_type_name']])) {
|
||||
$ytdPYItemAttribute[$result['itemmgmt_attr_type_name']] = [
|
||||
'net_sales' => 0,
|
||||
'net_profit' => 0,
|
||||
'value_l11n' => $result['itemmgmt_attr_value_l11n_title'],
|
||||
];
|
||||
|
||||
$ytdAItemAttribute[$result['itemmgmt_attr_type_name']] = [
|
||||
'net_sales' => 0,
|
||||
'net_profit' => 0,
|
||||
'value_l11n' => $result['itemmgmt_attr_value_l11n_title'],
|
||||
];
|
||||
}
|
||||
|
||||
if ($period === 1) {
|
||||
$ytdPYItemAttribute[$result['itemmgmt_attr_type_name']]['net_sales'] += $temp['net_sales'];
|
||||
$ytdPYItemAttribute[$result['itemmgmt_attr_type_name']]['net_profit'] += $temp['net_profit'];
|
||||
} else {
|
||||
$ytdAItemAttribute[$result['itemmgmt_attr_type_name']]['net_sales'] += $temp['net_sales'];
|
||||
$ytdAItemAttribute[$result['itemmgmt_attr_type_name']]['net_profit'] += $temp['net_profit'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
$mtdAItemAttribute,
|
||||
$mtdPYItemAttribute,
|
||||
$ytdAItemAttribute,
|
||||
$ytdPYItemAttribute,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
|
|
@ -21,10 +21,10 @@ use phpOMS\Stdlib\Base\Enum;
|
|||
*
|
||||
* @package Modules\SalesAnalysis\Models
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
abstract class PermissionState extends Enum
|
||||
abstract class PermissionCategory extends Enum
|
||||
{
|
||||
public const DASHBOARD = 1;
|
||||
}
|
||||
554
Models/RegionMapper.php
Normal file
554
Models/RegionMapper.php
Normal file
|
|
@ -0,0 +1,554 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 7.4
|
||||
*
|
||||
* @package Modules\SalesAnalysis\Models
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\SalesAnalysis\Models;
|
||||
|
||||
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
|
||||
use phpOMS\DataStorage\Database\Query\Builder;
|
||||
use phpOMS\Localization\ISO3166TwoEnum;
|
||||
use phpOMS\Stdlib\Base\SmartDateTime;
|
||||
|
||||
/**
|
||||
* Permision state enum.
|
||||
*
|
||||
* @package Modules\SalesAnalysis\Models
|
||||
* @license OMS License 1.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @todo: the periods are wrong if they are disjunct (e.g. A vs PPY)
|
||||
* solution: functions need a clear start-end time and then called twice for A vs PY comparison
|
||||
*/
|
||||
class RegionMapper extends DataMapperFactory
|
||||
{
|
||||
public static function monthlySalesProfit(
|
||||
\DateTime $start,
|
||||
\DateTime $end,
|
||||
int $businessStart = 1
|
||||
) {
|
||||
$endCurrentIndex = SmartDateTime::calculateMonthIndex((int) $end->format('m'), $businessStart);
|
||||
|
||||
$query = new Builder(self::$db);
|
||||
$query->raw(
|
||||
'SELECT
|
||||
address_country,
|
||||
YEAR(billing_bill_performance_date) as salesyear,
|
||||
MONTH(billing_bill_performance_date) as salesmonth,
|
||||
SUM(billing_bill_netsales) as netsales,
|
||||
SUM(billing_bill_netprofit) as netprofit
|
||||
FROM billing_bill
|
||||
LEFT JOIN clientmgmt_client
|
||||
ON clientmgmt_client_id = billing_bill_client
|
||||
LEFT JOIN address
|
||||
ON clientmgmt_client_address = address_id
|
||||
WHERE
|
||||
billing_bill_performance_date >= \'' . $start->format('Y-m-d') . '\'
|
||||
AND billing_bill_performance_date <= \'' . $end->format('Y-m-d') . '\'
|
||||
GROUP BY
|
||||
address_country,
|
||||
YEAR(billing_bill_performance_date),
|
||||
MONTH(billing_bill_performance_date)
|
||||
ORDER BY
|
||||
YEAR(billing_bill_performance_date) ASC,
|
||||
MONTH(billing_bill_performance_date) ASC,
|
||||
address_country'
|
||||
);
|
||||
|
||||
$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['address_country']])) {
|
||||
$monthlySales[$result['address_country']] = [];
|
||||
|
||||
$mtdA[$result['address_country']] = ['net_sales' => 0, 'net_profit' => 0];
|
||||
$mtdPY[$result['address_country']] = ['net_sales' => 0, 'net_profit' => 0];
|
||||
|
||||
$ytdA[$result['address_country']] = ['net_sales' => 0, 'net_profit' => 0];
|
||||
$ytdPY[$result['address_country']] = ['net_sales' => 0, 'net_profit' => 0];
|
||||
|
||||
for ($i = 1; $i < 3; ++$i) {
|
||||
$monthlySales[$result['address_country']][$i] = \array_fill(1, 12, [
|
||||
'net_sales' => null,
|
||||
'net_profit' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// indexed according to the fiscal year
|
||||
$monthlySales[$result['address_country']][$monthIndex] = [
|
||||
'net_sales' => (int) $result['netsales'],
|
||||
'net_profit' => (int) $result['netprofit'],
|
||||
];
|
||||
|
||||
if ($monthIndex === $endCurrentIndex) {
|
||||
$mtd[$result['address_country']] = $monthlySales[$result['address_country']][$monthIndex];
|
||||
}
|
||||
|
||||
if ($monthIndex <= $endCurrentIndex) {
|
||||
$ytd[$result['address_country']]['net_sales'] += $monthlySales[$result['address_country']][$monthIndex]['net_sales'];
|
||||
$ytd[$result['address_country']]['net_profit'] += $monthlySales[$result['address_country']][$monthIndex]['net_profit'];
|
||||
}
|
||||
}
|
||||
|
||||
return [$mtd, $ytd, $monthlySales];
|
||||
}
|
||||
|
||||
public static function annualCustomerCountry(
|
||||
SmartDateTime $historyStart,
|
||||
\DateTime $endCurrent,
|
||||
int $businessStart = 1
|
||||
) : array {
|
||||
$query = new Builder(self::$db);
|
||||
$query->raw(
|
||||
'SELECT
|
||||
address_country,
|
||||
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 clientmgmt_client
|
||||
ON clientmgmt_client_id = billing_bill_client
|
||||
LEFT JOIN address
|
||||
ON clientmgmt_client_address = address_id
|
||||
WHERE
|
||||
billing_bill_performance_date >= \'' . $historyStart->format('Y-m-d') . '\'
|
||||
AND billing_bill_performance_date <= \'' . $endCurrent->format('Y-m-d') . '\'
|
||||
GROUP BY
|
||||
address_country,
|
||||
YEAR(billing_bill_performance_date),
|
||||
MONTH(billing_bill_performance_date)
|
||||
ORDER BY
|
||||
YEAR(billing_bill_performance_date) ASC,
|
||||
MONTH(billing_bill_performance_date) ASC,
|
||||
address_country'
|
||||
);
|
||||
|
||||
$results = $query->execute()->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
$annualCustomer = [];
|
||||
|
||||
$oldIndex = 1;
|
||||
// @todo: this calculation doesn't consider the start of the fiscal year
|
||||
$period = ((((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['address_country']])) {
|
||||
for ($i = 1; $i < 11; ++$i) {
|
||||
$annualCustomer[$result['address_country']][$i] = [
|
||||
'client_count' => 0
|
||||
];
|
||||
|
||||
$historyStart->smartModify(1);
|
||||
}
|
||||
|
||||
$historyStart->smartModify(-10);
|
||||
}
|
||||
|
||||
// indexed according to the fiscal year
|
||||
$annualCustomer[$result['address_country']][$period]['client_count'] += (int) $result['client_count'];
|
||||
}
|
||||
|
||||
return $annualCustomer;
|
||||
}
|
||||
|
||||
public static function mtdYtdClientCountry(
|
||||
\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
|
||||
address_country,
|
||||
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 clientmgmt_client
|
||||
ON clientmgmt_client_id = billing_bill_client
|
||||
LEFT JOIN address
|
||||
ON clientmgmt_client_address = address_id
|
||||
WHERE
|
||||
billing_bill_performance_date >= \'' . $startComparison->format('Y-m-d') . '\'
|
||||
AND billing_bill_performance_date <= \'' . $endCurrent->format('Y-m-d') . '\'
|
||||
GROUP BY
|
||||
address_country,
|
||||
YEAR(billing_bill_performance_date),
|
||||
MONTH(billing_bill_performance_date)
|
||||
ORDER BY
|
||||
YEAR(billing_bill_performance_date) ASC,
|
||||
MONTH(billing_bill_performance_date) ASC,
|
||||
address_country 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['address_country']] = $temp;
|
||||
} else {
|
||||
$mtdAClientCountry[$result['address_country']] = $temp;
|
||||
}
|
||||
}
|
||||
|
||||
if ($monthIndex <= $endCurrentIndex) {
|
||||
if (!isset($ytdPYClientCountry[$result['address_country']])) {
|
||||
$ytdPYClientCountry[$result['address_country']] = [
|
||||
'client_count' => 0,
|
||||
];
|
||||
|
||||
$ytdAClientCountry[$result['address_country']] = [
|
||||
'client_count' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
if ($period === 1) {
|
||||
$ytdPYClientCountry[$result['address_country']]['client_count'] += $temp['client_count'];
|
||||
} else {
|
||||
$ytdAClientCountry[$result['address_country']]['client_count'] += $temp['client_count'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
$mtdPYClientCountry,
|
||||
$mtdAClientCountry,
|
||||
$ytdPYClientCountry,
|
||||
$ytdAClientCountry,
|
||||
];
|
||||
}
|
||||
|
||||
public static function annualSalesProfitCountry(
|
||||
SmartDateTime $historyStart,
|
||||
\DateTime $endCurrent,
|
||||
int $businessStart = 1
|
||||
) : array {
|
||||
$query = new Builder(self::$db);
|
||||
$query->raw(
|
||||
'SELECT
|
||||
address_country,
|
||||
YEAR(billing_bill_performance_date) as salesyear,
|
||||
MONTH(billing_bill_performance_date) as salesmonth,
|
||||
SUM(billing_bill_netsales) as netsales,
|
||||
SUM(billing_bill_netprofit) as netprofit
|
||||
FROM billing_bill
|
||||
LEFT JOIN clientmgmt_client
|
||||
ON clientmgmt_client_id = billing_bill_client
|
||||
LEFT JOIN address
|
||||
ON clientmgmt_client_address = address_id
|
||||
WHERE
|
||||
billing_bill_performance_date >= \'' . $historyStart->format('Y-m-d') . '\'
|
||||
AND billing_bill_performance_date <= \'' . $endCurrent->format('Y-m-d') . '\'
|
||||
GROUP BY
|
||||
address_country,
|
||||
YEAR(billing_bill_performance_date),
|
||||
MONTH(billing_bill_performance_date)
|
||||
ORDER BY
|
||||
YEAR(billing_bill_performance_date) ASC,
|
||||
MONTH(billing_bill_performance_date) ASC,
|
||||
address_country'
|
||||
);
|
||||
|
||||
$results = $query->execute()->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
$annualSales = [];
|
||||
|
||||
$oldIndex = 1;
|
||||
// @todo: this calculation doesn't consider the start of the fiscal year
|
||||
$period = ((((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($annualSales[$result['address_country']])) {
|
||||
for ($i = 1; $i < 11; ++$i) {
|
||||
$annualSales[$result['address_country']][$i] = [
|
||||
'net_sales' => 0,
|
||||
'net_profit' => 0,
|
||||
'year' => $historyStart->format('Y'),
|
||||
];
|
||||
|
||||
$historyStart->smartModify(1);
|
||||
}
|
||||
|
||||
$historyStart->smartModify(-10);
|
||||
}
|
||||
|
||||
// indexed according to the fiscal year
|
||||
$annualSales[$result['address_country']][$period]['net_sales'] += (int) $result['netsales'];
|
||||
$annualSales[$result['address_country']][$period]['net_profit'] += (int) $result['netprofit'];
|
||||
}
|
||||
|
||||
return $annualSales;
|
||||
}
|
||||
|
||||
public static function mtdYtdCountry(
|
||||
\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
|
||||
address_country,
|
||||
YEAR(billing_bill_performance_date) as salesyear,
|
||||
MONTH(billing_bill_performance_date) as salesmonth,
|
||||
SUM(billing_bill_netsales) as netsales,
|
||||
SUM(billing_bill_netprofit) as netprofit
|
||||
FROM billing_bill
|
||||
LEFT JOIN clientmgmt_client
|
||||
ON clientmgmt_client_id = billing_bill_client
|
||||
LEFT JOIN address
|
||||
ON clientmgmt_client_address = address_id
|
||||
WHERE
|
||||
billing_bill_performance_date >= \'' . $startComparison->format('Y-m-d') . '\'
|
||||
AND billing_bill_performance_date <= \'' . $endCurrent->format('Y-m-d') . '\'
|
||||
GROUP BY
|
||||
address_country,
|
||||
YEAR(billing_bill_performance_date),
|
||||
MONTH(billing_bill_performance_date)
|
||||
ORDER BY
|
||||
YEAR(billing_bill_performance_date) ASC,
|
||||
MONTH(billing_bill_performance_date) ASC,
|
||||
address_country 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['address_country']] = $temp;
|
||||
} else {
|
||||
$mtdAClientCountry[$result['address_country']] = $temp;
|
||||
}
|
||||
}
|
||||
|
||||
if ($monthIndex <= $endCurrentIndex) {
|
||||
if (!isset($ytdPYClientCountry[$result['address_country']])) {
|
||||
$ytdPYClientCountry[$result['address_country']] = [
|
||||
'net_sales' => 0,
|
||||
'net_profit' => 0,
|
||||
];
|
||||
|
||||
$ytdAClientCountry[$result['address_country']] = [
|
||||
'net_sales' => 0,
|
||||
'net_profit' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
if ($period === 1) {
|
||||
$ytdPYClientCountry[$result['address_country']]['net_sales'] += $temp['net_sales'];
|
||||
$ytdPYClientCountry[$result['address_country']]['net_profit'] += $temp['net_profit'];
|
||||
} else {
|
||||
$ytdAClientCountry[$result['address_country']]['net_sales'] += $temp['net_sales'];
|
||||
$ytdAClientCountry[$result['address_country']]['net_profit'] += $temp['net_profit'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
$mtdPYClientCountry,
|
||||
$mtdAClientCountry,
|
||||
$ytdPYClientCountry,
|
||||
$ytdAClientCountry,
|
||||
];
|
||||
}
|
||||
|
||||
public static function countryToRegion(array $countries, array $region, array $columns) : array
|
||||
{
|
||||
$tempStruct = [];
|
||||
foreach ($columns as $column) {
|
||||
$tempStruct[$column] = 0;
|
||||
}
|
||||
|
||||
$regions = ['Other' => $tempStruct];
|
||||
|
||||
foreach ($region as $r) {
|
||||
$definitions[$r] = ($temp = ISO3166TwoEnum::getRegion($r)) === [] ? [$r] : $temp;
|
||||
$regions[$r] = $tempStruct;
|
||||
}
|
||||
|
||||
foreach ($countries as $country => $data) {
|
||||
$found = false;
|
||||
foreach ($definitions as $r => $c) {
|
||||
if (\in_array($country, $c)) {
|
||||
foreach ($columns as $column) {
|
||||
$regions[$r][$column] += $data[$column];
|
||||
}
|
||||
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
foreach ($columns as $column) {
|
||||
$regions['Other'][$column] += $data[$column];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $regions;
|
||||
}
|
||||
|
||||
public static function countryIntervalToRegion(array $countries, array $region, array $columns) : array
|
||||
{
|
||||
$count = \count(\reset($countries));
|
||||
|
||||
$tempStruct = [];
|
||||
foreach ($columns as $column) {
|
||||
$tempStruct[$column] = 0;
|
||||
}
|
||||
|
||||
$regions = [
|
||||
'Other' => \array_fill(1, $count, $tempStruct),
|
||||
];
|
||||
|
||||
foreach ($region as $r) {
|
||||
$definitions[$r] = ($temp = ISO3166TwoEnum::getRegion($r)) === [] ? [$r] : $temp;
|
||||
$regions[$r] = \array_fill(1, $count, $tempStruct);
|
||||
}
|
||||
|
||||
foreach ($countries as $country => $data) {
|
||||
$found = false;
|
||||
foreach ($definitions as $r => $c) {
|
||||
if (\in_array($country, $c)) {
|
||||
foreach ($data as $idx => $value) {
|
||||
foreach ($columns as $column) {
|
||||
$regions[$r][$idx][$column] += $value[$column];
|
||||
}
|
||||
}
|
||||
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
foreach ($data as $idx => $value) {
|
||||
foreach ($columns as $column) {
|
||||
$regions['Other'][$idx][$column] += $value[$column];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $regions;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,13 +8,13 @@
|
|||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
return ['Navigation' => [
|
||||
'Analysis' => 'Analysis',
|
||||
'Customers' => 'Customers',
|
||||
'Dashboard' => 'Dashboard',
|
||||
'Database' => 'Database',
|
||||
'Invoices' => 'Invoices',
|
||||
'Marketing' => 'Marketing',
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 7.4
|
||||
*
|
||||
* @package Modules\SalesAnalysis
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
$MODLANG[1] = [
|
||||
];
|
||||
|
|
@ -8,13 +8,65 @@
|
|||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
return ['SalesAnalysis' => [
|
||||
'ItemAttribute' => 'Item Attribute',
|
||||
'ClientAttribute' => 'Client Attribute',
|
||||
'SalesRegion' => 'Sales Region',
|
||||
'Customers' => 'Customers',
|
||||
'Months' => 'Months',
|
||||
'Total' => 'Total',
|
||||
'Year' => 'Year',
|
||||
'Month' => 'Month',
|
||||
'A' => 'A',
|
||||
'PY' => 'PY',
|
||||
'B' => 'B',
|
||||
'All' => 'All',
|
||||
'New' => 'New',
|
||||
'Lost' => 'Lost',
|
||||
'Product' => 'Product',
|
||||
'Continent' => 'Continent',
|
||||
'SalesPY' => 'Sales PY',
|
||||
'SalesB' => 'Sales B',
|
||||
'SalesA' => 'Sales A',
|
||||
'ProfitPY' => 'Profit PY',
|
||||
'ProfitB' => 'Profit B',
|
||||
'ProfitA' => 'Profit A',
|
||||
'DiffPY' => 'Δ PY',
|
||||
'DiffB' => 'Δ B',
|
||||
'Data' => 'Data',
|
||||
'Actual' => 'Actual',
|
||||
'Budget' => 'Budget',
|
||||
'Segment' => 'Segment',
|
||||
'Section' => 'Section',
|
||||
'Group' => 'Group',
|
||||
'Region' => 'Region',
|
||||
'Filter' => 'Filter',
|
||||
'Country' => 'Country',
|
||||
'Category' => 'Category',
|
||||
'General' => 'General',
|
||||
'MTD' => 'MTD',
|
||||
'Other' => 'Other',
|
||||
'YTD' => 'YTD',
|
||||
'GrossProfit' => 'Gross Profit',
|
||||
'SalesProfit' => 'Sales / Profit',
|
||||
'monthly' => 'monthly',
|
||||
'annually' => 'annually',
|
||||
'Profit' => 'Profit',
|
||||
'Start' => 'Start',
|
||||
'End' => 'End',
|
||||
'Sales' => 'Sales',
|
||||
'Client' => 'Client',
|
||||
'Clients' => 'Clients',
|
||||
'Item' => 'Item',
|
||||
'World' => 'World',
|
||||
'Africa' => 'Africa',
|
||||
'America' => 'America',
|
||||
'DomesticExport' => 'Domestic & Export',
|
||||
'Oceania' => 'Oceania',
|
||||
'Analysis' => 'Analysis',
|
||||
'Asia' => 'Asia',
|
||||
'Change' => 'Change',
|
||||
|
|
@ -47,6 +99,8 @@ return ['SalesAnalysis' => [
|
|||
'Marketing' => 'Marketing',
|
||||
'Misc' => 'Misc',
|
||||
'Month' => 'Month',
|
||||
'Current' => 'Current',
|
||||
'Analyze' => 'Analyze',
|
||||
'Overview' => 'Overview',
|
||||
'Products' => 'Products',
|
||||
'Regions' => 'Regions',
|
||||
|
|
|
|||
23
Theme/Backend/analysis-bill.tpl.php
Executable file
23
Theme/Backend/analysis-bill.tpl.php
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.1
|
||||
*
|
||||
* @package Modules\SalesAnalysis
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
/* @todo: single month/quarter/fiscal year/calendar year */
|
||||
/* @todo: time range (<= 12 month = monthly view; else annual view/comparison) */
|
||||
|
||||
/**
|
||||
* @var \phpOMS\Views\View $this
|
||||
*/
|
||||
echo $this->data['nav']->render();
|
||||
?>
|
||||
<img height="100%" src="Web/Backend/img/under_construction.svg">
|
||||
52
Theme/Backend/analysis-client.tpl.php
Executable file
52
Theme/Backend/analysis-client.tpl.php
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.1
|
||||
*
|
||||
* @package Modules\SalesAnalysis
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
use phpOMS\Localization\Money;
|
||||
use phpOMS\Utils\RnG\Name;
|
||||
|
||||
/* @todo: single month/quarter/fiscal year/calendar year */
|
||||
/* @todo: time range (<= 12 month = monthly view; else annual view/comparison) */
|
||||
|
||||
/**
|
||||
* @var \phpOMS\Views\View $this
|
||||
*/
|
||||
echo $this->data['nav']->render();
|
||||
?>
|
||||
|
||||
<div class="tabview tab-2">
|
||||
<div class="box">
|
||||
<ul class="tab-links">
|
||||
<li><label for="c-tab-1"><?= $this->getHtml('All'); ?></label></li>
|
||||
<li><label for="c-tab-2"><?= $this->getHtml('New'); ?></label></li>
|
||||
<li><label for="c-tab-3"><?= $this->getHtml('Lost'); ?></label></li>
|
||||
<!--<li><label for="c-tab-1"><?= $this->getHtml('Filter'); ?></label></li>-->
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<input type="radio" id="c-tab-1" name="tabular-2"<?= $this->request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<img height="100%" src="Web/Backend/img/under_construction.svg">
|
||||
</div>
|
||||
|
||||
<input type="radio" id="c-tab-2" name="tabular-2"<?= $this->request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<img height="100%" src="Web/Backend/img/under_construction.svg">
|
||||
</div>
|
||||
|
||||
<input type="radio" id="c-tab-3" name="tabular-2"<?= $this->request->uri->fragment === 'c-tab-3' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<img height="100%" src="Web/Backend/img/under_construction.svg">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
216
Theme/Backend/analysis-item.tpl.php
Executable file
216
Theme/Backend/analysis-item.tpl.php
Executable file
|
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.1
|
||||
*
|
||||
* @package Modules\SalesAnalysis
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @var \phpOMS\Views\View $this
|
||||
*/
|
||||
|
||||
echo $this->data['nav']->render();
|
||||
?>
|
||||
<div class="tabview tab-2">
|
||||
<div class="box">
|
||||
<ul class="tab-links">
|
||||
<li><label for="c-tab-1"><?= $this->getHtml('Segment'); ?></label></li>
|
||||
<li><label for="c-tab-2"><?= $this->getHtml('Section'); ?></label></li>
|
||||
<li><label for="c-tab-3"><?= $this->getHtml('Group'); ?></label></li>
|
||||
<li><label for="c-tab-4"><?= $this->getHtml('Type'); ?></label></li>
|
||||
<!--<li><label for="c-tab-5"><?= $this->getHtml('Filter'); ?></label></li>-->
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<input type="radio" id="c-tab-1" name="tabular-2"<?= $this->request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<section class="portlet">
|
||||
<div class="portlet-head"><?= $this->getHtml('Segment'); ?></div>
|
||||
<div class="slider">
|
||||
<table class="default">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><?= $this->getHtml('Category'); ?>
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ($this->data['ytdAItemAttribute'] as $type => $values) :
|
||||
if ($type !== 'segment') {
|
||||
continue;
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td><?= $this->printHtml($this->data['ytdPYItemAttribute'][$type]['value_l11n']); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="radio" id="c-tab-2" name="tabular-2"<?= $this->request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<section class="portlet">
|
||||
<div class="portlet-head"><?= $this->getHtml('Section'); ?></div>
|
||||
<div class="slider">
|
||||
<table class="default">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><?= $this->getHtml('Category'); ?>
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ($this->data['ytdAItemAttribute'] as $type => $values) :
|
||||
if ($type !== 'section') {
|
||||
continue;
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td><?= $this->printHtml($this->data['ytdPYItemAttribute'][$type]['value_l11n']); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="radio" id="c-tab-3" name="tabular-2"<?= $this->request->uri->fragment === 'c-tab-3' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<section class="portlet">
|
||||
<div class="portlet-head"><?= $this->getHtml('Group'); ?></div>
|
||||
<div class="slider">
|
||||
<table class="default">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><?= $this->getHtml('Category'); ?>
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ($this->data['ytdAItemAttribute'] as $type => $values) :
|
||||
if ($type !== 'product_group') {
|
||||
continue;
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td><?= $this->printHtml($this->data['ytdPYItemAttribute'][$type]['value_l11n']); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="radio" id="c-tab-4" name="tabular-2"<?= $this->request->uri->fragment === 'c-tab-4' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<section class="portlet">
|
||||
<div class="portlet-head"><?= $this->getHtml('Type'); ?></div>
|
||||
<div class="slider">
|
||||
<table class="default">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><?= $this->getHtml('Category'); ?>
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<tbody>
|
||||
<?php
|
||||
foreach ($this->data['ytdAItemAttribute'] as $type => $values) :
|
||||
if ($type !== 'product_type') {
|
||||
continue;
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td><?= $this->printHtml($this->data['ytdPYItemAttribute'][$type]['value_l11n']); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,190 +1,530 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 7.4
|
||||
* PHP Version 8.1
|
||||
*
|
||||
* @package Modules\SalesAnalysis
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
use phpOMS\Localization\ISO3166NameEnum;
|
||||
use phpOMS\Localization\Money;
|
||||
use phpOMS\Uri\UriFactory;
|
||||
|
||||
/**
|
||||
* @var \phpOMS\Views\View $this
|
||||
*/
|
||||
echo $this->data['nav']->render();
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-lg-4">
|
||||
<section class="portlet">
|
||||
<form id="sales-dashboard-analysis" action="<?= UriFactory::build('{/backend}sales/analysis'); ?>" method="get">
|
||||
<div class="portlet-body">
|
||||
<div><?= $this->getHtml('Current'); ?></div>
|
||||
<div class="form-group">
|
||||
<div class="input-control">
|
||||
<label for="iStartCurrent"><?= $this->getHtml('Start'); ?></label>
|
||||
<input id="iStartCurrent" name="startcurrent" type="date" value="<?= $this->data['startCurrent']->format('Y-m-d'); ?>">
|
||||
</div>
|
||||
|
||||
echo $this->getData('nav')->render(); ?>
|
||||
<div class="input-control">
|
||||
<label for="iEndCurrent"><?= $this->getHtml('End'); ?></label>
|
||||
<input id="iEndCurrent" name="endcurrent" type="date" value="<?= $this->data['endCurrent']->format('Y-m-d'); ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box w-100">
|
||||
<div class="tabview tab-2">
|
||||
<ul class="tab-links">
|
||||
<li><label for="c-tab2-1"><?= $this->getHtml('Overview') ?></label>
|
||||
<li><label for="c-tab2-2"><?= $this->getHtml('Month') ?></label>
|
||||
<li><label for="c-tab2-3"><?= $this->getHtml('Year') ?></label>
|
||||
<li><label for="c-tab2-4"><?= $this->getHtml('Top10') ?></label>
|
||||
<li><label for="c-tab2-5"><?= $this->getHtml('Charts') ?></label>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<input type="radio" id="c-tab2-1" name="tabular-2" checked>
|
||||
<div class="tab">
|
||||
<section class="box wf-100 floatLeft">
|
||||
<table class="default">
|
||||
<caption><?= $this->getHtml('Overview'); ?><i class="fa fa-download floatRight download btn"></i></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<td><?= $this->getHtml('Type') ?>
|
||||
<td><?= $this->getHtml('LastMonth') ?>
|
||||
<td><?= $this->getHtml('CurrentMonth') ?>
|
||||
<td><?= $this->getHtml('Change') ?>
|
||||
<td><?= $this->getHtml('LastYear') ?>
|
||||
<td><?= $this->getHtml('CurrentYear') ?>
|
||||
<td><?= $this->getHtml('Change') ?>
|
||||
<td><?= $this->getHtml('LastYearAcc') ?>
|
||||
<td><?= $this->getHtml('CurrentYearAcc') ?>
|
||||
<td><?= $this->getHtml('Change') ?>
|
||||
<td><?= $this->getHtml('LastYear') ?>
|
||||
<td><?= $this->getHtml('Forecast') ?>
|
||||
<td><?= $this->getHtml('Change') ?>
|
||||
<tbody>
|
||||
<tr><th><?= $this->getHtml('Domestic') ?><td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th><?= $this->getHtml('Export') ?><td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th><?= $this->getHtml('Developed') ?><td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th><?= $this->getHtml('Undeveloped') ?><td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th><?= $this->getHtml('Europe') ?><td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th><?= $this->getHtml('America') ?><td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th><?= $this->getHtml('Asia') ?><td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th><?= $this->getHtml('Africa') ?><td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th><?= $this->getHtml('Total') ?><td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
</table>
|
||||
</section>
|
||||
<div><?= $this->getHtml('Comparison'); ?></div>
|
||||
|
||||
<section class="box wf-100 floatLeft">
|
||||
<div class="form-group">
|
||||
<div class="input-control">
|
||||
<label for="iStartComparison"><?= $this->getHtml('Start'); ?></label>
|
||||
<input id="iStartComparison" name="startcomparison" type="date" value="<?= $this->data['startComparison']->format('Y-m-d'); ?>">
|
||||
</div>
|
||||
|
||||
<div class="input-control">
|
||||
<label for="iEndComparison"><?= $this->getHtml('End'); ?></label>
|
||||
<input id="iEndComparison" name="endcomparison" type="date" value="<?= $this->data['endComparison']->format('Y-m-d'); ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="portlet-foot">
|
||||
<input id="iSubmitGeneral" name="submitGeneral" type="submit" value="<?= $this->getHtml('Analyze'); ?>">
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-lg-4">
|
||||
<section class="portlet highlight-3">
|
||||
<div class="portlet-head"><?= $this->getHtml('Actual'); ?></div>
|
||||
<div class="portlet-body">
|
||||
<div class="form-group">
|
||||
<div><?= $this->getHtml('Sales'); ?> (<?= $this->getHtml('MTD'); ?>):</div>
|
||||
<div> <?= \sprintf('%+.2f', $this->data['mtdPY']['net_sales'] == 0 ? 0 : $this->data['mtdA']['net_sales'] * 100 / $this->data['mtdPY']['net_sales'] - 100); ?> %</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div><?= $this->getHtml('Sales'); ?> (<?= $this->getHtml('YTD'); ?>):</div>
|
||||
<div> <?= \sprintf('%+.2f', $this->data['ytdPY']['net_sales'] == 0 ? 0 : $this->data['ytdA']['net_sales'] * 100 / $this->data['ytdPY']['net_sales'] - 100); ?> %</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div><?= $this->getHtml('GrossProfit'); ?> (<?= $this->getHtml('MTD'); ?>):</div>
|
||||
<div> <?= \sprintf('%+.2f', $this->data['mtdPY']['net_profit'] == 0 ? 0 : $this->data['mtdA']['net_profit'] * 100 / $this->data['mtdPY']['net_profit'] - 100); ?> %</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div><?= $this->getHtml('GrossProfit'); ?> (<?= $this->getHtml('YTD'); ?>):</div>
|
||||
<div> <?= \sprintf('%+.2f', $this->data['ytdPY']['net_profit'] == 0 ? 0 : $this->data['ytdA']['net_profit'] * 100 / $this->data['ytdPY']['net_profit'] - 100); ?> %</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-lg-6">
|
||||
<section class="portlet">
|
||||
<div class="portlet-head">
|
||||
<?= $this->getHtml('SalesProfit'); ?> (<?= $this->getHtml('monthly'); ?>)
|
||||
</div>
|
||||
<?php $sales = $this->data['monthlySales']; ?>
|
||||
<div class="portlet-body">
|
||||
<canvas id="sales-profit-monthly" data-chart='{
|
||||
"type": "bar",
|
||||
"data": {
|
||||
"labels": [
|
||||
<?php
|
||||
$temp = [];
|
||||
for ($i = 1; $i < 13; ++$i) {
|
||||
$temp[] = \sprintf('"%02d"', $i);
|
||||
}
|
||||
echo \implode(',', $temp);
|
||||
?>
|
||||
],
|
||||
"datasets": [
|
||||
{
|
||||
"label": "<?= $this->getHtml('Profit'); ?> PY",
|
||||
"type": "line",
|
||||
"data": [
|
||||
<?php
|
||||
$temp = [];
|
||||
for ($i = 0; $i < 12; ++$i) {
|
||||
if (!isset($sales[1][$i]['net_sales']) || !isset($sales[1][$i]['net_profit'])) {
|
||||
$temp[] = 'null';
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$temp[] = $sales[1][$i]['net_sales'] == 0
|
||||
? 0
|
||||
: $sales[1][$i]['net_profit'] * 100 / $sales[1][$i]['net_sales'];
|
||||
}
|
||||
echo \implode(',', $temp);
|
||||
?>
|
||||
],
|
||||
"yAxisID": "y1",
|
||||
"fill": false,
|
||||
"tension": 0.0,
|
||||
"borderColor": "rgb(166, 193, 178)",
|
||||
"backgroundColor": "rgb(166, 193, 178)"
|
||||
},
|
||||
{
|
||||
"label": "<?= $this->getHtml('Profit'); ?> A",
|
||||
"type": "line",
|
||||
"data": [
|
||||
<?php
|
||||
$temp = [];
|
||||
for ($i = 0; $i < 12; ++$i) {
|
||||
if (!isset($sales[2][$i]['net_sales']) || !isset($sales[2][$i]['net_profit'])) {
|
||||
$temp[] = 'null';
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$temp[] = $sales[2][$i]['net_sales'] == 0
|
||||
? 0
|
||||
: $sales[2][$i]['net_profit'] * 100 / $sales[2][$i]['net_sales'];
|
||||
}
|
||||
echo \implode(',', $temp);
|
||||
?>
|
||||
],
|
||||
"yAxisID": "y1",
|
||||
"fill": false,
|
||||
"tension": 0.0,
|
||||
"borderColor": "rgb(46, 204, 113)",
|
||||
"backgroundColor": "rgb(46, 204, 113)"
|
||||
},
|
||||
{
|
||||
"label": "<?= $this->getHtml('Sales'); ?> PY",
|
||||
"type": "bar",
|
||||
"data": [
|
||||
<?php
|
||||
$temp = [];
|
||||
for ($i = 0; $i < 12; ++$i) {
|
||||
$temp[] = ($sales[1][$i]['net_sales'] ?? 0) / 10000;
|
||||
}
|
||||
echo \implode(',', $temp);
|
||||
?>
|
||||
],
|
||||
"yAxisID": "y",
|
||||
"fill": false,
|
||||
"tension": 0.0,
|
||||
"backgroundColor": "rgb(177, 195, 206)"
|
||||
},
|
||||
{
|
||||
"label": "<?= $this->getHtml('Sales'); ?> A",
|
||||
"type": "bar",
|
||||
"data": [
|
||||
<?php
|
||||
$temp = [];
|
||||
for ($i = 0; $i < 12; ++$i) {
|
||||
$temp[] = ($sales[2][$i]['net_sales'] ?? 0) / 10000;
|
||||
}
|
||||
echo \implode(',', $temp);
|
||||
?>
|
||||
],
|
||||
"yAxisID": "y",
|
||||
"fill": false,
|
||||
"tension": 0.0,
|
||||
"backgroundColor": "rgb(54, 162, 235)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"responsive": true,
|
||||
"scales": {
|
||||
"x": {
|
||||
"title": {
|
||||
"display": true,
|
||||
"text": "<?= $this->getHtml('Months'); ?>"
|
||||
}
|
||||
},
|
||||
"y": {
|
||||
"title": {
|
||||
"display": true,
|
||||
"text": "<?= $this->getHtml('Sales'); ?>"
|
||||
},
|
||||
"display": true,
|
||||
"position": "left"
|
||||
},
|
||||
"y1": {
|
||||
"title": {
|
||||
"display": true,
|
||||
"text": "<?= $this->getHtml('Profit'); ?> %"
|
||||
},
|
||||
"display": true,
|
||||
"position": "right",
|
||||
"scaleLabel": {
|
||||
"display": true,
|
||||
"labelString": "<?= $this->getHtml('Profit'); ?>"
|
||||
},
|
||||
"grid": {
|
||||
"drawOnChartArea": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}'></canvas>
|
||||
<div class="more-container">
|
||||
<input id="more-customer-sales" type="checkbox" name="more-container">
|
||||
<label for="more-customer-sales">
|
||||
<span><?= $this->getHtml('Data'); ?></span>
|
||||
<i class="fa fa-chevron-right expand"></i>
|
||||
</label>
|
||||
<div class="slider">
|
||||
<table class="default">
|
||||
<caption><?= $this->getHtml('Misc'); ?><i class="fa fa-download floatRight download btn"></i></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<td><?= $this->getHtml('Type') ?>
|
||||
<td><?= $this->getHtml('LastYear') ?>
|
||||
<td><?= $this->getHtml('CurrentYear') ?>
|
||||
<td><?= $this->getHtml('LastMonth') ?>
|
||||
<td><?= $this->getHtml('CurrentMonth') ?>
|
||||
<td><?= $this->getHtml('Yesterday') ?>
|
||||
<td><?= $this->getHtml('Today') ?>
|
||||
<td><?= $this->getHtml('Month'); ?>
|
||||
<td><?= $this->getHtml('SalesPY'); ?>
|
||||
<td><?= $this->getHtml('SalesA'); ?>
|
||||
<td><?= $this->getHtml('ProfitPY'); ?>
|
||||
<td><?= $this->getHtml('ProfitA'); ?>
|
||||
<tbody>
|
||||
<tr><th><?= $this->getHtml('Customers') ?><td><td><td><td><td><td>
|
||||
<tr><th><?= $this->getHtml('Invoices') ?><td><td><td><td><td><td>
|
||||
<?php
|
||||
$sum1 = 0;
|
||||
$sum2 = 0;
|
||||
$sum3 = 0;
|
||||
$sum4 = 0;
|
||||
for ($i = 0; $i < 12; ++$i) :
|
||||
$sum1 += (int) ($sales[1][$i]['net_sales'] ?? 0);
|
||||
$sum2 += (int) ($sales[2][$i]['net_sales'] ?? 0);
|
||||
$sum3 += (int) ($sales[1][$i]['net_profit'] ?? 0);
|
||||
$sum4 += (int) ($sales[2][$i]['net_profit'] ?? 0);
|
||||
?>
|
||||
<tr>
|
||||
<td><?= \sprintf('%02d', $i + 1); ?>
|
||||
<td><?= $this->getCurrency((int) ($sales[1][$i]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($sales[2][$i]['net_sales'] ?? 0)); ?>
|
||||
<td><?= \sprintf('%.2f', ($sales[1][$i]['net_sales'] ?? 0) == 0 ? 0 : $sales[1][$i]['net_profit'] * 100 / $sales[1][$i]['net_sales']); ?> %
|
||||
<td><?= \sprintf('%.2f', ($sales[2][$i]['net_sales'] ?? 0) == 0 ? 0 : $sales[2][$i]['net_profit'] * 100 / $sales[2][$i]['net_sales']); ?> %
|
||||
<?php endfor; ?>
|
||||
<tr>
|
||||
<td><?= $this->getHtml('Total'); ?>
|
||||
<td><?= $this->getCurrency($sum1); ?>
|
||||
<td><?= $this->getCurrency($sum2); ?>
|
||||
<td><?= \sprintf('%.2f', $sum3 == 0 ? 0 : $sum1 / $sum3); ?> %
|
||||
<td><?= \sprintf('%.2f', $sum3 == 0 ? 0 : $sum2 / $sum4); ?> %
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<input type="radio" id="c-tab2-2" name="tabular-2">
|
||||
<div class="tab">
|
||||
<section class="box wf-100 floatLeft">
|
||||
|
||||
<div class="col-xs-12 col-lg-6">
|
||||
<section class="portlet">
|
||||
<div class="portlet-head">
|
||||
<?= $this->getHtml('SalesProfit'); ?> (<?= $this->getHtml('annually'); ?>)
|
||||
</div>
|
||||
<?php $sales = $this->data['annualSales']; ?>
|
||||
<div class="portlet-body">
|
||||
<canvas id="sales-profit-annually" data-chart='{
|
||||
"type": "bar",
|
||||
"data": {
|
||||
"labels": [
|
||||
<?php
|
||||
$temp = [];
|
||||
for ($i = 1; $i < 11; ++$i) {
|
||||
$temp[] = $sales[$i]['year'];
|
||||
}
|
||||
echo \implode(',', $temp);
|
||||
?>
|
||||
],
|
||||
"datasets": [
|
||||
{
|
||||
"label": "<?= $this->getHtml('Profit'); ?>",
|
||||
"type": "line",
|
||||
"data": [
|
||||
<?php
|
||||
$temp = [];
|
||||
for ($i = 1; $i < 11; ++$i) {
|
||||
if ($sales[$i]['net_sales'] === null || $sales[$i]['net_profit'] === null) {
|
||||
$temp[] = 'null';
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$temp[] = $sales[$i]['net_sales'] == 0
|
||||
? 0
|
||||
: $sales[$i]['net_profit'] * 100 / $sales[$i]['net_sales'];
|
||||
}
|
||||
echo \implode(',', $temp);
|
||||
?>
|
||||
],
|
||||
"yAxisID": "y1",
|
||||
"fill": false,
|
||||
"tension": 0.0,
|
||||
"borderColor": "rgb(46, 204, 113)",
|
||||
"backgroundColor": "rgb(46, 204, 113)"
|
||||
},
|
||||
{
|
||||
"label": "<?= $this->getHtml('Sales'); ?>",
|
||||
"type": "bar",
|
||||
"data": [
|
||||
<?php
|
||||
$temp = [];
|
||||
for ($i = 1; $i < 11; ++$i) {
|
||||
$temp[] = $sales[$i]['net_sales'] / 10000;
|
||||
}
|
||||
echo \implode(',', $temp);
|
||||
?>
|
||||
],
|
||||
"yAxisID": "y",
|
||||
"fill": false,
|
||||
"tension": 0.0,
|
||||
"backgroundColor": "rgb(54, 162, 235)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"responsive": true,
|
||||
"scales": {
|
||||
"x": {
|
||||
"title": {
|
||||
"display": true,
|
||||
"text": "<?= $this->getHtml('Months'); ?>"
|
||||
}
|
||||
},
|
||||
"y": {
|
||||
"title": {
|
||||
"display": true,
|
||||
"text": "<?= $this->getHtml('Sales'); ?>"
|
||||
},
|
||||
"display": true,
|
||||
"position": "left"
|
||||
},
|
||||
"y1": {
|
||||
"title": {
|
||||
"display": true,
|
||||
"text": "<?= $this->getHtml('Profit'); ?> %"
|
||||
},
|
||||
"display": true,
|
||||
"position": "right",
|
||||
"scaleLabel": {
|
||||
"display": true,
|
||||
"labelString": "<?= $this->getHtml('Profit'); ?>"
|
||||
},
|
||||
"grid": {
|
||||
"drawOnChartArea": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}'></canvas>
|
||||
<div class="more-container">
|
||||
<input id="more-customer-sales-annual" type="checkbox" name="more-container">
|
||||
<label for="more-customer-sales-annual">
|
||||
<span><?= $this->getHtml('Data'); ?></span>
|
||||
<i class="fa fa-chevron-right expand"></i>
|
||||
</label>
|
||||
<div class="slider">
|
||||
<table class="default">
|
||||
<caption><?= $this->getHtml('Month'); ?><i class="fa fa-download floatRight download btn"></i></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<td><?= $this->getHtml('Day') ?>
|
||||
<td><?= $this->getHtml('Day') ?>
|
||||
<td><?= $this->getHtml('LastMonth') ?>
|
||||
<td><?= $this->getHtml('CurrentMonth') ?>
|
||||
<td><?= $this->getHtml('Change') ?>
|
||||
<td><?= $this->getHtml('ChangeAcc') ?>
|
||||
<td><?= $this->getHtml('Year'); ?>
|
||||
<td><?= $this->getHtml('Sales'); ?>
|
||||
<td><?= $this->getHtml('Profit'); ?>
|
||||
<tbody>
|
||||
<tr><td><td><td><td><td><td>
|
||||
<?php
|
||||
foreach ($sales as $values) :
|
||||
?>
|
||||
<tr>
|
||||
<td><?= (string) $values['year']; ?>
|
||||
<td><?= $this->getCurrency(((int) $values['net_sales']) / 10000); ?>
|
||||
<td><?= \sprintf('%.2f', $values['net_sales'] == 0 ? 0 : $values['net_profit'] * 100 / $values['net_sales']); ?> %
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<input type="radio" id="c-tab2-3" name="tabular-2">
|
||||
<div class="tabview tab-3">
|
||||
<section class="box wf-100 floatLeft">
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<section class="portlet">
|
||||
<div class="portlet-head">
|
||||
<?= $this->getHtml('ItemAttribute'); ?>
|
||||
</div>
|
||||
<div class="slider">
|
||||
<table class="default">
|
||||
<caption><?= $this->getHtml('Year'); ?><i class="fa fa-download floatRight download btn"></i></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<td><?= $this->getHtml('Year') ?>
|
||||
<td><?= $this->getHtml('January') ?>
|
||||
<td><?= $this->getHtml('February') ?>
|
||||
<td><?= $this->getHtml('March') ?>
|
||||
<td><?= $this->getHtml('April') ?>
|
||||
<td><?= $this->getHtml('May') ?>
|
||||
<td><?= $this->getHtml('June') ?>
|
||||
<td><?= $this->getHtml('July') ?>
|
||||
<td><?= $this->getHtml('August') ?>
|
||||
<td><?= $this->getHtml('September') ?>
|
||||
<td><?= $this->getHtml('October') ?>
|
||||
<td><?= $this->getHtml('November') ?>
|
||||
<td><?= $this->getHtml('December') ?>
|
||||
<td><?= $this->getHtml('Category'); ?>
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<tbody>
|
||||
<tr><th>2013<td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th>2014<td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th>2015<td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th>CY 2016<td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th>2017<td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th>2018<td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th>2019<td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th>2020<td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<tr><th>2021<td><td><td><td><td><td><td><td><td><td><td><td>
|
||||
<?php foreach ($this->data['ytdAItemAttribute'] as $type => $values) : ?>
|
||||
<tr>
|
||||
<td><?= $this->printHtml($this->data['ytdPYItemAttribute'][$type]['value_l11n']); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</section>
|
||||
</div>
|
||||
<input type="radio" id="c-tab2-4" name="tabular-2">
|
||||
<div class="tabview tab-4">
|
||||
<section class="box w-33 floatLeft">
|
||||
<header>
|
||||
<h1><?= $this->getHtml('Customers') ?></h1>
|
||||
</header>
|
||||
<div class="inner">
|
||||
</div>
|
||||
</section>
|
||||
<section class="box w-33 floatLeft">
|
||||
<header>
|
||||
<h1><?= $this->getHtml('Products') ?></h1>
|
||||
</header>
|
||||
<div class="inner">
|
||||
</div>
|
||||
</section>
|
||||
<section class="box w-33 floatLeft">
|
||||
<header>
|
||||
<h1><?= $this->getHtml('Employees') ?></h1>
|
||||
</header>
|
||||
<div class="inner">
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<input type="radio" id="c-tab2-5" name="tabular-2">
|
||||
<div class="tabview tab-5">
|
||||
<section class="box w-33 floatLeft">
|
||||
<header>
|
||||
<h1><?= $this->getHtml('Domestic/Export') ?></h1>
|
||||
</header>
|
||||
<div class="inner">
|
||||
</div>
|
||||
</section>
|
||||
<section class="box w-33 floatLeft">
|
||||
<header>
|
||||
<h1><?= $this->getHtml('Developed/Undeveloped') ?></h1>
|
||||
</header>
|
||||
<div class="inner">
|
||||
</div>
|
||||
</section>
|
||||
<section class="box w-33 floatLeft">
|
||||
<header>
|
||||
<h1><?= $this->getHtml('Continents') ?></h1>
|
||||
</header>
|
||||
<div class="inner">
|
||||
</div>
|
||||
</section>
|
||||
<section class="box w-100 floatLeft">
|
||||
<header>
|
||||
<h1><?= $this->getHtml('Development') ?></h1>
|
||||
</header>
|
||||
<div class="inner">
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<section class="portlet">
|
||||
<div class="portlet-head">
|
||||
<?= $this->getHtml('ClientAttribute'); ?>
|
||||
</div>
|
||||
<div class="slider">
|
||||
<table class="default">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><?= $this->getHtml('Category'); ?>
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<tbody>
|
||||
<?php foreach ($this->data['ytdAClientAttribute'] as $type => $values) : ?>
|
||||
<tr>
|
||||
<td><?= $this->printHtml($this->data['ytdPYClientAttribute'][$type]['value_l11n']); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdPYClientAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdAClientAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['ytdAClientAttribute'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['ytdPYClientAttribute'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdPYClientAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdAClientAttribute'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['mtdAClientAttribute'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['mtdPYClientAttribute'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<section class="portlet">
|
||||
<div class="portlet-head">
|
||||
<?= $this->getHtml('Country'); ?>
|
||||
</div>
|
||||
<div class="slider">
|
||||
<table class="default">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><?= $this->getHtml('Country'); ?>
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('YTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('SalesA'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<td><?= $this->getHtml('DiffPY'); ?> (<?= $this->getHtml('MTD'); ?>)
|
||||
<tbody>
|
||||
<?php foreach ($this->data['ytdAClientCountry'] as $type => $values) : ?>
|
||||
<tr>
|
||||
<td><?= $this->printHtml(ISO3166NameEnum::getBy2Code($type)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdPYClientCountry'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['ytdAClientCountry'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['ytdAClientCountry'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['ytdPYClientCountry'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdPYClientCountry'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency((int) ($this->data['mtdAClientCountry'][$type]['net_sales'] ?? 0)); ?>
|
||||
<td><?= $this->getCurrency(
|
||||
((int) ($this->data['mtdAClientCountry'][$type]['net_sales'] ?? 0)) -
|
||||
((int) ($this->data['mtdPYClientCountry'][$type]['net_sales'] ?? 0))
|
||||
); ?>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
1480
Theme/Backend/analysis-region.tpl.php
Executable file
1480
Theme/Backend/analysis-region.tpl.php
Executable file
File diff suppressed because it is too large
Load Diff
36
Theme/Backend/analysis-rep.tpl.php
Executable file
36
Theme/Backend/analysis-rep.tpl.php
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.1
|
||||
*
|
||||
* @package Modules\SalesAnalysis
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
/* @todo: single month/quarter/fiscal year/calendar year */
|
||||
/* @todo: time range (<= 12 month = monthly view; else annual view/comparison) */
|
||||
|
||||
/**
|
||||
* @var \phpOMS\Views\View $this
|
||||
*/
|
||||
echo $this->data['nav']->render();
|
||||
?>
|
||||
|
||||
<div class="tabview tab-2">
|
||||
<div class="box">
|
||||
<ul class="tab-links">
|
||||
<li><label for="c-tab-1"><?= $this->getHtml('General'); ?></label></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<input type="radio" id="c-tab-1" name="tabular-2"<?= $this->request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<img height="100%" src="Web/Backend/img/under_construction.svg">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -11,13 +11,16 @@
|
|||
"phpOMS-db": "1.0.0"
|
||||
},
|
||||
"creator": {
|
||||
"name": "Orange Management",
|
||||
"website": "www.spl1nes.com"
|
||||
"name": "Jingga",
|
||||
"website": "jingga.app"
|
||||
},
|
||||
"description": "Sales module.",
|
||||
"directory": "SalesAnalysis",
|
||||
"dependencies": {
|
||||
"Admin": "1.0.0"
|
||||
"Admin": "1.0.0",
|
||||
"Sales": "1.0.0",
|
||||
"ClientManagement": "1.0.0",
|
||||
"Billing": "1.0.0"
|
||||
},
|
||||
"providing": {
|
||||
"Navigation": "*"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user