Give us contact

Do you prefer to ask us directly?

Call us +420 605 203 938 (the Czech Republic)

or use this contacts

AyMINE

Related links


FI - Finance Management

Report hierarchical objects

AyMINE report engine supports generation of the hierarchical object structure.

Typically, the children objects are of the same type as the parent objects (like subtasks under the master task). We call them genuine children. But the hierarchical reports can be used even when child objects are various (e.g. all object stored in the baseline repository.)

Generate report with genuine children

When children have the same type as parent, all objects are generated from the same report template. Report support infinite depth of children.

The template for hierarchical object structure should contain a tag where to put the children:

<body>
   <h1>@name</h1>
     // object report definition

   @children

   // optional object report part after the children section
</body>

The template contains template pattern inserted instead of @children if children are available:

<subitems>
   <h2>Children section name</h2>
   <div class="subreports">
      <table class="noVisible">
         <tr>
            <td style="width:5%;border-top:none">&nbsp;</td>
            <td style="width:95%;border-top: none">@subitems</td>
         </tr>
      </table>   
   </div>
</subitems>

The example creates report with children indented 5% from left

Report generation

public static function getReport(reportDesc $rep): htmlPattern
   {
      // Create report pattern for parent object
      $rep->setTemplateName();            // creates report with default name
      $pat = htmlPattern::init($rep);     // create location where is stored the the parent report

      // build parent report

      // check if there are children
      $childrenID = $tskDB->getSQLIDList("<select children>");
      if ($childrenID) {      
         // reuse the report pattern for children
         // pattern (object reportDesc) is the same for children 
         // but children are generated to the new report storage (object htmlPattern)
         $childPat = $rep->reusePattern = null;  
         foreach ($childrenID as $childID) {
            $rep->objID = $childID;
            // all children are reported one after another to the same htmlPattern
            $childPat = static::getReport($rep);
         }
         // complete the children generations
         $childPat->finalizeReport();  
         // Change level of header so that they have sub-header level (from h1->h2 etc.)
         $childPat->shiftHeader();
         // Put all generated children reports to the parent report
         $pat->fillPlaceTag('subitems', ['subitems' => $childPat->superbody]);
      } else {
         // If parent has no children, remove the @subitems from the pattern
         $pat->fillSingle('subitems', '');
      } 
      return $pat;  // return generated report
   }

Description of the steps in the method

The method creates new placement where is the report generated only if the placement is not yet created:

$pat = htmlPattern::init($rep);  

$pat contain instance of with the location, but location is also referenced by the $rep->reusePattern attribute of the reportDesc. If method getReport is called several times for several sibbling, new htmlPattern is not generated for each sibling. All siblings are generated one after another – see more in the section generate list of siblings. However,:

$childPat = $rep->reusePattern = null;  

removes the reference to the htmlPattern from the reportDesc $rep and so, when function getReport is called for 1st child, it creates new htmlPattern location for children.

Put children to the parent report

All children have generated report in the single report stored in the $childPat variable. The report is placed to the $childPat in the line

$childPat = static::getReport($rep);

because the last line of the method returns that return $pat;.

Let' note that after the children generation, the returned htmlPattern with the children reports is also in the $rep->reusePattern attribute, i.e. $rep->reusePattern == returned $pat; But the $pat variable in the parent report method contain report of the parent, not the children.

The line:

$pat->fillPlaceTag('subitems', ['subitems' => $childPat->superbody]);

puts the generated report with children to the report of the $parent.
The fillPlaceTag method:

  1. Finds template pattern with the name from the 1st attribute (subitems in the example) in the report definition
  2. Fill all fields in the template pattern using the array in the second attribute. It creates from the template pattern the filled block
  3. Replaces the pattern placement with the same name in the $pat report with the filled block (the filled block replaces the @subitems in the example).

When object has no children report script should remove the pattern placement from the report. It is made by:

$pat->fillSingle('subitems', '');

line that replace the @subitems placement by empty string.

Generate "children" of various types

Some objects have children of various types. E.g. a project have several types of children as well as baseline in the configuration management. However, there are significant differences between both examples:

  • Although project has several types of subordinated objects (like plans, requirements, tasks etc.) they are generally known because they are pre-defined by the project methodology. The report for project could make section for each object from the report management
  • On the other hand, Baseline has not pre-defined objects and report cannot count with that. It should be able to report all kinds object configuration items.

This section describes how to create reports with the overview of the uknonwn list of children objects. It is made similarly with as is desribes for gunine children herinbefore, so that only differences are described here.

While gunine children uses the same report pattern as the parent object and all children siblings uses the same report pattern the arbitrary children uses their own patterns. For each child should be used the appropriate pattern.

How is the right report pattern selected

Name of the report patterns follows the same logic for all object. The pattern is called <objectName>-<patternName>. When pattern name is not specified default pattern name is used.

Children always use the pattern with the same name as parent object has regardless of the object name.

Example:
Configuration baseline is reported by the report ShortOverview so that, report is generated from the html template tskCMBundle__ShortOverview.

For each configuration item is called report <objectName>__ShortOverview, e.g. when a requirement is reported, the template tskRequirement__ShortOverview is used.

Generate reports with ungenuine children

The following code is a part of the getReport function that generates report of various children. Children are grouped by the object type (objectName).

// get list of children object names
$setObjects = $dbo->query("SELECT distinct  objectName, count(0) as objC from ...");
// go over all of them
foreach ($setObjects as $objInfo) {
   // get list of childern IDs for the $objInfo->objectName object type 
   $objList = $tskDB->getSQLIDList(
         "SELECT objectID 
         FROM <... table for objectName> 
         WHERE <... id field anme> = $rep->objID and objectName='$objInfo->objectName' 
         order by 1"
   );
   // get name of the object ($objInfo->objectName) translated to the report language ($rep->lang)
   $objInfo->objTrans = langProvider::getObjName($objInfo->objectName, $rep->lang);
   $objTrans = new langProvider::getObjName($objInfo->objectName, $pat->lang);   
   $objTrans->loadObjectLang($objInfo->objectName);
   // Write header for section with objects:
   $objectSuperbody .= '<h2 class="sectionHeader">' . $plural . '</h2>';
   // Clear report pattern – new type of object requires new report pattern
   $report = null;
   try {
      // Create report description for new type of objects
      $childRep = new reportDesc(
         $objInfo->objectName,  // name of the object
         null,                  // objectID – filed later in the cycle
         $rep->lang,            // report language
         $rep->templateName,    // copy the template name – the same name is used in all levels
         $rep->format           // use the same format (html / text / markdown) at all levels
      );
      // Generate reports for all object of the type objectName
      foreach ($objList as $objID) {
         $childRep->objID = $objID;
         // Put the htmlPattern that is used for all children – each part is placed at the end
         $childRep->reusePattern = $report;
         $report = $objInfo->objectName::getReport($childRep);
      }
      // Complete the section for single object type but does not finalise until all children objects will be generated
      $report->prepareForNext();
      $report->shiftHeader();
      $objectSuperbody .= $report->superbody;


      if (isset($childRep->userTag) && $childRep->userTag != []) {
         $objInfo->groupedMultiEnum = implode(' ', $childRep->userTag);
      } else $objInfo->groupedMultiEnum = '';

      $pat->fillTagToPart('objOverview', 'objOverview',  $objInfo);
   } catch (\Throwable $th) {
      $objectSuperbody .= "<p>Error: missing object template?</p>";
   }
}

Note:
Translation of the object name is loaded from the objectName counter by line:

$objInfo->objTrans = langProvider::getObjName($objInfo->objectName, $rep->lang);

The command returns singular translation, not plural. AyMINE mostly have also plural version translated and the plural is available by using calls:

   $objTrans->loadObjectLang($objInfo->objectName);
   $plural = $objTrans->get("$objInfo->objectName.plural");   // Warning: EXPENSIVE

However this call is rather time consuming and so not recomended for more complex scripts. If plural translation are often used, write their translation to the m_<module>Lang file.