Files
gustavoo-me/wp-content/plugins/fluent-crm/app/Services/Reporting.php
T
2026-07-23 22:56:30 -03:00

331 lines
12 KiB
PHP

<?php
namespace FluentCrm\App\Services;
use FluentCrm\App\Models\CampaignEmail;
use FluentCrm\App\Models\CampaignUrlMetric;
use FluentCrm\App\Models\FunnelMetric;
use FluentCrm\App\Models\FunnelSequence;
use FluentCrm\App\Models\FunnelSubscriber;
use FluentCrm\Framework\Support\Arr;
use FluentCrm\Framework\Support\Collection;
class Reporting
{
use ReportingHelperTrait;
public function getSubscribersGrowth($from = false, $to = false, $tagId = 0, $listId = 0)
{
$period = $this->makeDatePeriod(
$from = $this->makeFromDate($from),
$to = $this->makeToDate($to),
$frequency = $this->getFrequency($from, $to)
);
list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);
$query = fluentCrmDb()->table('fc_subscribers')
->select($this->prepareSelect($frequency))
->whereBetween('created_at', [$from->format('Y-m-d'), $to->format('Y-m-d')])
->where('status', 'subscribed');
if ($tagId) {
$query->whereIn('id', function ($q) use ($tagId) {
$q->select('subscriber_id')
->from('fc_subscriber_pivot')
->where('object_id', $tagId)
->where('object_type', 'FluentCrm\App\Models\Tag');
});
}
if ($listId) {
$query->whereIn('id', function ($q) use ($listId) {
$q->select('subscriber_id')
->from('fc_subscriber_pivot')
->where('object_id', $listId)
->where('object_type', 'FluentCrm\App\Models\Lists');
});
}
$items = $query->groupBy($groupBy)
->orderBy($orderBy, 'ASC')
->get();
return $this->getResult($period, $items);
}
public function getEmailOpenStats($from = false, $to = false, $campaignId = 0)
{
$period = $this->makeDatePeriod(
$from = $this->makeFromDate($from),
$to = $this->makeToDate($to),
$frequency = $this->getFrequency($from, $to)
);
list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);
$query = fluentCrmDb()->table('fc_campaign_emails')
->select($this->prepareSelect($frequency, 'updated_at'))
->whereBetween('updated_at', [$from->format('Y-m-d'), $to->format('Y-m-d')])
->where('is_open', 1);
if ($campaignId) {
$query->where('campaign_id', $campaignId);
}
$items = $query->groupBy($groupBy)
->orderBy($orderBy, 'ASC')
->get();
return $this->getResult($period, $items);
}
public function getEmailClickStats($from = false, $to = false, $campaignId = 0)
{
$period = $this->makeDatePeriod(
$from = $this->makeFromDate($from),
$to = $this->makeToDate($to),
$frequency = $this->getFrequency($from, $to)
);
list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);
$query = fluentCrmDb()->table('fc_campaign_url_metrics')
->select($this->prepareSelect($frequency))
->whereBetween('created_at', [$from->format('Y-m-d'), $to->format('Y-m-d')])
->where('type', 'click');
if ($campaignId) {
$query->where('campaign_id', $campaignId);
}
$items = $query->groupBy($groupBy)
->orderBy($orderBy, 'ASC')
->get();
return $this->getResult($period, $items);
}
public function getEmailStats($from = false, $to = false, $status = 'sent', $campaignId = 0)
{
$period = $this->makeDatePeriod(
$from = $this->makeFromDate($from),
$to = $this->makeToDate($to),
$frequency = $this->getFrequency($from, $to)
);
list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);
$query = fluentCrmDb()->table('fc_campaign_emails')
->select($this->prepareSelect($frequency, 'scheduled_at'))
->whereBetween('scheduled_at', [$from->format('Y-m-d'), $to->format('Y-m-d')])
->where('status', $status);
if ($campaignId) {
$query->where('campaign_id', $campaignId);
}
$items = $query->groupBy($groupBy)
->orderBy($orderBy, 'ASC')
->get();
return $this->getResult($period, $items);
}
public function getUnsubscribeStats($from = false, $to = false)
{
$period = $this->makeDatePeriod(
$from = $this->makeFromDate($from),
$to = $this->makeToDate($to),
$frequency = $this->getFrequency($from, $to)
);
list($groupBy, $orderBy) = $this->getGroupAndOrder($frequency);
$items = fluentCrmDb()->table('fc_subscriber_meta')
->select($this->prepareSelect($frequency))
->whereBetween('created_at', [$from->format('Y-m-d'), $to->format('Y-m-d')])
->where('key', 'unsubscribe_reason')
->groupBy($groupBy)
->orderBy($orderBy, 'ASC')
->get();
return $this->getResult($period, $items);
}
public function funnelStat($funnelId, $sequences = [], $from = false, $to = false)
{
if (!$sequences) {
$sequences = FunnelSequence::where('funnel_id', $funnelId)
->orderBy('sequence', 'ASC')
->get();
}
if (!$sequences || $sequences->isEmpty()) {
return [];
}
$sequenceIds = $sequences->pluck('id')->toArray();
$totalSubscriberCount = FunnelSubscriber::where('funnel_id', $funnelId)
->distinct()
->count('subscriber_id');
$items = FunnelMetric::select([
'sequence_id',
'benchmark_currency',
'benchmark_value',
fluentCrmDb()->raw('COUNT(sequence_id) AS count'),
])
->groupBy('sequence_id')
->whereIn('sequence_id', $sequenceIds)
->get()->keyBy('sequence_id');
$totalRevenue = FunnelMetric::select([
fluentCrmDb()->raw('SUM(benchmark_value) AS benchmark_total'),
])->whereIn('sequence_id', $sequenceIds)->first();
if ($totalRevenue && $totalRevenue->benchmark_total) {
$totalRevenue = $totalRevenue->benchmark_total / 100;
} else {
$totalRevenue = 0;
}
$formattedReports = [
[
'label' => __('Entrance', 'fluent-crm'),
'count' => $totalSubscriberCount,
'sequence_id' => 0,
'type' => 'root',
'percent' => 100,
'percent_text' => 100,
'previous_step_count' => $totalSubscriberCount,
'drop_count' => 0,
'drop_percent' => 0
]
];
$currency = 'USD';
$prevCount = $totalSubscriberCount;
foreach ($sequences as $sequence) {
if (empty($items[$sequence->id])) {
continue;
}
$count = ($items[$sequence->id]->count) ? $items[$sequence->id]->count : 0;
$dropCount = $prevCount - $count;
$percent = ($totalSubscriberCount) ? ceil(($count / $totalSubscriberCount) * 100) : 0;
$report = [
'label' => $sequence->title,
'count' => intval($count),
'sequence_id' => $sequence->id,
'type' => $sequence->type,
'percent' => $percent > 100 ? 100 : $percent,
'percent_text' => $percent,
'previous_step_count' => $prevCount,
'drop_count' => $dropCount,
'drop_percent' => ($dropCount && $count && $prevCount) ? floor((1 - ($count / $prevCount)) * 100) : 0
];
if ($sequence->action_name == 'send_custom_email') {
// Calculate the revenue of this campaign
$refCampaign = Arr::get($sequence->settings, 'reference_campaign');
if ($refCampaign) {
if ($revenue = fluentcrm_get_campaign_meta($refCampaign, '_campaign_revenue')) {
$revs = [];
foreach ($revenue->value as $currency => $cents) {
if (is_numeric($cents) && $cents && $currency !== 'orderIds') {
$money = $cents / 100;
$money = number_format($money, (is_int($money)) ? 0 : 2);
$revs[] = strtoupper($currency) . ' ' . $money;
}
}
if ($revs) {
$report['revenues'] = $revs;
}
}
$report['link_clicks'] = CampaignUrlMetric::where('campaign_id', $refCampaign)
->where('type', 'click')
->count();
$report['email_opens'] = CampaignEmail::where('campaign_id', $refCampaign)
->where('is_open', 1)
->count();
}
}
$formattedReports[] = $report;
if ($items[$sequence->id]->benchmark_value > 0) {
$currency = $items[$sequence->id]->benchmark_currency;
}
}
return [
'metrics' => $formattedReports,
'total_revenue' => $totalRevenue,
'total_revenue_formatted' => number_format($totalRevenue, 2, '.', ' '),
'revenue_currency' => $currency
];
}
public function getEmailPerformance($from = null, $to = null)
{
$from = $this->makeFromDate($from);
$to = $this->makeToDate($to);
$sent = fluentCrmDb()->table('fc_campaign_emails')
->where('status', 'sent')
->whereBetween('scheduled_at', [$from->format('Y-m-d'), $to->format('Y-m-d')])
->count();
$bounced = fluentCrmDb()->table('fc_campaign_emails')
->where('status', 'bounced')
->whereBetween('scheduled_at', [$from->format('Y-m-d'), $to->format('Y-m-d')])
->count();
$delivered = max(0, $sent - $bounced);
$opened = fluentCrmDb()->table('fc_campaign_emails')
->where('status', 'sent')
->whereBetween('scheduled_at', [$from->format('Y-m-d'), $to->format('Y-m-d')])
->where('is_open', 1)
->count();
$clicked = fluentCrmDb()->table('fc_campaign_emails')
->where('status', 'sent')
->whereBetween('scheduled_at', [$from->format('Y-m-d'), $to->format('Y-m-d')])
->whereNotNull('click_counter')
->where('click_counter', '>', 0)
->count();
$deliveryBase = $sent + $bounced;
$rateBase = $delivered;
$deliveryPercent = $deliveryBase ? round(($delivered / $deliveryBase) * 100, 2) : 0;
$bouncePercent = $deliveryBase ? round(($bounced / $deliveryBase) * 100, 2) : 0;
$openPercent = $rateBase ? round(($opened / $rateBase) * 100, 2) : 0;
$clickPercent = $rateBase ? round(($clicked / $rateBase) * 100, 2) : 0;
return [
'totals' => [
'sent' => (int)$sent,
'delivered' => (int)$delivered,
'opened' => (int)$opened,
'clicked' => (int)$clicked,
'bounced' => (int)$bounced,
],
'percentages' => [
'delivered' => $deliveryPercent,
'opened' => $openPercent,
'clicked' => $clickPercent,
'bounced' => $bouncePercent,
]
];
}
}