PageRenderTime 59ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 1ms

/app/protected/modules/reports/models/Report.php

https://bitbucket.org/ddonthula/zurmofeb
PHP | 734 lines | 368 code | 73 blank | 293 comment | 34 complexity | 7bb0ca939b29f523b4180d55001f2469 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-2-Clause, GPL-3.0, BSD-3-Clause, LGPL-3.0
  1. <?php
  2. /*********************************************************************************
  3. * Zurmo is a customer relationship management program developed by
  4. * Zurmo, Inc. Copyright (C) 2012 Zurmo Inc.
  5. *
  6. * Zurmo is free software; you can redistribute it and/or modify it under
  7. * the terms of the GNU General Public License version 3 as published by the
  8. * Free Software Foundation with the addition of the following permission added
  9. * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
  10. * IN WHICH THE COPYRIGHT IS OWNED BY ZURMO, ZURMO DISCLAIMS THE WARRANTY
  11. * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
  12. *
  13. * Zurmo is distributed in the hope that it will be useful, but WITHOUT
  14. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  15. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU General Public License along with
  19. * this program; if not, see http://www.gnu.org/licenses or write to the Free
  20. * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  21. * 02110-1301 USA.
  22. *
  23. * You can contact Zurmo, Inc. with a mailing address at 113 McHenry Road Suite 207,
  24. * Buffalo Grove, IL 60089, USA. or at email address contact@zurmo.com.
  25. ********************************************************************************/
  26. /**
  27. * Class for interacting with Report definitions. Gets information from either a SavedReport or via a POST.
  28. * Contains information about how a report should be constructed including how it looks in the user interface
  29. * when run. The components of a report are filters, orderBys, groupBys, displayAttributes,
  30. * drillDownDisplayAttributes, and a chart.
  31. *
  32. * There are 3 different types of reports: TYPE_ROWS_AND_COLUMNS, TYPE_SUMMATION, and TYPE_MATRIX. Only Summation
  33. * utilizes a chart and can have drillDownDisplayAttributes
  34. */
  35. class Report extends CComponent
  36. {
  37. /**
  38. * Defines a report type of Rows and Columns. This is the basic report type and isa simple list result
  39. */
  40. const TYPE_ROWS_AND_COLUMNS = 'RowsAndColumns';
  41. /**
  42. * Defines a report type of Summation. A summation report can group data into a result grid. It can also
  43. * have a chart and also allow for drill down into each row to get further information about each group
  44. */
  45. const TYPE_SUMMATION = 'Summation';
  46. /**
  47. * Defines a report type of Matrix. Complex report type that allows multiple groupings across both the x and y
  48. * axises.
  49. */
  50. const TYPE_MATRIX = 'Matrix';
  51. /**
  52. * Currency Conversion Type for rendering currency information. "Actual" means the currency will not be converted
  53. * to the base or a spot currency. It can produce mixed results depending on how the data is being aggregated
  54. * for a report.
  55. */
  56. const CURRENCY_CONVERSION_TYPE_ACTUAL = 1;
  57. /**
  58. * Currency Conversion Type for rendering currency information. "Base" means that currency data is converted
  59. * into the system base currency and displayed in this currency
  60. */
  61. const CURRENCY_CONVERSION_TYPE_BASE = 2;
  62. /**
  63. * Currency Conversion Type for rendering currency information. "Spot" means the currency is converted into
  64. * the base currency and then converted into a spot currency defined by the user when creating the report
  65. */
  66. const CURRENCY_CONVERSION_TYPE_SPOT = 3;
  67. /**
  68. * User defined description of the report. This is optional
  69. * @var string
  70. */
  71. private $description;
  72. /**
  73. * Set from SavedReport
  74. * @var object ExplicitReadWriteModelPermissions
  75. */
  76. private $explicitReadWriteModelPermissions;
  77. /**
  78. * Id of the saved report if it has already been saved
  79. * @var integer
  80. */
  81. private $id;
  82. /**
  83. * Module class name that the report is constructed on
  84. * @var string
  85. */
  86. private $moduleClassName;
  87. /**
  88. * User defined name of the report
  89. * @var string
  90. */
  91. private $name;
  92. /**
  93. * Set from the SavedReport
  94. * @var object User
  95. */
  96. private $owner;
  97. /**
  98. * Defines the report type
  99. * @var string
  100. */
  101. private $type;
  102. /**
  103. * Defines the filters structure. An example is "1 AND 2". This example would be used if there are 2 filters
  104. * for the report.
  105. * @var
  106. */
  107. private $filtersStructure;
  108. /**
  109. * Array of of FilterForReportForm objects
  110. * @var array
  111. */
  112. private $filters = array();
  113. /**
  114. * Array of OrderByFoReportForm objects
  115. * @var array
  116. */
  117. private $orderBys = array();
  118. /**
  119. * Array of DisplayAttributeForReportForm objects
  120. * @var array
  121. */
  122. private $displayAttributes = array();
  123. /**
  124. * Array of DrillDownDisplayAttributeForReportForm objects
  125. * @var array
  126. */
  127. private $drillDownDisplayAttributes = array();
  128. /**
  129. * Array of GroupByForReportForm objects
  130. * @var array
  131. */
  132. private $groupBys = array();
  133. /**
  134. * @var object ChartForReportForm
  135. */
  136. private $chart;
  137. /**
  138. * Currency conversion type used for rendering currency data. There are three types
  139. * CURRENCY_CONVERSION_TYPE_ACTUAL, CURRENCY_CONVERSION_TYPE_BASE, and CURRENCY_CONVERSION_TYPE_SPOT
  140. * @var integer
  141. */
  142. private $currencyConversionType;
  143. /**
  144. * If the $currencyConversionType is CURRENCY_CONVERSION_TYPE_SPOT, then this property is utilized to define
  145. * the currency code for spot conversion
  146. * @var string
  147. */
  148. private $spotConversionCurrencyCode;
  149. /**
  150. * @return array of report type values and labels
  151. */
  152. public static function getTypeDropDownArray()
  153. {
  154. return array(self::TYPE_ROWS_AND_COLUMNS => Zurmo::t('ReportsModule', 'Rows and Columns'),
  155. self::TYPE_SUMMATION => Zurmo::t('ReportsModule', 'Summation'),
  156. self::TYPE_MATRIX => Zurmo::t('ReportsModule', 'Matrix'),);
  157. }
  158. /**
  159. * Based on the current user, return the reportable modules and their display labels. Only include modules
  160. * that the user has a right to access.
  161. * @return array of module class names and display labels.
  162. */
  163. public static function getReportableModulesAndLabelsForCurrentUser()
  164. {
  165. $moduleClassNamesAndLabels = array();
  166. $modules = Module::getModuleObjects();
  167. foreach (self::getReportableModulesClassNamesCurrentUserHasAccessTo() as $moduleClassName)
  168. {
  169. if($moduleClassName::getStateMetadataAdapterClassName() != null)
  170. {
  171. $reportRules = ReportRules::makeByModuleClassName($moduleClassName);
  172. $label = $reportRules->getVariableStateModuleLabel(Yii::app()->user->userModel);
  173. }
  174. else
  175. {
  176. $label = $moduleClassName::getModuleLabelByTypeAndLanguage('Plural');
  177. }
  178. if($label != null)
  179. {
  180. $moduleClassNamesAndLabels[$moduleClassName] = $label;
  181. }
  182. }
  183. return $moduleClassNamesAndLabels;
  184. }
  185. /**
  186. * @return array of module class names and display labels the current user has access to
  187. */
  188. public static function getReportableModulesClassNamesCurrentUserHasAccessTo()
  189. {
  190. $moduleClassNames = array();
  191. $modules = Module::getModuleObjects();
  192. foreach ($modules as $module)
  193. {
  194. if($module::isReportable())
  195. {
  196. if (ReportSecurityUtil::canCurrentUserCanAccessModule(get_class($module)))
  197. {
  198. $moduleClassNames[] = get_class($module);
  199. }
  200. }
  201. }
  202. return $moduleClassNames;
  203. }
  204. public function __toString()
  205. {
  206. if (trim($this->name) == '')
  207. {
  208. return Zurmo::t('ReportsModule', '(Unnamed)');
  209. }
  210. return $this->name;
  211. }
  212. /**
  213. * Returns true if the current user can render a report's results properly. This method checks to see if the
  214. * user has full access to all the related modules and data that the report uses in construction. This method
  215. * is needed because it is possible the author of a report added access for users that do not have complete
  216. * rights to the modules that are part of the report. It is also possible this access changed over time and
  217. * a report that was once properly rendered is no longer.
  218. * @return bool
  219. */
  220. public function canCurrentUserProperlyRenderResults()
  221. {
  222. if(!ReportSecurityUtil::canCurrentUserCanAccessModule($this->moduleClassName))
  223. {
  224. return false;
  225. }
  226. if(!ReportSecurityUtil::canCurrentUserAccessAllComponents($this->displayAttributes))
  227. {
  228. return false;
  229. }
  230. if(!ReportSecurityUtil::canCurrentUserAccessAllComponents($this->filters))
  231. {
  232. return false;
  233. }
  234. if(!ReportSecurityUtil::canCurrentUserAccessAllComponents($this->orderBys))
  235. {
  236. return false;
  237. }
  238. if(!ReportSecurityUtil::canCurrentUserAccessAllComponents($this->groupBys))
  239. {
  240. return false;
  241. }
  242. if(!ReportSecurityUtil::canCurrentUserAccessAllComponents($this->drillDownDisplayAttributes))
  243. {
  244. return false;
  245. }
  246. return true;
  247. }
  248. /**
  249. * @return string
  250. */
  251. public function getModuleClassName()
  252. {
  253. return $this->moduleClassName;
  254. }
  255. /**
  256. * @param $moduleClassName string
  257. */
  258. public function setModuleClassName($moduleClassName)
  259. {
  260. assert('is_string($moduleClassName)');
  261. $this->moduleClassName = $moduleClassName;
  262. }
  263. /**
  264. * @return string
  265. */
  266. public function getDescription()
  267. {
  268. return $this->description;
  269. }
  270. /**
  271. * @param $description
  272. */
  273. public function setDescription($description)
  274. {
  275. assert('is_string($description) || $description == null');
  276. $this->description = $description;
  277. }
  278. /**
  279. * @param $filtersStructure string
  280. */
  281. public function setFiltersStructure($filtersStructure)
  282. {
  283. assert('is_string($filtersStructure)');
  284. $this->filtersStructure = $filtersStructure;
  285. }
  286. /**
  287. * @return array of FilterForReportForm objects
  288. */
  289. public function getFiltersStructure()
  290. {
  291. return $this->filtersStructure;
  292. }
  293. /**
  294. * @return int
  295. */
  296. public function getId()
  297. {
  298. return $this->id;
  299. }
  300. /**
  301. * @param $id int
  302. */
  303. public function setId($id)
  304. {
  305. assert('is_int($id)');
  306. $this->id = $id;
  307. }
  308. /**
  309. * @return string
  310. */
  311. public function getName()
  312. {
  313. return $this->name;
  314. }
  315. /**
  316. * @param $name string
  317. */
  318. public function setName($name)
  319. {
  320. assert('is_string($name)');
  321. $this->name = $name;
  322. }
  323. /**
  324. * @return string
  325. */
  326. public function getType()
  327. {
  328. return $this->type;
  329. }
  330. /**
  331. * @param $type string
  332. */
  333. public function setType($type)
  334. {
  335. assert('$type == self::TYPE_ROWS_AND_COLUMNS || $type == self::TYPE_SUMMATION || $type == self::TYPE_MATRIX');
  336. $this->type = $type;
  337. }
  338. /**
  339. * @return int
  340. */
  341. public function getCurrencyConversionType()
  342. {
  343. return $this->currencyConversionType;
  344. }
  345. /**
  346. * @param $currencyConversionType int
  347. */
  348. public function setCurrencyConversionType($currencyConversionType)
  349. {
  350. assert('is_int($currencyConversionType)');
  351. $this->currencyConversionType = $currencyConversionType;
  352. }
  353. /**
  354. * @return string
  355. */
  356. public function getSpotConversionCurrencyCode()
  357. {
  358. return $this->spotConversionCurrencyCode;
  359. }
  360. /**
  361. * @param $spotConversionCurrencyCode string
  362. */
  363. public function setSpotConversionCurrencyCode($spotConversionCurrencyCode)
  364. {
  365. assert('is_string($spotConversionCurrencyCode)');
  366. $this->spotConversionCurrencyCode = $spotConversionCurrencyCode;
  367. }
  368. /**
  369. * @return float
  370. */
  371. public function getFromBaseToSpotRate()
  372. {
  373. return 1 / Yii::app()->currencyHelper->getConversionRateToBase($this->spotConversionCurrencyCode);
  374. }
  375. /**
  376. * @return bool
  377. */
  378. public function isNew()
  379. {
  380. if($this->id > 0)
  381. {
  382. return false;
  383. }
  384. return true;
  385. }
  386. /**
  387. * @return object
  388. */
  389. public function getOwner()
  390. {
  391. if($this->owner == null)
  392. {
  393. $this->owner = Yii::app()->user->userModel;
  394. }
  395. return $this->owner;
  396. }
  397. /**
  398. * @param User $owner
  399. */
  400. public function setOwner(User $owner)
  401. {
  402. $this->owner = $owner;
  403. }
  404. /**
  405. * @return array of FilterForReportForm objects
  406. */
  407. public function getFilters()
  408. {
  409. return $this->filters;
  410. }
  411. /**
  412. * @param FilterForReportForm $filter
  413. */
  414. public function addFilter(FilterForReportForm $filter)
  415. {
  416. $this->filters[] = $filter;
  417. }
  418. /**
  419. * Removes all FilterForReportForm objects on this report
  420. */
  421. public function removeAllFilters()
  422. {
  423. $this->filters = array();
  424. }
  425. /**
  426. * @return array of GroupByForReportForm objects
  427. */
  428. public function getGroupBys()
  429. {
  430. return $this->groupBys;
  431. }
  432. /**
  433. * @param GroupByForReportForm $groupBy
  434. */
  435. public function addGroupBy(GroupByForReportForm $groupBy)
  436. {
  437. $this->groupBys[] = $groupBy;
  438. }
  439. /**
  440. * Removes all GroupByForReportForm objects on this report
  441. */
  442. public function removeAllGroupBys()
  443. {
  444. $this->groupBys = array();
  445. }
  446. /**
  447. * @return array of OrderByForReportForm objects
  448. */
  449. public function getOrderBys()
  450. {
  451. return $this->orderBys;
  452. }
  453. /**
  454. * @param OrderByForReportForm $orderBy
  455. */
  456. public function addOrderBy(OrderByForReportForm $orderBy)
  457. {
  458. $this->orderBys[] = $orderBy;
  459. }
  460. /**
  461. * Removes all OrderByForReportForm objects on this report
  462. */
  463. public function removeAllOrderBys()
  464. {
  465. $this->orderBys = array();
  466. }
  467. /**
  468. * @return array of DisplayAttributeForReportForm objects
  469. */
  470. public function getDisplayAttributes()
  471. {
  472. return $this->displayAttributes;
  473. }
  474. /**
  475. * @param DisplayAttributeForReportForm $displayAttribute
  476. */
  477. public function addDisplayAttribute(DisplayAttributeForReportForm $displayAttribute)
  478. {
  479. $this->displayAttributes[] = $displayAttribute;
  480. }
  481. /**
  482. * Removes all DisplayAttributeForReportForm objects on this report
  483. */
  484. public function removeAllDisplayAttributes()
  485. {
  486. $this->displayAttributes = array();
  487. }
  488. /**
  489. * @return array of DrillDownDisplayAttributeForReportForm objects
  490. */
  491. public function getDrillDownDisplayAttributes()
  492. {
  493. return $this->drillDownDisplayAttributes;
  494. }
  495. /**
  496. * @param DrillDownDisplayAttributeForReportForm $drillDownDisplayAttribute
  497. */
  498. public function addDrillDownDisplayAttribute(DrillDownDisplayAttributeForReportForm $drillDownDisplayAttribute)
  499. {
  500. $this->drillDownDisplayAttributes[] = $drillDownDisplayAttribute;
  501. }
  502. /**
  503. * Removes all DrillDownDisplayAttributeForReportForm objects on this report
  504. */
  505. public function removeAllDrillDownDisplayAttributes()
  506. {
  507. $this->drillDownDisplayAttributes = array();
  508. }
  509. /**
  510. * @return ChartForReportForm|object
  511. */
  512. public function getChart()
  513. {
  514. if($this->chart == null)
  515. {
  516. $this->chart = new ChartForReportForm();
  517. }
  518. return $this->chart;
  519. }
  520. /**
  521. * @param ChartForReportForm $chart
  522. */
  523. public function setChart(ChartForReportForm $chart)
  524. {
  525. $this->chart = $chart;
  526. }
  527. /**
  528. * Returns true if the report has a chart
  529. * @return bool
  530. */
  531. public function hasChart()
  532. {
  533. if($this->getChart()->type == null)
  534. {
  535. return false;
  536. }
  537. return true;
  538. }
  539. /**
  540. * @return ExplicitReadWriteModelPermissions|object
  541. */
  542. public function getExplicitReadWriteModelPermissions()
  543. {
  544. if($this->explicitReadWriteModelPermissions == null)
  545. {
  546. $this->explicitReadWriteModelPermissions = new ExplicitReadWriteModelPermissions();
  547. }
  548. return $this->explicitReadWriteModelPermissions;
  549. }
  550. /**
  551. * Set from the value in the SavedReport
  552. * @param ExplicitReadWriteModelPermissions $explicitReadWriteModelPermissions
  553. */
  554. public function setExplicitReadWriteModelPermissions(ExplicitReadWriteModelPermissions $explicitReadWriteModelPermissions)
  555. {
  556. $this->explicitReadWriteModelPermissions = $explicitReadWriteModelPermissions;
  557. }
  558. /**
  559. * Returns true if at least one filter is available at runtime.
  560. * @return bool
  561. */
  562. public function hasRuntimeFilters()
  563. {
  564. foreach($this->getFilters() as $filter)
  565. {
  566. if($filter->availableAtRunTime)
  567. {
  568. return true;
  569. }
  570. }
  571. return false;
  572. }
  573. /**
  574. * Given an attributeIndexOrDerivedType, return the key of the $displayAttributes that corresponds to the
  575. * DisplayAttributeForReportForm object that has the given attribute
  576. * @param $attribute
  577. * @return int|null|string
  578. */
  579. public function getDisplayAttributeIndex($attribute)
  580. {
  581. foreach($this->displayAttributes as $key => $displayAttribute)
  582. {
  583. if($attribute == $displayAttribute->attributeIndexOrDerivedType)
  584. {
  585. return $key;
  586. }
  587. }
  588. return null;
  589. }
  590. /**
  591. * Given an attributeIndexOrDerivedType, return the DisplayAttributeForReportForm object that has that
  592. * attributeIndexOrDerivedType
  593. * @param $attribute
  594. * @return mixed
  595. * @throws NotFoundException if it is not found
  596. */
  597. public function getDisplayAttributeByAttribute($attribute)
  598. {
  599. foreach($this->getDisplayAttributes() as $displayAttribute)
  600. {
  601. if($attribute == $displayAttribute->attributeIndexOrDerivedType)
  602. {
  603. return $displayAttribute;
  604. }
  605. }
  606. throw new NotFoundException();
  607. }
  608. /**
  609. * Utilized for summation with drill down rows. For a given group, the grouped value needs to be used
  610. * as a filter for the drilled down row. This method will add that groupBy as a filter and update the
  611. * filterStructure accordingly.
  612. * @param array $getData
  613. */
  614. public function resolveGroupBysAsFilters(Array $getData)
  615. {
  616. $newStartingStructurePosition = count($this->filters) + 1;
  617. $structure = null;
  618. foreach($this->getGroupBys() as $groupBy)
  619. {
  620. $index = ReportResultsRowData::resolveDataParamKeyForDrillDown($groupBy->attributeIndexOrDerivedType);
  621. $value = $getData[$index];
  622. $filter = new FilterForReportForm($groupBy->getModuleClassName(),
  623. $groupBy->getModelClassName(),
  624. $this->type);
  625. $filter->attributeIndexOrDerivedType = $groupBy->attributeIndexOrDerivedType;
  626. self::resolveGroupByAsFilterValue($value, $filter);
  627. $this->addFilter($filter);
  628. if($structure != null)
  629. {
  630. $structure .= ' AND ';
  631. }
  632. $structure .= $newStartingStructurePosition;
  633. $newStartingStructurePosition ++;
  634. }
  635. $structure = '(' . $structure . ')';
  636. if($this->filtersStructure != null)
  637. {
  638. $this->filtersStructure .= ' AND ';
  639. }
  640. $this->filtersStructure .= $structure;
  641. }
  642. /**
  643. * Given a value and a filter, resolve the value for being null or not. If null then a different operator
  644. * is used on the value than if it is not null.
  645. * @param $value
  646. * @param FilterForReportForm $filter
  647. */
  648. protected static function resolveGroupByAsFilterValue($value, FilterForReportForm $filter)
  649. {
  650. if($value != null)
  651. {
  652. $filter->operator = OperatorRules::TYPE_EQUALS;
  653. $filter->value = $value;
  654. }
  655. else
  656. {
  657. $filter->operator = OperatorRules::TYPE_IS_NULL;
  658. }
  659. }
  660. }
  661. ?>