Backend Calculation - get Value from other Module

When making a backend request, a number of fields are passed to the Code allowing it to make calculations upon it. The docs mention the ability to use values from multiple modules. What is the recommend way to do this?

For example, I have a custom Module where you select a project and the code should respond with the company that commissioned the project. However, the project and the company are separate modules from the one where the data entry is done.

The way I thought about doing this was making a direct SQL request to the database and selecting the company based on the id given by Suitecrm. I think this is not the recommended way because I found no documentation on this besides old one from 7.0 but I am trying to do this in SuiteCRM 8. Any help is appreciated!

The Beans, has documented for v7, should work anywhere in v8 where a legacy session is open (which is a common thing in the back-end of most requests, since you need to touch data).

This is the docs:

And then just try using it. You might need commands like these on the top of your file:

use BeanFactory;
use SugarBean;

Do you want to do this on save of the record, or directly in the browser while it’s being entered. They are two different approaches.

while it is being entered

Is there any way to do this with non legacy code I would like to future-proof the code base as much as possible?

I wouldn’t call this legacy code. I don’t think there are any plans to move way from those classes. They are the main object abstractions that SuiteCRM has to deal with the data layer.

The normal v8 backend uses this.

1 Like

This approach might work for you. It works in 7 and with a legacy module I’m pretty sure. You have the custom module and the project relation selected, you can grab the account from the project and populate the account name (I think??).

$dictionary['Case']['fields']['account_name']['populate_list']=
        array('name','id','industry','account_type');
$dictionary['Case']['fields']['account_name']['field_list']=
        array('account_name','account_id','account_industry_c','account_type_c');

In this case we want to populate the fields:

‘account_name’,‘account_id’,‘account_industry_c’,‘account_type_c’

in a case when the appropriate account is related.

add the custom file - ‘custom/Extension/modules/Cases/Ext/Vardefs/account_name.php’

After a repair and rebuild, if it does not work, Check the file cache/modules/{your_module}/{your_module}vardefs.php . It should have populate_list and field_list arrays in the field array $GLOBALS[“dictionary”]… If not delete/rename the file and do a Quick Repair and Rebuild and it should work.

Also make sure developer mode is not enabled. It will not work with developer mode enabled.

https://johndopenotes.wordpress.com/2013/03/18/sugarcrm-populating-fields-using-a-relate-field/

1 Like

Hey @dwaltsch,

I was looking at the code and there maybe a suite 8 ready way to do what you want. Kind of in the lines of what @pgr suggested but not the same.

By looking at the code there seem to be several suite 8 classes that access legacy code. They extend a class called LegacyHandler.

Take core/backend/Currency/LegacyHandler/CurrencyHandler.php as an example.

On legacy handler classes they always seem to call init() and close(). I think it initializes a “legacy” context within suite 8, but as it seems you also need to call close() which means that you need to switch back from that legacy context to the Suite 8 context.

By looking at the code in CurrencyHandler it seems that you can even call BeanFactory and then work with beans.

With that I think you would be able to load related beans like we did on suite 7 with $bean->load_relationship($relName) and so forth.

public function getCurrencies(): array
    {
        $this->init();

        $currencies = [];

        $currency = $this->getCurrency('-99');
        if (!empty($currency)) {
            $currencies['-99'] = $currency;
        }


        $bean = BeanFactory::newBean('Currencies');

        if (!$bean) {
            return $currencies;
        }

        $list = $bean->get_full_list('name');

        if (!empty($list)) {
            foreach ($list as $item) {
                $currencies[$item->id] = [
                    'id' => $item->id,
                    'name' => $item->name,
                    'symbol' => html_entity_decode($item->symbol),
                    'iso4217' => $item->iso4217,
                    'conversion_rate' => $item->conversion_rate
                ];
            }
        }

        $this->close();

        return $currencies;
    }

There seem to already exist some process handlers that are legacy handlers, have a look at core/backend/Process/LegacyHandler, it has several examples

2 Likes

Thank you for the advice. I have tried adapting the CurrencyHandler like the following:

<?php

namespace App\Extension\eks\modules\eks_maintenance\Service\Fields\Legacy;

use App\Engine\LegacyHandler\LegacyHandler;
use BeanFactory;

class LegacyCompany extends LegacyHandler
{
    public const HANDLER_KEY = 'legacycompany';

    /**
     * @inheritDoc
     */
    public function getHandlerKey(): string
    {
        return self::HANDLER_KEY;
    }

    public function getLegacyCompany(?string $itemid)
    {
        $this->init();

        $maintenancebean = BeanFactory::getBean('eks_maintenance',$itemid);
        $this->close();

        return $maintenancebean;
    }
}

and call the LegacyHandler from the following Backend Process

<?php

namespace App\Extension\eks\modules\eks_maintenance\Service\Fields;

use ApiPlatform\Core\Exception\InvalidArgumentException;
use App\Extension\eks\modules\eks_maintenance\Service\Fields\Legacy\LegacyCompany;
use App\Process\Entity\Process;
use App\Process\Service\ProcessHandlerInterface;
use BeanFactory;
use SugarBean;
use Sugarcrm\Util\LoggerManager;


class SearchCompany implements ProcessHandlerInterface
{
    protected const MSG_OPTIONS_NOT_FOUND = 'Process options are not defined';
    public const PROCESS_TYPE = 'search_company';
    public function __construct()
    {
    }
    public function getProcessType(): string
    {
        return self::PROCESS_TYPE;
    }

    public function requiredAuthRole(): string
    {
        return '';
    }

    public function getRequiredACLs(Process $process): array
    {
        return [];
    }

    public function configure(Process $process): void
    {
        $process->setId(self::PROCESS_TYPE);
        $process->setAsync(false);
    }

    public function validate(Process $process): void
    {
    }

    public function getCompany($itemid){
        $legacyCompany = new LegacyCompany();
        $company = $legacyCompany->getLegacyCompany($itemid);
    }
    public function run(Process $process)
    {
        $options = $process->getOptions();
        $record = $options['record'];
        $attributes = $record['attributes'];
        $value = $attributes['eks_project_position_eks_maintenance_1_name']['name'];
        $maintenanceid = $attributes['eks_project_position_eks_maintenance_1_name']['id'];
        $this->getCompany($maintenanceid);
        $responseData = [
            'value' => $value
        ];

        $process->setStatus('success');
        $process->setMessages([]);
        $process->setData($responseData);
    }
}

however when I execute the code I get the following error:


saying that I passed not enough arguments to the constructur even though I think I implemented all the functions from the CurrencyHandler Code. Is there a mistaken in the way I have implemented this?

I see a few examples that use this form

class SomeClass extends LegacyHandler implements ProcessHandlerInterface

this way you wouldn’t have to redefine so many functions, only the ones you mean to change. That might solve other problems for you.

Anyway, your error should be easy to trace in PHPStorm since it should be able to figure out for you, which constructor it is actually trying to call, that has more arguments than 0… maybe the stack trace will give you some clues.

Hey @dwaltsch,

The problem here is due to the way you are loading LegacyCompany within SearchCompany.

Since you are doing new LegacyCompany() the dependencies that are required for the LegacyHandler aren’t injected. (See LegacyHandler class constructor). When using dependency injection the constructor dependencies are automatically injected.

When using symfony you should use dependency injection and the container as much as possible.

I personally try to avoid instantiating new classes using new SomeClass() as much as possible. With that you loose all the benefits of using dependency injection and the container.

<?php

namespace App\Extension\eks\modules\eks_maintenance\Service\Fields;

use ApiPlatform\Core\Exception\InvalidArgumentException;
use App\Extension\eks\modules\eks_maintenance\Service\Fields\Legacy\LegacyCompany;
use App\Process\Entity\Process;
use App\Process\Service\ProcessHandlerInterface;
use BeanFactory;
use SugarBean;
use Sugarcrm\Util\LoggerManager;


class SearchCompany implements ProcessHandlerInterface
{
    protected const MSG_OPTIONS_NOT_FOUND = 'Process options are not defined';
    public const PROCESS_TYPE = 'search_company';
    
    /**
     * @var LegacyCompany
     */
    protected $legacyCompany;

    public function __construct(LegacyCompany $legacyCompany)
    {
        $this->legacyCompany = $legacyCompany;
    }
    public function getProcessType(): string
    {
        return self::PROCESS_TYPE;
    }

    public function requiredAuthRole(): string
    {
        return '';
    }

    public function getRequiredACLs(Process $process): array
    {
        return [];
    }

    public function configure(Process $process): void
    {
        $process->setId(self::PROCESS_TYPE);
        $process->setAsync(false);
    }

    public function validate(Process $process): void
    {
    }

    public function getCompany($itemid) {
        return $this->legacyCompany->getLegacyCompany($itemid);
    }
    public function run(Process $process)
    {
        $options = $process->getOptions();
        $record = $options['record'];
        $attributes = $record['attributes'];
        $value = $attributes['eks_project_position_eks_maintenance_1_name']['name'];
        $maintenanceid = $attributes['eks_project_position_eks_maintenance_1_name']['id'];
        $company = $this->getCompany($maintenanceid);
        $responseData = [
            'value' => $value
        ];

        $process->setStatus('success');
        $process->setMessages([]);
        $process->setData($responseData);
    }
}

(PS: I did the changes on the code directly here, there could be an import missing or something)

By the way, I agree with @pgr it could be easier to just extends LegacyHandler implements ProcessHandlerInterface in SearchCompany