Introdução
As tarefas agendadas são realizadas no SuiteCRM pelo módulo do agendador. Os trabalhos são colocados na fila por meio das tarefas agendadas definidas ou, para tarefas únicas, por meio de objetos de trabalho de criação de código. Observe que tanto as tarefas agendadas quanto o uso da fila de trabalho requerem que você tenha os agendadores configurados. Isso irá variar dependendo do seu sistema, mas geralmente requer a adição de uma entrada no Cron (para sistemas Linux) ou nas tarefas agendadas do Windows (para Windows). Abrir a página de tarefas agendadas no SuiteCRM permitirá que você saiba o formato da entrada.
Agendador
As tarefas agendadas permitem que o SuiteCRM execute tarefas recorrentes. Os exemplos que vêm com o SuiteCRM incluem a verificação de e-mails recebidos, o envio de notificações de lembretes por e-mail e a indexação da pesquisa de texto completo. E se você quiser criar suas próprias tarefas? SuiteCRM permite que você defina seu próprio Scheduler.
Fazemos isso criando um arquivo em custom / Extension / modules / Schedulers / Ext / ScheduledTasks /. Você pode dar a esse arquivo um nome de sua escolha, mas é mais útil dar a ele um nome descritivo.
Vamos criar um arquivo simples chamado custom / Extension / modules / Schedulers / Ext / ScheduledTasks / CleanMeetingsScheduler.php.
Isso adicionará um novo trabalho às strings de trabalho e um novo método que o planejador chamará:
Exemplo 13.1: Exemplo de agendador de reuniões limpas
<?php
/*
We add the method name to the $job_strings array.
This is the method that jobs for this scheduler will call.
*/
$job_strings[] = ‘cleanMeetingsScheduler’;
/**
Example scheduled job to change any ‘Planned’ meetings older than a month
to ‘Not Held’.
@return bool
*/
function cleanMeetingsScheduler(){
//Get the cutoff date for which meetings will be considered
$cutOff = new DateTime(‘now – 1 month’);
$cutOff = $cutOff->format(‘Y-m-d H:i:s’);
//Get an instance of the meetings bean for querying
//see the Working With Beans chapter.
$bean = BeanFactory::getBean(‘Meetings’);
//Get the list of meetings older than the cutoff that are marked as Planned
$query = “meetings.date_start < ‘$cutOff’ AND meetings.status = ‘Planned'”; $meetings = $bean->get_full_list(”,$query);
foreach($meetings as $meeting){
//Mark each meeting as Not Held
$meeting->status = ‘Not Held’;
//Save the meeting changes
$meeting->save();
}
//Signify we have successfully ran
return true;
}
Também nos certificamos de adicionar um arquivo de idioma em custom / Extension / modules / Schedulers / Ext / Language / en_us.cleanMeetingsScheduler.php novamente, o nome do arquivo não importa, mas é útil usar algo descritivo.
Isso definirá a string de idioma para nosso trabalho para que o usuário veja um bom rótulo. Consulte a seção sobre strings de idioma para obter mais informações.
A chave para as strings do mod será LBL_UPPERMETHODNAME. Em nosso caso, o nome do nosso método é cleanMeetingsScheduler, portanto, nossa chave de rótulo de idioma será LBL_CLEANMEETINGSSCHEDULER.
Exemplo 13.2: Exemplo de string de idioma para o Clean Meetings Scheduler
<?php
//We add the mod string for our method here.
$mod_strings[‘LBL_CLEANMEETINGSSCHEDULER’] = ‘Mark old, planned meetings as Not \
Held’;
Se executarmos um reparo e reconstruirmos, nosso método será empacotado no arquivo ext do planejador (consulte a seção Extensões para obter mais informações sobre este processo) e estará disponível na página de planejadores. Observe que para qualquer alteração no método do planejador, você precisará executar outro reparo rápido e reconstruir – mesmo no modo de desenvolvedor. Agora podemos criar um novo agendador para chamar nosso novo método:
Isso agora se comportará como qualquer outro agendador e podemos executá-lo com a freqüência (ou raramente) que desejarmos. Cuidado aqui. A frequência padrão é a cada minuto. Se sua tarefa for pesada ou de longa duração, isso pode não ser o que você prefere. Aqui nos contentamos com uma vez por dia.
Fila de trabalho
Às vezes, você precisará de um código para realizar uma tarefa de longa execução, mas não precisa que o usuário espere que isso seja concluído. Um bom exemplo disso é o envio de um e-mail em um gancho lógico (consulte o capítulo Ganchos lógicos para obter informações sobre eles). Supondo que temos o seguinte gancho lógico:
Exemplo 13.3: Exemplo de envio de email Logic Hook
class SomeClass
{
function SomeMethod($bean, $event, $arguments)
{
//Perform some setup of the email class require_once “include/SugarPHPMailer.php”; $mailer=new SugarPHPMailer(); $mailer->prepForOutbound(); $mailer->setMailerForSystem(); $admin = new Administration(); $admin->retrieveSettings(); $admin->settings[‘notify_fromname’] $mailer->From = $admin->settings[‘notify_fromaddress’] $mailer->FromName = $admin->settings[‘notify_fromname’]; $mailer->IsHTML(true); //Add message and recipient. //We could go all out here and load and populate an email template //or get the email address from the bean $mailer->Subject = ‘My Email Notification! ‘.$bean->name; $mailer->Body = $event. ‘ fired for bean ‘.$bean->name; $mailer->AddAddress(‘Jim@example.com’); return $mailer->Send(); } }
Isso vai funcionar bem. No entanto, você não deseja que o usuário tenha que esperar o e-mail ser enviado, pois isso pode fazer com que a IU pareça lenta.
Em vez disso, você pode criar um trabalho e colocá-lo na fila de trabalho e isso será escolhido pelo planejador. Vejamos um exemplo de como você faria isso.
Primeiro, queremos que nossa classe Logic Hook crie o trabalho agendado:
Exemplo 13.4: Exemplo de criação de trabalho agendado
class SomeClass
{
function SomeMethod($bean, $event, $arguments)
{
require_once 'include/SugarQueue/SugarJobQueue.php';
$scheduledJob = new SchedulersJob();
//Give it a useful name $scheduledJob->name = "Email job for {$bean->module_name} {$bean->id}"; //Jobs need an assigned user in order to run. You can use the id //of the current user if you wish, grab the assigned user from the //current bean or anything you like. //Here we use the default admin user id for simplicity $scheduledJob->assigned_user_id = '1'; //Pass the information that our Email job will need $scheduledJob->data = json_encode(array( 'id' => $bean->id, 'module' => $bean->module_name) ); //Tell the scheduler what class to use $scheduledJob->target = "class::BeanEmailJob"; $queue = new SugarJobQueue(); $queue->submitJob($scheduledJob); } }
Em seguida, criamos a classe BeanEmailJob. Isso é colocado no diretório / Extensions / modules / Schedulers / Ext / ScheduledTasks / customizado com o mesmo nome da classe. Portanto, em nosso exemplo teremos: custom / Extensions / modules / Schedulers / Ext / ScheduledTasks / BeanEmailJob.php
Exemplo 13.5: Exemplo de trabalho do Scheduler
class BeanEmailJob implements RunnableSchedulerJob
{
public function run($arguments)
{
//Only different part of the email code. //We grab the bean using the supplied arguments. $arguments = json_decode($arguments,1); $bean = BeanFactory::getBean($arguments['module'],$arguments['id']); //Perform some setup of the email class require_once "include/SugarPHPMailer.php"; $mailer=new SugarPHPMailer(); $admin = new Administration(); $admin->retrieveSettings(); $mailer->prepForOutbound(); $mailer->setMailerForSystem(); $admin = new Administration(); $admin->retrieveSettings(); $mailer->From = $admin->settings['notify_fromaddress']; $mailer->FromName = $emailSettings['from_name']; $mailer->IsHTML(true); //Add message and recipient. //We could go all out here and load and populate an email template //or get the email address from the bean $mailer->Subject = 'My Email Notification! '.$bean->name; $mailer->Body = $event. ' fired for bean '.$bean->name; $mailer->AddAddress('Jim@example.com'); return $mailer->Send(); } public function setJob(SchedulersJob $job) { $this->job = $job; } }
Agora, sempre que um usuário acionar o gancho, será muito mais rápido, pois estamos simplesmente persistindo algumas informações no banco de dados. O planejador o executará em segundo plano. Novas tentativas Ocasionalmente, você pode ter tarefas agendadas que podem falhar intermitentemente. Talvez você tenha um trabalho que chama uma API externa. Se a API não estiver disponível, será lamentável se a tarefa falhar e nunca mais ser tentada. Felizmente, a classe SchedulersJob tem duas propriedades que controlam como as novas tentativas são tratadas. Esses são requeue e retry_count. requeue Significa que este trabalho está qualificado para novas tentativas.
retry_count
Significa quantas tentativas faltam para este trabalho. Se o trabalho falhar, esse valor será diminuído. Podemos revisitar nosso exemplo anterior e adicionar duas tentativas:
Exemplo 13.6: Configurando a contagem de novas tentativas em um trabalho agendado
$scheduledJob = new SchedulersJob(); //Give it a useful name $scheduledJob->name = “Email job for {$bean->module_name} {$bean->id}”; //Jobs need an assigned user in order to run. You can use the id //of the current user if you wish, grab the assigned user from the //current bean or anything you like. //Here we use the default admin user id for simplicity $scheduledJob->assigned_user_id = ‘1’; //Pass the information that our Email job will need $scheduledJob->data = json_encode(array( ‘id’ => $bean->id, ‘module’ => $bean->module_name) ); //Tell the scheduler what class to use $scheduledJob->target = “class::BeanEmailJob”; //Mark this job for 2 retries. $scheduledJob->requeue = true; $scheduledJob->retry_count = 2;
Consulte a seção sobre ganchos lógicos para obter mais informações sobre como as falhas de trabalho podem ser tratadas.
Depurando
Com tarefas e trabalhos agendados em execução em segundo plano, às vezes pode ser difícil determinar o que está acontecendo quando algo dá errado. Se você estiver depurando uma tarefa agendada, a página da tarefa agendada é um bom lugar para começar. Para tarefas agendadas e tarefas da fila de trabalho, você também pode verificar a tabela job_queue. Por exemplo, no MySQL podemos verificar os últimos cinco trabalhos agendados:
Exemplo 13.7: Exemplo de consulta MySQL para listar empregos
SELECT * FROM job_queue ORDER BY date_entered DESC LIMIT 5
Isso nos dará informações sobre os últimos cinco trabalhos. Alternativamente, podemos verificar trabalhos específicos:
Exemplo 13.8: Exemplo de consulta MySQL para listar BeanEmailJobs
SELECT * FROM job_queue WHERE target = ‘class::BeanEmailJob’
Isso nos dará informações sobre os últimos cinco trabalhos. Alternativamente, podemos verificar trabalhos específicos:
Exemplo 13.8: Exemplo de consulta MySQL para listar BeanEmailJob
SELECT * FROM job_queue WHERE target = ‘class::BeanEmailJob’
Em ambos os casos, isso fornecerá detalhes para o (s) trabalho (s):
Exemplo 13.9: Exemplo de lista de empregos MySQL
* 1. row *
assigned_user_id: 1
id: 6cdf13d5-55e9-946e-9c98-55044c5cecee
name: Email job for Accounts 103c4c9b-336f-0e87-782e-5501defb5900
deleted: 0
date_entered: 2015-03-14 14:58:15
date_modified: 2015-03-14 14:58:25
scheduler_id:
execute_time: 2015-03-14 14:58:00
status: done
resolution: success
message: NULL
target: class::BeanEmailJob
data: {"id":"103c4c9b-336f-0e87-782e-5501defb5900","module":"Accounts"}
requeue: 0
retry_count: NULL
failure_count: NULL
job_delay: 0
client: CRON3b06401793b3975cd00c0447c071ef9a:7781
percent_complete: NULL
1 row in set (0.00 sec)
Aqui podemos verificar os campos de status, resolução e mensagem. Se o status estiver na fila, o agendador ainda não foi executado ou não está em execução. Verifique novamente as configurações do Cron se for esse o caso. Pode ser que o trabalho tenha sido executado, mas falhou por algum motivo.
Nesse caso, você receberá uma mensagem solicitando que verifique os logs. Verificar os logs geralmente fornece informações suficientes, especialmente se você fez uso criterioso do log (consulte o capítulo sobre log) em seu trabalho.
É possível que a tarefa esteja falhando completamente; nesse caso, o registro pode não receber saída antes de o planejador sair. Nesse caso, geralmente você pode verificar seus logs de PHP. Como último recurso, você pode executar manualmente o agendador do diretório SuiteCRM usando:
Exemplo 13.10: Executando o planejador manualmente
php -f cron.php
Usar isso, além de produzir qualquer informação útil, deve rastrear até mesmo o mais estranho dos bugs.