Possible Bug in Espo\Core\FieldProcessing\NextNumber\BeforeSaveProcessor::process()

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • aldisa
    Junior Member
    • Jun 2023
    • 28

    Possible Bug in Espo\Core\FieldProcessing\NextNumber\BeforeSaveProcessor::process()

    I am encountering the following error when trying to save a Custom Entity:

    Code:
    Espo\Core\FieldProcessing\NextNumber\BeforeSaveProcessor::process():
    Argument #1 ($entity) must be of type Espo\Core\ORM\Entity,
    Espo\Modules\Natera\Entities\NateraJob given,
    called in /App/application/Espo/Hooks/Common/NextNumber.php on line 51


    EspoCRM Version: 8.1.1
    PHP Version: PHP 8.2.7 (cli) (built: Jun 9 2023 19:37:27) (NTS)

    STEPS TO REPRODUCE

    My Custom Entity inside a custom Module is Defined as follows.

    File: custom/Espo/Modules/Natera/Resources/metadata/entityDefs/NateraJob.json
    PHP Code:
    {
        "fields": {
            "jobId": {
                "label": "Natera Job ID",
                "type": "varchar",
                "length": 12,
                "required": true,
                "readOnlyAfterCreate": true
            },
            "assignmentId": {
                "label": "Assignment",
                "type": "varchar",
                "length": 17,
                "required": false
            },
            "technicianId": {
                "label": "Natera Technician ID",
                "type": "varchar",
                "length": 12,
                "required": true
            },
            "status": {
                "label": "Job Status",
                "type": "enum",
                "required": true,
                "default": "available",
                "options": [
                    "available",
                    "published",
                    "assigned",
                    "scheduled",
                    "on_the_way",
                    "on_site",
                    "in_progress",
                    "completed",
                    "cancelled",
                    "error"
                ]
            },
            "mpxData": {
                "type": "text",
                "required": true,
                "isEncrypted": true
            },
            "mpxLog": {
                "type": "text",
                "required": true
            },
            "createdAt": {
                "type": "datetime",
                "readOnly": true
            },
            "modifiedAt": {
                "type": "datetime",
                "readOnly": true
            },
            "address": {
                "type": "address",
                "notStorable": true,
                "utility": true
            }
        },
        "indexes": {
            "natera_id": {
                "columns": ["jobId"],
                "unique": true
            },
            "entity_id": {
                "columns": ["assignmentId"]
            },
            "by_status": {
                "columns": [
                    "status",
                    "createdAt"
                ]
            }
        },
        "collection": {
            "orderBy": "createdAt",
            "order": "asc",
            "textFilterFields": [
                "status"
            ]
        }
    } 
    
    File: custom/Espo/Modules/Natera/Entities/NateraJob.php
    PHP Code:
    namespace Espo\Modules\Natera\Entities;
    
    use Espo\ORM\BaseEntity;
    use Espo\ORM\EntityManager;
    use Espo\ORM\Value\ValueAccessorFactory;
    use Espo\Core\Utils\Crypt;
    
    class NateraJob extends BaseEntity
    {
        private $isEncrypted;
    
        public function __construct(
            string $entityType,
            array $defs,
            ?EntityManager $entityManager = null,
            ?ValueAccessorFactory $valueAccessorFactory = null,
            private Crypt $crypt,
        ) {
            parent::__construct(
                $entityType,
                $defs,
                $entityManager,
                $valueAccessorFactory
            );
    
            $this->isEncrypted = [];
            foreach ($defs['fields'] as $field => $value) {
                if (array_key_exists('isEncrypted', $value) && $value['isEncrypted']) {
                    $this->isEncrypted[] = $field;
                }
            }
        }
    
        protected function setInContainer(string $attribute, $value): void
        {
            if (in_array($attribute, $this->isEncrypted)) {
                $value = $this->crypt->encrypt($value);
            }
    
            parent::setInContainer($attribute, $value);
    
            return;
        }
    
        public function setFetched(string $attribute, $value): void
        {
            if (in_array($attribute, $this->isEncrypted)) {
                $value = $this->crypt->decrypt($value);
                parent::setInContainer($attribute, $value);
            }
    
            parent::setFetched($attribute, $value);
        }
    
        protected function getFromContainer(string $attribute)
        {
            $value = parent::getFromContainer($attribute);
    
            if (in_array($attribute, $this->isEncrypted)) {
                return $this->crypt->decrypt($value);
            }
    
            return $value;
        }
    
        public function getFetched(string $attribute)
        {
            $value = parent::getFetched($attribute);
    
            if (in_array($attribute, $this->isEncrypted) && !is_null($value)) {
                $value = $this->crypt->decrypt($value);
            }
    
            return $value;
        }
    } 
    
    When I try to update an Entity I get the above error.
    For testing to recreate the error, I created code as follows inside a CLI job:

    PHP Code:
    try {
    
        $em = $this->container->get('entityManager');
    
        $entity = $em->getEntityById('NateraJob', '65e26caac9c5d60f9');
        print_r($entity->getValueMap());
        $entity->set('assignmentId', '65a0d16b7f222e498');
        $em->saveEntity($entity, ['SKIP_NATERA' => true]);
    
    } catch (\Throwable $error) {
    
        echo $error->getMessage();
        echo "\n";
        echo $error->getTraceAsString();
    
    } 
    
    Output from code:
    Code:
    stdClass Object
    (
        [id] => 65e26caac9c5d60f9
        [deleted] =>
        [jobId] => h9c74fg2
        [assignmentId] =>
        [technicianId] => wosv2mwl
        [status] => scheduled
        [mpxData] => {"id":"h9c74fg2",/** removed private data **/}
        [mpxLog] => [{"at":"2024-01-12 19:33:19","type":"EVENT","message":"Complete Job Data Updated"},{"at":"2024-01-17 17:55:25","type":"EVENT","message":"Job Status changed to ASSIGNED"},{"at":"2024-01-17 17:55:25","type":"EVENT","message":"Job assignee changed"},{"at":"2024-01-17 17:55:25","type":"EVENT","message":"Complete Job Data Updated"},{"at":"2024-01-31 15:58:42","type":"EVENT","message":"Job Status changed to SCHEDULED"},{"at":"2024-01-31 15:58:42","type":"EVENT","message":"Job startTime changed"},{"at":"2024-03-02 00:02:50","type":"EVENT","message":"Job Migrated"}]
        [createdAt] => 2024-03-02 00:02:50
        [modifiedAt] => 2024-03-02 00:02:50
    )
    Espo\Core\FieldProcessing\NextNumber\BeforeSaveProcessor::process(): Argument #1 ($entity) must be of type Espo\Core\ORM\Entity, Espo\Modules\Natera\Entities\NateraJob given, called in /App/application/Espo/Hooks/Common/NextNumber.php on line 51
    #0 /App/application/Espo/Hooks/Common/NextNumber.php(51): Espo\Core\FieldProcessing\NextNumber\BeforeSaveProcessor->process()
    #1 /App/application/Espo/Core/Hook/GeneralInvoker.php(186): Espo\Hooks\Common\NextNumber->beforeSave()
    #2 /App/application/Espo/Core/HookManager.php(118): Espo\Core\Hook\GeneralInvoker->invoke()
    #3 /App/application/Espo/Core/Repositories/Database.php(298): Espo\Core\HookManager->process()
    #4 /App/application/Espo/ORM/Repository/RDBRepository.php(139): Espo\Core\Repositories\Database->beforeSave()
    #5 /App/application/Espo/Core/Repositories/Database.php(136): Espo\ORM\Repository\RDBRepository->save()
    #6 /App/application/Espo/ORM/EntityManager.php(244): Espo\Core\Repositories\Database->save()
    #7 /App/custom/Espo/Modules/Natera/Services/Utils/Tasks/Task/Test.php(25): Espo\ORM\EntityManager->saveEntity()
    #8 /App/custom/Espo/Modules/Natera/Services/Utils/Tasks/TaskManager.php(49): Espo\Modules\Natera\Services\Utils\Tasks\Task\Test->execute()
    #9 /App/application/Espo/Core/Job/JobRunner.php(233): Espo\Modules\Natera\Services\Utils\Tasks\TaskManager->run()
    #10 /App/application/Espo/Core/Job/JobRunner.php(194): Espo\Core\Job\JobRunner->runJob()
    #11 /App/application/Espo/Core/Job/JobRunner.php(123): Espo\Core\Job\JobRunner->runJobNamed()
    #12 /App/application/Espo/Core/Job/JobRunner.php(77): Espo\Core\Job\JobRunner->runInternal()
    #13 /App/application/Espo/Core/Job/JobManager.php(145): Espo\Core\Job\JobRunner->runThrowingException()
    #14 /App/application/Espo/Core/Console/Commands/RunJob.php(92): Espo\Core\Job\JobManager->runJob()
    #15 /App/application/Espo/Core/Console/CommandManager.php(95): Espo\Core\Console\Commands\RunJob->run()
    #16 /App/application/Espo/Core/ApplicationRunners/Command.php(51): Espo\Core\Console\CommandManager->run()
    #17 /App/application/Espo/Core/Application/RunnerRunner.php(84): Espo\Core\ApplicationRunners\Command->run()
    #18 /App/application/Espo/Core/Application.php(78): Espo\Core\Application\RunnerRunner->run()
    #19 /App/command.php(35): Espo\Core\Application->run()
    #20 /App/bin/command(4): include('...')
    #21 {main}
    Last edited by aldisa; 05-04-2024, 02:19 PM.
  • aldisa
    Junior Member
    • Jun 2023
    • 28

    #2
    I took a look at this Class: \Espo\Core\FieldProcessing\NextNumber\BeforeSavePr ocessor

    It has the following relevant portions.

    PHP Code:
    namespace Espo\Core\FieldProcessing\NextNumber;
    
    use Espo\Core\ORM\Entity;/**
         * @param array<string, mixed> $options
         */
        public function process(Entity $entity, array $options): void
        {
            $fieldList = $this->getFieldList($entity->getEntityType());
    
            foreach ($fieldList as $field) {
                $this->processItem($entity, $field, $options);
            }
        } 
    
    If the Entity type hint for the process() function was = \Espo\ORM\Entity, then the above error would not occur.

    My custom entity (NateraJob) extends \Espo\ORM\BaseEntity which implements \Espo\ORM\Entity.
    \Espo\Core\ORM\Entity extend \Espo\ORM\BaseEntity which implements \Espo\ORM\Entity.

    Would the \Espo\Core\FieldProcessing\NextNumber\BeforeSavePr ocessor->process() function still work if the Entity implements \Espo\ORM\Entity?

    Unfortunately, I am not able to implement upgrade version of EspoCRM for my client, but I could manually make this change if appropriate.

    Look forward to your guidance in this regard.
    Last edited by aldisa; 05-03-2024, 10:50 PM.

    Comment

    • yuri
      Member
      • Mar 2014
      • 8458

      #3
      Hi,

      Consider extending from Espo\Core\ORM\Entity instead.
      If you find EspoCRM good, we would greatly appreciate if you could give the project a star on GitHub. We believe our work truly deserves more recognition. Thanks.

      Comment

      Working...