add: full multi-tenancy control

This commit is contained in:
Cauê Faleiros
2026-02-02 15:31:15 -03:00
commit c6ec92802b
1711 changed files with 258106 additions and 0 deletions

View File

@@ -0,0 +1,103 @@
<?php
namespace Webkul\Automation\Helpers;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\EmailTemplate\Repositories\EmailTemplateRepository;
class Entity
{
/**
* Create a new repository instance.
*
* @return void
*/
public function __construct(
protected AttributeRepository $attributeRepository,
protected EmailTemplateRepository $emailTemplateRepository
) {}
/**
* Returns events to match for the entity
*
* @return array
*/
public function getEvents()
{
$entities = config('workflows.trigger_entities');
$events = [];
foreach ($entities as $key => $entity) {
$object = app($entity['class']);
$events[$key] = [
'id' => $key,
'name' => $entity['name'],
'events' => $entity['events'],
];
}
return $events;
}
/**
* Returns conditions to match for the entity
*
* @return array
*/
public function getConditions()
{
$entities = config('workflows.trigger_entities');
$conditions = [];
foreach ($entities as $key => $entity) {
$object = app($entity['class']);
$conditions[$key] = $object->getConditions();
}
return $conditions;
}
/**
* Returns workflow actions
*
* @return array
*/
public function getActions()
{
$entities = config('workflows.trigger_entities');
$conditions = [];
foreach ($entities as $key => $entity) {
$object = app($entity['class']);
$conditions[$key] = $object->getActions();
}
return $conditions;
}
/**
* Returns placeholders for email templates
*
* @return array
*/
public function getEmailTemplatePlaceholders()
{
$entities = config('workflows.trigger_entities');
$placeholders = [];
foreach ($entities as $key => $entity) {
$object = app($entity['class']);
$placeholders[] = $object->getEmailTemplatePlaceholders($entity);
}
return $placeholders;
}
}

View File

@@ -0,0 +1,232 @@
<?php
namespace Webkul\Automation\Helpers\Entity;
use Carbon\Carbon;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Automation\Repositories\WebhookRepository;
use Webkul\Automation\Services\WebhookService;
abstract class AbstractEntity
{
/**
* Attribute repository instance.
*/
protected AttributeRepository $attributeRepository;
/**
* Create a new repository instance.
*/
public function __construct(
protected WebhookService $webhookService,
protected WebhookRepository $webhookRepository,
) {}
/**
* Listing of the entities.
*/
abstract public function getEntity(mixed $entity);
/**
* Returns workflow actions.
*/
abstract public function getActions();
/**
* Execute workflow actions.
*/
abstract public function executeActions(mixed $workflow, mixed $entity): void;
/**
* Returns attributes for workflow conditions.
*/
public function getConditions(): array
{
return $this->getAttributes($this->entityType);
}
/**
* Get attributes for entity.
*/
public function getAttributes(string $entityType, array $skipAttributes = ['textarea', 'image', 'file', 'address']): array
{
$attributes = [];
foreach ($this->attributeRepository->findByField('entity_type', $entityType) as $attribute) {
if (in_array($attribute->type, $skipAttributes)) {
continue;
}
if ($attribute->lookup_type) {
$options = [];
} else {
$options = $attribute->options;
}
$attributes[] = [
'id' => $attribute->code,
'type' => $attribute->type,
'name' => $attribute->name,
'lookup_type' => $attribute->lookup_type,
'options' => $options,
];
}
return $attributes;
}
/**
* Returns placeholders for email templates.
*/
public function getEmailTemplatePlaceholders(array $entity): array
{
$menuItems = [];
foreach ($this->getAttributes($this->entityType) as $attribute) {
$menuItems[] = [
'text' => $attribute['name'],
'value' => '{%'.$this->entityType.'.'.$attribute['id'].'%}',
];
}
return [
'text' => $entity['name'],
'menu' => $menuItems,
];
}
/**
* Replace placeholders with values.
*/
public function replacePlaceholders(mixed $entity, string $content): string
{
foreach ($this->getAttributes($this->entityType, []) as $attribute) {
$value = '';
switch ($attribute['type']) {
case 'price':
$value = core()->formatBasePrice($entity->{$attribute['id']});
break;
case 'boolean':
$value = $entity->{$attribute['id']} ? __('admin::app.common.yes') : __('admin::app.common.no');
break;
case 'select':
case 'radio':
case 'lookup':
if ($attribute['lookup_type']) {
$option = $this->attributeRepository->getLookUpEntity($attribute['lookup_type'], $entity->{$attribute['id']});
} else {
$option = $attribute['options']->where('id', $entity->{$attribute['id']})->first();
}
$value = $option ? $option->name : '';
break;
case 'multiselect':
case 'checkbox':
if ($attribute['lookup_type']) {
$options = $this->attributeRepository->getLookUpEntity($attribute['lookup_type'], explode(',', $entity->{$attribute['id']}));
} else {
$options = $attribute['options']->whereIn('id', explode(',', $entity->{$attribute['id']}));
}
$optionsLabels = [];
foreach ($options as $key => $option) {
$optionsLabels[] = $option->name;
}
$value = implode(', ', $optionsLabels);
break;
case 'email':
case 'phone':
if (! is_array($entity->{$attribute['id']})) {
break;
}
$optionsLabels = [];
foreach ($entity->{$attribute['id']} as $item) {
$optionsLabels[] = $item['value'].' ('.$item['label'].')';
}
$value = implode(', ', $optionsLabels);
break;
case 'address':
if (! $entity->{$attribute['id']} || ! count(array_filter($entity->{$attribute['id']}))) {
break;
}
$value = $entity->{$attribute['id']}['address'].'<br>'
.$entity->{$attribute['id']}['postcode'].' '.$entity->{$attribute['id']}['city'].'<br>'
.core()->state_name($entity->{$attribute['id']}['state']).'<br>'
.core()->country_name($entity->{$attribute['id']}['country']).'<br>';
break;
case 'date':
if ($entity->{$attribute['id']}) {
$value = ! is_object($entity->{$attribute['id']})
? Carbon::parse($entity->{$attribute['id']})
: $entity->{$attribute['id']}->format('D M d, Y');
} else {
$value = 'N/A';
}
break;
case 'datetime':
if ($entity->{$attribute['id']}) {
$value = ! is_object($entity->{$attribute['id']})
? Carbon::parse($entity->{$attribute['id']})
: $entity->{$attribute['id']}->format('D M d, Y H:i A');
} else {
$value = 'N/A';
}
break;
default:
$value = $entity->{$attribute['id']};
break;
}
$content = strtr($content, [
'{%'.$this->entityType.'.'.$attribute['id'].'%}' => $value,
'{% '.$this->entityType.'.'.$attribute['id'].' %}' => $value,
]);
}
return $content;
}
/**
* Trigger webhook.
*
* @return void
*/
public function triggerWebhook(int $webhookId, mixed $entity)
{
$webhook = $this->webhookRepository->findOrFail($webhookId);
$payload = [
'method' => $webhook->method,
'query_params' => $this->replacePlaceholders($entity, json_encode($webhook->query_params)),
'end_point' => $this->replacePlaceholders($entity, $webhook->end_point),
'payload' => $this->replacePlaceholders($entity, json_encode($webhook->payload)),
'headers' => $this->replacePlaceholders($entity, json_encode($webhook->headers)),
];
$this->webhookService->triggerWebhook($payload);
}
}

View File

@@ -0,0 +1,323 @@
<?php
namespace Webkul\Automation\Helpers\Entity;
use Carbon\Carbon;
use Illuminate\Support\Facades\Mail;
use Webkul\Activity\Contracts\Activity as ContractsActivity;
use Webkul\Activity\Repositories\ActivityRepository;
use Webkul\Admin\Notifications\Common;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Automation\Repositories\WebhookRepository;
use Webkul\Automation\Services\WebhookService;
use Webkul\Contact\Repositories\PersonRepository;
use Webkul\EmailTemplate\Repositories\EmailTemplateRepository;
use Webkul\Lead\Repositories\LeadRepository;
class Activity extends AbstractEntity
{
/**
* Define the entity type.
*
* @var string
*/
protected $entityType = 'activities';
/**
* Create a new repository instance.
*
* @return void
*/
public function __construct(
protected AttributeRepository $attributeRepository,
protected EmailTemplateRepository $emailTemplateRepository,
protected LeadRepository $leadRepository,
protected PersonRepository $personRepository,
protected ActivityRepository $activityRepository,
protected WebhookRepository $webhookRepository,
protected WebhookService $webhookService
) {}
/**
* Get the attributes for workflow conditions.
*/
public function getAttributes(string $entityType, array $skipAttributes = []): array
{
$attributes = [
[
'id' => 'title',
'type' => 'text',
'name' => 'Title',
'lookup_type' => null,
'options' => collect(),
], [
'id' => 'type',
'type' => 'multiselect',
'name' => 'Type',
'lookup_type' => null,
'options' => collect([
(object) [
'id' => 'note',
'name' => 'Note',
], (object) [
'id' => 'call',
'name' => 'Call',
], (object) [
'id' => 'meeting',
'name' => 'Meeting',
], (object) [
'id' => 'lunch',
'name' => 'Lunch',
], (object) [
'id' => 'file',
'name' => 'File',
],
]),
], [
'id' => 'location',
'type' => 'text',
'name' => 'Location',
'lookup_type' => null,
'options' => collect(),
], [
'id' => 'comment',
'type' => 'textarea',
'name' => 'Comment',
'lookup_type' => null,
'options' => collect(),
], [
'id' => 'schedule_from',
'type' => 'datetime',
'name' => 'Schedule From',
'lookup_type' => null,
'options' => collect(),
], [
'id' => 'schedule_to',
'type' => 'datetime',
'name' => 'Schedule To',
'lookup_type' => null,
'options' => collect(),
], [
'id' => 'user_id',
'type' => 'select',
'name' => 'User',
'lookup_type' => 'users',
'options' => $this->attributeRepository->getLookUpOptions('users'),
],
];
return $attributes;
}
/**
* Returns placeholders for email templates.
*/
public function getEmailTemplatePlaceholders(array $entity): array
{
$emailTemplates = parent::getEmailTemplatePlaceholders($entity);
$emailTemplates['menu'][] = [
'text' => 'Participants',
'value' => '{%activities.participants%}',
];
return $emailTemplates;
}
/**
* Replace placeholders with values.
*/
public function replacePlaceholders(mixed $entity, string $content): string
{
$content = parent::replacePlaceholders($entity, $content);
$value = '<ul style="padding-left: 18px;margin: 0;">';
foreach ($entity->participants as $participant) {
$value .= '<li>'.($participant->user ? $participant->user->name : $participant->person->name).'</li>';
}
$value .= '</ul>';
return strtr($content, [
'{%'.$this->entityType.'.participants%}' => $value,
'{% '.$this->entityType.'.participants %}' => $value,
]);
}
/**
* Listing of the entities.
*/
public function getEntity(mixed $entity): mixed
{
if (! $entity instanceof ContractsActivity) {
$entity = $this->activityRepository->find($entity);
}
return $entity;
}
/**
* Returns workflow actions.
*/
public function getActions(): array
{
$emailTemplates = $this->emailTemplateRepository->all(['id', 'name']);
$webhooksOptions = $this->webhookRepository->all(['id', 'name']);
return [
[
'id' => 'update_related_leads',
'name' => trans('admin::app.settings.workflows.helpers.update-related-leads'),
'attributes' => $this->getAttributes('leads'),
], [
'id' => 'send_email_to_sales_owner',
'name' => trans('admin::app.settings.workflows.helpers.send-email-to-sales-owner'),
'options' => $emailTemplates,
], [
'id' => 'send_email_to_participants',
'name' => trans('admin::app.settings.workflows.helpers.send-email-to-participants'),
'options' => $emailTemplates,
], [
'id' => 'trigger_webhook',
'name' => trans('admin::app.settings.workflows.helpers.add-webhook'),
'options' => $webhooksOptions,
],
];
}
/**
* Execute workflow actions.
*/
public function executeActions(mixed $workflow, mixed $activity): void
{
foreach ($workflow->actions as $action) {
switch ($action['id']) {
case 'update_related_leads':
$leadIds = $this->activityRepository->getModel()
->leftJoin('lead_activities', 'activities.id', 'lead_activities.activity_id')
->leftJoin('leads', 'lead_activities.lead_id', 'leads.id')
->addSelect('leads.id')
->where('activities.id', $activity->id)
->pluck('id');
foreach ($leadIds as $leadId) {
$this->leadRepository->update(
[
'entity_type' => 'leads',
$action['attribute'] => $action['value'],
],
$leadId,
[$action['attribute']]
);
}
break;
case 'send_email_to_sales_owner':
$emailTemplate = $this->emailTemplateRepository->find($action['value']);
if (! $emailTemplate) {
break;
}
try {
Mail::queue(new Common([
'to' => $activity->user->email,
'subject' => $this->replacePlaceholders($activity, $emailTemplate->subject),
'body' => $this->replacePlaceholders($activity, $emailTemplate->content),
'attachments' => [
[
'name' => 'invite.ics',
'mime' => 'text/calendar',
'content' => $this->getICSContent($activity),
],
],
]));
} catch (\Exception $e) {
}
break;
case 'send_email_to_participants':
$emailTemplate = $this->emailTemplateRepository->find($action['value']);
if (! $emailTemplate) {
break;
}
try {
foreach ($activity->participants as $participant) {
Mail::queue(new Common([
'to' => $participant->user
? $participant->user->email
: data_get($participant->person->emails, '*.value'),
'subject' => $this->replacePlaceholders($activity, $emailTemplate->subject),
'body' => $this->replacePlaceholders($activity, $emailTemplate->content),
'attachments' => [
[
'name' => 'invite.ics',
'mime' => 'text/calendar',
'content' => $this->getICSContent($activity),
],
],
]));
}
} catch (\Exception $e) {
}
break;
case 'trigger_webhook':
try {
$this->triggerWebhook($action['value'], $activity);
} catch (\Exception $e) {
report($e);
}
break;
}
}
}
/**
* Returns .ics file for attachments.
*/
public function getICSContent(ContractsActivity $activity): string
{
$content = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//Krayincrm//Krayincrm//EN',
'BEGIN:VEVENT',
'UID:'.time().'-'.$activity->id,
'DTSTAMP:'.Carbon::now()->format('YmdTHis'),
'CREATED:'.$activity->created_at->format('YmdTHis'),
'SEQUENCE:1',
'ORGANIZER;CN='.$activity->user->name.':MAILTO:'.$activity->user->email,
];
foreach ($activity->participants as $participant) {
if ($participant->user) {
$content[] = 'ATTENDEE;ROLE=REQ-PARTICIPANT;CN='.$participant->user->name.';PARTSTAT=NEEDS-ACTION:MAILTO:'.$participant->user->email;
} else {
foreach (data_get($participant->person->emails, '*.value') as $email) {
$content[] = 'ATTENDEE;ROLE=REQ-PARTICIPANT;CN='.$participant->person->name.';PARTSTAT=NEEDS-ACTION:MAILTO:'.$email;
}
}
}
$content = array_merge($content, [
'DTSTART:'.$activity->schedule_from->format('YmdTHis'),
'DTEND:'.$activity->schedule_to->format('YmdTHis'),
'SUMMARY:'.$activity->title,
'LOCATION:'.$activity->location,
'DESCRIPTION:'.$activity->comment,
'END:VEVENT',
'END:VCALENDAR',
]);
return implode("\r\n", $content);
}
}

View File

@@ -0,0 +1,210 @@
<?php
namespace Webkul\Automation\Helpers\Entity;
use Illuminate\Support\Facades\Mail;
use Webkul\Activity\Repositories\ActivityRepository;
use Webkul\Admin\Notifications\Common;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Automation\Repositories\WebhookRepository;
use Webkul\Automation\Services\WebhookService;
use Webkul\Contact\Repositories\PersonRepository;
use Webkul\EmailTemplate\Repositories\EmailTemplateRepository;
use Webkul\Lead\Contracts\Lead as ContractsLead;
use Webkul\Lead\Repositories\LeadRepository;
use Webkul\Tag\Repositories\TagRepository;
class Lead extends AbstractEntity
{
/**
* Define the entity type.
*/
protected string $entityType = 'leads';
/**
* Create a new repository instance.
*
* @return void
*/
public function __construct(
protected AttributeRepository $attributeRepository,
protected EmailTemplateRepository $emailTemplateRepository,
protected LeadRepository $leadRepository,
protected ActivityRepository $activityRepository,
protected PersonRepository $personRepository,
protected TagRepository $tagRepository,
protected WebhookRepository $webhookRepository,
protected WebhookService $webhookService
) {}
/**
* Listing of the entities.
*/
public function getEntity(mixed $entity)
{
if (! $entity instanceof ContractsLead) {
$entity = $this->leadRepository->find($entity);
}
return $entity;
}
/**
* Returns attributes.
*/
public function getAttributes(string $entityType, array $skipAttributes = ['textarea', 'image', 'file', 'address']): array
{
return parent::getAttributes($entityType, $skipAttributes);
}
/**
* Returns workflow actions.
*/
public function getActions(): array
{
$emailTemplates = $this->emailTemplateRepository->all(['id', 'name']);
$webhooksOptions = $this->webhookRepository->all(['id', 'name']);
return [
[
'id' => 'update_lead',
'name' => trans('admin::app.settings.workflows.helpers.update-lead'),
'attributes' => $this->getAttributes('leads'),
], [
'id' => 'update_person',
'name' => trans('admin::app.settings.workflows.helpers.update-person'),
'attributes' => $this->getAttributes('persons'),
], [
'id' => 'send_email_to_person',
'name' => trans('admin::app.settings.workflows.helpers.send-email-to-person'),
'options' => $emailTemplates,
], [
'id' => 'send_email_to_sales_owner',
'name' => trans('admin::app.settings.workflows.helpers.send-email-to-sales-owner'),
'options' => $emailTemplates,
], [
'id' => 'add_tag',
'name' => trans('admin::app.settings.workflows.helpers.add-tag'),
], [
'id' => 'add_note_as_activity',
'name' => trans('admin::app.settings.workflows.helpers.add-note-as-activity'),
], [
'id' => 'trigger_webhook',
'name' => trans('admin::app.settings.workflows.helpers.add-webhook'),
'options' => $webhooksOptions,
],
];
}
/**
* Execute workflow actions.
*/
public function executeActions(mixed $workflow, mixed $lead): void
{
foreach ($workflow->actions as $action) {
switch ($action['id']) {
case 'update_lead':
$this->leadRepository->update(
[
'entity_type' => 'leads',
$action['attribute'] => $action['value'],
],
$lead->id,
[$action['attribute']]
);
break;
case 'update_person':
$this->personRepository->update([
'entity_type' => 'persons',
$action['attribute'] => $action['value'],
], $lead->person_id);
break;
case 'send_email_to_person':
$emailTemplate = $this->emailTemplateRepository->find($action['value']);
if (! $emailTemplate) {
break;
}
try {
Mail::queue(new Common([
'to' => data_get($lead->person->emails, '*.value'),
'subject' => $this->replacePlaceholders($lead, $emailTemplate->subject),
'body' => $this->replacePlaceholders($lead, $emailTemplate->content),
]));
} catch (\Exception $e) {
}
break;
case 'send_email_to_sales_owner':
$emailTemplate = $this->emailTemplateRepository->find($action['value']);
if (! $emailTemplate) {
break;
}
try {
Mail::queue(new Common([
'to' => $lead->user->email,
'subject' => $this->replacePlaceholders($lead, $emailTemplate->subject),
'body' => $this->replacePlaceholders($lead, $emailTemplate->content),
]));
} catch (\Exception $e) {
}
break;
case 'add_tag':
$colors = [
'#337CFF',
'#FEBF00',
'#E5549F',
'#27B6BB',
'#FB8A3F',
'#43AF52',
];
if (! $tag = $this->tagRepository->findOneByField('name', $action['value'])) {
$tag = $this->tagRepository->create([
'name' => $action['value'],
'color' => $colors[rand(0, 5)],
'user_id' => auth()->guard('user')->user()->id,
]);
}
if (! $lead->tags->contains($tag->id)) {
$lead->tags()->attach($tag->id);
}
break;
case 'add_note_as_activity':
$activity = $this->activityRepository->create([
'type' => 'note',
'comment' => $action['value'],
'is_done' => 1,
'user_id' => auth()->guard('user')->user()->id,
]);
$lead->activities()->attach($activity->id);
break;
case 'trigger_webhook':
try {
$this->triggerWebhook($action['value'], $lead);
} catch (\Exception $e) {
report($e);
}
break;
}
}
}
}

View File

@@ -0,0 +1,140 @@
<?php
namespace Webkul\Automation\Helpers\Entity;
use Illuminate\Support\Facades\Mail;
use Webkul\Admin\Notifications\Common;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Automation\Contracts\Workflow;
use Webkul\Automation\Repositories\WebhookRepository;
use Webkul\Automation\Services\WebhookService;
use Webkul\Contact\Contracts\Person as PersonContract;
use Webkul\Contact\Repositories\PersonRepository;
use Webkul\EmailTemplate\Repositories\EmailTemplateRepository;
use Webkul\Lead\Repositories\LeadRepository;
class Person extends AbstractEntity
{
/**
* Define the entity type.
*/
protected string $entityType = 'persons';
/**
* Create a new repository instance.
*
* @return void
*/
public function __construct(
protected AttributeRepository $attributeRepository,
protected EmailTemplateRepository $emailTemplateRepository,
protected LeadRepository $leadRepository,
protected PersonRepository $personRepository,
protected WebhookRepository $webhookRepository,
protected WebhookService $webhookService
) {}
/**
* Listing of the entities.
*/
public function getEntity(mixed $entity): mixed
{
if (! $entity instanceof PersonContract) {
$entity = $this->personRepository->find($entity);
}
return $entity;
}
/**
* Returns workflow actions.
*/
public function getActions(): array
{
$emailTemplates = $this->emailTemplateRepository->all(['id', 'name']);
$webhooksOptions = $this->webhookRepository->all(['id', 'name']);
return [
[
'id' => 'update_person',
'name' => trans('admin::app.settings.workflows.helpers.update-person'),
'attributes' => $this->getAttributes('persons'),
], [
'id' => 'update_related_leads',
'name' => trans('admin::app.settings.workflows.helpers.update-related-leads'),
'attributes' => $this->getAttributes('leads'),
], [
'id' => 'send_email_to_person',
'name' => trans('admin::app.settings.workflows.helpers.send-email-to-person'),
'options' => $emailTemplates,
], [
'id' => 'trigger_webhook',
'name' => trans('admin::app.settings.workflows.helpers.add-webhook'),
'options' => $webhooksOptions,
],
];
}
/**
* Execute workflow actions.
*/
public function executeActions(mixed $workflow, mixed $person): void
{
foreach ($workflow->actions as $action) {
switch ($action['id']) {
case 'update_person':
$this->personRepository->update([
'entity_type' => 'persons',
$action['attribute'] => $action['value'],
], $person->id);
break;
case 'update_related_leads':
$leads = $this->leadRepository->findByField('person_id', $person->id);
foreach ($leads as $lead) {
$this->leadRepository->update(
[
'entity_type' => 'leads',
$action['attribute'] => $action['value'],
],
$lead->id,
[$action['attribute']]
);
}
break;
case 'send_email_to_person':
$emailTemplate = $this->emailTemplateRepository->find($action['value']);
if (! $emailTemplate) {
break;
}
try {
Mail::queue(new Common([
'to' => data_get($person->emails, '*.value'),
'subject' => $this->replacePlaceholders($person, $emailTemplate->subject),
'body' => $this->replacePlaceholders($person, $emailTemplate->content),
]));
} catch (\Exception $e) {
report($e);
}
break;
case 'trigger_webhook':
try {
$this->triggerWebhook($action['value'], $person);
} catch (\Exception $e) {
report($e);
}
break;
}
}
}
}

View File

@@ -0,0 +1,172 @@
<?php
namespace Webkul\Automation\Helpers\Entity;
use Illuminate\Support\Facades\Mail;
use Webkul\Admin\Notifications\Common;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Automation\Repositories\WebhookRepository;
use Webkul\Automation\Services\WebhookService;
use Webkul\Contact\Repositories\PersonRepository;
use Webkul\EmailTemplate\Repositories\EmailTemplateRepository;
use Webkul\Lead\Repositories\LeadRepository;
use Webkul\Quote\Contracts\Quote as ContractsQuote;
use Webkul\Quote\Repositories\QuoteRepository;
class Quote extends AbstractEntity
{
/**
* Define the entity type.
*/
protected string $entityType = 'quotes';
/**
* Create a new repository instance.
*
* @return void
*/
public function __construct(
protected AttributeRepository $attributeRepository,
protected EmailTemplateRepository $emailTemplateRepository,
protected QuoteRepository $quoteRepository,
protected LeadRepository $leadRepository,
protected PersonRepository $personRepository,
protected WebhookRepository $webhookRepository,
protected WebhookService $webhookService
) {}
/**
* Listing of the entities.
*/
public function getEntity(mixed $entity): mixed
{
if (! $entity instanceof ContractsQuote) {
$entity = $this->quoteRepository->find($entity);
}
return $entity;
}
/**
* Returns workflow actions.
*/
public function getActions(): array
{
$emailTemplates = $this->emailTemplateRepository->all(['id', 'name']);
$webhookOptions = $this->webhookRepository->all(['id', 'name']);
return [
[
'id' => 'update_quote',
'name' => trans('admin::app.settings.workflows.helpers.update-quote'),
'attributes' => $this->getAttributes('quotes'),
], [
'id' => 'update_person',
'name' => trans('admin::app.settings.workflows.helpers.update-person'),
'attributes' => $this->getAttributes('persons'),
], [
'id' => 'update_related_leads',
'name' => trans('admin::app.settings.workflows.helpers.update-related-leads'),
'attributes' => $this->getAttributes('leads'),
], [
'id' => 'send_email_to_person',
'name' => trans('admin::app.settings.workflows.helpers.send-email-to-person'),
'options' => $emailTemplates,
], [
'id' => 'send_email_to_sales_owner',
'name' => trans('admin::app.settings.workflows.helpers.send-email-to-sales-owner'),
'options' => $emailTemplates,
], [
'id' => 'trigger_webhook',
'name' => trans('admin::app.settings.workflows.helpers.add-webhook'),
'options' => $webhookOptions,
],
];
}
/**
* Execute workflow actions.
*/
public function executeActions(mixed $workflow, mixed $quote): void
{
foreach ($workflow->actions as $action) {
switch ($action['id']) {
case 'update_quote':
$this->quoteRepository->update([
'entity_type' => 'quotes',
$action['attribute'] => $action['value'],
], $quote->id);
break;
case 'update_person':
$this->personRepository->update([
'entity_type' => 'persons',
$action['attribute'] => $action['value'],
], $quote->person_id);
break;
case 'update_related_leads':
foreach ($quote->leads as $lead) {
$this->leadRepository->update(
[
'entity_type' => 'leads',
$action['attribute'] => $action['value'],
],
$lead->id,
[$action['attribute']]
);
}
break;
case 'send_email_to_person':
$emailTemplate = $this->emailTemplateRepository->find($action['value']);
if (! $emailTemplate) {
break;
}
try {
Mail::queue(new Common([
'to' => data_get($quote->person->emails, '*.value'),
'subject' => $this->replacePlaceholders($quote, $emailTemplate->subject),
'body' => $this->replacePlaceholders($quote, $emailTemplate->content),
]));
} catch (\Exception $e) {
}
break;
case 'send_email_to_sales_owner':
$emailTemplate = $this->emailTemplateRepository->find($action['value']);
if (! $emailTemplate) {
break;
}
try {
Mail::queue(new Common([
'to' => $quote->user->email,
'subject' => $this->replacePlaceholders($quote, $emailTemplate->subject),
'body' => $this->replacePlaceholders($quote, $emailTemplate->content),
]));
} catch (\Exception $e) {
}
break;
case 'trigger_webhook':
try {
$this->triggerWebhook($action['value'], $quote);
} catch (\Exception $e) {
report($e);
}
break;
}
}
}
}

View File

@@ -0,0 +1,183 @@
<?php
namespace Webkul\Automation\Helpers;
class Validator
{
/**
* Validate workflow for condition
*
* @param \Webkul\Automation\Contracts\Workflow $workflow
* @param mixed $entity
* @return bool
*/
public function validate($workflow, $entity)
{
if (! $workflow->conditions) {
return true;
}
$validConditionCount = $totalConditionCount = 0;
foreach ($workflow->conditions as $condition) {
if (! $condition['attribute']
|| ! isset($condition['value'])
|| is_null($condition['value'])
|| $condition['value'] == ''
) {
continue;
}
$totalConditionCount++;
if ($workflow->condition_type == 'and') {
if (! $this->validateEntity($condition, $entity)) {
return false;
} else {
$validConditionCount++;
}
} else {
if ($this->validateEntity($condition, $entity)) {
return true;
}
}
}
return $validConditionCount == $totalConditionCount ? true : false;
}
/**
* Validate object
*
* @param array $condition
* @param mixed $entity
* @return bool
*/
private function validateEntity($condition, $entity)
{
return $this->validateAttribute(
$condition,
$this->getAttributeValue($condition, $entity)
);
}
/**
* Return value for the attribute
*
* @param array $condition
* @param mixed $entity
* @return bool
*/
public function getAttributeValue($condition, $entity)
{
$value = $entity->{$condition['attribute']};
if (! in_array($condition['attribute_type'], ['multiselect', 'checkbox'])) {
return $value;
}
return $value ? explode(',', $value) : [];
}
/**
* Validate attribute value for condition
*
* @param array $condition
* @param mixed $attributeValue
* @return bool
*/
public function validateAttribute($condition, $attributeValue)
{
switch ($condition['operator']) {
case '==': case '!=':
if (is_array($condition['value'])) {
if (! is_array($attributeValue)) {
return false;
}
$result = ! empty(array_intersect($condition['value'], $attributeValue));
} elseif (is_object($attributeValue)) {
$result = $attributeValue->value == $condition['value'];
} else {
if (is_array($attributeValue)) {
$result = count($attributeValue) == 1 && array_shift($attributeValue) == $condition['value'];
} else {
$result = $attributeValue == $condition['value'];
}
}
break;
case '<=': case '>':
if (! is_scalar($attributeValue)) {
return false;
}
$result = $attributeValue <= $condition['value'];
break;
case '>=': case '<':
if (! is_scalar($attributeValue)) {
return false;
}
$result = $attributeValue >= $condition['value'];
break;
case '{}': case '!{}':
if (is_scalar($attributeValue) && is_array($condition['value'])) {
foreach ($condition['value'] as $item) {
if (stripos($attributeValue, $item) !== false) {
$result = true;
break;
}
}
} elseif (is_array($condition['value'])) {
if (! is_array($attributeValue)) {
return false;
}
$result = ! empty(array_intersect($condition['value'], $attributeValue));
} else {
if (is_array($attributeValue)) {
$result = self::validateArrayValues($attributeValue, $condition['value']);
} else {
$result = strpos($attributeValue, $condition['value']) !== false;
}
}
break;
}
if (in_array($condition['operator'], ['!=', '>', '<', '!{}'])) {
$result = ! $result;
}
return $result;
}
/**
* Validate the condition value against a multi dimensional array recursively
*/
private static function validateArrayValues(array $attributeValue, string $conditionValue): bool
{
if (in_array($conditionValue, $attributeValue, true) === true) {
return true;
}
foreach ($attributeValue as $subValue) {
if (! is_array($subValue)) {
continue;
}
if (self::validateArrayValues($subValue, $conditionValue) === true) {
return true;
}
}
return false;
}
}