bp/app/Modules/CRM/Controllers/ContactsController.php

401 lines
14 KiB
PHP

<?php
namespace App\Modules\CRM\Controllers;
use App\Controllers\BaseController;
use App\Modules\CRM\Models\ContactModel;
use App\Modules\Clients\Models\ClientModel;
class ContactsController extends BaseController
{
protected ContactModel $contactModel;
protected ClientModel $clientModel;
public function __construct()
{
$this->contactModel = new ContactModel();
$this->clientModel = new ClientModel();
}
/**
* Конфигурация таблицы контактов
*/
protected function getContactsTableConfig(): array
{
$organizationId = $this->requireActiveOrg();
return [
'id' => 'contacts-table',
'url' => '/crm/contacts/table',
'model' => $this->contactModel,
'columns' => [
'id' => ['label' => 'ID', 'width' => '60px'],
'name' => ['label' => 'Имя'],
'email' => ['label' => 'Email', 'width' => '180px'],
'phone' => ['label' => 'Телефон', 'width' => '140px'],
'position' => ['label' => 'Должность', 'width' => '150px'],
'customer_name' => ['label' => 'Клиент'],
'created_at' => ['label' => 'Дата', 'width' => '100px'],
],
'searchable' => ['name', 'email', 'phone', 'position', 'customer_name'],
'sortable' => ['id', 'name', 'created_at'],
'defaultSort' => 'created_at',
'order' => 'desc',
'fieldMap' => [
'customer_name' => 'customers.name',
'name' => 'contacts.name',
'email' => 'contacts.email',
'phone' => 'contacts.phone',
'position' => 'contacts.position',
'created_at' => 'contacts.created_at',
'id' => 'contacts.id',
],
'scope' => function($builder) use ($organizationId) {
$builder->from('contacts')
->select('contacts.id, contacts.name, contacts.email, contacts.phone, contacts.position, contacts.created_at, contacts.deleted_at, customers.name as customer_name')
->join('organizations_clients customers', 'customers.id = contacts.customer_id', 'left')
->where('contacts.organization_id', $organizationId)
->where('contacts.deleted_at', null);
},
'actions' => ['label' => 'Действия', 'width' => '120px'],
'actionsConfig' => [
[
'label' => '',
'url' => '/crm/contacts/{id}/edit',
'icon' => 'fa-solid fa-pen',
'class' => 'btn-outline-primary',
'title' => 'Редактировать',
],
],
'emptyMessage' => 'Контактов пока нет',
'emptyIcon' => 'fa-solid fa-users',
];
}
/**
* Список контактов
*/
public function index()
{
$config = $this->getContactsTableConfig();
$tableHtml = $this->renderTable($config);
return $this->renderTwig('@CRM/contacts/index', [
'title' => 'Контакты',
'tableHtml' => $tableHtml,
'config' => $config,
]);
}
/**
* AJAX таблица контактов
*/
public function contactsTable()
{
return parent::table($this->getContactsTableConfig(), '/crm/contacts');
}
/**
* Форма создания контакта
*/
public function create()
{
$organizationId = $this->requireActiveOrg();
$clients = $this->clientModel
->where('organization_id', $organizationId)
->findAll();
return $this->renderTwig('@CRM/contacts/form', [
'title' => 'Новый контакт',
'actionUrl' => '/crm/contacts',
'clients' => $clients,
]);
}
/**
* Сохранить новый контакт
*/
public function store()
{
$organizationId = $this->requireActiveOrg();
$data = [
'organization_id' => $organizationId,
'customer_id' => $this->request->getPost('customer_id') ?: null,
'name' => $this->request->getPost('name'),
'email' => $this->request->getPost('email') ?: null,
'phone' => $this->request->getPost('phone') ?: null,
'position' => $this->request->getPost('position') ?: null,
'is_primary' => $this->request->getPost('is_primary') ? 1 : 0,
'notes' => $this->request->getPost('notes') ?: null,
];
$this->contactModel->save($data);
$contactId = $this->contactModel->getInsertID();
if ($contactId) {
return redirect()->to('/crm/contacts')->with('success', 'Контакт успешно создан');
}
return redirect()->back()->with('error', 'Ошибка при создании контакта')->withInput();
}
/**
* Форма редактирования контакта
*/
public function edit(int $id)
{
$organizationId = $this->requireActiveOrg();
$contact = $this->contactModel->find($id);
if (!$contact || $contact->organization_id !== $organizationId) {
return redirect()->to('/crm/contacts')->with('error', 'Контакт не найден');
}
$clients = $this->clientModel
->where('organization_id', $organizationId)
->findAll();
return $this->renderTwig('@CRM/contacts/form', [
'title' => 'Редактирование контакта',
'actionUrl' => "/crm/contacts/{$id}",
'contact' => $contact,
'clients' => $clients,
]);
}
/**
* Обновить контакт
*/
public function update(int $id)
{
$organizationId = $this->requireActiveOrg();
$contact = $this->contactModel->find($id);
if (!$contact || $contact->organization_id !== $organizationId) {
return redirect()->to('/crm/contacts')->with('error', 'Контакт не найден');
}
$data = [
'customer_id' => $this->request->getPost('customer_id') ?: null,
'name' => $this->request->getPost('name'),
'email' => $this->request->getPost('email') ?: null,
'phone' => $this->request->getPost('phone') ?: null,
'position' => $this->request->getPost('position') ?: null,
'is_primary' => $this->request->getPost('is_primary') ? 1 : 0,
'notes' => $this->request->getPost('notes') ?: null,
];
$this->contactModel->update($id, $data);
return redirect()->to('/crm/contacts')->with('success', 'Контакт обновлён');
}
/**
* Удалить контакт
*/
public function destroy(int $id)
{
$organizationId = $this->requireActiveOrg();
$contact = $this->contactModel->find($id);
if (!$contact || $contact->organization_id !== $organizationId) {
return redirect()->to('/crm/contacts')->with('error', 'Контакт не найден');
}
$this->contactModel->delete($id);
return redirect()->to('/crm/contacts')->with('success', 'Контакт удалён');
}
// =========================================================================
// AJAX API для inline-редактирования в модуле Clients
// =========================================================================
/**
* Получить список контактов клиента (AJAX)
* GET /crm/contacts/list/{clientId}
*/
public function ajaxList(int $clientId)
{
$organizationId = $this->requireActiveOrg();
// Проверяем что клиент принадлежит организации
$client = $this->clientModel->forCurrentOrg()->find($clientId);
if (!$client) {
return $this->response->setJSON([
'success' => false,
'message' => 'Клиент не найден',
]);
}
$contacts = $this->contactModel
->where('organization_id', $organizationId)
->where('customer_id', $clientId)
->orderBy('name', 'ASC')
->findAll();
$items = array_map(function ($contact) {
return [
'id' => $contact->id,
'name' => $contact->name,
'email' => $contact->email,
'phone' => $contact->phone,
'position' => $contact->position,
];
}, $contacts);
return $this->response->setJSON([
'success' => true,
'items' => $items,
'total' => count($items),
]);
}
/**
* Создать контакт (AJAX)
* POST /crm/contacts/store
*/
public function ajaxStore()
{
$organizationId = $this->requireActiveOrg();
$customerId = $this->request->getPost('customer_id');
// Проверяем клиента если указан
if ($customerId) {
$client = $this->clientModel->forCurrentOrg()->find($customerId);
if (!$client) {
return $this->response->setJSON([
'success' => false,
'message' => 'Клиент не найден',
])->setStatusCode(422);
}
}
$data = [
'organization_id' => $organizationId,
'customer_id' => $customerId ?: null,
'name' => $this->request->getPost('name'),
'email' => $this->request->getPost('email') ?: null,
'phone' => $this->request->getPost('phone') ?: null,
'position' => $this->request->getPost('position') ?: null,
];
// Валидация
if (empty($data['name'])) {
return $this->response->setJSON([
'success' => false,
'message' => 'Имя контакта обязательно',
'errors' => ['name' => 'Имя контакта обязательно'],
])->setStatusCode(422);
}
$contactId = $this->contactModel->insert($data);
if (!$contactId) {
return $this->response->setJSON([
'success' => false,
'message' => 'Ошибка при создании контакта',
'errors' => $this->contactModel->errors(),
])->setStatusCode(422);
}
return $this->response->setJSON([
'success' => true,
'message' => 'Контакт создан',
'item' => [
'id' => $contactId,
'name' => $data['name'],
'email' => $data['email'],
'phone' => $data['phone'],
'position' => $data['position'],
],
]);
}
/**
* Обновить контакт (AJAX)
* POST /crm/contacts/update/{id}
*/
public function ajaxUpdate(int $id)
{
$organizationId = $this->requireActiveOrg();
$contact = $this->contactModel->find($id);
if (!$contact || $contact->organization_id !== $organizationId) {
return $this->response->setJSON([
'success' => false,
'message' => 'Контакт не найден',
])->setStatusCode(404);
}
$data = [
'name' => $this->request->getPost('name'),
'email' => $this->request->getPost('email') ?: null,
'phone' => $this->request->getPost('phone') ?: null,
'position' => $this->request->getPost('position') ?: null,
];
// Валидация
if (empty($data['name'])) {
return $this->response->setJSON([
'success' => false,
'message' => 'Имя контакта обязательно',
'errors' => ['name' => 'Имя контакта обязательно'],
])->setStatusCode(422);
}
$result = $this->contactModel->update($id, $data);
if (!$result) {
return $this->response->setJSON([
'success' => false,
'message' => 'Ошибка при обновлении контакта',
'errors' => $this->contactModel->errors(),
])->setStatusCode(422);
}
return $this->response->setJSON([
'success' => true,
'message' => 'Контакт обновлён',
'item' => [
'id' => $id,
'name' => $data['name'],
'email' => $data['email'],
'phone' => $data['phone'],
'position' => $data['position'],
],
]);
}
/**
* Удалить контакт (AJAX)
* POST /crm/contacts/delete/{id}
*/
public function ajaxDelete(int $id)
{
$organizationId = $this->requireActiveOrg();
$contact = $this->contactModel->find($id);
if (!$contact || $contact->organization_id !== $organizationId) {
return $this->response->setJSON([
'success' => false,
'message' => 'Контакт не найден',
])->setStatusCode(404);
}
$this->contactModel->delete($id);
return $this->response->setJSON([
'success' => true,
'message' => 'Контакт удалён',
]);
}
}