updated advanced pack to 3.12.0:

Reports:

    Non-aggregated columns in Grid report export.
    Normalized table mode for 2-dimensional Grid reports.
    Ability to create internal reports via the UI.
    Ability to show/hide and resize columns in the list report result view.
This commit is contained in:
2026-02-07 16:09:20 +01:00
parent 26db904407
commit f95246f99f
384 changed files with 6184 additions and 3643 deletions

View File

@@ -11,7 +11,7 @@
* usage to the software or any modified version or derivative work of the software
* created by or for you.
*
* Copyright (C) 2015-2025 EspoCRM, Inc.
* Copyright (C) 2015-2026 EspoCRM, Inc.
*
* License ID: 19bc86a68a7bb01f458cb391d43a9212
************************************************************************************/
@@ -20,8 +20,13 @@ namespace Espo\Modules\Advanced\Tools\Report;
use DateTime;
use DateTimeZone;
use Espo\Core\Binding\BindingContainerBuilder;
use Espo\Core\Binding\ContextualBinder;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Exceptions\Forbidden;
use Espo\Core\InjectableFactory;
use Espo\Core\Select\Applier\AdditionalApplier;
use Espo\Core\Select\SearchParams;
use Espo\Core\Select\Where\Converter;
use Espo\Core\Select\Where\ConverterFactory;
use Espo\Core\Select\Where\Item as WhereItem;
@@ -58,7 +63,8 @@ class SelectHelper
private GridHelper $gridHelper,
private FieldUtil $fieldUtil,
private User $user,
private ConverterFactory $converterFactory
private ConverterFactory $converterFactory,
private InjectableFactory $injectableFactory,
) {}
/**
@@ -415,8 +421,9 @@ class SelectHelper
/**
* @param string[] $columns
* @param bool $isList Should be true only for List report. Should not be true for Sub-List.
*/
public function handleColumns(array $columns, SelectBuilder $queryBuilder): void
public function handleColumns(array $columns, SelectBuilder $queryBuilder, bool $isList = false): void
{
$entityType = $queryBuilder->build()->getFrom();
@@ -425,21 +432,24 @@ class SelectHelper
}
foreach ($columns as $item) {
$this->handleColumnsItem($item, $entityType, $queryBuilder);
$this->handleColumnsItem($item, $entityType, $queryBuilder, $isList);
}
}
/**
* @todo Use the selectDefs attribute dependency map.
* @todo Use the selectDefs attribute dependency map? Or not needed as already applied with the select manager.
*/
private function handleColumnsItem(
string $item,
string $entityType,
SelectBuilder $queryBuilder
SelectBuilder $queryBuilder,
bool $isList = false,
): void {
$columnData = $this->gridHelper->getDataFromColumnName($entityType, $item);
$entityDefs = $this->entityManager->getDefs()->getEntity($entityType);
if ($columnData->function && !$columnData->link && $columnData->field) {
$this->addSelect($item, $queryBuilder);
@@ -460,6 +470,10 @@ class SelectHelper
return;
}
if ($isList) {
return;
}
if (str_contains($item, ':') && str_contains($item, '.')) {
$this->handleLeftJoins($item, $entityType, $queryBuilder);
}
@@ -486,8 +500,11 @@ class SelectHelper
if ($type === 'currency') {
$this->addSelect($item, $queryBuilder);
$this->addSelect($item . 'Currency', $queryBuilder);
$this->addSelect($item . 'Converted', $queryBuilder);
if (!$entityDefs->tryGetField($item)?->getParam('notStorable')) {
$this->addSelect($item . 'Currency', $queryBuilder);
$this->addSelect($item . 'Converted', $queryBuilder);
}
return;
}
@@ -695,6 +712,8 @@ class SelectHelper
throw new LogicException("No from.");
}
$this->applyWhereFilterAdditionalAppliers($entityType, $whereItem, $queryBuilder);
// Supposed to be applied by the scanner.
//$this->applyLeftJoinsFromWhere($whereItem, $queryBuilder);
@@ -886,4 +905,47 @@ class SelectHelper
{
return $this->converterFactory->create($entityType, $this->user);
}
/**
* @return class-string<AdditionalApplier>[]
*/
private function getWhereFiltersApplierClassNameList(string $entityType): array
{
/** @var class-string<AdditionalApplier>[] */
return $this->metadata
->get("app.advancedReport.entityParams.$entityType.whereFilterAdditionalApplierClassNameList") ?? [];
}
/**
* @param class-string<AdditionalApplier> $className
*/
private function createAdditionalApplier(string $entityType, string $className): AdditionalApplier
{
return $this->injectableFactory->createWithBinding(
$className,
BindingContainerBuilder::create()
->bindInstance(User::class, $this->user)
->inContext($className, function (ContextualBinder $binder) use ($entityType) {
$binder->bindValue('$entityType', $entityType);
})
->build()
);
}
private function applyWhereFilterAdditionalAppliers(
string $entityType,
WhereItem $whereItem,
SelectBuilder $queryBuilder,
): void {
$additionalApplierClassNameList = $this->getWhereFiltersApplierClassNameList($entityType);
foreach ($additionalApplierClassNameList as $className) {
$applier = $this->createAdditionalApplier($entityType, $className);
$searchParams = SearchParams::create()->withWhere($whereItem);
$applier->apply($queryBuilder, $searchParams);
}
}
}