Is it possible to add a button beside edit in detail view that triggers a workflow or changes status of lead so that this change triggers a workflow
This will much easier in a future version of SuiteCRM when my PowerWorfklows add-on is integrated into core, I am working on that right now. You will just need to select an option in the Workdflow definition saying it is meant to be triggered by the user using a button.
So i need power workflow add on?
No, you need to wait for the future version of SuiteCRM v8 that has the feature, or develop it today using code.
What I do is just put a check box field in the record like βactivate workflowβ. When checked, the workflow runs and then re-sets the checkbox back to unchecked.
Works like a charm.
that is a great idea but it is a requirement to put a button
I was able to add a button on the detail view page and trigger a legacy action. The following post helps on how we can add a button and call functions/objects from the legacy.
This is what I implemented
<?php
namespace App\Extension\defaultExt\modules\Leads\Services;
use ApiPlatform\Core\Exception\InvalidArgumentException;
use App\Module\Service\ModuleNameMapperInterface;
use App\Process\Entity\Process;
use App\Process\Service\ProcessHandlerInterface;
class AssignLeadAction implements ProcessHandlerInterface
{
protected const MSG_OPTIONS_NOT_FOUND = 'Process options are not defined';
protected const PROCESS_TYPE = 'record-assign-lead';
/**
* @var ModuleNameMapperInterface
*/
private $moduleNameMapper;
/**
* MergeRecordsBulkAction constructor.
* @param ModuleNameMapperInterface $moduleNameMapper
*/
public function __construct(ModuleNameMapperInterface $moduleNameMapper)
{
$this->moduleNameMapper = $moduleNameMapper;
}
/**
* @inheritDoc
*/
public function getProcessType(): string
{
return self::PROCESS_TYPE;
}
/**
* @inheritDoc
*/
public function requiredAuthRole(): string
{
return 'ROLE_USER';
}
/**
* @inheritDoc
*/
public function getRequiredACLs(Process $process): array
{
$options = $process->getOptions();
$module = $options['module'] ?? '';
$id = $options['id'] ?? '';
return [
$module => [
[
'action' => 'view',
'record' => $id
],
],
];
}
/**
* @inheritDoc
*/
public function configure(Process $process): void
{
//This process is synchronous
//We aren't going to store a record on db
//thus we will use process type as the id
$process->setId(self::PROCESS_TYPE);
$process->setAsync(false);
}
/**
* @inheritDoc
*/
public function validate(Process $process): void
{
}
/**
* @inheritDoc
*/
public function run(Process $process)
{
$options = $process->getOptions();
$leadid=$options['record']['attributes']['id'];
$leadbean=BeanFactory::getBean('Leads',$leadid);
$leadbean->status="Assigned";
$responseData = [
'handler' => 'redirect',
'params' => [
'route' => $options['module'] . '/convert-lead/' . $options['id'],
'queryParams' => [
]
]
];
$process->setStatus('success');
$process->setMessages([]);
$process->setData($responseData);
}
}
this is my code but when pressing the button unexpected error when calling action appears
I tried to implement the functionality on my machine. I could get it successfully working. The following are the code changes:
- public\legacy\custom\Extension\application\Ext\Language\en_us.lang.php
<?php
$app_strings['LBL_ASSIGN_STATUS_CONFIRMATION']='Are you sure to change the status of this lead?';
- extensions\defaultExt\config\modules\Leads\recordview\actions\assign_lead.php
<?php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
return static function (ContainerBuilder $container): void {
$actions = $container->getParameter('module.recordview.actions') ?? [];
$modules = $actions['modules'] ?? [];
$leads = $modules['leads'] ?? [];
$recordActions = $leads['actions'] ?? [];
$recordActions['leads-assign'] = [
'key' => 'leads-assign',
'labelKey' => 'LBL_ASSIGN',
'asyncProcess' => 'true',
'modes' => ['detail', 'edit'],
'params' => [
'displayConfirmation' => true,
'confirmationLabel' => 'LBL_ASSIGN_STATUS_CONFIRMATION',
'module' => 'leads',
]
];
$leads['actions'] = $recordActions;
$modules['leads'] = $leads;
$actions['modules'] = $modules;
$container->setParameter('module.recordview.actions', $actions);
};
- extensions\defaultExt\modules\Leads\Process\Service\RecordActions\AssignLeadAction.php
<?php
namespace App\Extension\defaultExt\modules\Leads\Process\Service\RecordActions;
use ApiPlatform\Exception\InvalidArgumentException;
use App\Engine\LegacyHandler\LegacyHandler;
use App\Process\Entity\Process;
use App\Process\Service\ProcessHandlerInterface;
class AssignLeadAction extends LegacyHandler implements ProcessHandlerInterface
{
protected const MSG_OPTIONS_NOT_FOUND = 'Process options are not defined';
protected const MSG_INVALID_TYPE = 'Invalid type';
public const PROCESS_TYPE = 'record-leads-assign';
private $moduleNameMapper;
public function getProcessType(): string
{
return self::PROCESS_TYPE;
}
public function getHandlerKey(): string{
return $this->getProcessType();
}
public function configure(Process $process): void
{
$process->setId(self::PROCESS_TYPE);
$process->setAsync(false);
}
public function requiredAuthRole(): string
{
return 'ROLE_USER';
}
public function getRequiredACLs(Process $process): array
{
$options = $process->getOptions();
$module = $options['module'] ?? '';
$id = $options['id'] ?? '';
return [
$module => [
[
'action' => 'view',
'record' => $id
],
],
];
}
public function validate(Process $process): void
{
if (empty($process->getOptions())) {
throw new InvalidArgumentException(self::MSG_OPTIONS_NOT_FOUND);
}
$options = $process->getOptions();
if (empty($options['module']) || empty($options['action'])) {
throw new InvalidArgumentException(self::MSG_OPTIONS_NOT_FOUND);
}
if (empty($options['id'])) {
throw new InvalidArgumentException(self::MSG_OPTIONS_NOT_FOUND);
}
}
public function run(Process $process)
{
$options = $process->getOptions();
['id' => $leadsId] = $options;
$this->init();
$lead = \BeanFactory::getBean('Leads', $leadsId);
if (empty($lead)) {
$this->close();
return [
'status' => 'error',
'data' => [],
'messages' => [
'LBL_RECORD_NOT_FOUND'
]
];
}
$lead->status = 'Converted';
$lead->save();
$responseData = [
'reload' => true,
];
// Close legacy handler
$this->close();
$process->setStatus('success');
$process->setMessages([]);
$process->setData($responseData);
}
}
- Quick repair & rebuid and run the command: php bin/console cache:clear
It changed the status and reload the page to display the updated lead record.
in addition to this i add button in button in detailviewdefs?
No. Because we are injecting the record action. One more thing I forgot to mention about the button label. Please add a label LBL_ASSIGN in custom/extension/modules/leads/ext/language/en_us.lang.php.
i tried this code and it is working thank you so much for the help i just have a small question is it an option to add the button next to edit button not in actions menu
<?php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
return static function (ContainerBuilder $container): void {
$actions = $container->getParameter('module.recordview.actions') ?? [];
$modules = $actions['modules'] ?? [];
$leads = $modules['leads'] ?? [];
$recordActions = $leads['actions'] ?? [];
$recordActions['leads-assign'] = [
'key' => 'leads-assign',
"display" => "hide",
"asyncProcess" => true,
'labelKey' => 'LBL_ASSIGN',
'modes' => ['detail', 'edit'],
'params' => [
'displayConfirmation' => true,
'confirmationLabel' => 'LBL_ASSIGN_STATUS_CONFIRMATION',
'module' => 'leads',
],
'acl'=>["view"],
"aclModule" =>"Leads",
"displayLogic" => [
"leads_assign_visibility" => [
"modes" => [
0 => "detail",
1 => "edit",
2 => "create",
],
"params" => [
"activeOnFields" => [
"assign_flag_c" => [
'operator'=>'equal',
'value'=> 0,
],
],
],
],
]
];
$leads['actions'] = $recordActions;
$modules['leads'] = $leads;
$actions['modules'] = $modules;
$container->setParameter('module.recordview.actions', $actions);
};
this change in code correct if i want button to be only visible in case flag=0