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,343 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
use Carbon\CarbonPeriod;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
abstract class AbstractReporting
{
/**
* The starting date for a given period.
*/
protected Carbon $startDate;
/**
* The ending date for a given period.
*/
protected Carbon $endDate;
/**
* The starting date for the previous period.
*/
protected Carbon $lastStartDate;
/**
* The ending date for the previous period.
*/
protected Carbon $lastEndDate;
/**
* Create a helper instance.
*
* @return void
*/
public function __construct()
{
$this->setStartDate(request()->date('start'));
$this->setEndDate(request()->date('end'));
}
/**
* Set the start date or default to 30 days ago if not provided.
*
* @param \Carbon\Carbon|null $startDate
* @return void
*/
public function setStartDate(?Carbon $startDate = null): self
{
$this->startDate = $startDate ? $startDate->startOfDay() : now()->subDays(30)->startOfDay();
$this->setLastStartDate();
return $this;
}
/**
* Sets the end date to the provided date's end of day, or to the current
* date if not provided or if the provided date is in the future.
*
* @param \Carbon\Carbon|null $endDate
* @return void
*/
public function setEndDate(?Carbon $endDate = null): self
{
$this->endDate = ($endDate && $endDate->endOfDay() <= now()) ? $endDate->endOfDay() : now();
$this->setLastEndDate();
return $this;
}
/**
* Get the start date.
*
* @return \Carbon\Carbon
*/
public function getStartDate(): Carbon
{
return $this->startDate;
}
/**
* Get the end date.
*
* @return \Carbon\Carbon
*/
public function getEndDate(): Carbon
{
return $this->endDate;
}
/**
* Sets the start date for the last period.
*/
private function setLastStartDate(): void
{
if (! isset($this->startDate)) {
$this->setStartDate(request()->date('start'));
}
if (! isset($this->endDate)) {
$this->setEndDate(request()->date('end'));
}
$this->lastStartDate = $this->startDate->clone()->subDays($this->startDate->diffInDays($this->endDate));
}
/**
* Sets the end date for the last period.
*/
private function setLastEndDate(): void
{
$this->lastEndDate = $this->startDate->clone();
}
/**
* Get the last start date.
*
* @return \Carbon\Carbon
*/
public function getLastStartDate(): Carbon
{
return $this->lastStartDate;
}
/**
* Get the last end date.
*
* @return \Carbon\Carbon
*/
public function getLastEndDate(): Carbon
{
return $this->lastEndDate;
}
/**
* Calculate the percentage change between previous and current values.
*
* @param float|int $previous
* @param float|int $current
*/
public function getPercentageChange($previous, $current): float|int
{
if (! $previous) {
return $current ? 100 : 0;
}
return ($current - $previous) / $previous * 100;
}
/**
* Returns time intervals.
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @param string $period
* @return array
*/
public function getTimeInterval($startDate, $endDate, $dateColumn, $period)
{
if ($period == 'auto') {
$totalMonths = $startDate->diffInMonths($endDate) + 1;
/**
* If the difference between the start and end date is more than 5 months
*/
$intervals = $this->getMonthsInterval($startDate, $endDate);
if (! empty($intervals)) {
return [
'group_column' => "MONTH($dateColumn)",
'intervals' => $intervals,
];
}
/**
* If the difference between the start and end date is more than 6 weeks
*/
$intervals = $this->getWeeksInterval($startDate, $endDate);
if (! empty($intervals)) {
return [
'group_column' => "WEEK($dateColumn)",
'intervals' => $intervals,
];
}
/**
* If the difference between the start and end date is less than 6 weeks
*/
return [
'group_column' => "DAYOFYEAR($dateColumn)",
'intervals' => $this->getDaysInterval($startDate, $endDate),
];
} else {
$datePeriod = CarbonPeriod::create($this->startDate, "1 $period", $this->endDate);
if ($period == 'year') {
$formatter = '?';
} elseif ($period == 'month') {
$formatter = '?-?';
} else {
$formatter = '?-?-?';
}
$groupColumn = 'DATE_FORMAT('.$dateColumn.', "'.Str::replaceArray('?', ['%Y', '%m', '%d'], $formatter).'")';
$intervals = [];
foreach ($datePeriod as $date) {
$formattedDate = $date->format(Str::replaceArray('?', ['Y', 'm', 'd'], $formatter));
$intervals[] = [
'filter' => $formattedDate,
'start' => $formattedDate,
];
}
return [
'group_column' => $groupColumn,
'intervals' => $intervals,
];
}
}
/**
* Returns time intervals.
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @return array
*/
public function getMonthsInterval($startDate, $endDate)
{
$intervals = [];
$totalMonths = $startDate->diffInMonths($endDate) + 1;
/**
* If the difference between the start and end date is less than 5 months
*/
if ($totalMonths <= 5) {
return $intervals;
}
for ($i = 0; $i < $totalMonths; $i++) {
$intervalStartDate = clone $startDate;
$intervalStartDate->addMonths($i);
$start = $intervalStartDate->startOfDay();
$end = ($totalMonths - 1 == $i)
? $endDate
: $intervalStartDate->addMonth()->subDay()->endOfDay();
$intervals[] = [
'filter' => $start->month,
'start' => $start->format('d M'),
'end' => $end->format('d M'),
];
}
return $intervals;
}
/**
* Returns time intervals.
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @return array
*/
public function getWeeksInterval($startDate, $endDate)
{
$intervals = [];
$startWeekDay = Carbon::createFromTimeString(core()->xWeekRange($startDate, 0).' 00:00:01');
$endWeekDay = Carbon::createFromTimeString(core()->xWeekRange($endDate, 1).' 23:59:59');
$totalWeeks = $startWeekDay->diffInWeeks($endWeekDay);
/**
* If the difference between the start and end date is less than 6 weeks
*/
if ($totalWeeks <= 6) {
return $intervals;
}
for ($i = 0; $i < $totalWeeks; $i++) {
$intervalStartDate = clone $startDate;
$intervalStartDate->addWeeks($i);
$start = $i == 0
? $startDate
: Carbon::createFromTimeString(core()->xWeekRange($intervalStartDate, 0).' 00:00:01');
$end = ($totalWeeks - 1 == $i)
? $endDate
: Carbon::createFromTimeString(core()->xWeekRange($intervalStartDate->subDay(), 1).' 23:59:59');
$intervals[] = [
'filter' => $start->week,
'start' => $start->format('d M'),
'end' => $end->format('d M'),
];
}
return $intervals;
}
/**
* Returns time intervals.
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @return array
*/
public function getDaysInterval($startDate, $endDate)
{
$intervals = [];
$totalDays = $startDate->diffInDays($endDate) + 1;
for ($i = 0; $i < $totalDays; $i++) {
$intervalStartDate = clone $startDate;
$intervalStartDate->addDays($i);
$intervals[] = [
'filter' => $intervalStartDate->dayOfYear,
'start' => $intervalStartDate->startOfDay()->format('d M'),
'end' => $intervalStartDate->endOfDay()->format('d M'),
];
}
return $intervals;
}
}

View File

@@ -0,0 +1,5 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
class Activity extends AbstractReporting {}

View File

@@ -0,0 +1,483 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Webkul\Lead\Repositories\LeadRepository;
use Webkul\Lead\Repositories\StageRepository;
class Lead extends AbstractReporting
{
/**
* The channel ids.
*/
protected array $stageIds;
/**
* The all stage ids.
*/
protected array $allStageIds;
/**
* The won stage ids.
*/
protected array $wonStageIds;
/**
* The lost stage ids.
*/
protected array $lostStageIds;
/**
* Create a helper instance.
*
* @return void
*/
public function __construct(
protected LeadRepository $leadRepository,
protected StageRepository $stageRepository
) {
$this->allStageIds = $this->stageRepository->pluck('id')->toArray();
$this->wonStageIds = $this->stageRepository->where('code', 'won')->pluck('id')->toArray();
$this->lostStageIds = $this->stageRepository->where('code', 'lost')->pluck('id')->toArray();
parent::__construct();
}
/**
* Returns current customers over time
*
* @param string $period
*/
public function getTotalLeadsOverTime($period = 'auto'): array
{
$this->stageIds = $this->allStageIds;
$period = $this->determinePeriod($period);
return $this->getOverTimeStats($this->startDate, $this->endDate, 'leads.id', 'created_at', $period);
}
/**
* Returns current customers over time
*
* @param string $period
*/
public function getTotalWonLeadsOverTime($period = 'auto'): array
{
$this->stageIds = $this->wonStageIds;
$period = $this->determinePeriod($period);
return $this->getOverTimeStats($this->startDate, $this->endDate, 'leads.id', 'closed_at', $period);
}
/**
* Returns current customers over time
*
* @param string $period
*/
public function getTotalLostLeadsOverTime($period = 'auto'): array
{
$this->stageIds = $this->lostStageIds;
$period = $this->determinePeriod($period);
return $this->getOverTimeStats($this->startDate, $this->endDate, 'leads.id', 'closed_at', $period);
}
/**
* Determine the appropriate period based on date range
*
* @param string $period
*/
protected function determinePeriod($period = 'auto'): string
{
if ($period !== 'auto') {
return $period;
}
$diffInDays = $this->startDate->diffInDays($this->endDate);
$diffInMonths = $this->startDate->diffInMonths($this->endDate);
$diffInYears = $this->startDate->diffInYears($this->endDate);
if ($diffInYears > 3) {
return 'year';
} elseif ($diffInMonths > 6) {
return 'month';
} elseif ($diffInDays > 60) {
return 'week';
} else {
return 'day';
}
}
/**
* Retrieves total leads and their progress.
*/
public function getTotalLeadsProgress(): array
{
return [
'previous' => $previous = $this->getTotalLeads($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalLeads($this->startDate, $this->endDate),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves total leads by date
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getTotalLeads($startDate, $endDate): int
{
return $this->leadRepository
->resetModel()
->whereBetween('created_at', [$startDate, $endDate])
->count();
}
/**
* Retrieves average leads per day and their progress.
*/
public function getAverageLeadsPerDayProgress(): array
{
return [
'previous' => $previous = $this->getAverageLeadsPerDay($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getAverageLeadsPerDay($this->startDate, $this->endDate),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves average leads per day
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getAverageLeadsPerDay($startDate, $endDate): float
{
$days = $startDate->diffInDays($endDate);
if ($days == 0) {
return 0;
}
return $this->getTotalLeads($startDate, $endDate) / $days;
}
/**
* Retrieves total lead value and their progress.
*/
public function getTotalLeadValueProgress(): array
{
return [
'previous' => $previous = $this->getTotalLeadValue($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalLeadValue($this->startDate, $this->endDate),
'formatted_total' => core()->formatBasePrice($current),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves total lead value
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getTotalLeadValue($startDate, $endDate): float
{
return $this->leadRepository
->resetModel()
->whereBetween('created_at', [$startDate, $endDate])
->sum('lead_value');
}
/**
* Retrieves average lead value and their progress.
*/
public function getAverageLeadValueProgress(): array
{
return [
'previous' => $previous = $this->getAverageLeadValue($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getAverageLeadValue($this->startDate, $this->endDate),
'formatted_total' => core()->formatBasePrice($current),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves average lead value
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getAverageLeadValue($startDate, $endDate): float
{
return $this->leadRepository
->resetModel()
->whereBetween('created_at', [$startDate, $endDate])
->avg('lead_value') ?? 0;
}
/**
* Retrieves total won lead value and their progress.
*/
public function getTotalWonLeadValueProgress(): array
{
return [
'previous' => $previous = $this->getTotalWonLeadValue($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalWonLeadValue($this->startDate, $this->endDate),
'formatted_total' => core()->formatBasePrice($current),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves average won lead value
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @return array
*/
public function getTotalWonLeadValue($startDate, $endDate): ?float
{
return $this->leadRepository
->resetModel()
->whereIn('lead_pipeline_stage_id', $this->wonStageIds)
->whereBetween('created_at', [$startDate, $endDate])
->sum('lead_value');
}
/**
* Retrieves average lost lead value and their progress.
*/
public function getTotalLostLeadValueProgress(): array
{
return [
'previous' => $previous = $this->getTotalLostLeadValue($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalLostLeadValue($this->startDate, $this->endDate),
'formatted_total' => core()->formatBasePrice($current),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves average lost lead value
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @return array
*/
public function getTotalLostLeadValue($startDate, $endDate): ?float
{
return $this->leadRepository
->resetModel()
->whereIn('lead_pipeline_stage_id', $this->lostStageIds)
->whereBetween('created_at', [$startDate, $endDate])
->sum('lead_value');
}
/**
* Retrieves total lead value by sources.
*/
public function getTotalWonLeadValueBySources()
{
return $this->leadRepository
->resetModel()
->select(
'lead_sources.name',
DB::raw('SUM(lead_value) as total')
)
->leftJoin('lead_sources', 'leads.lead_source_id', '=', 'lead_sources.id')
->whereIn('lead_pipeline_stage_id', $this->wonStageIds)
->whereBetween('leads.created_at', [$this->startDate, $this->endDate])
->groupBy('lead_source_id')
->get();
}
/**
* Retrieves total lead value by types.
*/
public function getTotalWonLeadValueByTypes()
{
return $this->leadRepository
->resetModel()
->select(
'lead_types.name',
DB::raw('SUM(lead_value) as total')
)
->leftJoin('lead_types', 'leads.lead_type_id', '=', 'lead_types.id')
->whereIn('lead_pipeline_stage_id', $this->wonStageIds)
->whereBetween('leads.created_at', [$this->startDate, $this->endDate])
->groupBy('lead_type_id')
->get();
}
/**
* Retrieves open leads by states.
*/
public function getOpenLeadsByStates()
{
return $this->leadRepository
->resetModel()
->select(
'lead_pipeline_stages.name',
DB::raw('COUNT(lead_value) as total')
)
->leftJoin('lead_pipeline_stages', 'leads.lead_pipeline_stage_id', '=', 'lead_pipeline_stages.id')
->whereNotIn('lead_pipeline_stage_id', $this->wonStageIds)
->whereNotIn('lead_pipeline_stage_id', $this->lostStageIds)
->whereBetween('leads.created_at', [$this->startDate, $this->endDate])
->groupBy('lead_pipeline_stage_id')
->orderByDesc('total')
->get();
}
/**
* Returns over time stats.
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
* @param string $valueColumn
* @param string $dateColumn
* @param string $period
*/
public function getOverTimeStats($startDate, $endDate, $valueColumn, $dateColumn = 'created_at', $period = 'auto'): array
{
$period = $this->determinePeriod($period);
$intervals = $this->generateTimeIntervals($startDate, $endDate, $period);
$groupColumn = $this->getGroupColumn($dateColumn, $period);
$query = $this->leadRepository
->resetModel()
->select(
DB::raw("$groupColumn AS date"),
DB::raw('COUNT(DISTINCT id) AS count'),
DB::raw('SUM('.\DB::getTablePrefix()."$valueColumn) AS total")
)
->whereIn('lead_pipeline_stage_id', $this->stageIds)
->whereBetween($dateColumn, [$startDate, $endDate])
->groupBy(DB::raw($groupColumn))
->orderBy(DB::raw($groupColumn));
$results = $query->get();
$resultLookup = $results->keyBy('date');
$stats = [];
foreach ($intervals as $interval) {
$result = $resultLookup->get($interval['key']);
$stats[] = [
'label' => $interval['label'],
'count' => $result ? (int) $result->count : 0,
'total' => $result ? (float) $result->total : 0,
];
}
return $stats;
}
/**
* Generate time intervals based on period
*/
protected function generateTimeIntervals(Carbon $startDate, Carbon $endDate, string $period): array
{
$intervals = [];
$current = $startDate->copy();
while ($current <= $endDate) {
$interval = [
'key' => $this->formatDateForGrouping($current, $period),
'label' => $this->formatDateForLabel($current, $period),
];
$intervals[] = $interval;
switch ($period) {
case 'day':
$current->addDay();
break;
case 'week':
$current->addWeek();
break;
case 'month':
$current->addMonth();
break;
case 'year':
$current->addYear();
break;
}
}
return $intervals;
}
/**
* Get the SQL group column based on period
*/
protected function getGroupColumn(string $dateColumn, string $period): string
{
switch ($period) {
case 'day':
return "DATE($dateColumn)";
case 'week':
return "DATE_FORMAT($dateColumn, '%Y-%u')";
case 'month':
return "DATE_FORMAT($dateColumn, '%Y-%m')";
case 'year':
return "YEAR($dateColumn)";
default:
return "DATE($dateColumn)";
}
}
/**
* Format date for grouping key
*/
protected function formatDateForGrouping(Carbon $date, string $period): string
{
switch ($period) {
case 'day':
return $date->format('Y-m-d');
case 'week':
return $date->format('Y-W');
case 'month':
return $date->format('Y-m');
case 'year':
return $date->format('Y');
default:
return $date->format('Y-m-d');
}
}
/**
* Format date for display label
*/
protected function formatDateForLabel(Carbon $date, string $period): string
{
switch ($period) {
case 'day':
return $date->format('M d');
case 'week':
return 'Week '.$date->format('W, Y');
case 'month':
return $date->format('M Y');
case 'year':
return $date->format('Y');
default:
return $date->format('M d');
}
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Webkul\Contact\Repositories\OrganizationRepository;
class Organization extends AbstractReporting
{
/**
* Create a helper instance.
*
* @return void
*/
public function __construct(protected OrganizationRepository $organizationRepository)
{
parent::__construct();
}
/**
* Retrieves total organizations and their progress.
*/
public function getTotalOrganizationsProgress(): array
{
return [
'previous' => $previous = $this->getTotalOrganizations($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalOrganizations($this->startDate, $this->endDate),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves total organizations by date
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getTotalOrganizations($startDate, $endDate): int
{
return $this->organizationRepository
->resetModel()
->whereBetween('created_at', [$startDate, $endDate])
->count();
}
/**
* Gets top customers by revenue.
*
* @param int $limit
*/
public function getTopOrganizationsByRevenue($limit = null): Collection
{
$tablePrefix = DB::getTablePrefix();
$items = $this->organizationRepository
->resetModel()
->leftJoin('persons', 'organizations.id', '=', 'persons.organization_id')
->leftJoin('leads', 'persons.id', '=', 'leads.person_id')
->select('*', 'persons.id as id')
->addSelect(DB::raw('SUM('.$tablePrefix.'leads.lead_value) as revenue'))
->whereBetween('leads.closed_at', [$this->startDate, $this->endDate])
->having(DB::raw('SUM('.$tablePrefix.'leads.lead_value)'), '>', 0)
->groupBy('organization_id')
->orderBy('revenue', 'DESC')
->limit($limit)
->get();
$items = $items->map(function ($item) {
return [
'id' => $item->id,
'name' => $item->name,
'revenue' => $item->revenue,
'formatted_revenue' => core()->formatBasePrice($item->revenue),
];
});
return $items;
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Webkul\Contact\Repositories\PersonRepository;
class Person extends AbstractReporting
{
/**
* Create a helper instance.
*
* @return void
*/
public function __construct(protected PersonRepository $personRepository)
{
parent::__construct();
}
/**
* Retrieves total persons and their progress.
*/
public function getTotalPersonsProgress(): array
{
return [
'previous' => $previous = $this->getTotalPersons($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalPersons($this->startDate, $this->endDate),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves total persons by date
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getTotalPersons($startDate, $endDate): int
{
return $this->personRepository
->resetModel()
->whereBetween('created_at', [$startDate, $endDate])
->count();
}
/**
* Gets top customers by revenue.
*
* @param int $limit
*/
public function getTopCustomersByRevenue($limit = null): Collection
{
$tablePrefix = DB::getTablePrefix();
$items = $this->personRepository
->resetModel()
->leftJoin('leads', 'persons.id', '=', 'leads.person_id')
->select('*', 'persons.id as id')
->addSelect(DB::raw('SUM('.$tablePrefix.'leads.lead_value) as revenue'))
->whereBetween('leads.closed_at', [$this->startDate, $this->endDate])
->having(DB::raw('SUM('.$tablePrefix.'leads.lead_value)'), '>', 0)
->groupBy('person_id')
->orderBy('revenue', 'DESC')
->limit($limit)
->get();
$items = $items->map(function ($item) {
return [
'id' => $item->id,
'name' => $item->name,
'emails' => $item->emails,
'contact_numbers' => $item->contact_numbers,
'revenue' => $item->revenue,
'formatted_revenue' => core()->formatBasePrice($item->revenue),
];
});
return $items;
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Webkul\Lead\Repositories\ProductRepository;
class Product extends AbstractReporting
{
/**
* Create a helper instance.
*
* @return void
*/
public function __construct(
protected ProductRepository $productRepository
) {
parent::__construct();
}
/**
* Gets top-selling products by revenue.
*
* @param int $limit
*/
public function getTopSellingProductsByRevenue($limit = null): Collection
{
$tablePrefix = DB::getTablePrefix();
$items = $this->productRepository
->resetModel()
->with('product')
->leftJoin('leads', 'lead_products.lead_id', '=', 'leads.id')
->leftJoin('products', 'lead_products.product_id', '=', 'products.id')
->select('*')
->addSelect(DB::raw('SUM('.$tablePrefix.'lead_products.amount) as revenue'))
->whereBetween('leads.closed_at', [$this->startDate, $this->endDate])
->having(DB::raw('SUM('.$tablePrefix.'lead_products.amount)'), '>', 0)
->groupBy('product_id')
->orderBy('revenue', 'DESC')
->limit($limit)
->get();
$items = $items->map(function ($item) {
return [
'id' => $item->product_id,
'name' => $item->name,
'price' => $item->product?->price,
'formatted_price' => core()->formatBasePrice($item->price),
'revenue' => $item->revenue,
'formatted_revenue' => core()->formatBasePrice($item->revenue),
];
});
return $items;
}
/**
* Gets top-selling products by quantity.
*
* @param int $limit
*/
public function getTopSellingProductsByQuantity($limit = null): Collection
{
$tablePrefix = DB::getTablePrefix();
$items = $this->productRepository
->resetModel()
->with('product')
->leftJoin('leads', 'lead_products.lead_id', '=', 'leads.id')
->leftJoin('products', 'lead_products.product_id', '=', 'products.id')
->select('*')
->addSelect(DB::raw('SUM('.$tablePrefix.'lead_products.quantity) as total_qty_ordered'))
->whereBetween('leads.closed_at', [$this->startDate, $this->endDate])
->having(DB::raw('SUM('.$tablePrefix.'lead_products.quantity)'), '>', 0)
->groupBy('product_id')
->orderBy('total_qty_ordered', 'DESC')
->limit($limit)
->get();
$items = $items->map(function ($item) {
return [
'id' => $item->product_id,
'name' => $item->name,
'price' => $item->product?->price,
'formatted_price' => core()->formatBasePrice($item->price),
'total_qty_ordered' => $item->total_qty_ordered,
];
});
return $items;
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Webkul\Admin\Helpers\Reporting;
use Webkul\Quote\Repositories\QuoteRepository;
class Quote extends AbstractReporting
{
/**
* Create a helper instance.
*
* @return void
*/
public function __construct(protected QuoteRepository $quoteRepository)
{
parent::__construct();
}
/**
* Retrieves total quotes and their progress.
*/
public function getTotalQuotesProgress(): array
{
return [
'previous' => $previous = $this->getTotalQuotes($this->lastStartDate, $this->lastEndDate),
'current' => $current = $this->getTotalQuotes($this->startDate, $this->endDate),
'progress' => $this->getPercentageChange($previous, $current),
];
}
/**
* Retrieves total quotes by date
*
* @param \Carbon\Carbon $startDate
* @param \Carbon\Carbon $endDate
*/
public function getTotalQuotes($startDate, $endDate): int
{
return $this->quoteRepository
->resetModel()
->whereBetween('created_at', [$startDate, $endDate])
->count();
}
}