230 lines
8.2 KiB
PHP
230 lines
8.2 KiB
PHP
<?php
|
||
|
||
namespace App\Modules\CRM\Models;
|
||
|
||
use CodeIgniter\Model;
|
||
|
||
class DealModel extends Model
|
||
{
|
||
protected $table = 'deals';
|
||
protected $primaryKey = 'id';
|
||
protected $useSoftDeletes = true;
|
||
protected $useTimestamps = true;
|
||
protected $createdField = 'created_at';
|
||
protected $updatedField = 'updated_at';
|
||
protected $deletedField = 'deleted_at';
|
||
protected $returnType = 'array';
|
||
protected $allowedFields = [
|
||
'organization_id',
|
||
'contact_id',
|
||
'company_id',
|
||
'title',
|
||
'description',
|
||
'amount',
|
||
'currency',
|
||
'stage_id',
|
||
'assigned_user_id',
|
||
'expected_close_date',
|
||
'created_by',
|
||
];
|
||
|
||
/**
|
||
* Получить сделки с JOIN-ами для таблицы
|
||
*/
|
||
public function getForTable(int $organizationId): array
|
||
{
|
||
return $this->select('
|
||
deals.id,
|
||
deals.title,
|
||
deals.amount,
|
||
deals.currency,
|
||
deals.expected_close_date,
|
||
deals.created_at,
|
||
deals.deleted_at,
|
||
ds.name as stage_name,
|
||
ds.color as stage_color,
|
||
ds.type as stage_type,
|
||
ds.probability as stage_probability,
|
||
c.name as contact_name,
|
||
c.email as contact_email,
|
||
oc.name as client_name,
|
||
au.name as assigned_user_name,
|
||
au.email as assigned_user_email,
|
||
cb.name as created_by_name
|
||
')
|
||
->join('deal_stages ds', 'deals.stage_id = ds.id', 'left')
|
||
->join('contacts c', 'deals.contact_id = c.id', 'left')
|
||
->join('organizations_clients oc', 'deals.company_id = oc.id', 'left')
|
||
->join('users au', 'deals.assigned_user_id = au.id', 'left')
|
||
->join('users cb', 'deals.created_by = cb.id', 'left')
|
||
->where('deals.organization_id', $organizationId)
|
||
->orderBy('deals.created_at', 'DESC')
|
||
->findAll();
|
||
}
|
||
|
||
/**
|
||
* Получить сделки организации с фильтрами
|
||
*/
|
||
public function getDealsByOrganization(
|
||
int $organizationId,
|
||
?int $stageId = null,
|
||
?int $assignedUserId = null,
|
||
?string $search = null,
|
||
?string $dateFrom = null,
|
||
?string $dateTo = null
|
||
): array {
|
||
$builder = $this->where('organization_id', $organizationId);
|
||
|
||
if ($stageId) {
|
||
$builder->where('stage_id', $stageId);
|
||
}
|
||
|
||
if ($assignedUserId) {
|
||
$builder->where('assigned_user_id', $assignedUserId);
|
||
}
|
||
|
||
if ($search) {
|
||
$builder->groupStart()
|
||
->like('title', $search)
|
||
->orLike('description', $search)
|
||
->groupEnd();
|
||
}
|
||
|
||
if ($dateFrom) {
|
||
$builder->where('expected_close_date >=', $dateFrom);
|
||
}
|
||
|
||
if ($dateTo) {
|
||
$builder->where('expected_close_date <=', $dateTo);
|
||
}
|
||
|
||
return $builder->orderBy('created_at', 'DESC')->findAll();
|
||
}
|
||
|
||
/**
|
||
* Получить сделки, сгруппированные по этапам (для Канбана)
|
||
*/
|
||
public function getDealsGroupedByStage(int $organizationId): array
|
||
{
|
||
$deals = $this->select('deals.*, ds.name as stage_name, ds.color as stage_color, ds.type as stage_type, au.name as assigned_user_name')
|
||
->join('deal_stages ds', 'deals.stage_id = ds.id', 'left')
|
||
->join('users au', 'deals.assigned_user_id = au.id', 'left')
|
||
->join('contacts c', 'deals.contact_id = c.id', 'left')
|
||
->join('organizations_clients oc', 'deals.company_id = oc.id', 'left')
|
||
->where('deals.organization_id', $organizationId)
|
||
->where('deals.deleted_at', null)
|
||
->orderBy('ds.order_index', 'ASC')
|
||
->orderBy('deals.created_at', 'DESC')
|
||
->findAll();
|
||
|
||
$grouped = [];
|
||
foreach ($deals as $deal) {
|
||
$stageId = $deal['stage_id'] ?? 0;
|
||
if (!isset($grouped[$stageId])) {
|
||
$grouped[$stageId] = [
|
||
'stage_name' => $deal['stage_name'] ?? 'Без этапа',
|
||
'stage_color' => $deal['stage_color'] ?? '#6B7280',
|
||
'stage_type' => $deal['stage_type'] ?? 'progress',
|
||
'deals' => [],
|
||
'total_amount' => 0,
|
||
];
|
||
}
|
||
$grouped[$stageId]['deals'][] = $deal;
|
||
$grouped[$stageId]['total_amount'] += (float) $deal['amount'];
|
||
}
|
||
|
||
return $grouped;
|
||
}
|
||
|
||
/**
|
||
* Получить сделки для календаря (по дате закрытия)
|
||
*/
|
||
public function getDealsForCalendar(int $organizationId, string $month): array
|
||
{
|
||
return $this->select('deals.*, ds.color as stage_color, ds.name as stage_name')
|
||
->join('deal_stages ds', 'deals.stage_id = ds.id', 'left')
|
||
->where('deals.organization_id', $organizationId)
|
||
->where('deals.deleted_at', null)
|
||
->where('deals.expected_close_date >=', date('Y-m-01', strtotime($month)))
|
||
->where('deals.expected_close_date <=', date('Y-m-t', strtotime($month)))
|
||
->orderBy('expected_close_date', 'ASC')
|
||
->findAll();
|
||
}
|
||
|
||
/**
|
||
* Получить статистику по сделкам
|
||
*/
|
||
public function getDealStats(int $organizationId): array
|
||
{
|
||
$openDeals = $this->select('COUNT(*) as count, COALESCE(SUM(amount), 0) as total')
|
||
->where('organization_id', $organizationId)
|
||
->where('deleted_at', null)
|
||
->whereIn('stage_id', function($builder) use ($organizationId) {
|
||
return $builder->select('id')
|
||
->from('deal_stages')
|
||
->where('organization_id', $organizationId)
|
||
->whereIn('type', ['progress']);
|
||
})
|
||
->get()
|
||
->getRow();
|
||
|
||
$wonDeals = $this->select('COUNT(*) as count, COALESCE(SUM(amount), 0) as total')
|
||
->where('organization_id', $organizationId)
|
||
->where('deleted_at', null)
|
||
->whereIn('stage_id', function($builder) use ($organizationId) {
|
||
return $builder->select('id')
|
||
->from('deal_stages')
|
||
->where('organization_id', $organizationId)
|
||
->where('type', 'won');
|
||
})
|
||
->get()
|
||
->getRow();
|
||
|
||
$lostDeals = $this->select('COUNT(*) as count')
|
||
->where('organization_id', $organizationId)
|
||
->where('deleted_at', null)
|
||
->whereIn('stage_id', function($builder) use ($organizationId) {
|
||
return $builder->select('id')
|
||
->from('deal_stages')
|
||
->where('organization_id', $organizationId)
|
||
->where('type', 'lost');
|
||
})
|
||
->get()
|
||
->getRow();
|
||
|
||
return [
|
||
'open_count' => $openDeals->count,
|
||
'open_total' => $openDeals->total,
|
||
'won_count' => $wonDeals->count,
|
||
'won_total' => $wonDeals->total,
|
||
'lost_count' => $lostDeals->count,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Получить сделку по ID с JOIN-ами
|
||
*/
|
||
public function getWithJoins(int $dealId, int $organizationId): ?array
|
||
{
|
||
return $this->select('
|
||
deals.*,
|
||
ds.name as stage_name,
|
||
ds.color as stage_color,
|
||
ds.type as stage_type,
|
||
ds.probability as stage_probability,
|
||
c.name as contact_name,
|
||
c.email as contact_email,
|
||
oc.name as client_name,
|
||
au.name as assigned_user_name,
|
||
au.email as assigned_user_email
|
||
')
|
||
->join('deal_stages ds', 'deals.stage_id = ds.id', 'left')
|
||
->join('contacts c', 'deals.contact_id = c.id', 'left')
|
||
->join('organizations_clients oc', 'deals.company_id = oc.id', 'left')
|
||
->join('users au', 'deals.assigned_user_id = au.id', 'left')
|
||
->where('deals.id', $dealId)
|
||
->where('deals.organization_id', $organizationId)
|
||
->first();
|
||
}
|
||
}
|