From 29c1efb3e51254b2b236bf06212296a3b2205c09 Mon Sep 17 00:00:00 2001
From: "e.kremnev" <e.kremnev@bingosoft.ru>
Date: Tue, 15 Nov 2022 14:09:18 +0300
Subject: [PATCH 01/10] =?UTF-8?q?fix:=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?=
 =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D1=80=D0=B5=D0=B7=D1=83=D0=BB=D1=8C?=
 =?UTF-8?q?=D1=82=D0=B0=D1=82=20=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D0=B8=20?=
 =?UTF-8?q?=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D0=BE=D0=B2=20=D0=B4=D0=BB?=
 =?UTF-8?q?=D1=8F=20ApiQL=20=D0=BF=D0=BE=20=D1=81=D0=BE=D1=81=D1=82=D0=BE?=
 =?UTF-8?q?=D1=8F=D0=BD=D0=B8=D1=8F=D0=BC=20=D0=B2=20StateService?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../Service/StateServiceInterface.php          |  4 ++--
 .../State/Formatter/ComplexStateFormatter.php  |  6 +++---
 .../Service/State/Formatter/Formatter.php      | 18 ++++++++++++++++--
 .../State/Formatter/SimpleStateFormatter.php   | 12 +++++++++++-
 .../Service/State/StateService.php             |  4 ++--
 5 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/src/Application/Service/StateServiceInterface.php b/src/Application/Service/StateServiceInterface.php
index fba3ac3..2e6e3f5 100644
--- a/src/Application/Service/StateServiceInterface.php
+++ b/src/Application/Service/StateServiceInterface.php
@@ -10,7 +10,7 @@ namespace ServiceCore\Application\Service;
 interface StateServiceInterface
 {
     /**
-     * @param int[]|int $states
+     * @param int[] $states
      * @param array $recordData
      * @param string $logicalOperator
      * @param bool $addWhere
@@ -18,7 +18,7 @@ interface StateServiceInterface
      * @return array
      */
     public function convertToApiQl(
-        $states,
+        array $states,
         array $recordData = [],
         string $logicalOperator = 'and',
         bool $addWhere = true
diff --git a/src/Infrastructure/Service/State/Formatter/ComplexStateFormatter.php b/src/Infrastructure/Service/State/Formatter/ComplexStateFormatter.php
index ab52cac..a518c3f 100644
--- a/src/Infrastructure/Service/State/Formatter/ComplexStateFormatter.php
+++ b/src/Infrastructure/Service/State/Formatter/ComplexStateFormatter.php
@@ -46,14 +46,14 @@ class ComplexStateFormatter
         $this->recordData = $recordData;
     }
 
-    public function format(array $state): array
+    public function format(array $state): ?array
     {
         if (!isset($state['properties'])) {
-            return [];
+            return null;
         }
 
         if (!isset($state['properties']['filters'])) {
-            return [];
+            return null;
         }
 
         return $this->finalFormat([
diff --git a/src/Infrastructure/Service/State/Formatter/Formatter.php b/src/Infrastructure/Service/State/Formatter/Formatter.php
index ef2648a..e05b33f 100644
--- a/src/Infrastructure/Service/State/Formatter/Formatter.php
+++ b/src/Infrastructure/Service/State/Formatter/Formatter.php
@@ -121,7 +121,21 @@ class Formatter
         ];
 
         foreach ($states as $state) {
-            $exprGroups['query']['children'][] = $this->formatting($state);
+            $childGroup = $this->formatting($state);
+
+            if (!is_null($childGroup)) {
+                if (
+                    $childGroup['query']['logical_operator'] === $logicalOperator
+                    && count($childGroup['query']['children']) === 1
+                ) {
+                    $exprGroups['query']['children'] = array_merge(
+                        $exprGroups['query']['children'],
+                        $childGroup['query']['children']
+                    );
+                } else {
+                    $exprGroups['query']['children'][] = $childGroup;
+                }
+            }
         }
 
         return $exprGroups;
@@ -132,7 +146,7 @@ class Formatter
      *
      * @return array
      */
-    private function formatting(array $state): array {
+    private function formatting(array $state): ?array {
         if ($state['is_complex'] === true) {
             return $this->complexFormatter->format($state);
         } else {
diff --git a/src/Infrastructure/Service/State/Formatter/SimpleStateFormatter.php b/src/Infrastructure/Service/State/Formatter/SimpleStateFormatter.php
index fffb813..a4b4596 100644
--- a/src/Infrastructure/Service/State/Formatter/SimpleStateFormatter.php
+++ b/src/Infrastructure/Service/State/Formatter/SimpleStateFormatter.php
@@ -46,8 +46,18 @@ class SimpleStateFormatter
         $this->recordData = $recordData;
     }
 
-    public function format(array $state): array
+    public function format(array $state): ?array
     {
+        if (
+            !is_array($state['state_fields'])
+            || (
+                is_array($state['state_fields'])
+                && count($state['state_fields']) === 0
+            )
+        ) {
+            return null;
+        }
+
         $expressions = [];
 
         foreach ($state['state_fields'] as $stateField) {
diff --git a/src/Infrastructure/Service/State/StateService.php b/src/Infrastructure/Service/State/StateService.php
index 29ec2e1..08238e3 100644
--- a/src/Infrastructure/Service/State/StateService.php
+++ b/src/Infrastructure/Service/State/StateService.php
@@ -49,7 +49,7 @@ class StateService implements StateServiceInterface
     /**
      * Конвертирует состояния в ApiQL
      *
-     * @param int[]|int $states
+     * @param int[] $states
      * @param array $recordData
      * @param string $logicalOperator
      * @param bool $addWhere
@@ -57,7 +57,7 @@ class StateService implements StateServiceInterface
      * @return array
      */
     public function convertToApiQl(
-        $states,
+        array $states,
         array $recordData = [],
         string $logicalOperator = 'and',
         bool $addWhere = true
-- 
GitLab


From cad59a204b63464b07ae13bc696478f53f604750 Mon Sep 17 00:00:00 2001
From: "e.kremnev" <e.kremnev@bingosoft.ru>
Date: Fri, 18 Nov 2022 11:26:58 +0300
Subject: [PATCH 02/10] =?UTF-8?q?fix:=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?=
 =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83?=
 =?UTF-8?q?=D0=B7=D0=BA=D0=B0=20=D1=80=D0=B5=D1=81=D1=83=D1=80=D1=81=D0=BE?=
 =?UTF-8?q?=D0=B2=20=D0=B2=20=D0=B1=D1=83=D1=82=D1=81=D1=82=D1=80=D0=B0?=
 =?UTF-8?q?=D0=BF=D0=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../Query/Service/QueryBusHandlerLocator.php  |  5 ++
 .../Delivery/AbstractBootstrap.php            | 80 +++++++++++++++++--
 .../Domain/Repository/QueryableTrait.php      |  9 ++-
 3 files changed, 85 insertions(+), 9 deletions(-)

diff --git a/src/Application/Query/Service/QueryBusHandlerLocator.php b/src/Application/Query/Service/QueryBusHandlerLocator.php
index edb96cd..0ceff68 100644
--- a/src/Application/Query/Service/QueryBusHandlerLocator.php
+++ b/src/Application/Query/Service/QueryBusHandlerLocator.php
@@ -44,6 +44,11 @@ class QueryBusHandlerLocator
         }
     }
 
+    public function setLazyLoader(QueryHandlerLoaderInterface $loader): void
+    {
+        $this->loader = $loader;
+    }
+
     /**
      * @param QueryInterface $query
      *
diff --git a/src/Infrastructure/Delivery/AbstractBootstrap.php b/src/Infrastructure/Delivery/AbstractBootstrap.php
index cc966ec..4e947f0 100644
--- a/src/Infrastructure/Delivery/AbstractBootstrap.php
+++ b/src/Infrastructure/Delivery/AbstractBootstrap.php
@@ -3,7 +3,7 @@
 namespace ServiceCore\Infrastructure\Delivery;
 
 use Doctrine\DBAL\Connection;
-use Doctrine\DBAL\DBALException;
+use Doctrine\DBAL\Exception as DBALException;
 use Doctrine\DBAL\Types\Type;
 use Doctrine\ORM\EntityManager;
 use Doctrine\ORM\ORMException;
@@ -153,18 +153,84 @@ abstract class AbstractBootstrap
         $this->di->set('event_provider', new DomainEventProvider($this->di->get('user_information')));
         $this->di->set('event_bus', new DomainEventBus());
 
-        $this->di->set("command_bus", new CommandBus(
-            new CommandBusHandlerLocator([], $this->getCommandContext()),
+        $this->loadCommandBus();
+        $this->loadQueryBus();
+
+        $this->afterLoadDi();
+    }
+
+    /**
+     * Загрузка шины команд
+     */
+    protected function loadCommandBus()
+    {
+        $commandContext = $this->getCommandContext();
+        $commandLocator = new CommandBusHandlerLocator(
+            $this->getCommandBusRoutes(),
+            $commandContext
+        );
+
+        $this->di->set('command_bus', new CommandBus(
+            $commandLocator,
             new DoctrineORMTransactionService($this->entityManager),
             $this->di->get('event_provider'),
             $this->di->get('event_bus')
         ));
+        $this->di->set('command_context', $commandContext);
+        $this->di->set('query_loader', $commandLocator);
+    }
 
-        $this->di->set('query_bus', new QueryBus(
-            new QueryBusHandlerLocator([], $this->getQueryContext())
-        ));
+    /**
+     * Routes for calling command handlers using CommandBus
+     *
+     * ```php
+     * return [
+     *      EntityCreateCommand::class => new EntityCreateHandler(
+     *          $this->di->get('entity_command_repository')
+     *      )
+     * ]
+     * ```
+     *
+     * @return array
+     */
+    protected function getCommandBusRoutes(): array
+    {
+        return [];
+    }
 
-        $this->afterLoadDi();
+    /**
+     * Загрузка шины запросов
+     */
+    protected function loadQueryBus()
+    {
+        $queryContext = $this->getQueryContext();
+
+        $queryLocator = new QueryBusHandlerLocator(
+            $this->getQueryBusRoutes(),
+            $queryContext
+        );
+
+        $this->di->set('query_bus', new QueryBus($queryLocator));
+        $this->di->set('query_context', $queryContext);
+        $this->di->set('query_locator', $queryLocator);
+    }
+
+    /**
+     * Routes for calling query handlers using QueryBus
+     *
+     * ```php
+     * return [
+     *      EntityQuery::class => new EntityQueryHandler(
+     *          $this->di->get('entity_query_repository')
+     *      )
+     * ]
+     * ```
+     *
+     * @return array
+     */
+    protected function getQueryBusRoutes(): array
+    {
+        return [];
     }
 
     /**
diff --git a/src/Infrastructure/Domain/Repository/QueryableTrait.php b/src/Infrastructure/Domain/Repository/QueryableTrait.php
index ffa2ff3..c6e9fb4 100644
--- a/src/Infrastructure/Domain/Repository/QueryableTrait.php
+++ b/src/Infrastructure/Domain/Repository/QueryableTrait.php
@@ -11,7 +11,10 @@ use Doctrine\DBAL\Query\QueryBuilder;
  *
  * @property QueryBuilder $queryBuilder
  * @property Connection $connection
- *
+ * @property string[] $jsonFields
+ * @property bool $payload
+ * @property bool $ordered
+ * @property string $orderedBy
  * @property string $tableName
  *
  * @package ServiceCore\Infrastructure\Domain\Repository
@@ -134,7 +137,9 @@ trait QueryableTrait
             if (isset($result[0])) {
                 foreach ($result as $index => $value) {
                     foreach ($this->jsonFields as $field) {
-                        $result[$index][$field] = json_decode($value[$field], true);
+                        if (isset($result[$index][$field])) {
+                            $result[$index][$field] = json_decode($value[$field], true);
+                        }
                     }
                 }
             } else {
-- 
GitLab


From f45dc49cfe7079bf0b08a0dd3f4f79f951f56108 Mon Sep 17 00:00:00 2001
From: "e.kremnev" <e.kremnev@bingosoft.ru>
Date: Fri, 18 Nov 2022 11:40:39 +0300
Subject: [PATCH 03/10] =?UTF-8?q?fix:=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?=
 =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=B8=D0=BC=D1=8F=20=D0=BA?=
 =?UTF-8?q?=D0=BB=D1=8E=D1=87=D0=B0=20=D0=B4=D0=BB=D1=8F=20=D0=BA=D0=BE?=
 =?UTF-8?q?=D0=BC=D0=B0=D0=BD=D0=B4=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=BB=D0=BE?=
 =?UTF-8?q?=D0=BA=D0=B0=D1=82=D0=BE=D1=80=D0=B0=20=D0=B2=20=D0=BA=D0=BE?=
 =?UTF-8?q?=D0=BD=D1=82=D0=B5=D0=B9=D0=BD=D0=B5=D1=80=D0=B5=20=D0=B7=D0=B0?=
 =?UTF-8?q?=D0=B2=D0=B8=D1=81=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D0=B5=D0=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/Infrastructure/Delivery/AbstractBootstrap.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Infrastructure/Delivery/AbstractBootstrap.php b/src/Infrastructure/Delivery/AbstractBootstrap.php
index 4e947f0..c0eb980 100644
--- a/src/Infrastructure/Delivery/AbstractBootstrap.php
+++ b/src/Infrastructure/Delivery/AbstractBootstrap.php
@@ -177,7 +177,7 @@ abstract class AbstractBootstrap
             $this->di->get('event_bus')
         ));
         $this->di->set('command_context', $commandContext);
-        $this->di->set('query_loader', $commandLocator);
+        $this->di->set('command_locator', $commandLocator);
     }
 
     /**
-- 
GitLab


From 47c575065cafd72578799cb77ca2fb4ef57b1934 Mon Sep 17 00:00:00 2001
From: "e.kremnev" <e.kremnev@bingosoft.ru>
Date: Thu, 29 Dec 2022 12:07:37 +0300
Subject: [PATCH 04/10] =?UTF-8?q?fix:=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?=
 =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=81=D0=B1=D0=BE=D1=80=D0=BA?=
 =?UTF-8?q?=D0=B0=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=B0=20=D0=B8?=
 =?UTF-8?q?=D0=B7=20=D0=B8=D1=81=D1=82=D0=BE=D1=87=D0=BD=D0=B8=D0=BA=D0=BE?=
 =?UTF-8?q?=D0=B2=20=D0=B4=D0=BB=D1=8F=20=D0=B3=D0=B5=D0=BD=D0=B5=D1=80?=
 =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BE=D1=82=D1=87=D1=91=D1=82=D0=BE?=
 =?UTF-8?q?=D0=B2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../Domain/Repository/QueryableTrait.php      |   2 +-
 .../Exception/ParameterNotSet.php             |  15 ++
 .../ReportEditor/ReportQueryRepository.php    | 205 +++++++++++++++++-
 3 files changed, 211 insertions(+), 11 deletions(-)
 create mode 100644 src/Infrastructure/Domain/Repository/ReportEditor/Exception/ParameterNotSet.php

diff --git a/src/Infrastructure/Domain/Repository/QueryableTrait.php b/src/Infrastructure/Domain/Repository/QueryableTrait.php
index c6e9fb4..af8ddd2 100644
--- a/src/Infrastructure/Domain/Repository/QueryableTrait.php
+++ b/src/Infrastructure/Domain/Repository/QueryableTrait.php
@@ -144,7 +144,7 @@ trait QueryableTrait
                 }
             } else {
                 foreach ($this->jsonFields as $field) {
-                    if (isset($result[$field])) {
+                    if (isset($result[$field]) && is_string($result[$field])) {
                         $result[$field] = json_decode($result[$field], true);
                     }
                 }
diff --git a/src/Infrastructure/Domain/Repository/ReportEditor/Exception/ParameterNotSet.php b/src/Infrastructure/Domain/Repository/ReportEditor/Exception/ParameterNotSet.php
new file mode 100644
index 0000000..33fc3fe
--- /dev/null
+++ b/src/Infrastructure/Domain/Repository/ReportEditor/Exception/ParameterNotSet.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace ServiceCore\Infrastructure\Domain\Repository\ReportEditor\Exception;
+
+use ServiceCore\Domain\Exception\AbstractDomainException;
+
+/**
+ * Class UnexpectedFileType
+ *
+ * @package ReportEditor\Domain\Exception
+ */
+class ParameterNotSet extends AbstractDomainException
+{
+    protected $code = 'parameter_not_set';
+}
diff --git a/src/Infrastructure/Domain/Repository/ReportEditor/ReportQueryRepository.php b/src/Infrastructure/Domain/Repository/ReportEditor/ReportQueryRepository.php
index 7415079..1c35281 100644
--- a/src/Infrastructure/Domain/Repository/ReportEditor/ReportQueryRepository.php
+++ b/src/Infrastructure/Domain/Repository/ReportEditor/ReportQueryRepository.php
@@ -6,6 +6,7 @@ use ApiQL\ApiQL;
 use Doctrine\DBAL\Connection;
 use Doctrine\DBAL\Query\QueryBuilder;
 use ServiceCore\Domain\Repository\ReportEditor\ReportQueryRepositoryInterface;
+use ServiceCore\Infrastructure\Domain\Repository\ReportEditor\Exception\ParameterNotSet;
 use ServiceCore\Infrastructure\Domain\Repository\ReportEditor\Exception\ReportSourceAliasNotDefined;
 use ServiceCore\Infrastructure\Domain\Repository\ReportEditor\Exception\ReportSourcePropertiesIsInvalid;
 use ServiceCore\Infrastructure\Domain\Repository\ReportEditor\Exception\ReportSourceRelationNotDefined;
@@ -126,7 +127,7 @@ class ReportQueryRepository implements ReportQueryRepositoryInterface
                 if ($this->validateProperties($reportSource['properties'])) {
                     if ($reportSource['report_source_type_id'] === 'extended_object') {
                         $extendedObject = $this->getExtendedObject($reportSource['properties']['id']);
-                        $sourceQuery = $this->buildExtendedObjectQuery($extendedObject);
+                        $sourceQuery = $this->buildExtendedObjectQuery($extendedObject, $payload ?? []);
                     } else {
                         $sourceQuery = sprintf("SELECT * FROM %s", $this->getSourceTable(
                             $reportSource['report_source_type_id'],
@@ -168,10 +169,11 @@ class ReportQueryRepository implements ReportQueryRepositoryInterface
 
     /**
      * @param array $extendedObject
+     * @param array $payload
      *
      * @return string
      */
-    private function buildExtendedObjectQuery(array $extendedObject): string
+    private function buildExtendedObjectQuery(array $extendedObject, array $payload): string
     {
         $sqlStatements = $extendedObject['sql_statements'];
 
@@ -192,8 +194,20 @@ class ReportQueryRepository implements ReportQueryRepositoryInterface
             ));
         }
 
-        if (!empty($extendedObject['filter'])) {
-            $apiQL = new ApiQL($extendedObject['filter'], $query);
+        $filter = $extendedObject['filter'];
+
+        // Форматируем фильтры
+        if (!empty($filter) && isset($filter['logical_operator']) && isset($filter['children'])) {
+            if (is_array($filter['children']) && count($filter['children']) > 0) {
+                $filter = $this->toApiQl($filter, $payload);
+            } else {
+                $filter = null;
+            }
+        }
+
+        // Крепим после форматирования
+        if (!empty($filter)) {
+            $apiQL = new ApiQL($filter, $query);
             $apiQL->execute();
         }
 
@@ -210,6 +224,166 @@ class ReportQueryRepository implements ReportQueryRepositoryInterface
         return $sourceQuery;
     }
 
+    /**
+     * @param array $filter
+     * @param array $parameters
+     *
+     * @return array|array[]
+     *
+     * @throws \Doctrine\DBAL\Driver\Exception
+     * @throws \Doctrine\DBAL\Exception
+     */
+    private function toApiQl(array $filter, array $parameters = [])
+    {
+        $logicalOperator = $filter['logical_operator'];
+
+        $result = [
+            $logicalOperator => []
+        ];
+
+        foreach ($filter['children'] as $child) {
+            $query = $child['query'];
+            if ($child['type'] === 'condition_group') {
+                $result[$logicalOperator][] = $this->toApiQl($query, $parameters);
+            } else {
+                if (in_array($query['operator'], ['is_null', 'is_not_null'])) {
+                    $result[$logicalOperator][] = [
+                        $query['operator'] => $query['field']
+                    ];
+                } else {
+                    $value = $this->getValueByFilterType($query, $parameters);
+
+                    if ($value) {
+                        $result[$logicalOperator][] = [
+                            $query['operator'] => [
+                                $query['field'] => $value
+                            ]
+                        ];
+                    }
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param array $filter
+     * @param array $parameters
+     *
+     * @return mixed|null
+     *
+     * @throws \Doctrine\DBAL\Driver\Exception
+     * @throws \Doctrine\DBAL\Exception
+     */
+    private function getValueByFilterType(array $filter, array $parameters) {
+        // Докрутить таблицу сотрудников, текущего пользователя и параметр
+        switch ($filter['filter_type']) {
+            case 'users_table':
+                if ($filter['value']) {
+                    return $this->getUserFieldValue($filter['value'], $this->userInfo->id);
+                }
+
+                return null;
+            case 'current_user':
+                return $this->userInfo->id;
+            case 'current_datetime':
+                return date('Y-m-d H:i:s');
+            case 'parameter':
+                $key = $filter['value'];
+
+                if (isset($parameters[$key])) {
+                    return $parameters[$key];
+                }
+
+                return null;
+            default:
+                return $filter['value'];
+        }
+    }
+
+    /**
+     * @return false|int
+     *
+     * @throws \Doctrine\DBAL\Driver\Exception
+     * @throws \Doctrine\DBAL\Exception
+     */
+    private function getUsersTableId()
+    {
+        return $this->connection
+            ->createQueryBuilder()
+            ->select('id')
+            ->from("object_editor.entities")
+            ->where("\"getPropertyById\"(properties, 'is_users_table') = 'true'")
+            ->execute()
+            ->fetchOne();
+    }
+
+    /**
+     * @param int $fieldId
+     *
+     * @return string
+     *
+     * @throws \Doctrine\DBAL\Driver\Exception
+     * @throws \Doctrine\DBAL\Exception
+     */
+    private function getFieldType(int $fieldId): string
+    {
+        return $this->connection
+            ->createQueryBuilder()
+            ->select("entity_type_id")
+            ->from("object_editor.entities")
+            ->where("id = :fieldId")
+            ->setParameter("fieldId", $fieldId)
+            ->execute()
+            ->fetchOne();
+    }
+
+    /**
+     * @param int $fieldId
+     * @param int $userId
+     *
+     * @return mixed|null
+     *
+     * @throws \Doctrine\DBAL\Driver\Exception
+     * @throws \Doctrine\DBAL\Exception
+     */
+    private function getUserFieldValue(int $fieldId, int $userId)
+    {
+        $tableId = $this->getUsersTableId();
+        if (!$tableId) {
+            return null;
+        }
+
+        $value = $this->connection
+            ->createQueryBuilder()
+            ->select(sprintf("attr_%s_", $fieldId))
+            ->from(sprintf("registry.object_%s_main", $tableId))
+            ->where("id = :userId")
+            ->setParameter("userId", $userId)
+            ->execute()
+            ->fetchOne();
+
+        if (!$value) {
+            return null;
+        }
+
+        $fieldType = $this->getFieldType($fieldId);
+        if ($fieldType) {
+            if (in_array($fieldType, ['xref_field', 'xref_multi_field'])) {
+                return array_column(
+                    json_decode(
+                        $value,
+                        true
+                    ),
+                    'id'
+                );
+            }
+        }
+
+        return $value;
+    }
+
     /**
      * @param array $sources
      * @param int|null $parentId
@@ -384,9 +558,11 @@ class ReportQueryRepository implements ReportQueryRepositoryInterface
 
     /**
      * @param array $filters
-     * @param array $payload
+     * @param array|null $payload
      *
      * @return array
+     *
+     * @throws ParameterNotSet
      */
     private function buildExpression(array $filters, ?array $payload): array
     {
@@ -398,14 +574,23 @@ class ReportQueryRepository implements ReportQueryRepositoryInterface
             $fieldValue = $filter['properties']['value'];
 
             if ($filterType === 'parameter' && !empty($payload)) {
-                isset($payload[$fieldName]) ? $fieldValue = $payload[$fieldName] : $fieldValue = null;
+                $parameterName = empty($filter['properties']['value'])
+                    ? 'id'
+                    : $filter['properties']['value']
+                ;
+
+                if (!isset($payload[$parameterName])) {
+                    throw new ParameterNotSet('Undefined values for one or more parameters', [
+                        'payload' => $payload,
+                        'filter' => $filter
+                    ]);
+                }
+
+                $fieldValue = $payload[$parameterName];
             } elseif ($filterType === 'constant') {
-                $fieldValue = $filter['properties']['value'];
+                $fieldValue = $filter['properties']['value'] ?? null;
             } elseif ($filterType === 'current_user') {
                 $fieldValue = $this->userInfo->id;
-            } elseif ($filterType === 'current_date') {
-            } elseif ($filterType === 'current_datetime') {
-            } elseif ($filterType === 'users_table') {
             } elseif ($filterType === 'condition') {
                 $expression['and'][] = [
                     $fieldValue => sprintf('"s.%s"', $fieldValue)
-- 
GitLab


From 200a6e84268a25a7293bc902dd80cd05afbc3916 Mon Sep 17 00:00:00 2001
From: "e.kremnev" <e.kremnev@bingosoft.ru>
Date: Mon, 13 Mar 2023 15:03:08 +0300
Subject: [PATCH 05/10] =?UTF-8?q?fix:=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?=
 =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=B1=D0=B0=D0=B3=20=D1=81=20=D0=BB?=
 =?UTF-8?q?=D0=B8=D1=88=D0=BD=D0=B5=D0=B9=20=D0=BB=D0=BE=D0=B3=D0=B8=D1=87?=
 =?UTF-8?q?=D0=B5=D1=81=D0=BA=D0=BE=D0=B9=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF?=
 =?UTF-8?q?=D0=BE=D0=B9=20=D0=B2=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81?=
 =?UTF-8?q?=D0=B5=20=D0=B4=D0=BB=D1=8F=20=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5?=
 =?UTF-8?q?=D1=80=D1=82=D0=B0=D1=86=D0=B8=D0=B8=20=D1=81=D0=BE=D1=81=D1=82?=
 =?UTF-8?q?=D0=BE=D1=8F=D0=BD=D0=B8=D0=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../State/Formatter/SimpleStateFormatter.php  |  40 ++-
 .../Service/State/StateService.php            |  10 +-
 tests/Unit/Repository/StateRepository.php     | 255 ++++++++++--------
 tests/Unit/StateUnitTest.php                  |  16 +-
 4 files changed, 183 insertions(+), 138 deletions(-)

diff --git a/src/Infrastructure/Service/State/Formatter/SimpleStateFormatter.php b/src/Infrastructure/Service/State/Formatter/SimpleStateFormatter.php
index a4b4596..c76da48 100644
--- a/src/Infrastructure/Service/State/Formatter/SimpleStateFormatter.php
+++ b/src/Infrastructure/Service/State/Formatter/SimpleStateFormatter.php
@@ -82,7 +82,7 @@ class SimpleStateFormatter
                 if (!empty($stateField['current_table_field_id'])) {
                     $field = sprintf('attr_%d_', $stateField['current_table_field_id']);
 
-                    $value = isset($this->recordData[$field]) ? $this->recordData[$field] : null;
+                    $value = $this->recordData[$field] ?? null;
                 }
             } elseif ($stateField['state_field_type_id'] === 'constant') {
                 $value = $stateField['properties']['value'];
@@ -105,31 +105,27 @@ class SimpleStateFormatter
                     $operator = 'equals_any';
                 }
 
-                if (!empty($this->recordData)) {
-                    if (!$value) {
-                        $value = isset($this->recordData[$attribute]) ? $this->recordData[$attribute] : null;
-                    }
-
-                    if (is_string($value)) {
-                        $value = json_decode($value, true);
-                    }
+                if (is_string($value)) {
+                    $value = json_decode($value, true);
+                }
 
-                    if (is_null($value)) {
-                        $value = [];
-                    }
+                if (is_null($value)) {
+                    $value = [];
                 }
             }
 
-            $expressions[] = [
-                'type' => 'condition',
-                'query' => [
-                    'field' => $attribute,
-                    'value' => $value,
-                    'filter_type' => 'constant',
-                    'operator' => $operator,
-                    'field_type' => $fieldType
-                ]
-            ];
+            if (!is_null($value)) {
+                $expressions[] = [
+                    'type' => 'condition',
+                    'query' => [
+                        'field' => $attribute,
+                        'value' => $value,
+                        'filter_type' => 'constant',
+                        'operator' => $operator,
+                        'field_type' => $fieldType
+                    ]
+                ];
+            }
         }
 
         return [
diff --git a/src/Infrastructure/Service/State/StateService.php b/src/Infrastructure/Service/State/StateService.php
index 08238e3..ebf55dd 100644
--- a/src/Infrastructure/Service/State/StateService.php
+++ b/src/Infrastructure/Service/State/StateService.php
@@ -49,7 +49,7 @@ class StateService implements StateServiceInterface
     /**
      * Конвертирует состояния в ApiQL
      *
-     * @param int[] $states
+     * @param int[]|int $states
      * @param array $recordData
      * @param string $logicalOperator
      * @param bool $addWhere
@@ -57,7 +57,7 @@ class StateService implements StateServiceInterface
      * @return array
      */
     public function convertToApiQl(
-        array $states,
+        $states,
         array $recordData = [],
         string $logicalOperator = 'and',
         bool $addWhere = true
@@ -86,7 +86,11 @@ class StateService implements StateServiceInterface
         );
 
         if (is_array($states)) {
-            return $formatter->format($states)->many($logicalOperator);
+            if (count($states) > 1) {
+                return $formatter->format($states)->many($logicalOperator);
+            } else {
+                return $formatter->format($states)->one();
+            }
         } elseif (is_numeric($states)) {
             return $formatter->format($states)->one();
         } else {
diff --git a/tests/Unit/Repository/StateRepository.php b/tests/Unit/Repository/StateRepository.php
index 281ced7..da45815 100644
--- a/tests/Unit/Repository/StateRepository.php
+++ b/tests/Unit/Repository/StateRepository.php
@@ -6,100 +6,160 @@ use ServiceCore\Domain\Repository\StateQueryRepositoryInterface;
 
 class StateRepository implements StateQueryRepositoryInterface
 {
-    public function getState(int $id): ?array
-    {
-        $states = [
-            [
-                'id' => 1,
-                'name' => 'Simple State',
-                'is_complex' => false,
-                'properties' => null,
-                'state_fields' => [
-                    [
-                        'id' => 1,
-                        'state_id' => 1,
-                        'field_id' => 1000,
-                        'state_field_type_id' => 'constant',
-                        'properties' => [
-                            'value' => 'str1'
-                        ]
-                    ],
-                    [
-                        'id' => 2,
-                        'state_id' => 1,
-                        'field_id' => 1001,
-                        'state_field_type_id' => 'constant',
-                        'properties' => [
-                            'value' => '314zdec'
-                        ]
-                    ],
-                    [
-                        'id' => 3,
-                        'state_id' => 1,
-                        'field_id' => 1002,
-                        'state_field_type_id' => 'condition',
-                        'condition_field_type_id' => 'not_empty'
-                    ],
-                    [
-                        'id' => 4,
-                        'state_id' => 1,
-                        'field_id' => 1003,
-                        'state_field_type_id' => 'current_table_field',
-                        'current_table_field_id' => 2000
+    static $states = [
+        [
+            'id' => 1,
+            'name' => 'Simple State',
+            'is_complex' => false,
+            'properties' => null,
+            'state_fields' => [
+                [
+                    'id' => 1,
+                    'state_id' => 1,
+                    'field_id' => 1000,
+                    'state_field_type_id' => 'constant',
+                    'properties' => [
+                        'value' => 'str1'
                     ]
+                ],
+                [
+                    'id' => 2,
+                    'state_id' => 1,
+                    'field_id' => 1001,
+                    'state_field_type_id' => 'constant',
+                    'properties' => [
+                        'value' => '314zdec'
+                    ]
+                ],
+                [
+                    'id' => 3,
+                    'state_id' => 1,
+                    'field_id' => 1002,
+                    'state_field_type_id' => 'condition',
+                    'condition_field_type_id' => 'not_empty'
+                ],
+                [
+                    'id' => 4,
+                    'state_id' => 1,
+                    'field_id' => 1003,
+                    'state_field_type_id' => 'current_table_field',
+                    'current_table_field_id' => 2000
                 ]
-            ],
-            [
-                'id' => 2,
-                'name' => 'Complex State',
-                'is_complex' => true,
-                'properties' => [
-                    'filters' => [
-                        'logical_operator' => 'and',
-                        'children' => [
-                            [
-                                'type' => 'condition',
-                                'query' => [
-                                    'field' => 'attr_1010_',
-                                    'filter_type' => 'constant',
-                                    'operator' => 'eq',
-                                    'value' => '2021-07-15'
-                                ]
-                            ],
-                            [
-                                'type' => 'condition_group',
-                                'query' => [
-                                    'logical_operator' => 'or',
-                                    'children' => [
-                                        [
-                                            'type' => 'condition',
-                                            'query' => [
-                                                'field' => 'attr_1011_',
-                                                'filter_type' => 'constant',
-                                                'operator' => 'gte',
-                                                'value' => '2022-01-01'
-                                            ]
-                                        ],
-                                        [
-                                            'type' => 'condition',
-                                            'query' => [
-                                                'field' => 'attr_1011_',
-                                                'filter_type' => 'constant',
-                                                'operator' => 'lte',
-                                                'value' => '2021-06-01'
-                                            ]
+            ]
+        ],
+        [
+            'id' => 2,
+            'name' => 'Complex State',
+            'is_complex' => true,
+            'properties' => [
+                'filters' => [
+                    'logical_operator' => 'and',
+                    'children' => [
+                        [
+                            'type' => 'condition',
+                            'query' => [
+                                'field' => 'attr_1010_',
+                                'filter_type' => 'constant',
+                                'operator' => 'eq',
+                                'value' => '2021-07-15'
+                            ]
+                        ],
+                        [
+                            'type' => 'condition_group',
+                            'query' => [
+                                'logical_operator' => 'or',
+                                'children' => [
+                                    [
+                                        'type' => 'condition',
+                                        'query' => [
+                                            'field' => 'attr_1011_',
+                                            'filter_type' => 'constant',
+                                            'operator' => 'gte',
+                                            'value' => '2022-01-01'
+                                        ]
+                                    ],
+                                    [
+                                        'type' => 'condition',
+                                        'query' => [
+                                            'field' => 'attr_1011_',
+                                            'filter_type' => 'constant',
+                                            'operator' => 'lte',
+                                            'value' => '2021-06-01'
                                         ]
                                     ]
                                 ]
                             ]
                         ]
                     ]
+                ]
+            ],
+            'state_fields' => []
+        ],
+        [
+            'id' => 3,
+            'name' => 'Simple State 2',
+            'is_complex' => false,
+            'properties' => null,
+            'state_fields' => [
+                [
+                    'id' => 1,
+                    'state_id' => 1,
+                    'field_id' => 2000,
+                    'state_field_type_id' => 'constant',
+                    'properties' => [
+                        'value' => 4
+                    ]
                 ],
-                'state_fields' => []
+                [
+                    'id' => 2,
+                    'state_id' => 1,
+                    'field_id' => 2001,
+                    'state_field_type_id' => 'constant',
+                    'properties' => [
+                        'value' => 0
+                    ]
+                ]
             ]
-        ];
+        ]
+    ];
+    static $fields = [
+        [
+            'id' => 1000,
+            'type' => 'string_field'
+        ],
+        [
+            'id' => 1001,
+            'type' => 'string_field'
+        ],
+        [
+            'id' => 1002,
+            'type' => 'string_field'
+        ],
+        [
+            'id' => 1003,
+            'type' => 'string_field'
+        ],
+        [
+            'id' => 1010,
+            'type' => 'date_field'
+        ],
+        [
+            'id' => 1011,
+            'type' => 'date_field'
+        ],
+        [
+            'id' => 2000,
+            'type' => 'xref_field'
+        ],
+        [
+            'id' => 2001,
+            'type' => 'xref_field'
+        ]
+    ];
 
-        foreach ($states as $state) {
+    public function getState(int $id): ?array
+    {
+        foreach (self::$states as $state) {
             if ($state['id'] === $id) {
                 return $state;
             }
@@ -109,34 +169,7 @@ class StateRepository implements StateQueryRepositoryInterface
     }
 
     public function getFieldType(int $fieldId): string {
-        $fields = [
-            [
-                'id' => 1000,
-                'type' => 'string_field'
-            ],
-            [
-                'id' => 1001,
-                'type' => 'string_field'
-            ],
-            [
-                'id' => 1002,
-                'type' => 'string_field'
-            ],
-            [
-                'id' => 1003,
-                'type' => 'string_field'
-            ],
-            [
-                'id' => 1010,
-                'type' => 'date_field'
-            ],
-            [
-                'id' => 1011,
-                'type' => 'date_field'
-            ]
-        ];
-
-        foreach ($fields as $field) {
+        foreach (self::$fields as $field) {
             if ($field['id'] === $fieldId) {
                 return $field['type'];
             }
diff --git a/tests/Unit/StateUnitTest.php b/tests/Unit/StateUnitTest.php
index 91f018e..994ee57 100644
--- a/tests/Unit/StateUnitTest.php
+++ b/tests/Unit/StateUnitTest.php
@@ -39,6 +39,18 @@ class StateUnitTest extends TestCase
         $this->repository = new StateRepository();
     }
 
+    /**
+     * Баг с логической группировкой состояний для множественной конвертации
+     */
+    public function testStateBag1()
+    {
+        $service = new StateService($this->repository, $this->userInfo);
+
+        $result = $service->convertToApiQl(3, $this->recordData);
+        $testResult = '{"where":[{"and":[{"equals_any":{"attr_2000_id":4}},{"equals_any":{"attr_2001_id":0}}]}]}';
+        self::assertEquals(json_encode($result), $testResult);
+    }
+
     public function testStateFormatting(): void
     {
         $service = new StateService($this->repository, $this->userInfo);
@@ -91,14 +103,14 @@ class StateUnitTest extends TestCase
     {
         $service = new StateService($this->repository, $this->userInfo);
 
-        $result = $service->convertToApiQl(1, $this->recordData);
+        $result = $service->convertToApiQl([1], $this->recordData);
         $testResult =
             '{"where":[{"and":[{"eq":{"attr_1000_":"str1"}},{"eq":{"attr_1001_":"314zdec"}},{"is_null":"attr_1002_"},' .
             '{"eq":{"attr_1003_":"value of the current table field"}}]}]}'
         ;
         self::assertEquals(json_encode($result), $testResult);
 
-        $result = $service->convertToApiQl(2, $this->recordData);
+        $result = $service->convertToApiQl([2], $this->recordData);
         $testResult =
             '{"where":[{"and":[{"eq":{"attr_1010_":"2021-07-15"}},{"or":[{"gte":{"attr_1011_":"2022-01-01"}},{"lte":{' .
             '"attr_1011_":"2021-06-01"}}]}]}]}'
-- 
GitLab


From a6008d4ea7e7a44a661d9c0709c1613508d3bcb7 Mon Sep 17 00:00:00 2001
From: "e.kremnev" <e.kremnev@bingosoft.ru>
Date: Wed, 29 Mar 2023 16:13:07 +0300
Subject: [PATCH 06/10] =?UTF-8?q?fix:=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?=
 =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5?=
 =?UTF-8?q?=D1=80=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=D0=BE=D1=81=D1=82?=
 =?UTF-8?q?=D0=BE=D1=8F=D0=BD=D0=B8=D0=B9=20=D0=B2=20=D1=84=D0=B8=D0=BB?=
 =?UTF-8?q?=D1=8C=D1=82=D1=80=D1=8B=20ApiQL?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/Infrastructure/Service/State/StateService.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Infrastructure/Service/State/StateService.php b/src/Infrastructure/Service/State/StateService.php
index ebf55dd..6e5302f 100644
--- a/src/Infrastructure/Service/State/StateService.php
+++ b/src/Infrastructure/Service/State/StateService.php
@@ -89,7 +89,7 @@ class StateService implements StateServiceInterface
             if (count($states) > 1) {
                 return $formatter->format($states)->many($logicalOperator);
             } else {
-                return $formatter->format($states)->one();
+                return $formatter->format($states[0])->one();
             }
         } elseif (is_numeric($states)) {
             return $formatter->format($states)->one();
-- 
GitLab


From a4d135c8cdcacfec12b45709e25af1b403c9d297 Mon Sep 17 00:00:00 2001
From: "e.kremnev" <e.kremnev@bingosoft.ru>
Date: Wed, 29 Mar 2023 16:14:13 +0300
Subject: [PATCH 07/10] =?UTF-8?q?fix:=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?=
 =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5?=
 =?UTF-8?q?=D1=80=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=D0=BE=D1=81=D1=82?=
 =?UTF-8?q?=D0=BE=D1=8F=D0=BD=D0=B8=D0=B9=20=D0=B2=20=D1=84=D0=B8=D0=BB?=
 =?UTF-8?q?=D1=8C=D1=82=D1=80=D1=8B=20ApiQL?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/Infrastructure/Service/State/StateService.php | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/Infrastructure/Service/State/StateService.php b/src/Infrastructure/Service/State/StateService.php
index 6e5302f..75dfbbc 100644
--- a/src/Infrastructure/Service/State/StateService.php
+++ b/src/Infrastructure/Service/State/StateService.php
@@ -88,8 +88,10 @@ class StateService implements StateServiceInterface
         if (is_array($states)) {
             if (count($states) > 1) {
                 return $formatter->format($states)->many($logicalOperator);
-            } else {
+            } else if (count($states) == 1) {
                 return $formatter->format($states[0])->one();
+            } else {
+                return [];
             }
         } elseif (is_numeric($states)) {
             return $formatter->format($states)->one();
-- 
GitLab


From c35c9ff24eb62c35283d001f8f144caeeedbe138 Mon Sep 17 00:00:00 2001
From: "e.kremnev" <e.kremnev@bingosoft.ru>
Date: Fri, 14 Apr 2023 12:39:01 +0300
Subject: [PATCH 08/10] =?UTF-8?q?fix:=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?=
 =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=BE=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0?=
 =?UTF-8?q?=D1=82=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=81?=
 =?UTF-8?q?=D0=BB=D0=BE=D0=B6=D0=BD=D1=8B=D1=85=20=D1=81=D0=BE=D1=81=D1=82?=
 =?UTF-8?q?=D0=BE=D1=8F=D0=BD=D0=B8=D0=B9=20=D0=BF=D0=BE=20=D1=82=D0=B8?=
 =?UTF-8?q?=D0=BF=D1=83=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D0=B0.=20?=
 =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF?=
 =?UTF-8?q?=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=BA=D0=B0=20=D1=81=D0=BB?=
 =?UTF-8?q?=D0=B5=D0=B4=D1=83=D1=8E=D1=89=D0=B8=D1=85=20=D1=82=D0=B8=D0=BF?=
 =?UTF-8?q?=D0=BE=D0=B2=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D0=BE=D0=B2?=
 =?UTF-8?q?:=20"=D0=9F=D0=BE=D0=BB=D0=B5=20=D1=82=D0=B5=D0=BA=D1=83=D1=89?=
 =?UTF-8?q?=D0=B5=D0=B9=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=D1=8B",=20"?=
 =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B5=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86?=
 =?UTF-8?q?=D1=8B=20=D1=81=D0=BE=D1=81=D1=82=D1=80=D1=83=D0=B4=D0=BD=D0=B8?=
 =?UTF-8?q?=D0=BA=D0=BE=D0=B2",=20"=D0=A2=D0=B5=D0=BA=D1=83=D1=89=D0=B8?=
 =?UTF-8?q?=D0=B9=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82?=
 =?UTF-8?q?=D0=B5=D0=BB=D1=8C"=20=D0=B8=20"=D0=A2=D0=B5=D0=BA=D1=83=D1=89?=
 =?UTF-8?q?=D0=B0=D1=8F=20=D0=B4=D0=B0=D1=82=D0=B0=20=D0=B8=20=D0=B2=D1=80?=
 =?UTF-8?q?=D0=B5=D0=BC=D1=8F"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../State/Formatter/ComplexStateFormatter.php | 29 +++++++-
 tests/Unit/Repository/StateRepository.php     | 68 +++++++++++++++++--
 tests/Unit/StateUnitTest.php                  | 15 +++-
 3 files changed, 106 insertions(+), 6 deletions(-)

diff --git a/src/Infrastructure/Service/State/Formatter/ComplexStateFormatter.php b/src/Infrastructure/Service/State/Formatter/ComplexStateFormatter.php
index a518c3f..b400cef 100644
--- a/src/Infrastructure/Service/State/Formatter/ComplexStateFormatter.php
+++ b/src/Infrastructure/Service/State/Formatter/ComplexStateFormatter.php
@@ -80,8 +80,9 @@ class ComplexStateFormatter
             } else {
                 $fieldId = (int)filter_var($item['query']['field'], FILTER_SANITIZE_NUMBER_INT);
                 $fieldType = $this->stateRepository->getFieldType($fieldId);
+                $filterType = $item['query']['filter_type'] ?? 'constant';
 
-                if ($fieldType === 'xref_field' || $fieldType === 'xref_multi_field') {
+                if (in_array($fieldType, ['xref_field', 'xref_multi_field'])) {
                     $item['query']['field'] = sprintf('%sid', $item['query']['field']);
 
                     if ($item['query']['operator'] === 'eq') {
@@ -91,6 +92,32 @@ class ComplexStateFormatter
                     }
                 }
 
+                if ($filterType === 'users_table') {
+                    $item['query']['value'] = $this->stateRepository->getUserFieldValue(
+                        $item['query']['value'],
+                        $this->userInfo->id
+                    );
+                } elseif ($fieldType === 'current_table_field') {
+                    $attribute = sprintf('attr_%s_', $item['query']['value']);
+                    $attributeType = $this->stateRepository->getFieldType($item['query']['value']);
+                    $attributeValue = null;
+                    if (in_array($attributeType, ['xref_field', 'xref_multi_field'])) {
+                        $attribute .= 'id';
+                        $attributeValue = $this->recordData[$attribute] ?? null;
+                        if (is_string($attributeValue)) {
+                            $attributeValue = json_decode($attributeValue, true);
+                        }
+                    } else {
+                        $attributeValue = $this->recordData[$attribute] ?? null;
+                    }
+
+                    $item['query']['value'] = $attributeValue;
+                } elseif ($filterType === 'current_user') {
+                    $item['query']['value'] = $this->userInfo->id;
+                } elseif ($filterType === 'current_datetime') {
+                    $item['query']['value'] = date("Y-m-d H:i:s");
+                }
+
                 $item['query']['field_type'] = $fieldType;
 
                 $expressions[] = $item;
diff --git a/tests/Unit/Repository/StateRepository.php b/tests/Unit/Repository/StateRepository.php
index da45815..58e0219 100644
--- a/tests/Unit/Repository/StateRepository.php
+++ b/tests/Unit/Repository/StateRepository.php
@@ -120,8 +120,31 @@ class StateRepository implements StateQueryRepositoryInterface
                     ]
                 ]
             ]
+        ],
+        [
+            'id' => 4,
+            'name' => 'Complex State',
+            'is_complex' => true,
+            'properties' => [
+                'filters' => [
+                    'logical_operator' => 'and',
+                    'children' => [
+                        [
+                            'type' => 'condition',
+                            'query' => [
+                                'field' => 'attr_2001_',
+                                'filter_type' => 'users_table',
+                                'operator' => 'eq',
+                                'value' => 3001
+                            ]
+                        ]
+                    ]
+                ]
+            ],
+            'state_fields' => []
         ]
     ];
+
     static $fields = [
         [
             'id' => 1000,
@@ -154,6 +177,10 @@ class StateRepository implements StateQueryRepositoryInterface
         [
             'id' => 2001,
             'type' => 'xref_field'
+        ],
+        [
+            'id' => 3001,
+            'type' => 'xref_field'
         ]
     ];
 
@@ -168,17 +195,50 @@ class StateRepository implements StateQueryRepositoryInterface
         return null;
     }
 
-    public function getFieldType(int $fieldId): string {
+    public function getFieldType(int $fieldId): string
+    {
         foreach (self::$fields as $field) {
             if ($field['id'] === $fieldId) {
                 return $field['type'];
             }
         }
 
-        return 'huy_field';
+        return 'undef_field';
     }
 
-    public function getUserFieldValue(int $fieldId, int $userId): int {
-        return 666;
+    public function getUserFieldValue(int $fieldId, int $userId)
+    {
+        $users = [
+            [
+                'id' => 1,
+                'username' => 'test1',
+                'attr_3001_' => '[{"id": 1, "name": "Name1"}]',
+                'attr_3001_id' => 1
+            ]
+        ];
+
+        $targetAttribute = sprintf('attr_%s_', $fieldId);
+
+        $value = null;
+
+        foreach ($users as $user) {
+            if ($user['id'] === $userId) {
+                $value = $user[$targetAttribute] ?? null;
+                break;
+            }
+        }
+
+        $fieldType = $this->getFieldType($fieldId);
+        if (in_array($fieldType, ['xref_field', 'xref_multi_field'])) {
+            return array_column(
+                json_decode(
+                    $value,
+                    true
+                ),
+                'id'
+            );
+        }
+
+        return $value;
     }
 }
diff --git a/tests/Unit/StateUnitTest.php b/tests/Unit/StateUnitTest.php
index 994ee57..15ef58d 100644
--- a/tests/Unit/StateUnitTest.php
+++ b/tests/Unit/StateUnitTest.php
@@ -30,7 +30,8 @@ class StateUnitTest extends TestCase
             'attr_2010_' => '2022-12-01',
             'attr_2011_' => '2022-12-01',
             // Для сравнения одного поля с другим полем в текущей таблице
-            'attr_2000_' => 'value of the current table field'
+            'attr_2000_' => 'value of the current table field',
+            'attr_2001_' => null
         ];
 
         $this->userInfo = new \stdClass();
@@ -51,6 +52,18 @@ class StateUnitTest extends TestCase
         self::assertEquals(json_encode($result), $testResult);
     }
 
+    /**
+     * Баг с получением значения из поля таблицы сотрудников, если оно с типом "Ссылка"
+     */
+    public function testStateBag2()
+    {
+        $service = new StateService($this->repository, $this->userInfo);
+
+        $result = $service->convertToApiQl(4, $this->recordData);
+        $testResult = '{"where":[{"and":[{"equals_any":{"attr_2001_id":[1]}}]}]}';
+        self::assertEquals($testResult, json_encode($result));
+    }
+
     public function testStateFormatting(): void
     {
         $service = new StateService($this->repository, $this->userInfo);
-- 
GitLab


From f050e58f03ddaa3f577a8655b89ae32d3b2a740c Mon Sep 17 00:00:00 2001
From: "e.kremnev" <e.kremnev@bingosoft.ru>
Date: Wed, 19 Apr 2023 14:25:52 +0300
Subject: [PATCH 09/10] =?UTF-8?q?feat:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?=
 =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80?=
 =?UTF-8?q?=D0=BA=D0=B0=20=D1=83=D1=81=D0=BB=D0=BE=D0=B2=D0=B8=D0=B9=20?=
 =?UTF-8?q?=D0=BF=D0=BE=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8?=
 =?UTF-8?q?=D1=8F=D0=BC=20=D0=B4=D0=BB=D1=8F=20=D1=81=D0=B5=D1=80=D0=B2?=
 =?UTF-8?q?=D0=B8=D1=81=D0=B0=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD?=
 =?UTF-8?q?=D0=B8=D0=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../Service/State/Converter.php               |   4 +-
 .../State/Formatter/ComplexStateFormatter.php | 133 ++++++++++----
 .../State/Formatter/SimpleStateFormatter.php  | 111 +++++++-----
 .../Service/State/StateService.php            | 169 ++++++++++++++++++
 tests/Unit/Repository/StateRepository.php     |   2 +-
 tests/Unit/StateUnitTest.php                  | 112 +++++++-----
 6 files changed, 407 insertions(+), 124 deletions(-)

diff --git a/src/Infrastructure/Service/State/Converter.php b/src/Infrastructure/Service/State/Converter.php
index 86edceb..41cd791 100644
--- a/src/Infrastructure/Service/State/Converter.php
+++ b/src/Infrastructure/Service/State/Converter.php
@@ -25,7 +25,7 @@ class Converter
             ]
         );
 
-        return $addWhere ? ['where' => $result] : $result;
+        return $addWhere ? ['where' => $result[0]] : $result[0];
     }
 
     /**
@@ -50,7 +50,7 @@ class Converter
                 } else {
                     $result[] = [
                         $expr['query']['operator'] => [
-                            $expr['query']['field'] => $expr['query']['value']
+                            $expr['query']['field'] => $expr['query']['y_value']
                         ]
                     ];
                 }
diff --git a/src/Infrastructure/Service/State/Formatter/ComplexStateFormatter.php b/src/Infrastructure/Service/State/Formatter/ComplexStateFormatter.php
index b400cef..66bf136 100644
--- a/src/Infrastructure/Service/State/Formatter/ComplexStateFormatter.php
+++ b/src/Infrastructure/Service/State/Formatter/ComplexStateFormatter.php
@@ -64,6 +64,13 @@ class ComplexStateFormatter
         ])[0];
     }
 
+    /**
+     * @param array $data
+     *
+     * @return array
+     *
+     * @throws \Exception
+     */
     private function finalFormat(array $data): array
     {
         $expressions = [];
@@ -78,52 +85,112 @@ class ComplexStateFormatter
                     ]
                 ];
             } else {
+                $xField = $item['query']['field'];
                 $fieldId = (int)filter_var($item['query']['field'], FILTER_SANITIZE_NUMBER_INT);
-                $fieldType = $this->stateRepository->getFieldType($fieldId);
+                $xFieldType = !isset($item['query']['field_type'])
+                    ? $this->stateRepository->getFieldType($fieldId)
+                    : $item['query']['field_type'];
                 $filterType = $item['query']['filter_type'] ?? 'constant';
+                $operator = $item['query']['operator'] ?? 'eq';
+                $xValue = $this->getFieldValue($xField, $xFieldType);
+                $yValue = null;
+
+                if ($filterType === 'condition') {
+                    $operator = $item['query']['value'] === 'not_empty'
+                        ? 'is_not_null'
+                        : 'is_null';
+                } else {
+                    $yValue = $this->getYValue($filterType, $item['query']['value'] ?? null);
+                }
 
-                if (in_array($fieldType, ['xref_field', 'xref_multi_field'])) {
-                    $item['query']['field'] = sprintf('%sid', $item['query']['field']);
+                if (in_array($xFieldType, ['xref_field', 'xref_multi_field'])) {
+                    $xField = sprintf('%sid', $xField);
 
-                    if ($item['query']['operator'] === 'eq') {
-                        $item['query']['operator'] = 'equals_any';
-                    } elseif ($item['query']['operator'] === 'neq') {
-                        $item['query']['operator'] = 'not_equals_any';
+                    if ($operator === 'eq') {
+                        $operator = 'equals_any';
                     }
                 }
 
-                if ($filterType === 'users_table') {
-                    $item['query']['value'] = $this->stateRepository->getUserFieldValue(
-                        $item['query']['value'],
-                        $this->userInfo->id
-                    );
-                } elseif ($fieldType === 'current_table_field') {
-                    $attribute = sprintf('attr_%s_', $item['query']['value']);
-                    $attributeType = $this->stateRepository->getFieldType($item['query']['value']);
-                    $attributeValue = null;
-                    if (in_array($attributeType, ['xref_field', 'xref_multi_field'])) {
-                        $attribute .= 'id';
-                        $attributeValue = $this->recordData[$attribute] ?? null;
-                        if (is_string($attributeValue)) {
-                            $attributeValue = json_decode($attributeValue, true);
-                        }
-                    } else {
-                        $attributeValue = $this->recordData[$attribute] ?? null;
-                    }
+                $expressions[] = [
+                    'type' => 'condition',
+                    'query' => [
+                        'field' => $xField,
+                        'field_type' => $xFieldType,
+                        'x_value' => $xValue,
+                        'y_value' => $yValue,
+                        'operator' => $operator
+                    ]
+                ];
+            }
+        }
+
+        return $expressions;
+    }
 
-                    $item['query']['value'] = $attributeValue;
-                } elseif ($filterType === 'current_user') {
-                    $item['query']['value'] = $this->userInfo->id;
-                } elseif ($filterType === 'current_datetime') {
-                    $item['query']['value'] = date("Y-m-d H:i:s");
+    /**
+     * @param string $filterType
+     * @param $value
+     *
+     * @return mixed
+     *
+     * @throws \Exception
+     */
+    private function getYValue(string $filterType, $value)
+    {
+        switch ($filterType) {
+            case 'current_user':
+                return $this->userInfo->id;
+            case 'users_table':
+                return $this->stateRepository->getUserFieldValue(
+                    $value,
+                    $this->userInfo->id
+                );
+            case 'current_date':
+                return date("Y-m-d");
+            case 'current_datetime':
+                return date("Y-m-d H:i:s");
+            case 'current_table_field':
+                if (!empty($value)) {
+                    $field = sprintf('attr_%s_', $value);
+                    $fieldType = $this->stateRepository->getFieldType($value);
+
+                    return $this->getFieldValue($field, $fieldType);
+                } else {
+                    throw new \Exception('Состояние с типом "Поле текущей таблицы" не настроено');
+                }
+            case 'external_table_field':
+                throw new \Exception('Состояние с типом "Поле внещней таблицы" не обслуживается');
+            default:
+                if (is_null($value) || $value === '') {
+                    throw new \Exception('Состояние с типом "Константа" соддержит пустое значение');
                 }
 
-                $item['query']['field_type'] = $fieldType;
+                return $value;
+        }
+    }
+
+    private function getFieldValue(string $field, string $fieldType) {
+        if (in_array($fieldType, ['xref_field', 'xref_multi_field'])) {
+            $value = $this->recordData[$field . 'id'] ?? [];
+
+            if (is_string($value)) {
+                $value = str_replace('{', '[', $value);
+                $value = str_replace('}', ']', $value);
 
-                $expressions[] = $item;
+                $value = json_decode($value, true);
+            } elseif (is_null($value)) {
+                $value = [];
+            } elseif (is_numeric($value)) {
+                $value = [$value];
             }
+
+            return $value;
+        } elseif ($fieldType === 'boolean_field') {
+            $value = $this->recordData[$field] ?? null;
+
+            return $value === 'true' || $value === true;
         }
 
-        return $expressions;
+        return $this->recordData[$field] ?? null;
     }
 }
diff --git a/src/Infrastructure/Service/State/Formatter/SimpleStateFormatter.php b/src/Infrastructure/Service/State/Formatter/SimpleStateFormatter.php
index c76da48..31a3ba3 100644
--- a/src/Infrastructure/Service/State/Formatter/SimpleStateFormatter.php
+++ b/src/Infrastructure/Service/State/Formatter/SimpleStateFormatter.php
@@ -61,71 +61,65 @@ class SimpleStateFormatter
         $expressions = [];
 
         foreach ($state['state_fields'] as $stateField) {
-            $attribute = sprintf('attr_%d_', $stateField['field_id']);
-            $fieldType = $this->stateRepository->getFieldType($stateField['field_id']);
+            $xField = sprintf('attr_%d_', $stateField['field_id']);
+            $xFieldType = $this->stateRepository->getFieldType($stateField['field_id']);
+            $filterType = $stateField['state_field_type_id'];
             $operator = 'eq';
-            $value = null;
-
-            if ($stateField['state_field_type_id'] === 'current_user') {
-                $value = $this->userInfo->id;
-            } elseif ($stateField['state_field_type_id'] === 'users_table') {
-                $value = $this->stateRepository->getUserFieldValue($stateField['users_field_id'], $this->userInfo->id);
-            } elseif ($stateField['state_field_type_id'] === 'current_date') {
-                $value = date('Y-m-d');
-            } elseif ($stateField['state_field_type_id'] === 'condition') {
-                if ($stateField['state_field_type_id'] === 'not_empty') {
+            $xValue = $this->getFieldValue($xField, $xFieldType);
+            $yValue = null;
+
+            // Вычисляем значение Y
+            if ($filterType === 'current_user') {
+                $yValue = $this->userInfo->id;
+            } elseif ($filterType === 'users_table') {
+                $yValue = $this->stateRepository->getUserFieldValue($stateField['users_field_id'], $this->userInfo->id);
+            } elseif ($filterType === 'current_date') {
+                $yValue = date('Y-m-d');
+            } elseif ($filterType === 'current_datetime') {
+                $yValue = date('Y-m-d H:i:s');
+            } elseif ($filterType === 'condition') {
+                if ($stateField['condition_field_type_id'] === 'not_empty') {
                     $operator = 'is_not_null';
                 } else {
                     $operator = 'is_null';
                 }
-            } elseif ($stateField['state_field_type_id'] === 'current_table_field') {
+            } elseif ($filterType === 'current_table_field') {
                 if (!empty($stateField['current_table_field_id'])) {
-                    $field = sprintf('attr_%d_', $stateField['current_table_field_id']);
+                    $yField = sprintf('attr_%s_', $stateField['current_table_field_id']);
+                    $yFieldType = $this->stateRepository->getFieldType($stateField['current_table_field_id']);
 
-                    $value = $this->recordData[$field] ?? null;
+                    $yValue = $this->getFieldValue($yField, $yFieldType);
+                } else {
+                    throw new \Exception('Состояние с типом "Поле текущей таблицы" не настроено');
                 }
+            } elseif ($filterType === 'external_table_field') {
+                throw new \Exception('Состояние с типом "Поле внещней таблицы" не обслуживается');
             } elseif ($stateField['state_field_type_id'] === 'constant') {
-                $value = $stateField['properties']['value'];
-            } else {
-                continue;
-            }
+                $yValue = $stateField['properties']['value'] ?? null;
 
-            if ($fieldType === 'boolean_field') {
-                if ($value === 'true' || $value === true) {
-                    $value = true;
-                } else {
-                    $value = false;
+                if (is_null($yValue)) {
+                    throw new \Exception('Состояние с типом "Константа" соддержит пустое значение');
                 }
             }
 
-            if ($fieldType === 'xref_field' || $fieldType === 'xref_multi_field') {
-                $attribute = sprintf('%sid', $attribute);
+            if (in_array($xFieldType, ['xref_field', 'xref_multi_field'])) {
+                $xField = sprintf('%sid', $xField);
 
                 if ($operator === 'eq') {
                     $operator = 'equals_any';
                 }
-
-                if (is_string($value)) {
-                    $value = json_decode($value, true);
-                }
-
-                if (is_null($value)) {
-                    $value = [];
-                }
             }
 
-            if (!is_null($value)) {
-                $expressions[] = [
-                    'type' => 'condition',
-                    'query' => [
-                        'field' => $attribute,
-                        'value' => $value,
-                        'filter_type' => 'constant',
-                        'operator' => $operator,
-                        'field_type' => $fieldType
-                    ]
-                ];
-            }
+            $expressions[] = [
+                'type' => 'condition',
+                'query' => [
+                    'field' => $xField,
+                    'field_type' => $xFieldType,
+                    'x_value' => $xValue,
+                    'y_value' => $yValue,
+                    'operator' => $operator
+                ]
+            ];
         }
 
         return [
@@ -136,4 +130,29 @@ class SimpleStateFormatter
             ]
         ];
     }
+
+    private function getFieldValue(string $field, string $fieldType) {
+        if ($fieldType === 'xref_field' || $fieldType === 'xref_multi_field') {
+            $value = $this->recordData[$field . 'id'] ?? [];
+
+            if (is_string($value)) {
+                $value = str_replace('{', '[', $value);
+                $value = str_replace('}', ']', $value);
+
+                $value = json_decode($value, true);
+            } elseif (is_null($value)) {
+                $value = [];
+            } elseif (is_numeric($value)) {
+                $value = [$value];
+            }
+
+            return $value;
+        } elseif ($fieldType === 'boolean_field') {
+            $value = $this->recordData[$field] ?? null;
+
+            return $value === 'true' || $value === true;
+        }
+
+        return $this->recordData[$field] ?? null;
+    }
 }
diff --git a/src/Infrastructure/Service/State/StateService.php b/src/Infrastructure/Service/State/StateService.php
index 75dfbbc..e3b6ee1 100644
--- a/src/Infrastructure/Service/State/StateService.php
+++ b/src/Infrastructure/Service/State/StateService.php
@@ -68,6 +68,175 @@ class StateService implements StateServiceInterface
         );
     }
 
+    /**
+     * @param array $states - Список идентификаторов состояний
+     * @param array $recordData - Актуальные данные записи реестра, если необходимо использование типа "Поле текущей таблицы"
+     * @param string $matchType - Логическуая связка "all", "any", или "and", "or"
+     * @param bool $isDeny - Отрицание условия `if (!$myVar)`
+     *
+     * @return bool
+     *
+     * @throw \Exception
+     */
+    public function checkConditions(
+        array $states,
+        array $recordData = [],
+        string $matchType = 'all',
+        bool $isDeny = false
+    ) {
+        $matches = [];
+
+        if (!empty($states)) {
+            foreach ($states as $stateId) {
+                $matches[] = $this->isEqualsState(
+                    $stateId,
+                    $recordData
+                );
+            }
+
+            if (in_array($matchType, ['all', 'and'])) {
+                /*
+                 * Если логическая связка "Все", то есть "И"
+                 * Если (Состояние1 И Состояние2 И ...)
+                 */
+                if ($isDeny) {
+                    /*
+                     * Если "Не соответствует условию" И все состояния в логической связке "И" прошли проверку
+                     * Тогда пропускаем ограничение
+                     */
+                    if (!in_array(false, $matches)) {
+                        return false;
+                    }
+                } else {
+                    /*
+                     * Если "Соответствует условию"
+                     * И одно состояние в логической связке "И" не проходит проверку
+                     * Тогда пропускаем ограничение
+                     */
+                    if (in_array(false, $matches)) {
+                        return false;
+                    }
+                }
+            } else {
+                /*
+                 * Если логическая связка "Любое", то есть "ИЛИ"
+                 * Если (Состояние1 ИЛИ Состояние2 ИЛИ ...)
+                 */
+                if ($isDeny) {
+                    /*
+                     * Если "Не соответствует условию"
+                     * И одно из состояний в логической связке "ИЛИ" прошло проверку
+                     * Тогда пропускаем ограничение
+                     */
+                    if (in_array(true, $matches)) {
+                        return false;
+                    }
+                } else {
+                    /*
+                     * Если "Соответствует условию"
+                     * И все состояния в логической связке "ИЛИ" не прошли проверку
+                     * Тогда пропускаем ограничение
+                     */
+                    if (!in_array(true, $matches)) {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    public function isEqualsState(int $stateId, array $recordData = []): bool
+    {
+        $expressions = $this->format($stateId, $recordData);
+        $expressions = [$expressions];
+
+        return $this->isEqualsStateRecursive($expressions, $recordData);
+    }
+
+    private function isEqualsStateRecursive(array $expressions, array $recordData = [], string $logicalOperator = 'and'): bool
+    {
+        $matches = [];
+
+        foreach ($expressions as $expr) {
+            if ($expr['type'] === 'condition_group') {
+                $matches[] = $this->isEqualsStateRecursive($expr['query']['children'], $recordData, $expr['query']['logical_operator']);
+            } else {
+                $xValue = $expr['query']['x_value'];
+                $yValue = $expr['query']['y_value'];
+
+                switch ($expr['query']['operator']) {
+                    case 'eq':
+                        $matches[] = $xValue == $yValue;
+                        break;
+                    case 'neq':
+                        $matches[] = $xValue != $yValue;
+                        break;
+                    case 'in':
+                    case 'equals_any':
+                        $xValue = !is_array($xValue)
+                            ? (array) $xValue
+                            : $xValue;
+
+                        $yValue = !is_array($yValue)
+                            ? (array) $yValue
+                            : $yValue;
+
+                        $matches[] = count(array_intersect($xValue, $yValue)) > 0;
+                        break;
+                    case 'not_in':
+                    case 'not_equals_any':
+                        $xValue = !is_array($xValue)
+                            ? (array) $xValue
+                            : $xValue;
+
+                        $yValue = !is_array($yValue)
+                            ? (array) $yValue
+                            : $yValue;
+
+                        $matches[] = count(array_intersect($xValue, $yValue)) === 0;
+                        break;
+                    case 'gt':
+                        $matches[] = $xValue > $yValue;
+                        break;
+                    case 'lt':
+                        $matches[] = $xValue < $yValue;
+                        break;
+                    case 'gte':
+                        $matches[] = $xValue >= $yValue;
+                        break;
+                    case 'lte':
+                        $matches[] = $xValue <= $yValue;
+                        break;
+                    case 'is_null':
+                        $matches[] = empty($xValue);
+                        break;
+                    case 'is_not_null':
+                        $matches[] = !empty($xValue);
+                        break;
+                    case 'like':
+                        $matches[] = strpos(mb_strtolower($xValue), mb_strtolower($yValue)) !== false;
+                        break;
+                    case 'not_like':
+                        $matches[] = strpos(mb_strtolower($xValue), mb_strtolower($yValue)) === false;
+                        break;
+                    default:
+                        $matches[] = false;
+                        break;
+                }
+            }
+        }
+
+        if ($logicalOperator === 'and') {
+            // Если одно из всех выражение вернёт ЛОЖЬ, тогда условие не прошло проверку
+            return !in_array(false, $matches);
+        }
+
+        // если любое (any, or) из выражений равно ИСТИНА, тогда условие прошло проверку
+        return in_array(true, $matches);
+    }
+
     /**
      * Форматирует разные состояния, простые и сложные, одно или несколько, в один формат
      *
diff --git a/tests/Unit/Repository/StateRepository.php b/tests/Unit/Repository/StateRepository.php
index 58e0219..8913339 100644
--- a/tests/Unit/Repository/StateRepository.php
+++ b/tests/Unit/Repository/StateRepository.php
@@ -43,7 +43,7 @@ class StateRepository implements StateQueryRepositoryInterface
                     'state_id' => 1,
                     'field_id' => 1003,
                     'state_field_type_id' => 'current_table_field',
-                    'current_table_field_id' => 2000
+                    'current_table_field_id' => 1002
                 ]
             ]
         ],
diff --git a/tests/Unit/StateUnitTest.php b/tests/Unit/StateUnitTest.php
index 15ef58d..ac8b95b 100644
--- a/tests/Unit/StateUnitTest.php
+++ b/tests/Unit/StateUnitTest.php
@@ -24,14 +24,18 @@ class StateUnitTest extends TestCase
         $this->recordData = [
             'id' => 1,
             'attr_1000_' => 'str1',
-            'attr_1001_' => 'str2',
+            'attr_1001_' => '314zdec',
             'attr_1002_' => 'str3',
-            'attr_1003_' => 'str4',
+            'attr_1003_' => 'str3',
             'attr_2010_' => '2022-12-01',
             'attr_2011_' => '2022-12-01',
             // Для сравнения одного поля с другим полем в текущей таблице
-            'attr_2000_' => 'value of the current table field',
-            'attr_2001_' => null
+            'attr_2000_' => [],
+            'attr_2001_' => [],
+            'attr_3001_' => [],
+            'attr_2000_id' => null,
+            'attr_2001_id' => null,
+            'attr_3001_id' => null
         ];
 
         $this->userInfo = new \stdClass();
@@ -64,78 +68,102 @@ class StateUnitTest extends TestCase
         self::assertEquals($testResult, json_encode($result));
     }
 
+    public function testIsEqualsState()
+    {
+        $service = new StateService($this->repository, $this->userInfo);
+
+        $result = $service->isEqualsState(1, $this->recordData);
+        $testResult = true;
+        self::assertEquals($testResult, $result);
+    }
+
+    public function testCheckConditions()
+    {
+        $service = new StateService($this->repository, $this->userInfo);
+
+        $result = $service->checkConditions([1], $this->recordData);
+        $testResult = true;
+        self::assertEquals($testResult, $result);
+    }
+
     public function testStateFormatting(): void
     {
         $service = new StateService($this->repository, $this->userInfo);
 
-        $result = $service->format(1);
+        $result = $service->format(1, $this->recordData);
         $testResult =
             '{"type":"condition_group","query":{"logical_operator":"and","children":[{"type":"condition","query":{"fi' .
-            'eld":"attr_1000_","value":"str1","filter_type":"constant","operator":"eq","field_type":"string_field"}},' .
-            '{"type":"condition","query":{"field":"attr_1001_","value":"314zdec","filter_type":"constant","operator":' .
-            '"eq","field_type":"string_field"}},{"type":"condition","query":{"field":"attr_1002_","value":null,"filte' .
-            'r_type":"constant","operator":"is_null","field_type":"string_field"}},{"type":"condition","query":{"fiel' .
-            'd":"attr_1003_","value":null,"filter_type":"constant","operator":"eq","field_type":"string_field"}}]}}'
+            'eld":"attr_1000_","field_type":"string_field","x_value":"str1","y_value":"str1","operator":"eq"}},{"type' .
+            '":"condition","query":{"field":"attr_1001_","field_type":"string_field","x_value":"314zdec","y_value":"3' .
+            '14zdec","operator":"eq"}},{"type":"condition","query":{"field":"attr_1002_","field_type":"string_field",' .
+            '"x_value":"str3","y_value":null,"operator":"is_not_null"}},{"type":"condition","query":{"field":"attr_10' .
+            '03_","field_type":"string_field","x_value":"str3","y_value":"str3","operator":"eq"}}]}}'
         ;
 
-        self::assertEquals(json_encode($result), $testResult);
+        self::assertEquals($testResult, json_encode($result));
 
-        $result = $service->format(2);
+        $result = $service->format(2, $this->recordData);
         $testResult =
             '{"type":"condition_group","query":{"logical_operator":"and","children":[{"type":"condition","query":{"fi' .
-            'eld":"attr_1010_","filter_type":"constant","operator":"eq","value":"2021-07-15","field_type":"date_field' .
-            '"}},{"type":"condition_group","query":{"logical_operator":"or","children":[{"type":"condition","query":{' .
-            '"field":"attr_1011_","filter_type":"constant","operator":"gte","value":"2022-01-01","field_type":"date_f' .
-            'ield"}},{"type":"condition","query":{"field":"attr_1011_","filter_type":"constant","operator":"lte","val' .
-            'ue":"2021-06-01","field_type":"date_field"}}]}}]}}'
+            'eld":"attr_1010_","field_type":"date_field","x_value":null,"y_value":"2021-07-15","operator":"eq"}},{"ty' .
+            'pe":"condition_group","query":{"logical_operator":"or","children":[{"type":"condition","query":{"field":' .
+            '"attr_1011_","field_type":"date_field","x_value":null,"y_value":"2022-01-01","operator":"gte"}},{"type":' .
+            '"condition","query":{"field":"attr_1011_","field_type":"date_field","x_value":null,"y_value":"2021-06-01' .
+            '","operator":"lte"}}]}}]}}'
         ;
 
-        self::assertEquals(json_encode($result), $testResult);
+        self::assertEquals($testResult, json_encode($result));
 
-        $result = $service->format([1, 2]);
+        $result = $service->format([1, 2], $this->recordData);
         $testResult =
             '{"type":"condition_group","query":{"logical_operator":"and","children":[{"type":"condition_group","query' .
-            '":{"logical_operator":"and","children":[{"type":"condition","query":{"field":"attr_1000_","value":"str1"' .
-            ',"filter_type":"constant","operator":"eq","field_type":"string_field"}},{"type":"condition","query":{"fi' .
-            'eld":"attr_1001_","value":"314zdec","filter_type":"constant","operator":"eq","field_type":"string_field"' .
-            '}},{"type":"condition","query":{"field":"attr_1002_","value":null,"filter_type":"constant","operator":"i' .
-            's_null","field_type":"string_field"}},{"type":"condition","query":{"field":"attr_1003_","value":null,"fi' .
-            'lter_type":"constant","operator":"eq","field_type":"string_field"}}]}},{"type":"condition_group","query"' .
-            ':{"logical_operator":"and","children":[{"type":"condition","query":{"field":"attr_1010_","filter_type":"' .
-            'constant","operator":"eq","value":"2021-07-15","field_type":"date_field"}},{"type":"condition_group","qu' .
-            'ery":{"logical_operator":"or","children":[{"type":"condition","query":{"field":"attr_1011_","filter_type' .
-            '":"constant","operator":"gte","value":"2022-01-01","field_type":"date_field"}},{"type":"condition","quer' .
-            'y":{"field":"attr_1011_","filter_type":"constant","operator":"lte","value":"2021-06-01","field_type":"da' .
-            'te_field"}}]}}]}}]}}'
+            '":{"logical_operator":"and","children":[{"type":"condition","query":{"field":"attr_1000_","field_type":"' .
+            'string_field","x_value":"str1","y_value":"str1","operator":"eq"}},{"type":"condition","query":{"field":"' .
+            'attr_1001_","field_type":"string_field","x_value":"314zdec","y_value":"314zdec","operator":"eq"}},{"type' .
+            '":"condition","query":{"field":"attr_1002_","field_type":"string_field","x_value":"str3","y_value":null,' .
+            '"operator":"is_not_null"}},{"type":"condition","query":{"field":"attr_1003_","field_type":"string_field"' .
+            ',"x_value":"str3","y_value":"str3","operator":"eq"}}]}},{"type":"condition_group","query":{"logical_oper' .
+            'ator":"and","children":[{"type":"condition","query":{"field":"attr_1010_","field_type":"date_field","x_v' .
+            'alue":null,"y_value":"2021-07-15","operator":"eq"}},{"type":"condition_group","query":{"logical_operator' .
+            '":"or","children":[{"type":"condition","query":{"field":"attr_1011_","field_type":"date_field","x_value"' .
+            ':null,"y_value":"2022-01-01","operator":"gte"}},{"type":"condition","query":{"field":"attr_1011_","field' .
+            '_type":"date_field","x_value":null,"y_value":"2021-06-01","operator":"lte"}}]}}]}}]}}'
         ;
 
-        self::assertEquals(json_encode($result), $testResult);
+        self::assertEquals($testResult, json_encode($result));
     }
 
     public function testStateConvertingToApiQl(): void
     {
         $service = new StateService($this->repository, $this->userInfo);
 
+        $result = $service->convertToApiQl([1], $this->recordData, 'and', false);
+        $testResult =
+            '{"and":[{"eq":{"attr_1000_":"str1"}},{"eq":{"attr_1001_":"314zdec"}},{"is_not_null":"attr_1002_"},{"eq":' .
+            '{"attr_1003_":"str3"}}]}'
+        ;
+        self::assertEquals($testResult, json_encode($result));
+
         $result = $service->convertToApiQl([1], $this->recordData);
         $testResult =
-            '{"where":[{"and":[{"eq":{"attr_1000_":"str1"}},{"eq":{"attr_1001_":"314zdec"}},{"is_null":"attr_1002_"},' .
-            '{"eq":{"attr_1003_":"value of the current table field"}}]}]}'
+            '{"where":{"and":[{"eq":{"attr_1000_":"str1"}},{"eq":{"attr_1001_":"314zdec"}},{"is_not_null":"attr_1002_' .
+            '"},{"eq":{"attr_1003_":"str3"}}]}}'
         ;
-        self::assertEquals(json_encode($result), $testResult);
+        self::assertEquals($testResult, json_encode($result));
 
         $result = $service->convertToApiQl([2], $this->recordData);
         $testResult =
-            '{"where":[{"and":[{"eq":{"attr_1010_":"2021-07-15"}},{"or":[{"gte":{"attr_1011_":"2022-01-01"}},{"lte":{' .
-            '"attr_1011_":"2021-06-01"}}]}]}]}'
+            '{"where":{"and":[{"eq":{"attr_1010_":"2021-07-15"}},{"or":[{"gte":{"attr_1011_":"2022-01-01"}},{"lte":{"' .
+            'attr_1011_":"2021-06-01"}}]}]}}'
         ;
-        self::assertEquals(json_encode($result), $testResult);
+        self::assertEquals($testResult, json_encode($result));
 
         $result = $service->convertToApiQl([1, 2], $this->recordData);
         $testResult =
-            '{"where":[{"and":[{"and":[{"eq":{"attr_1000_":"str1"}},{"eq":{"attr_1001_":"314zdec"}},{"is_null":"attr_' .
-            '1002_"},{"eq":{"attr_1003_":"value of the current table field"}}]},{"and":[{"eq":{"attr_1010_":"2021-07-' .
-            '15"}},{"or":[{"gte":{"attr_1011_":"2022-01-01"}},{"lte":{"attr_1011_":"2021-06-01"}}]}]}]}]}'
+            '{"where":{"and":[{"and":[{"eq":{"attr_1000_":"str1"}},{"eq":{"attr_1001_":"314zdec"}},{"is_not_null":"at' .
+            'tr_1002_"},{"eq":{"attr_1003_":"str3"}}]},{"and":[{"eq":{"attr_1010_":"2021-07-15"}},{"or":[{"gte":{"att' .
+            'r_1011_":"2022-01-01"}},{"lte":{"attr_1011_":"2021-06-01"}}]}]}]}}'
         ;
-        self::assertEquals(json_encode($result), $testResult);
+        self::assertEquals($testResult, json_encode($result));
     }
 }
-- 
GitLab


From a6be6189810c6fed78aad4b4c3cb11cf61e9d2ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=9D=D0=B8=D0=BA=D0=B8=D1=82=D0=B0=20=D0=9A=D0=BE=D1=81?=
 =?UTF-8?q?=D1=82=D1=8E=D0=BD=D0=B8=D1=87=D0=B5=D0=B2?=
 <slipper7747@gmail.com>
Date: Wed, 10 May 2023 11:46:49 +0300
Subject: [PATCH 10/10] =?UTF-8?q?hotfix:=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?=
 =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D0=BF=D0=B0=D1=80=D1=81=D0=B8=D0=BD?=
 =?UTF-8?q?=D0=B3=20=D1=82=D0=B5=D0=BB=D0=B0=20=D0=B7=D0=B0=D0=BF=D1=80?=
 =?UTF-8?q?=D0=BE=D1=81=D0=B0=20=D0=BF=D1=80=D0=B8=20=D1=82=D0=B8=D0=BF?=
 =?UTF-8?q?=D0=B5=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B5=D0=BD=D1=82=D0=B0=20app?=
 =?UTF-8?q?lication/json?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/Infrastructure/Middleware/BodyParserMiddleware.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/Infrastructure/Middleware/BodyParserMiddleware.php b/src/Infrastructure/Middleware/BodyParserMiddleware.php
index ba2bcf0..c108f7f 100644
--- a/src/Infrastructure/Middleware/BodyParserMiddleware.php
+++ b/src/Infrastructure/Middleware/BodyParserMiddleware.php
@@ -24,7 +24,7 @@ class BodyParserMiddleware implements MiddlewareInterface
     public function beforeHandleRoute(Event $event, Micro $application): void
     {
         $contentType = strtolower($application->request->getHeader('CONTENT_TYPE'));
-        if ($contentType === 'application/json' || $contentType === 'application/json;charset=utf-8') {
+        if (stripos($contentType, 'application/json') === 0) {
             $requestBody = $application->request->getRawBody();
             if (!empty($requestBody)) {
                 $jsonRawBody = json_decode($requestBody, true);
-- 
GitLab