735 lines
24 KiB
PHP
Executable File
735 lines
24 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Webkul\Admin\Http\Controllers\Lead;
|
|
|
|
use Carbon\Carbon;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
|
use Illuminate\Support\Facades\Event;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Illuminate\View\View;
|
|
use Prettus\Repository\Criteria\RequestCriteria;
|
|
use Webkul\Admin\DataGrids\Lead\LeadDataGrid;
|
|
use Webkul\Admin\Http\Controllers\Controller;
|
|
use Webkul\Admin\Http\Requests\LeadForm;
|
|
use Webkul\Admin\Http\Requests\MassDestroyRequest;
|
|
use Webkul\Admin\Http\Requests\MassUpdateRequest;
|
|
use Webkul\Admin\Http\Resources\LeadResource;
|
|
use Webkul\Admin\Http\Resources\StageResource;
|
|
use Webkul\Attribute\Repositories\AttributeRepository;
|
|
use Webkul\Contact\Repositories\PersonRepository;
|
|
use Webkul\Lead\Helpers\MagicAI;
|
|
use Webkul\Lead\Repositories\LeadRepository;
|
|
use Webkul\Lead\Repositories\PipelineRepository;
|
|
use Webkul\Lead\Repositories\ProductRepository;
|
|
use Webkul\Lead\Repositories\SourceRepository;
|
|
use Webkul\Lead\Repositories\StageRepository;
|
|
use Webkul\Lead\Repositories\TypeRepository;
|
|
use Webkul\Lead\Services\MagicAIService;
|
|
use Webkul\Tag\Repositories\TagRepository;
|
|
use Webkul\User\Repositories\UserRepository;
|
|
|
|
class LeadController extends Controller
|
|
{
|
|
/**
|
|
* Const variable for supported types.
|
|
*/
|
|
const SUPPORTED_TYPES = 'pdf,bmp,jpeg,jpg,png,webp';
|
|
|
|
/**
|
|
* Create a new controller instance.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct(
|
|
protected UserRepository $userRepository,
|
|
protected AttributeRepository $attributeRepository,
|
|
protected SourceRepository $sourceRepository,
|
|
protected TypeRepository $typeRepository,
|
|
protected PipelineRepository $pipelineRepository,
|
|
protected StageRepository $stageRepository,
|
|
protected LeadRepository $leadRepository,
|
|
protected ProductRepository $productRepository,
|
|
protected PersonRepository $personRepository
|
|
) {
|
|
request()->request->add(['entity_type' => 'leads']);
|
|
}
|
|
|
|
/**
|
|
* Display a listing of the resource.
|
|
*/
|
|
public function index()
|
|
{
|
|
if (request()->ajax()) {
|
|
return datagrid(LeadDataGrid::class)->process();
|
|
}
|
|
|
|
if (request('pipeline_id')) {
|
|
$pipeline = $this->pipelineRepository->find(request('pipeline_id'));
|
|
} else {
|
|
$pipeline = $this->pipelineRepository->getDefaultPipeline();
|
|
}
|
|
|
|
return view('admin::leads.index', [
|
|
'pipeline' => $pipeline,
|
|
'columns' => $this->getKanbanColumns(),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Returns a listing of the resource.
|
|
*/
|
|
public function get(): JsonResponse
|
|
{
|
|
if (request()->query('pipeline_id')) {
|
|
$pipeline = $this->pipelineRepository->find(request()->query('pipeline_id'));
|
|
} else {
|
|
$pipeline = $this->pipelineRepository->getDefaultPipeline();
|
|
}
|
|
|
|
if ($stageId = request()->query('pipeline_stage_id')) {
|
|
$stages = $pipeline->stages->where('id', request()->query('pipeline_stage_id'));
|
|
} else {
|
|
$stages = $pipeline->stages;
|
|
}
|
|
|
|
foreach ($stages as $stage) {
|
|
/**
|
|
* We have to create a new instance of the lead repository every time, which is
|
|
* why we're not using the injected one.
|
|
*/
|
|
$query = app(LeadRepository::class)
|
|
->pushCriteria(app(RequestCriteria::class))
|
|
->where([
|
|
'lead_pipeline_id' => $pipeline->id,
|
|
'lead_pipeline_stage_id' => $stage->id,
|
|
]);
|
|
|
|
if ($userIds = bouncer()->getAuthorizedUserIds()) {
|
|
$query->whereIn('leads.user_id', $userIds);
|
|
}
|
|
|
|
$stage->lead_value = (clone $query)->sum('lead_value');
|
|
|
|
$data[$stage->sort_order] = (new StageResource($stage))->jsonSerialize();
|
|
|
|
$data[$stage->sort_order]['leads'] = [
|
|
'data' => LeadResource::collection($paginator = $query->with([
|
|
'tags',
|
|
'type',
|
|
'source',
|
|
'user',
|
|
'person',
|
|
'person.organization',
|
|
'pipeline',
|
|
'pipeline.stages',
|
|
'stage',
|
|
'attribute_values',
|
|
])->paginate(10)),
|
|
|
|
'meta' => [
|
|
'current_page' => $paginator->currentPage(),
|
|
'from' => $paginator->firstItem(),
|
|
'last_page' => $paginator->lastPage(),
|
|
'per_page' => $paginator->perPage(),
|
|
'to' => $paginator->lastItem(),
|
|
'total' => $paginator->total(),
|
|
],
|
|
];
|
|
}
|
|
|
|
return response()->json($data);
|
|
}
|
|
|
|
/**
|
|
* Show the form for creating a new resource.
|
|
*/
|
|
public function create(): View
|
|
{
|
|
return view('admin::leads.create');
|
|
}
|
|
|
|
/**
|
|
* Store a newly created resource in storage.
|
|
*/
|
|
public function store(LeadForm $request): RedirectResponse|JsonResponse
|
|
{
|
|
Event::dispatch('lead.create.before');
|
|
|
|
$data = request()->all();
|
|
|
|
$data['status'] = 1;
|
|
|
|
if (! empty($data['lead_pipeline_stage_id'])) {
|
|
$stage = $this->stageRepository->findOrFail($data['lead_pipeline_stage_id']);
|
|
|
|
$data['lead_pipeline_id'] = $stage->lead_pipeline_id;
|
|
} else {
|
|
if (empty($data['lead_pipeline_id'])) {
|
|
$pipeline = $this->pipelineRepository->getDefaultPipeline();
|
|
|
|
$data['lead_pipeline_id'] = $pipeline->id;
|
|
} else {
|
|
$pipeline = $this->pipelineRepository->findOrFail($data['lead_pipeline_id']);
|
|
}
|
|
|
|
$stage = $pipeline->stages()->first();
|
|
|
|
$data['lead_pipeline_stage_id'] = $stage->id;
|
|
}
|
|
|
|
if (in_array($stage->code, ['won', 'lost'])) {
|
|
$data['closed_at'] = Carbon::now();
|
|
}
|
|
|
|
$lead = $this->leadRepository->create($data);
|
|
|
|
if (request()->ajax()) {
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.create-success'),
|
|
'data' => new LeadResource($lead),
|
|
]);
|
|
}
|
|
|
|
Event::dispatch('lead.create.after', $lead);
|
|
|
|
session()->flash('success', trans('admin::app.leads.create-success'));
|
|
|
|
if (! empty($data['lead_pipeline_id'])) {
|
|
$params['pipeline_id'] = $data['lead_pipeline_id'];
|
|
}
|
|
|
|
return redirect()->route('admin.leads.index', $params ?? []);
|
|
}
|
|
|
|
/**
|
|
* Show the form for editing the specified resource.
|
|
*/
|
|
public function edit(int $id): View
|
|
{
|
|
$lead = $this->leadRepository->findOrFail($id);
|
|
|
|
return view('admin::leads.edit', compact('lead'));
|
|
}
|
|
|
|
/**
|
|
* Display a resource.
|
|
*/
|
|
public function view(int $id)
|
|
{
|
|
$lead = $this->leadRepository->findOrFail($id);
|
|
|
|
$userIds = bouncer()->getAuthorizedUserIds();
|
|
|
|
if (
|
|
$userIds
|
|
&& ! in_array($lead->user_id, $userIds)
|
|
) {
|
|
return redirect()->route('admin.leads.index');
|
|
}
|
|
|
|
return view('admin::leads.view', compact('lead'));
|
|
}
|
|
|
|
/**
|
|
* Update the specified resource in storage.
|
|
*/
|
|
public function update(LeadForm $request, int $id): RedirectResponse|JsonResponse
|
|
{
|
|
Event::dispatch('lead.update.before', $id);
|
|
|
|
$data = $request->all();
|
|
|
|
if (isset($data['lead_pipeline_stage_id'])) {
|
|
$stage = $this->stageRepository->findOrFail($data['lead_pipeline_stage_id']);
|
|
|
|
$data['lead_pipeline_id'] = $stage->lead_pipeline_id;
|
|
} else {
|
|
$pipeline = $this->pipelineRepository->getDefaultPipeline();
|
|
|
|
$stage = $pipeline->stages()->first();
|
|
|
|
$data['lead_pipeline_id'] = $pipeline->id;
|
|
|
|
$data['lead_pipeline_stage_id'] = $stage->id;
|
|
}
|
|
|
|
$lead = $this->leadRepository->update($data, $id);
|
|
|
|
Event::dispatch('lead.update.after', $lead);
|
|
|
|
if (request()->ajax()) {
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.update-success'),
|
|
]);
|
|
}
|
|
|
|
session()->flash('success', trans('admin::app.leads.update-success'));
|
|
|
|
if (request()->has('closed_at')) {
|
|
return redirect()->back();
|
|
} else {
|
|
return redirect()->route('admin.leads.index', $data['lead_pipeline_id']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the lead attributes.
|
|
*/
|
|
public function updateAttributes(int $id)
|
|
{
|
|
$data = request()->all();
|
|
|
|
$attributes = $this->attributeRepository->findWhere([
|
|
'entity_type' => 'leads',
|
|
['code', 'NOTIN', ['title', 'description']],
|
|
]);
|
|
|
|
Event::dispatch('lead.update.before', $id);
|
|
|
|
$lead = $this->leadRepository->update($data, $id, $attributes);
|
|
|
|
Event::dispatch('lead.update.after', $lead);
|
|
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.update-success'),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Update the lead stage.
|
|
*/
|
|
public function updateStage(int $id)
|
|
{
|
|
$this->validate(request(), [
|
|
'lead_pipeline_stage_id' => 'required',
|
|
]);
|
|
|
|
$lead = $this->leadRepository->findOrFail($id);
|
|
|
|
$stage = $lead->pipeline->stages()
|
|
->where('id', request()->input('lead_pipeline_stage_id'))
|
|
->firstOrFail();
|
|
|
|
Event::dispatch('lead.update.before', $id);
|
|
|
|
$payload = request()->merge([
|
|
'entity_type' => 'leads',
|
|
'lead_pipeline_stage_id' => $stage->id,
|
|
])->only([
|
|
'closed_at',
|
|
'lost_reason',
|
|
'lead_pipeline_stage_id',
|
|
'entity_type',
|
|
]);
|
|
|
|
$lead = $this->leadRepository->update($payload, $id, ['lead_pipeline_stage_id']);
|
|
|
|
Event::dispatch('lead.update.after', $lead);
|
|
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.update-success'),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Search person results.
|
|
*/
|
|
public function search(): AnonymousResourceCollection
|
|
{
|
|
if ($userIds = bouncer()->getAuthorizedUserIds()) {
|
|
$results = $this->leadRepository
|
|
->pushCriteria(app(RequestCriteria::class))
|
|
->findWhereIn('user_id', $userIds);
|
|
} else {
|
|
$results = $this->leadRepository
|
|
->pushCriteria(app(RequestCriteria::class))
|
|
->all();
|
|
}
|
|
|
|
return LeadResource::collection($results);
|
|
}
|
|
|
|
/**
|
|
* Remove the specified resource from storage.
|
|
*/
|
|
public function destroy(int $id): JsonResponse
|
|
{
|
|
$this->leadRepository->findOrFail($id);
|
|
|
|
try {
|
|
Event::dispatch('lead.delete.before', $id);
|
|
|
|
$this->leadRepository->delete($id);
|
|
|
|
Event::dispatch('lead.delete.after', $id);
|
|
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.destroy-success'),
|
|
]);
|
|
} catch (\Exception $exception) {
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.destroy-failed'),
|
|
], 400);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mass update the specified resources.
|
|
*/
|
|
public function massUpdate(MassUpdateRequest $massUpdateRequest): JsonResponse
|
|
{
|
|
$leads = $this->leadRepository->findWhereIn('id', $massUpdateRequest->input('indices'));
|
|
|
|
try {
|
|
foreach ($leads as $lead) {
|
|
Event::dispatch('lead.update.before', $lead->id);
|
|
|
|
$lead = $this->leadRepository->find($lead->id);
|
|
|
|
$lead?->update(['lead_pipeline_stage_id' => $massUpdateRequest->input('value')]);
|
|
|
|
Event::dispatch('lead.update.before', $lead->id);
|
|
}
|
|
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.update-success'),
|
|
]);
|
|
} catch (\Exception $th) {
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.update-failed'),
|
|
], 400);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mass delete the specified resources.
|
|
*/
|
|
public function massDestroy(MassDestroyRequest $massDestroyRequest): JsonResponse
|
|
{
|
|
$leads = $this->leadRepository->findWhereIn('id', $massDestroyRequest->input('indices'));
|
|
|
|
try {
|
|
foreach ($leads as $lead) {
|
|
Event::dispatch('lead.delete.before', $lead->id);
|
|
|
|
$this->leadRepository->delete($lead->id);
|
|
|
|
Event::dispatch('lead.delete.after', $lead->id);
|
|
}
|
|
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.destroy-success'),
|
|
]);
|
|
} catch (\Exception $exception) {
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.destroy-failed'),
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attach product to lead.
|
|
*/
|
|
public function addProduct(int $leadId): JsonResponse
|
|
{
|
|
$product = $this->productRepository->updateOrCreate(
|
|
[
|
|
'lead_id' => $leadId,
|
|
'product_id' => request()->input('product_id'),
|
|
],
|
|
array_merge(
|
|
request()->all(),
|
|
[
|
|
'lead_id' => $leadId,
|
|
'amount' => request()->input('price') * request()->input('quantity'),
|
|
],
|
|
)
|
|
);
|
|
|
|
return response()->json([
|
|
'data' => $product,
|
|
'message' => trans('admin::app.leads.update-success'),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Remove product attached to lead.
|
|
*/
|
|
public function removeProduct(int $id): JsonResponse
|
|
{
|
|
try {
|
|
Event::dispatch('lead.product.delete.before', $id);
|
|
|
|
$this->productRepository->deleteWhere([
|
|
'lead_id' => $id,
|
|
'product_id' => request()->input('product_id'),
|
|
]);
|
|
|
|
Event::dispatch('lead.product.delete.after', $id);
|
|
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.destroy-success'),
|
|
]);
|
|
} catch (\Exception $exception) {
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.destroy-failed'),
|
|
]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Kanban lookup.
|
|
*/
|
|
public function kanbanLookup()
|
|
{
|
|
$params = $this->validate(request(), [
|
|
'column' => ['required'],
|
|
'search' => ['required', 'min:2'],
|
|
]);
|
|
|
|
/**
|
|
* Finding the first column from the collection.
|
|
*/
|
|
$column = collect($this->getKanbanColumns())->where('index', $params['column'])->firstOrFail();
|
|
|
|
/**
|
|
* Fetching on the basis of column options.
|
|
*/
|
|
return app($column['filterable_options']['repository'])
|
|
->select([$column['filterable_options']['column']['label'].' as label', $column['filterable_options']['column']['value'].' as value'])
|
|
->where($column['filterable_options']['column']['label'], 'LIKE', '%'.$params['search'].'%')
|
|
->get()
|
|
->map
|
|
->only('label', 'value');
|
|
}
|
|
|
|
/**
|
|
* Get columns for the kanban view.
|
|
*/
|
|
private function getKanbanColumns(): array
|
|
{
|
|
return [
|
|
[
|
|
'index' => 'id',
|
|
'label' => trans('admin::app.leads.index.kanban.columns.id'),
|
|
'type' => 'integer',
|
|
'searchable' => false,
|
|
'search_field' => 'in',
|
|
'filterable' => true,
|
|
'filterable_type' => null,
|
|
'filterable_options' => [],
|
|
'allow_multiple_values' => true,
|
|
'sortable' => true,
|
|
'visibility' => true,
|
|
],
|
|
[
|
|
'index' => 'lead_value',
|
|
'label' => trans('admin::app.leads.index.kanban.columns.lead-value'),
|
|
'type' => 'string',
|
|
'searchable' => false,
|
|
'search_field' => 'in',
|
|
'filterable' => true,
|
|
'filterable_type' => null,
|
|
'filterable_options' => [],
|
|
'allow_multiple_values' => true,
|
|
'sortable' => true,
|
|
'visibility' => true,
|
|
],
|
|
[
|
|
'index' => 'user_id',
|
|
'label' => trans('admin::app.leads.index.kanban.columns.sales-person'),
|
|
'type' => 'string',
|
|
'searchable' => false,
|
|
'search_field' => 'in',
|
|
'filterable' => true,
|
|
'filterable_type' => 'searchable_dropdown',
|
|
'filterable_options' => [
|
|
'repository' => UserRepository::class,
|
|
'column' => [
|
|
'label' => 'name',
|
|
'value' => 'id',
|
|
],
|
|
],
|
|
'allow_multiple_values' => true,
|
|
'sortable' => true,
|
|
'visibility' => true,
|
|
],
|
|
[
|
|
'index' => 'person.id',
|
|
'label' => trans('admin::app.leads.index.kanban.columns.contact-person'),
|
|
'type' => 'string',
|
|
'searchable' => false,
|
|
'search_field' => 'in',
|
|
'filterable' => true,
|
|
'filterable_options' => [],
|
|
'allow_multiple_values' => true,
|
|
'sortable' => true,
|
|
'visibility' => true,
|
|
'filterable_type' => 'searchable_dropdown',
|
|
'filterable_options' => [
|
|
'repository' => PersonRepository::class,
|
|
'column' => [
|
|
'label' => 'name',
|
|
'value' => 'id',
|
|
],
|
|
],
|
|
],
|
|
[
|
|
'index' => 'lead_type_id',
|
|
'label' => trans('admin::app.leads.index.kanban.columns.lead-type'),
|
|
'type' => 'string',
|
|
'searchable' => false,
|
|
'search_field' => 'in',
|
|
'filterable' => true,
|
|
'filterable_type' => 'dropdown',
|
|
'filterable_options' => $this->typeRepository->all(['name as label', 'id as value'])->toArray(),
|
|
'allow_multiple_values' => true,
|
|
'sortable' => true,
|
|
'visibility' => true,
|
|
],
|
|
[
|
|
'index' => 'lead_source_id',
|
|
'label' => trans('admin::app.leads.index.kanban.columns.source'),
|
|
'type' => 'string',
|
|
'searchable' => false,
|
|
'search_field' => 'in',
|
|
'filterable' => true,
|
|
'filterable_type' => 'dropdown',
|
|
'filterable_options' => $this->sourceRepository->all(['name as label', 'id as value'])->toArray(),
|
|
'allow_multiple_values' => true,
|
|
'sortable' => true,
|
|
'visibility' => true,
|
|
],
|
|
[
|
|
'index' => 'tags.name',
|
|
'label' => trans('admin::app.leads.index.kanban.columns.tags'),
|
|
'type' => 'string',
|
|
'searchable' => false,
|
|
'search_field' => 'in',
|
|
'filterable' => true,
|
|
'filterable_options' => [],
|
|
'allow_multiple_values' => true,
|
|
'sortable' => true,
|
|
'visibility' => true,
|
|
'filterable_type' => 'searchable_dropdown',
|
|
'filterable_options' => [
|
|
'repository' => TagRepository::class,
|
|
'column' => [
|
|
'label' => 'name',
|
|
'value' => 'name',
|
|
],
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Create lead with specified AI.
|
|
*/
|
|
public function createByAI()
|
|
{
|
|
$leadData = [];
|
|
|
|
$errorMessages = [];
|
|
|
|
foreach (request()->file('files') as $file) {
|
|
$lead = $this->processFile($file);
|
|
|
|
if (
|
|
isset($lead['status'])
|
|
&& $lead['status'] === 'error'
|
|
) {
|
|
$errorMessages[] = $lead['message'];
|
|
} else {
|
|
$leadData[] = $lead;
|
|
}
|
|
}
|
|
|
|
if (isset($errorMessages[0]['code'])) {
|
|
return response()->json(MagicAI::errorHandler($errorMessages[0]['message']));
|
|
}
|
|
|
|
if (
|
|
empty($leadData)
|
|
&& ! empty($errorMessages)
|
|
) {
|
|
return response()->json(MagicAI::errorHandler(implode(', ', $errorMessages)), 400);
|
|
}
|
|
|
|
if (empty($leadData)) {
|
|
return response()->json(MagicAI::errorHandler(trans('admin::app.leads.no-valid-files')), 400);
|
|
}
|
|
|
|
return response()->json([
|
|
'message' => trans('admin::app.leads.create-success'),
|
|
'leads' => $this->createLeads($leadData),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Process file.
|
|
*
|
|
* @param mixed $file
|
|
*/
|
|
private function processFile($file)
|
|
{
|
|
$validator = Validator::make(
|
|
['file' => $file],
|
|
['file' => 'required|extensions:'.str_replace(' ', '', self::SUPPORTED_TYPES)]
|
|
);
|
|
|
|
if ($validator->fails()) {
|
|
return MagicAI::errorHandler($validator->errors()->first());
|
|
}
|
|
|
|
$base64Pdf = base64_encode(file_get_contents($file->getRealPath()));
|
|
|
|
$extractedData = MagicAIService::extractDataFromFile($base64Pdf);
|
|
|
|
$lead = MagicAI::mapAIDataToLead($extractedData);
|
|
|
|
return $lead;
|
|
}
|
|
|
|
/**
|
|
* Create multiple leads.
|
|
*/
|
|
private function createLeads($rawLeads): array
|
|
{
|
|
$leads = [];
|
|
|
|
foreach ($rawLeads as $rawLead) {
|
|
Event::dispatch('lead.create.before');
|
|
|
|
foreach ($rawLead['person']['emails'] as $email) {
|
|
$person = $this->personRepository
|
|
->whereJsonContains('emails', [['value' => $email['value']]])
|
|
->first();
|
|
|
|
if ($person) {
|
|
$rawLead['person']['id'] = $person->id;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
$pipeline = $this->pipelineRepository->getDefaultPipeline();
|
|
|
|
$stage = $pipeline->stages()->first();
|
|
|
|
$lead = $this->leadRepository->create(array_merge($rawLead, [
|
|
'lead_pipeline_id' => $pipeline->id,
|
|
'lead_pipeline_stage_id' => $stage->id,
|
|
]));
|
|
|
|
Event::dispatch('lead.create.after', $lead);
|
|
|
|
$leads[] = $lead;
|
|
}
|
|
|
|
return $leads;
|
|
}
|
|
}
|