mirror of
https://github.com/SuiteCRM/SuiteCRM-Core.git
synced 2025-08-29 04:47:10 +08:00
Accounts Chart Implementation
This commit is contained in:
parent
0ca4b558d5
commit
e9b9819e7d
4 changed files with 422 additions and 0 deletions
39
config/modules/accounts/listview/sidebar_widgets.php
Normal file
39
config/modules/accounts/listview/sidebar_widgets.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
|
||||
if (!isset($container)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Container $container */
|
||||
$widgets = $container->getParameter('module.listview.sidebar_widgets');
|
||||
|
||||
if (!isset($widgets['modules']['accounts'])) {
|
||||
$widgets['modules']['accounts'] = [];
|
||||
}
|
||||
|
||||
if (!isset($widgets['modules']['accounts']['widgets'])) {
|
||||
$widgets['modules']['accounts']['widgets'] = [];
|
||||
}
|
||||
|
||||
$widgets['modules']['accounts']['widgets']['accounts-new-by-month'] = [
|
||||
'type' => 'chart',
|
||||
'labelKey' => 'LBL_QUICK_CHARTS',
|
||||
'options' => [
|
||||
'toggle' => true,
|
||||
'headerTitle' => false,
|
||||
'charts' => [
|
||||
[
|
||||
'chartKey' => 'accounts-new-by-month',
|
||||
'chartType' => 'line-chart',
|
||||
'statisticsType' => 'accounts-new-by-month',
|
||||
'labelKey' => 'ACCOUNT_TYPES_PER_MONTH',
|
||||
'chartOptions' => [
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$container->setParameter('module.listview.sidebar_widgets', $widgets);
|
158
core/legacy/Statistics/Series/AccountsNewByMonth.php
Normal file
158
core/legacy/Statistics/Series/AccountsNewByMonth.php
Normal file
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
namespace App\Legacy\Statistics\Series;
|
||||
|
||||
use App\Entity\Statistic;
|
||||
use App\Legacy\Data\ListDataQueryHandler;
|
||||
use App\Legacy\LegacyHandler;
|
||||
use App\Legacy\LegacyScopeState;
|
||||
use App\Legacy\Statistics\StatisticsHandlingTrait;
|
||||
use App\Model\Statistics\ChartOptions;
|
||||
use App\Service\ModuleNameMapperInterface;
|
||||
use App\Service\StatisticsProviderInterface;
|
||||
use BeanFactory;
|
||||
use SugarBean;
|
||||
|
||||
class AccountsNewByMonth extends LegacyHandler implements StatisticsProviderInterface
|
||||
{
|
||||
use StatisticsHandlingTrait;
|
||||
|
||||
public const KEY = 'accounts-new-by-month';
|
||||
|
||||
/**
|
||||
* @var ListDataQueryHandler
|
||||
*/
|
||||
private $queryHandler;
|
||||
|
||||
/**
|
||||
* @var ModuleNameMapperInterface
|
||||
*/
|
||||
private $moduleNameMapper;
|
||||
|
||||
/**
|
||||
* LeadDaysOpen constructor.
|
||||
* @param string $projectDir
|
||||
* @param string $legacyDir
|
||||
* @param string $legacySessionName
|
||||
* @param string $defaultSessionName
|
||||
* @param LegacyScopeState $legacyScopeState
|
||||
* @param ListDataQueryHandler $queryHandler
|
||||
* @param ModuleNameMapperInterface $moduleNameMapper
|
||||
*/
|
||||
public function __construct(
|
||||
string $projectDir,
|
||||
string $legacyDir,
|
||||
string $legacySessionName,
|
||||
string $defaultSessionName,
|
||||
LegacyScopeState $legacyScopeState,
|
||||
ListDataQueryHandler $queryHandler,
|
||||
ModuleNameMapperInterface $moduleNameMapper
|
||||
) {
|
||||
parent::__construct($projectDir, $legacyDir, $legacySessionName, $defaultSessionName, $legacyScopeState);
|
||||
$this->queryHandler = $queryHandler;
|
||||
$this->moduleNameMapper = $moduleNameMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getHandlerKey(): string
|
||||
{
|
||||
return $this->getKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getKey(): string
|
||||
{
|
||||
return self::KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getData(array $query): Statistic
|
||||
{
|
||||
[$module, $id, $criteria, $sort] = $this->extractContext($query);
|
||||
|
||||
if (empty($module) || $module !== 'accounts') {
|
||||
return $this->getEmptySeriesResponse(self::KEY);
|
||||
}
|
||||
|
||||
$this->init();
|
||||
$this->startLegacyApp();
|
||||
|
||||
$legacyName = $this->moduleNameMapper->toLegacy($module);
|
||||
|
||||
$bean = $this->getBean($legacyName);
|
||||
|
||||
if (!$bean instanceof SugarBean) {
|
||||
return $this->getEmptySeriesResponse(self::KEY);
|
||||
}
|
||||
|
||||
$query = $this->queryHandler->getQuery($bean, $criteria, $sort);
|
||||
$query = $this->generateQuery($query);
|
||||
|
||||
$result = $this->runQuery($query, $bean);
|
||||
|
||||
$nameField = 'month';
|
||||
$valueField = 'value';
|
||||
$groupingFields = 'name';
|
||||
$months = $this->getMonths();
|
||||
|
||||
$series = $this->buildMultiSeries($result, $groupingFields, $nameField, $valueField, $months);
|
||||
|
||||
$chartOptions = new ChartOptions();
|
||||
$chartOptions->yAxisTickFormatting = true;
|
||||
$chartOptions->xAxisTicks = $months;
|
||||
|
||||
$statistic = $this->buildSeriesResponse(self::KEY, 'int', $series, $chartOptions);
|
||||
|
||||
$this->close();
|
||||
|
||||
return $statistic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $legacyName
|
||||
* @return bool|SugarBean
|
||||
*/
|
||||
protected function getBean(string $legacyName)
|
||||
{
|
||||
return BeanFactory::newBean($legacyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getMonths(): array
|
||||
{
|
||||
return [1,2,3,4,5,6,7,8,9,10,11,12];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $query
|
||||
* @param $bean
|
||||
* @return array
|
||||
*/
|
||||
protected function runQuery(array $query, $bean): array
|
||||
{
|
||||
// send limit -2 to not add a limit
|
||||
return $this->queryHandler->runQuery($bean, $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $query
|
||||
* @return array
|
||||
*/
|
||||
protected function generateQuery(array $query): array
|
||||
{
|
||||
$query['select'] = 'SELECT COUNT(accounts.name) as value, EXTRACT(MONTH FROM accounts.date_entered) as month, accounts.account_type as name';
|
||||
$query['where'] .= ' AND accounts.account_type is not null ';
|
||||
$query['order_by'] = '';
|
||||
$query['group_by'] = ' GROUP BY EXTRACT(MONTH FROM accounts.date_entered), accounts.account_type';
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Tests\_mock\Mock\core\legacy\Statistics\Series;
|
||||
|
||||
use App\Legacy\Statistics\Series\AccountsNewByMonth;
|
||||
use App\Tests\_mock\Helpers\core\legacy\Data\DBQueryResultsMocking;
|
||||
use SugarBean;
|
||||
|
||||
/**
|
||||
* Class AccountsNewByMonthMock
|
||||
* @package Mock\Core\Legacy\Statistics\Series
|
||||
*/
|
||||
class AccountsNewByMonthMock extends AccountsNewByMonth
|
||||
{
|
||||
use DBQueryResultsMocking;
|
||||
|
||||
/**
|
||||
* @var SugarBean
|
||||
*/
|
||||
public $bean;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function getBean(string $legacyName)
|
||||
{
|
||||
return $this->bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SugarBean $bean
|
||||
*/
|
||||
public function setBean(SugarBean $bean): void
|
||||
{
|
||||
$this->bean = $bean;
|
||||
}
|
||||
|
||||
protected function startLegacyApp(): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function runQuery(array $query, $bean): array
|
||||
{
|
||||
return $this->getAllMockQueryResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function generateQuery(array $query): array
|
||||
{
|
||||
return [
|
||||
'where'
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
namespace App\Tests\unit\core\legacy\Statistics\Series;
|
||||
|
||||
use App\Legacy\Data\FilterMapper\FilterMappers;
|
||||
use App\Legacy\Data\RecordMapper;
|
||||
use App\Legacy\ModuleNameMapperHandler;
|
||||
use App\Legacy\Data\FilterMapper\LegacyFilterMapper;
|
||||
use App\Tests\_mock\Mock\core\legacy\Data\ListDataQueryHandlerMock;
|
||||
use App\Tests\_mock\Mock\core\legacy\Statistics\Series\AccountsNewByMonthMock;
|
||||
use App\Tests\UnitTester;
|
||||
use BeanFactory;
|
||||
use Codeception\Test\Unit;
|
||||
use EmptyIterator;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class AccountsNewByMonthTest
|
||||
* @package App\Tests
|
||||
*/
|
||||
class AccountsNewByMonthTest extends Unit
|
||||
{
|
||||
/**
|
||||
* @var UnitTester
|
||||
*/
|
||||
protected $tester;
|
||||
|
||||
/**
|
||||
* @var AccountsNewByMonthMock
|
||||
*/
|
||||
private $handler;
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function _before(): void
|
||||
{
|
||||
$projectDir = $this->tester->getProjectDir();
|
||||
$legacyDir = $this->tester->getLegacyDir();
|
||||
$legacySessionName = $this->tester->getLegacySessionName();
|
||||
$defaultSessionName = $this->tester->getDefaultSessionName();
|
||||
|
||||
$legacyScope = $this->tester->getLegacyScope();
|
||||
|
||||
$moduleNameMapper = new ModuleNameMapperHandler(
|
||||
$projectDir,
|
||||
$legacyDir,
|
||||
$legacySessionName,
|
||||
$defaultSessionName,
|
||||
$legacyScope
|
||||
);
|
||||
|
||||
$filterMappers = new FilterMappers(new EmptyIterator());
|
||||
$legacyFilterMapper = new LegacyFilterMapper([], $filterMappers);
|
||||
$recordMapper = new RecordMapper($moduleNameMapper);
|
||||
|
||||
$queryHandler = new ListDataQueryHandlerMock($legacyFilterMapper, $recordMapper);
|
||||
|
||||
$this->handler = new AccountsNewByMonthMock(
|
||||
$projectDir,
|
||||
$legacyDir,
|
||||
$legacySessionName,
|
||||
$defaultSessionName,
|
||||
$legacyScope,
|
||||
$queryHandler,
|
||||
$moduleNameMapper
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Unsupported context module
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testUnsupportedContextModule(): void
|
||||
{
|
||||
$this->handler->reset();
|
||||
|
||||
$this->handler->setBean(BeanFactory::newBean('Accounts'));
|
||||
|
||||
$result = $this->handler->getData(
|
||||
[
|
||||
'context' => [
|
||||
'id' => '12345',
|
||||
'modules' => 'contacts'
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
static::assertNotNull($result);
|
||||
static::assertNotNull($result->getData());
|
||||
static::assertNotNull($result->getMetadata());
|
||||
static::assertIsArray($result->getData());
|
||||
static::assertIsArray($result->getMetadata());
|
||||
static::assertEquals('accounts-new-by-month', $result->getId());
|
||||
static::assertArrayHasKey('type', $result->getMetadata());
|
||||
static::assertEquals('series-statistic', $result->getMetadata()['type']);
|
||||
static::assertArrayHasKey('dataType', $result->getMetadata());
|
||||
static::assertEquals('int', $result->getMetadata()['dataType']);
|
||||
static::assertArrayHasKey('multiSeries', $result->getData());
|
||||
static::assertArrayHasKey('singleSeries', $result->getData());
|
||||
static::assertEquals([], $result->getData()['multiSeries']);
|
||||
static::assertEquals([], $result->getData()['singleSeries']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get Total response
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testGetCreatedAccounts(): void
|
||||
{
|
||||
$this->handler->reset();
|
||||
|
||||
$this->handler->setBean(BeanFactory::newBean('Accounts'));
|
||||
|
||||
$rows = [
|
||||
[
|
||||
"name" => 'Customer',
|
||||
"month" => '11',
|
||||
"value" => '12'
|
||||
]
|
||||
];
|
||||
$this->handler->setMockQueryResult($rows);
|
||||
|
||||
$expectedResult = [
|
||||
0 => [
|
||||
'name' => 'Customer',
|
||||
'series' => [
|
||||
['name' => 1, 'value' => '0'],
|
||||
['name' => 2, 'value' => '0'],
|
||||
['name' => 3, 'value' => '0'],
|
||||
['name' => 4, 'value' => '0'],
|
||||
['name' => 5, 'value' => '0'],
|
||||
['name' => 6, 'value' => '0'],
|
||||
['name' => 7, 'value' => '0'],
|
||||
['name' => 8, 'value' => '0'],
|
||||
['name' => 9, 'value' => '0'],
|
||||
['name' => 10, 'value' => '0'],
|
||||
['name' => 11, 'value' => '12'],
|
||||
['name' => 12, 'value' => '0']
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
$result = $this->handler->getData(
|
||||
[
|
||||
'context' => [
|
||||
'module' => 'accounts',
|
||||
'id' => '12345',
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
static::assertNotNull($result);
|
||||
static::assertNotNull($result->getData());
|
||||
static::assertNotNull($result->getMetadata());
|
||||
static::assertIsArray($result->getData());
|
||||
static::assertIsArray($result->getMetadata());
|
||||
static::assertArrayHasKey('multiSeries', $result->getData());
|
||||
static::assertEquals($expectedResult, $result->getData()['multiSeries']);
|
||||
static::assertEquals('accounts-new-by-month', $result->getId());
|
||||
static::assertArrayHasKey('type', $result->getMetadata());
|
||||
static::assertEquals('series-statistic', $result->getMetadata()['type']);
|
||||
static::assertArrayHasKey('dataType', $result->getMetadata());
|
||||
static::assertEquals('int', $result->getMetadata()['dataType']);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue