From ae44226b1f8ef60f199bf8b4d88df55c6ba48f20 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Mon, 9 Oct 2023 22:06:39 +0000 Subject: [PATCH] update --- Admin/Install/Navigation.install.json | 130 +- Admin/Install/Navigation.php | 14 +- Admin/Installer.php | 11 +- Admin/Routes/Web/Backend.php | 70 +- Admin/Status.php | 11 +- Admin/Uninstaller.php | 11 +- Admin/Updater.php | 11 +- Controller/BackendController.php | 628 ++++++- Controller/Controller.js | 73 + Controller/Controller.php | 13 +- Models/ClientMapper.php | 157 ++ Models/GeneralMapper.php | 193 +++ Models/ItemMapper.php | 159 ++ ...issionState.php => PermissionCategory.php} | 6 +- Models/RegionMapper.php | 554 ++++++ Theme/Backend/Lang/Navigation.en.lang.php | 4 +- Theme/Backend/Lang/api.en.lang.php | 16 - Theme/Backend/Lang/en.lang.php | 56 +- Theme/Backend/analysis-bill.tpl.php | 23 + Theme/Backend/analysis-client.tpl.php | 52 + Theme/Backend/analysis-item.tpl.php | 216 +++ .../analysis-overview-dashboard.tpl.php | 656 ++++++-- Theme/Backend/analysis-region.tpl.php | 1480 +++++++++++++++++ Theme/Backend/analysis-rep.tpl.php | 36 + info.json | 9 +- 25 files changed, 4278 insertions(+), 311 deletions(-) create mode 100644 Controller/Controller.js create mode 100644 Models/ClientMapper.php create mode 100644 Models/GeneralMapper.php create mode 100644 Models/ItemMapper.php rename Models/{PermissionState.php => PermissionCategory.php} (77%) create mode 100644 Models/RegionMapper.php delete mode 100644 Theme/Backend/Lang/api.en.lang.php create mode 100755 Theme/Backend/analysis-bill.tpl.php create mode 100755 Theme/Backend/analysis-client.tpl.php create mode 100755 Theme/Backend/analysis-item.tpl.php create mode 100755 Theme/Backend/analysis-region.tpl.php create mode 100755 Theme/Backend/analysis-rep.tpl.php diff --git a/Admin/Install/Navigation.install.json b/Admin/Install/Navigation.install.json index 66d3f22..c466b0f 100644 --- a/Admin/Install/Navigation.install.json +++ b/Admin/Install/Navigation.install.json @@ -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": [] } diff --git a/Admin/Install/Navigation.php b/Admin/Install/Navigation.php index c64d7a2..373e5d3 100644 --- a/Admin/Install/Navigation.php +++ b/Admin/Install/Navigation.php @@ -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 string $path Module path - * @param DatabasePool $dbPool Database pool for database interaction + * @param ApplicationAbstract $app Application + * @param string $path Module path * * @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']); } } diff --git a/Admin/Installer.php b/Admin/Installer.php index 1020eae..a4cd5fe 100644 --- a/Admin/Installer.php +++ b/Admin/Installer.php @@ -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__; } diff --git a/Admin/Routes/Web/Backend.php b/Admin/Routes/Web/Backend.php index b714d75..409c886 100644 --- a/Admin/Routes/Web/Backend.php +++ b/Admin/Routes/Web/Backend.php @@ -1,31 +1,75 @@ [ + '^.*/sales/analysis(\?.*|$)$' => [ [ - 'dest' => '\Modules\SalesAnalysis\Controller\BackendController:viewBackendDashboard', - 'verb' => RouteVerb::GET, + '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', - 'verb' => RouteVerb::GET, + 'dest' => '\Modules\SalesAnalysis\Controller\BackendController:viewBillAnalysis', + 'verb' => RouteVerb::GET, 'permission' => [ - 'module' => BackendController::MODULE_NAME, - 'type' => PermissionType::READ, - 'state' => PermissionState::DASHBOARD, + 'module' => BackendController::NAME, + 'type' => PermissionType::READ, + '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, ], ], ], diff --git a/Admin/Status.php b/Admin/Status.php index c13b96e..86b1012 100644 --- a/Admin/Status.php +++ b/Admin/Status.php @@ -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__; } diff --git a/Admin/Uninstaller.php b/Admin/Uninstaller.php index bb54adb..be8c394 100644 --- a/Admin/Uninstaller.php +++ b/Admin/Uninstaller.php @@ -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__; } diff --git a/Admin/Updater.php b/Admin/Updater.php index 546dac9..230887a 100644 --- a/Admin/Updater.php +++ b/Admin/Updater.php @@ -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__; } diff --git a/Controller/BackendController.php b/Controller/BackendController.php index 79477bf..7693eeb 100644 --- a/Controller/BackendController.php +++ b/Controller/BackendController.php @@ -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; } diff --git a/Controller/Controller.js b/Controller/Controller.js new file mode 100644 index 0000000..8eccbaf --- /dev/null +++ b/Controller/Controller.js @@ -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(); diff --git a/Controller/Controller.php b/Controller/Controller.php index 369cfa1..c18f36b 100644 --- a/Controller/Controller.php +++ b/Controller/Controller.php @@ -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 = []; } diff --git a/Models/ClientMapper.php b/Models/ClientMapper.php new file mode 100644 index 0000000..23abc66 --- /dev/null +++ b/Models/ClientMapper.php @@ -0,0 +1,157 @@ +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, + ]; + } +} \ No newline at end of file diff --git a/Models/GeneralMapper.php b/Models/GeneralMapper.php new file mode 100644 index 0000000..afdd720 --- /dev/null +++ b/Models/GeneralMapper.php @@ -0,0 +1,193 @@ +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; + } +} \ No newline at end of file diff --git a/Models/ItemMapper.php b/Models/ItemMapper.php new file mode 100644 index 0000000..3435c61 --- /dev/null +++ b/Models/ItemMapper.php @@ -0,0 +1,159 @@ +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, + ]; + } +} \ No newline at end of file diff --git a/Models/PermissionState.php b/Models/PermissionCategory.php similarity index 77% rename from Models/PermissionState.php rename to Models/PermissionCategory.php index 3c0272d..14fff13 100644 --- a/Models/PermissionState.php +++ b/Models/PermissionCategory.php @@ -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; } diff --git a/Models/RegionMapper.php b/Models/RegionMapper.php new file mode 100644 index 0000000..b454837 --- /dev/null +++ b/Models/RegionMapper.php @@ -0,0 +1,554 @@ +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; + } +} diff --git a/Theme/Backend/Lang/Navigation.en.lang.php b/Theme/Backend/Lang/Navigation.en.lang.php index fdfe5d1..e0f0255 100644 --- a/Theme/Backend/Lang/Navigation.en.lang.php +++ b/Theme/Backend/Lang/Navigation.en.lang.php @@ -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', diff --git a/Theme/Backend/Lang/api.en.lang.php b/Theme/Backend/Lang/api.en.lang.php deleted file mode 100644 index f58af37..0000000 --- a/Theme/Backend/Lang/api.en.lang.php +++ /dev/null @@ -1,16 +0,0 @@ - [ + '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', diff --git a/Theme/Backend/analysis-bill.tpl.php b/Theme/Backend/analysis-bill.tpl.php new file mode 100755 index 0000000..5901c1e --- /dev/null +++ b/Theme/Backend/analysis-bill.tpl.php @@ -0,0 +1,23 @@ +data['nav']->render(); +?> + \ No newline at end of file diff --git a/Theme/Backend/analysis-client.tpl.php b/Theme/Backend/analysis-client.tpl.php new file mode 100755 index 0000000..7b1fd60 --- /dev/null +++ b/Theme/Backend/analysis-client.tpl.php @@ -0,0 +1,52 @@ +data['nav']->render(); +?> + +
+
+ +
+
+ request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>> +
+ +
+ + request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>> +
+ +
+ + request->uri->fragment === 'c-tab-3' ? ' checked' : ''; ?>> +
+ +
+
+
\ No newline at end of file diff --git a/Theme/Backend/analysis-item.tpl.php b/Theme/Backend/analysis-item.tpl.php new file mode 100755 index 0000000..ae24a52 --- /dev/null +++ b/Theme/Backend/analysis-item.tpl.php @@ -0,0 +1,216 @@ +data['nav']->render(); +?> +
+
+ +
+
+ request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>> +
+
+
+
+
getHtml('Segment'); ?>
+
+ + + + + data['ytdAItemAttribute'] as $type => $values) : + if ($type !== 'segment') { + continue; + } + ?> + +
getHtml('Category'); ?> + getHtml('SalesPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesA'); ?> (getHtml('YTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesPY'); ?> (getHtml('MTD'); ?>) + getHtml('SalesA'); ?> (getHtml('MTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('MTD'); ?>) +
printHtml($this->data['ytdPYItemAttribute'][$type]['value_l11n']); ?> + getCurrency((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)) + ); ?> + getCurrency((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)) + ); ?> + +
+
+
+
+
+
+ + request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>> +
+
+
+
+
getHtml('Section'); ?>
+
+ + + + + data['ytdAItemAttribute'] as $type => $values) : + if ($type !== 'section') { + continue; + } + ?> + +
getHtml('Category'); ?> + getHtml('SalesPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesA'); ?> (getHtml('YTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesPY'); ?> (getHtml('MTD'); ?>) + getHtml('SalesA'); ?> (getHtml('MTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('MTD'); ?>) +
printHtml($this->data['ytdPYItemAttribute'][$type]['value_l11n']); ?> + getCurrency((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)) + ); ?> + getCurrency((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)) + ); ?> + +
+
+
+
+
+
+ + request->uri->fragment === 'c-tab-3' ? ' checked' : ''; ?>> +
+
+
+
+
getHtml('Group'); ?>
+
+ + + + + data['ytdAItemAttribute'] as $type => $values) : + if ($type !== 'product_group') { + continue; + } + ?> + +
getHtml('Category'); ?> + getHtml('SalesPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesA'); ?> (getHtml('YTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesPY'); ?> (getHtml('MTD'); ?>) + getHtml('SalesA'); ?> (getHtml('MTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('MTD'); ?>) +
printHtml($this->data['ytdPYItemAttribute'][$type]['value_l11n']); ?> + getCurrency((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)) + ); ?> + getCurrency((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)) + ); ?> + +
+
+
+
+
+
+ + request->uri->fragment === 'c-tab-4' ? ' checked' : ''; ?>> +
+
+
+
+
getHtml('Type'); ?>
+
+ + + + + data['ytdAItemAttribute'] as $type => $values) : + if ($type !== 'product_type') { + continue; + } + ?> + +
getHtml('Category'); ?> + getHtml('SalesPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesA'); ?> (getHtml('YTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesPY'); ?> (getHtml('MTD'); ?>) + getHtml('SalesA'); ?> (getHtml('MTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('MTD'); ?>) +
printHtml($this->data['ytdPYItemAttribute'][$type]['value_l11n']); ?> + getCurrency((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)) + ); ?> + getCurrency((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)) + ); ?> + +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/Theme/Backend/analysis-overview-dashboard.tpl.php b/Theme/Backend/analysis-overview-dashboard.tpl.php index 1d13239..e79eace 100644 --- a/Theme/Backend/analysis-overview-dashboard.tpl.php +++ b/Theme/Backend/analysis-overview-dashboard.tpl.php @@ -1,190 +1,530 @@ data['nav']->render(); +?> +
+
+
+
+
+
getHtml('Current'); ?>
+
+
+ + +
-echo $this->getData('nav')->render(); ?> +
+ + +
+
-
-
- -
- -
-
- - - - - -
getHtml('Overview'); ?>
getHtml('Type') ?> - getHtml('LastMonth') ?> - getHtml('CurrentMonth') ?> - getHtml('Change') ?> - getHtml('LastYear') ?> - getHtml('CurrentYear') ?> - getHtml('Change') ?> - getHtml('LastYearAcc') ?> - getHtml('CurrentYearAcc') ?> - getHtml('Change') ?> - getHtml('LastYear') ?> - getHtml('Forecast') ?> - getHtml('Change') ?> -
getHtml('Domestic') ?> -
getHtml('Export') ?> -
getHtml('Developed') ?> -
getHtml('Undeveloped') ?> -
getHtml('Europe') ?> -
getHtml('America') ?> -
getHtml('Asia') ?> -
getHtml('Africa') ?> -
getHtml('Total') ?> -
-
+
getHtml('Comparison'); ?>
-
+
+
+ + +
+ +
+ + +
+
+
+
+ +
+ +
+
+ +
+
+
getHtml('Actual'); ?>
+
+
+
getHtml('Sales'); ?> (getHtml('MTD'); ?>):
+
 data['mtdPY']['net_sales'] == 0 ? 0 : $this->data['mtdA']['net_sales'] * 100 / $this->data['mtdPY']['net_sales'] - 100); ?> %
+
+ +
+
getHtml('Sales'); ?> (getHtml('YTD'); ?>):
+
 data['ytdPY']['net_sales'] == 0 ? 0 : $this->data['ytdA']['net_sales'] * 100 / $this->data['ytdPY']['net_sales'] - 100); ?> %
+
+ +
+
getHtml('GrossProfit'); ?> (getHtml('MTD'); ?>):
+
 data['mtdPY']['net_profit'] == 0 ? 0 : $this->data['mtdA']['net_profit'] * 100 / $this->data['mtdPY']['net_profit'] - 100); ?> %
+
+ +
+
getHtml('GrossProfit'); ?> (getHtml('YTD'); ?>):
+
 data['ytdPY']['net_profit'] == 0 ? 0 : $this->data['ytdA']['net_profit'] * 100 / $this->data['ytdPY']['net_profit'] - 100); ?> %
+
+
+
+
+
+ +
+
+
+
+ getHtml('SalesProfit'); ?> (getHtml('monthly'); ?>) +
+ data['monthlySales']; ?> +
+ + ], + "datasets": [ + { + "label": "getHtml('Profit'); ?> PY", + "type": "line", + "data": [ + + ], + "yAxisID": "y1", + "fill": false, + "tension": 0.0, + "borderColor": "rgb(166, 193, 178)", + "backgroundColor": "rgb(166, 193, 178)" + }, + { + "label": "getHtml('Profit'); ?> A", + "type": "line", + "data": [ + + ], + "yAxisID": "y1", + "fill": false, + "tension": 0.0, + "borderColor": "rgb(46, 204, 113)", + "backgroundColor": "rgb(46, 204, 113)" + }, + { + "label": "getHtml('Sales'); ?> PY", + "type": "bar", + "data": [ + + ], + "yAxisID": "y", + "fill": false, + "tension": 0.0, + "backgroundColor": "rgb(177, 195, 206)" + }, + { + "label": "getHtml('Sales'); ?> A", + "type": "bar", + "data": [ + + ], + "yAxisID": "y", + "fill": false, + "tension": 0.0, + "backgroundColor": "rgb(54, 162, 235)" + } + ] + }, + "options": { + "responsive": true, + "scales": { + "x": { + "title": { + "display": true, + "text": "getHtml('Months'); ?>" + } + }, + "y": { + "title": { + "display": true, + "text": "getHtml('Sales'); ?>" + }, + "display": true, + "position": "left" + }, + "y1": { + "title": { + "display": true, + "text": "getHtml('Profit'); ?> %" + }, + "display": true, + "position": "right", + "scaleLabel": { + "display": true, + "labelString": "getHtml('Profit'); ?>" + }, + "grid": { + "drawOnChartArea": false + } + } + } + } + }'> +
+ + +
- - - + - + +
getHtml('Misc'); ?>
getHtml('Type') ?> - getHtml('LastYear') ?> - getHtml('CurrentYear') ?> - getHtml('LastMonth') ?> - getHtml('CurrentMonth') ?> - getHtml('Yesterday') ?> - getHtml('Today') ?> +
getHtml('Month'); ?> + getHtml('SalesPY'); ?> + getHtml('SalesA'); ?> + getHtml('ProfitPY'); ?> + getHtml('ProfitA'); ?>
getHtml('Customers') ?> -
getHtml('Invoices') ?> + +
+ getCurrency((int) ($sales[1][$i]['net_sales'] ?? 0)); ?> + getCurrency((int) ($sales[2][$i]['net_sales'] ?? 0)); ?> + % + % + +
getHtml('Total'); ?> + getCurrency($sum1); ?> + getCurrency($sum2); ?> + % + %
-
+
+
- -
-
+
+
+ +
+
+
+ getHtml('SalesProfit'); ?> (getHtml('annually'); ?>) +
+ data['annualSales']; ?> +
+ + ], + "datasets": [ + { + "label": "getHtml('Profit'); ?>", + "type": "line", + "data": [ + + ], + "yAxisID": "y1", + "fill": false, + "tension": 0.0, + "borderColor": "rgb(46, 204, 113)", + "backgroundColor": "rgb(46, 204, 113)" + }, + { + "label": "getHtml('Sales'); ?>", + "type": "bar", + "data": [ + + ], + "yAxisID": "y", + "fill": false, + "tension": 0.0, + "backgroundColor": "rgb(54, 162, 235)" + } + ] + }, + "options": { + "responsive": true, + "scales": { + "x": { + "title": { + "display": true, + "text": "getHtml('Months'); ?>" + } + }, + "y": { + "title": { + "display": true, + "text": "getHtml('Sales'); ?>" + }, + "display": true, + "position": "left" + }, + "y1": { + "title": { + "display": true, + "text": "getHtml('Profit'); ?> %" + }, + "display": true, + "position": "right", + "scaleLabel": { + "display": true, + "labelString": "getHtml('Profit'); ?>" + }, + "grid": { + "drawOnChartArea": false + } + } + } + } + }'> +
+ + +
- - - + - +
getHtml('Month'); ?>
getHtml('Day') ?> - getHtml('Day') ?> - getHtml('LastMonth') ?> - getHtml('CurrentMonth') ?> - getHtml('Change') ?> - getHtml('ChangeAcc') ?> +
getHtml('Year'); ?> + getHtml('Sales'); ?> + getHtml('Profit'); ?>
+ +
+ getCurrency(((int) $values['net_sales']) / 10000); ?> + % +
-
+
+ - -
-
- - - + + + + +
+
+
+
+ getHtml('ItemAttribute'); ?> +
+
+
getHtml('Year'); ?>
+ + + + data['ytdAItemAttribute'] as $type => $values) : ?> - -
getHtml('Category'); ?> + getHtml('SalesPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesA'); ?> (getHtml('YTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesPY'); ?> (getHtml('MTD'); ?>) + getHtml('SalesA'); ?> (getHtml('MTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('MTD'); ?>) +
getHtml('Year') ?> - getHtml('January') ?> - getHtml('February') ?> - getHtml('March') ?> - getHtml('April') ?> - getHtml('May') ?> - getHtml('June') ?> - getHtml('July') ?> - getHtml('August') ?> - getHtml('September') ?> - getHtml('October') ?> - getHtml('November') ?> - getHtml('December') ?> -
2013 -
2014 -
2015 -
CY 2016 -
2017 -
2018 -
2019 -
2020 -
2021 -
-
+ printHtml($this->data['ytdPYItemAttribute'][$type]['value_l11n']); ?> + getCurrency((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['ytdAItemAttribute'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['ytdPYItemAttribute'][$type]['net_sales'] ?? 0)) + ); ?> + getCurrency((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['mtdAItemAttribute'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['mtdPYItemAttribute'][$type]['net_sales'] ?? 0)) + ); ?> + +
- -
-
-
-

getHtml('Customers') ?>

-
-
-
-
-
-
-

getHtml('Products') ?>

-
-
-
-
-
-
-

getHtml('Employees') ?>

-
-
-
-
+ +
+ + +
+
+
+
+ getHtml('ClientAttribute'); ?>
- -
-
-
-

getHtml('Domestic/Export') ?>

-
-
-
-
-
-
-

getHtml('Developed/Undeveloped') ?>

-
-
-
-
-
-
-

getHtml('Continents') ?>

-
-
-
-
-
-
-

getHtml('Development') ?>

-
-
-
-
+
+ + + + + data['ytdAClientAttribute'] as $type => $values) : ?> + +
getHtml('Category'); ?> + getHtml('SalesPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesA'); ?> (getHtml('YTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesPY'); ?> (getHtml('MTD'); ?>) + getHtml('SalesA'); ?> (getHtml('MTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('MTD'); ?>) +
printHtml($this->data['ytdPYClientAttribute'][$type]['value_l11n']); ?> + getCurrency((int) ($this->data['ytdPYClientAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['ytdAClientAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['ytdAClientAttribute'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['ytdPYClientAttribute'][$type]['net_sales'] ?? 0)) + ); ?> + getCurrency((int) ($this->data['mtdPYClientAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['mtdAClientAttribute'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['mtdAClientAttribute'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['mtdPYClientAttribute'][$type]['net_sales'] ?? 0)) + ); ?> + +
-
+
+
+
+ +
+
+
+
+ getHtml('Country'); ?> +
+
+ + + + + data['ytdAClientCountry'] as $type => $values) : ?> + +
getHtml('Country'); ?> + getHtml('SalesPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesA'); ?> (getHtml('YTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesPY'); ?> (getHtml('MTD'); ?>) + getHtml('SalesA'); ?> (getHtml('MTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('MTD'); ?>) +
printHtml(ISO3166NameEnum::getBy2Code($type)); ?> + getCurrency((int) ($this->data['ytdPYClientCountry'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['ytdAClientCountry'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['ytdAClientCountry'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['ytdPYClientCountry'][$type]['net_sales'] ?? 0)) + ); ?> + getCurrency((int) ($this->data['mtdPYClientCountry'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['mtdAClientCountry'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['mtdAClientCountry'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['mtdPYClientCountry'][$type]['net_sales'] ?? 0)) + ); ?> + +
+
+
\ No newline at end of file diff --git a/Theme/Backend/analysis-region.tpl.php b/Theme/Backend/analysis-region.tpl.php new file mode 100755 index 0000000..21ca1cc --- /dev/null +++ b/Theme/Backend/analysis-region.tpl.php @@ -0,0 +1,1480 @@ +data['nav']->render(); +?> + +
+
+ +
+
+ request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>> +
+
+
+
+
getHtml('World'); ?>
+
+ $values) { + $temp[] = '{"id": "' . ISO3166CharEnum::getBy2Code($lang) . '", "value": ' . ($values['net_sales'] / 10000) . '}'; + }; ?> + + ] + }] + }, + "options": { + "responsive": true, + "showOutline": true, + "showGraticule": false, + "plugins": { + "legend": { + "display": false + } + }, + "scales": { + "projection": { + "axis": "x", + "projection": "equirectangular" + } + } + } + }'> +
+
+
+
+ +
+
+
+
+ getHtml('Country'); ?> +
+
+ + + + + data['ytdAClientCountry'] as $type => $values) : ?> + +
getHtml('Country'); ?> + getHtml('SalesPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesA'); ?> (getHtml('YTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesPY'); ?> (getHtml('MTD'); ?>) + getHtml('SalesA'); ?> (getHtml('MTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('MTD'); ?>) +
printHtml(ISO3166NameEnum::getBy2Code($type)); ?> + getCurrency((int) ($this->data['ytdPYClientCountry'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['ytdAClientCountry'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['ytdAClientCountry'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['ytdPYClientCountry'][$type]['net_sales'] ?? 0)) + ); ?> + getCurrency((int) ($this->data['mtdPYClientCountry'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['mtdAClientCountry'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['mtdAClientCountry'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['mtdPYClientCountry'][$type]['net_sales'] ?? 0)) + ); ?> + +
+
+
+
+
+
+ + request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>> +
+ +
+
+
+
+ getHtml('SalesProfit'); ?> (getHtml('monthly'); ?>) - getHtml('Domestic'); ?> +
+ $this->data['monthlyDomesticExportPY'], 2 => $this->data['monthlyDomesticExportCurrent']]; ?> +
+ + ], + "datasets": [ + { + "label": "getHtml('Profit'); ?> PY", + "type": "line", + "data": [ + data['domestic']][$i]['net_sales']) || !isset($sales[1][$this->data['domestic']][$i]['net_profit'])) { + $temp[] = 'null'; + + continue; + } + + $temp[] = $sales[1][$this->data['domestic']][$i]['net_sales'] == 0 + ? 0 + : $sales[1][$this->data['domestic']][$i]['net_profit'] * 100 / $sales[1][$this->data['domestic']][$i]['net_sales']; + } + echo \implode(',', $temp); + ?> + ], + "yAxisID": "y1", + "fill": false, + "tension": 0.0, + "borderColor": "rgb(166, 193, 178)", + "backgroundColor": "rgb(166, 193, 178)" + }, + { + "label": "getHtml('Profit'); ?> A", + "type": "line", + "data": [ + data['domestic']][$i]['net_sales']) || !isset($sales[2][$this->data['domestic']][$i]['net_profit'])) { + $temp[] = 'null'; + + continue; + } + + $temp[] = $sales[2][$this->data['domestic']][$i]['net_sales'] == 0 + ? 0 + : $sales[2][$this->data['domestic']][$i]['net_profit'] * 100 / $sales[2][$this->data['domestic']][$i]['net_sales']; + } + echo \implode(',', $temp); + ?> + ], + "yAxisID": "y1", + "fill": false, + "tension": 0.0, + "borderColor": "rgb(46, 204, 113)", + "backgroundColor": "rgb(46, 204, 113)" + }, + { + "label": "getHtml('Sales'); ?> PY", + "type": "bar", + "data": [ + data['domestic']][$i]['net_sales'] ?? 0) / 10000; + } + echo \implode(',', $temp); + ?> + ], + "yAxisID": "y", + "fill": false, + "tension": 0.0, + "backgroundColor": "rgb(177, 195, 206)" + }, + { + "label": "getHtml('Sales'); ?> A", + "type": "bar", + "data": [ + data['domestic']][$i]['net_sales'] ?? 0) / 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": "getHtml('Months'); ?>" + } + }, + "y": { + "title": { + "display": true, + "text": "getHtml('Sales'); ?>" + }, + "display": true, + "position": "left" + }, + "y1": { + "title": { + "display": true, + "text": "getHtml('Profit'); ?> %" + }, + "display": true, + "position": "right", + "scaleLabel": { + "display": true, + "labelString": "getHtml('Profit'); ?>" + }, + "grid": { + "drawOnChartArea": false + } + } + } + } + }'> +
+ + +
+ + + + + data['domestic']][$i]['net_sales'] ?? 0); + $sum2 += (int) ($sales[2][$this->data['domestic']][$i]['net_sales'] ?? 0); + $sum3 += (int) ($sales[1][$this->data['domestic']][$i]['net_profit'] ?? 0); + $sum4 += (int) ($sales[2][$this->data['domestic']][$i]['net_profit'] ?? 0); + ?> + + +
getHtml('Month'); ?> + getHtml('SalesPY'); ?> + getHtml('SalesA'); ?> + getHtml('ProfitPY'); ?> + getHtml('ProfitA'); ?> +
+ getCurrency((int) ($sales[1][$this->data['domestic']][$i]['net_sales'] ?? 0)); ?> + getCurrency((int) ($sales[2][$this->data['domestic']][$i]['net_sales'] ?? 0)); ?> + data['domestic']][$i]['net_sales'] ?? 0) == 0 ? 0 : $sales[1][$this->data['domestic']][$i]['net_profit'] * 100 / $sales[1][$this->data['domestic']][$i]['net_sales']); ?> % + data['domestic']][$i]['net_sales'] ?? 0) == 0 ? 0 : $sales[2][$this->data['domestic']][$i]['net_profit'] * 100 / $sales[2][$this->data['domestic']][$i]['net_sales']); ?> % + +
getHtml('Total'); ?> + getCurrency($sum1); ?> + getCurrency($sum2); ?> + % + % +
+
+
+
+
+
+ +
+
+
+ getHtml('SalesProfit'); ?> (getHtml('monthly'); ?>) - getHtml('Export'); ?> +
+ $this->data['monthlyDomesticExportPY'], 2 => $this->data['monthlyDomesticExportCurrent']]; ?> +
+ + ], + "datasets": [ + { + "label": "getHtml('Profit'); ?> PY", + "type": "line", + "data": [ + + ], + "yAxisID": "y1", + "fill": false, + "tension": 0.0, + "borderColor": "rgb(166, 193, 178)", + "backgroundColor": "rgb(166, 193, 178)" + }, + { + "label": "getHtml('Profit'); ?> A", + "type": "line", + "data": [ + + ], + "yAxisID": "y1", + "fill": false, + "tension": 0.0, + "borderColor": "rgb(46, 204, 113)", + "backgroundColor": "rgb(46, 204, 113)" + }, + { + "label": "getHtml('Sales'); ?> PY", + "type": "bar", + "data": [ + + ], + "yAxisID": "y", + "fill": false, + "tension": 0.0, + "backgroundColor": "rgb(177, 195, 206)" + }, + { + "label": "getHtml('Sales'); ?> A", + "type": "bar", + "data": [ + + ], + "yAxisID": "y", + "fill": false, + "tension": 0.0, + "backgroundColor": "rgb(54, 162, 235)" + } + ] + }, + "options": { + "responsive": true, + "scales": { + "x": { + "title": { + "display": true, + "text": "getHtml('Months'); ?>" + } + }, + "y": { + "title": { + "display": true, + "text": "getHtml('Sales'); ?>" + }, + "display": true, + "position": "left" + }, + "y1": { + "title": { + "display": true, + "text": "getHtml('Profit'); ?> %" + }, + "display": true, + "position": "right", + "scaleLabel": { + "display": true, + "labelString": "getHtml('Profit'); ?>" + }, + "grid": { + "drawOnChartArea": false + } + } + } + } + }'> +
+ + +
+ + + + + + + +
getHtml('Month'); ?> + getHtml('SalesPY'); ?> + getHtml('SalesA'); ?> + getHtml('ProfitPY'); ?> + getHtml('ProfitA'); ?> +
+ getCurrency((int) ($sales[1]['Other'][$i]['net_sales'] ?? 0)); ?> + getCurrency((int) ($sales[2]['Other'][$i]['net_sales'] ?? 0)); ?> + % + % + +
getHtml('Total'); ?> + getCurrency($sum1); ?> + getCurrency($sum2); ?> + % + % +
+
+
+
+
+
+
+ +
+
+
+
getHtml('Sales') ?> (getHtml('YTD'); ?>) - getHtml('DomesticExport') ?>
+
+ data['ytdADomesticExport'])); ?>" + ], + "datasets": [{ + "data": [ + data['ytdADomesticExport'] as $values) { + $temp[] = $values['net_sales'] / 10000; + } + echo \implode(',', $temp); + ?> + ] + }] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['ytdADomesticExport'] as $region => $values) : $sum += $values['net_sales']; ?> + + +
getHtml('Region'); ?> + getHtml('Sales'); ?> +
+ getCurrency($values['net_sales']); ?> + +
getHtml('Total'); ?> + getCurrency($sum); ?> +
+
+
+
+
+
+ +
+
+
getHtml('Sales'); ?> (getHtml('annually'); ?>) - getHtml('DomesticExport'); ?>
+
+ format('Y')) + $i; + } + echo \implode(',', $temp); + ?> + ], + "datasets": [ + data['annualDomesticExport'] as $region => $values) : + echo ($first ? '' : ','); + $first = false; + ?>{ + "label": "printHtml($region); ?>", + "type": "line", + "data": [ + + ], + "fill": false, + "tension": 0.0 + } + + ] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['annualDomesticExport'] as $region => $values) : ?> + + +
getHtml('Region'); ?> + + data['historyStart']->format('Y')) + $i; ?> + +
printHtml($region); ?> + $annual) : + $sum[$idx] = ($sum[$idx] ?? 0) + ($annual['net_sales'] ?? 0); + ?> + getCurrency($annual['net_sales'] ?? 0, symbol: '', format: 'short', divide: 1000); ?> + +
getHtml('Total'); ?> + + getCurrency($value, symbol: '', format: 'short', divide: 1000); ?> + +
+
+
+
+
+
+
+ +
+
+
+
getHtml('Profit') ?> (getHtml('YTD'); ?>) - getHtml('DomesticExport') ?>
+
+ data['ytdADomesticExport'])); ?>" + ], + "datasets": [{ + "data": [ + data['ytdADomesticExport'] as $values) { + $temp[] =$values['net_profit'] / 10000; + } + echo \implode(',', $temp); + ?> + ] + }] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['ytdADomesticExport'] as $region => $values) : $sum += $values['net_profit']; ?> + + +
getHtml('Region'); ?> + getHtml('Profit'); ?> +
+ getCurrency($values['net_profit']); ?> + +
getHtml('Total'); ?> + getCurrency($sum); ?> +
+
+
+
+
+
+ +
+
+
getHtml('Profit'); ?> (getHtml('annually'); ?>) - getHtml('DomesticExport'); ?>
+
+ format('Y')) + $i; + } + echo \implode(',', $temp); + ?> + ], + "datasets": [ + data['annualDomesticExport'] as $region => $values) : + echo ($first ? '' : ','); + $first = false; + ?>{ + "label": "printHtml($region); ?>", + "type": "line", + "data": [ + + ], + "fill": false, + "tension": 0.0 + } + + ] + }, + "options": { + "responsive": true, + "scales": { + "y": { + "title": { + "display": true, + "text": "getHtml('Profit'); ?> %" + }, + "display": true, + "position": "left" + } + } + } + }'> + +
+ + +
+ + + + + data['annualDomesticExport'] as $region => $values) : ?> + + +
getHtml('Region'); ?> + + data['historyStart']->format('Y')) + $i; ?> + +
printHtml($region); ?> + $annual) : + $sum[$idx] = ($sum[$idx] ?? 0) + ($annual['net_profit'] ?? 0); + ?> + getCurrency($annual['net_profit'] ?? 0, symbol: '', format: 'short', divide: 1000); ?> + +
getHtml('Total'); ?> + + getCurrency($value, symbol: '', format: 'short', divide: 1000); ?> + +
+
+
+
+
+
+
+ +
+
+
+
getHtml('Clients') ?> (getHtml('YTD'); ?>) - getHtml('DomesticExport') ?>
+
+ data['ytdADomesticExportCount'])); ?>" + ], + "datasets": [{ + "data": [ + data['ytdADomesticExportCount'] as $values) { + $temp[] = $values['client_count']; + } + echo \implode(',', $temp); + ?> + ] + }] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['ytdADomesticExportCount'] as $region => $values) : $sum += $values['client_count']; ?> + + +
getHtml('Region'); ?> + getHtml('Clients'); ?> +
+ getNumeric($values['client_count'], format: 'very_short'); ?> + +
getHtml('Total'); ?> + getNumeric($sum, format: 'very_short'); ?> +
+
+
+
+
+
+ +
+
+
getHtml('Clients'); ?> (getHtml('annually'); ?>) - getHtml('DomesticExport'); ?>
+
+ format('Y')) + $i; + } + echo \implode(',', $temp); + ?> + ], + "datasets": [ + data['annualDomesticExportCount'] as $region => $values) : + echo ($first ? '' : ','); + $first = false; + ?>{ + "label": "printHtml($region); ?>", + "type": "line", + "data": [ + + ], + "fill": false, + "tension": 0.0 + } + + ] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['annualDomesticExportCount'] as $region => $values) : ?> + + +
getHtml('Region'); ?> + + data['historyStart']->format('Y')) + $i; ?> + +
printHtml($region); ?> + $annual) : + $sum[$idx] = ($sum[$idx] ?? 0) + ($annual['client_count'] ?? 0); + ?> + getNumeric($annual['client_count'] ?? 0, format: 'very_short'); ?> + +
getHtml('Total'); ?> + + getNumeric($value, format: 'very_short'); ?> + +
+
+
+
+
+
+
+
+ + request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>> +
+
+
+
+
getHtml('Sales') ?> (getHtml('YTD'); ?>) - getHtml('Continent') ?>
+
+ data['ytdAContinent'])); ?>" + ], + "datasets": [{ + "data": [ + data['ytdAContinent'] as $values) { + $temp[] = $values['net_sales'] / 10000; + } + echo \implode(',', $temp); + ?> + ] + }] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['ytdAContinent'] as $region => $values) : $sum += $values['net_sales']; ?> + + +
getHtml('Region'); ?> + getHtml('Sales'); ?> +
+ getCurrency($values['net_sales']); ?> + +
getHtml('Total'); ?> + getCurrency($sum); ?> +
+
+
+
+
+
+ +
+
+
getHtml('Sales'); ?> (getHtml('annually'); ?>) - getHtml('Continent'); ?>
+
+ format('Y')) + $i; + } + echo \implode(',', $temp); + ?> + ], + "datasets": [ + data['annualContinent'] as $region => $values) : + echo ($first ? '' : ','); + $first = false; + ?>{ + "label": "getHtml($region); ?>", + "type": "line", + "data": [ + + ], + "fill": false, + "tension": 0.0 + } + + ] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['annualContinent'] as $region => $values) : ?> + + +
getHtml('Region'); ?> + + data['historyStart']->format('Y')) + $i; ?> + +
getHtml($region); ?> + $annual) : + $sum[$idx] = ($sum[$idx] ?? 0) + ($annual['net_sales'] ?? 0); + ?> + getCurrency($annual['net_sales'] ?? 0, symbol: '', format: 'short', divide: 1000); ?> + +
getHtml('Total'); ?> + + getCurrency($value, symbol: '', format: 'short', divide: 1000); ?> + +
+
+
+
+
+
+
+ +
+
+
+
getHtml('Profit') ?> (getHtml('YTD'); ?>) - getHtml('Continent') ?>
+
+ data['ytdAContinent'])); ?>" + ], + "datasets": [{ + "data": [ + data['ytdAContinent'] as $values) { + $temp[] =$values['net_profit'] / 10000; + } + echo \implode(',', $temp); + ?> + ] + }] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['ytdAContinent'] as $region => $values) : $sum += $values['net_profit']; ?> + + +
getHtml('Region'); ?> + getHtml('Profit'); ?> +
+ getCurrency($values['net_profit']); ?> + +
getHtml('Total'); ?> + getCurrency($sum); ?> +
+
+
+
+
+
+ +
+
+
getHtml('Profit'); ?> (getHtml('annually'); ?>) - getHtml('Continent'); ?>
+
+ format('Y')) + $i; + } + echo \implode(',', $temp); + ?> + ], + "datasets": [ + data['annualContinent'] as $region => $values) : + echo ($first ? '' : ','); + $first = false; + ?>{ + "label": "getHtml($region); ?>", + "type": "line", + "data": [ + + ], + "fill": false, + "tension": 0.0 + } + + ] + }, + "options": { + "responsive": true, + "scales": { + "y": { + "title": { + "display": true, + "text": "getHtml('Profit'); ?> %" + }, + "display": true, + "position": "left" + } + } + } + }'> + +
+ + +
+ + + + + data['annualContinent'] as $region => $values) : ?> + + +
getHtml('Region'); ?> + + data['historyStart']->format('Y')) + $i; ?> + +
getHtml($region); ?> + $annual) : + $sum[$idx] = ($sum[$idx] ?? 0) + ($annual['net_profit'] ?? 0); + ?> + getCurrency($annual['net_profit'] ?? 0, symbol: '', format: 'short', divide: 1000); ?> + +
getHtml('Total'); ?> + + getCurrency($value, symbol: '', format: 'short', divide: 1000); ?> + +
+
+
+
+
+
+
+ +
+
+
+
getHtml('Clients') ?> (getHtml('YTD'); ?>) - getHtml('Continent') ?>
+
+ data['ytdAContinentCount'])); ?>" + ], + "datasets": [{ + "data": [ + data['ytdAContinentCount'] as $values) { + $temp[] = $values['client_count']; + } + echo \implode(',', $temp); + ?> + ] + }] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['ytdAContinentCount'] as $region => $values) : $sum += $values['client_count']; ?> + + +
getHtml('Region'); ?> + getHtml('Clients'); ?> +
+ getNumeric($values['client_count'], format: 'very_short'); ?> + +
getHtml('Total'); ?> + getNumeric($sum, format: 'very_short'); ?> +
+
+
+
+
+
+ +
+
+
getHtml('Clients'); ?> (getHtml('annually'); ?>) - getHtml('Continent'); ?>
+
+ format('Y')) + $i; + } + echo \implode(',', $temp); + ?> + ], + "datasets": [ + data['annualContinentCount'] as $region => $values) : + echo ($first ? '' : ','); + $first = false; + ?>{ + "label": "printHtml($region); ?>", + "type": "line", + "data": [ + + ], + "fill": false, + "tension": 0.0 + } + + ] + }, + "options": { + "responsive": true + } + }'> + +
+ + +
+ + + + + data['annualContinentCount'] as $region => $values) : ?> + + +
getHtml('Region'); ?> + + data['historyStart']->format('Y')) + $i; ?> + +
printHtml($region); ?> + $annual) : + $sum[$idx] = ($sum[$idx] ?? 0) + ($annual['client_count'] ?? 0); + ?> + getNumeric($annual['client_count'] ?? 0, format: 'very_short'); ?> + +
getHtml('Total'); ?> + + getNumeric($value, format: 'very_short'); ?> + +
+
+
+
+
+
+
+
+ + request->uri->fragment === 'c-tab-2' ? ' checked' : ''; ?>> +
+
+
+
+
getHtml('Sales'); ?>
+
+ + + + + data['ytdARegions'] as $type => $values) : ?> + +
getHtml('Region'); ?> + getHtml('SalesPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesA'); ?> (getHtml('YTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('YTD'); ?>) + getHtml('SalesPY'); ?> (getHtml('MTD'); ?>) + getHtml('SalesA'); ?> (getHtml('MTD'); ?>) + getHtml('DiffPY'); ?> (getHtml('MTD'); ?>) +
printHtml($type); ?> + getCurrency((int) ($this->data['ytdPYRegions'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['ytdARegions'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['ytdARegions'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['ytdPYRegions'][$type]['net_sales'] ?? 0)) + ); ?> + getCurrency((int) ($this->data['mtdPYRegions'][$type]['net_sales'] ?? 0)); ?> + getCurrency((int) ($this->data['mtdARegions'][$type]['net_sales'] ?? 0)); ?> + getCurrency( + ((int) ($this->data['mtdARegions'][$type]['net_sales'] ?? 0)) - + ((int) ($this->data['mtdPYRegions'][$type]['net_sales'] ?? 0)) + ); ?> + +
+
+
+
+
+
+ + request->uri->fragment === 'c-tab-3' ? ' checked' : ''; ?>> +
+
+
+
+
+
getHtml('Filter'); ?>
+
+
+ + +
+ +
+
+ + +
+ +
+ + +
+
+ +
+
+ + +
+ +
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/Theme/Backend/analysis-rep.tpl.php b/Theme/Backend/analysis-rep.tpl.php new file mode 100755 index 0000000..0789c90 --- /dev/null +++ b/Theme/Backend/analysis-rep.tpl.php @@ -0,0 +1,36 @@ +data['nav']->render(); +?> + +
+
+ +
+
+ request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>> +
+ +
+
+
\ No newline at end of file diff --git a/info.json b/info.json index 8fb3c22..ae850a7 100644 --- a/info.json +++ b/info.json @@ -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": "*"