Какие проблемы решает кастомный веб-интерфейс CRM?
Представьте: у вас 50 менеджеров по продажам, каждый ведёт клиентов в Excel или своей голове. Сделки теряются, лиды не обрабатываются, отчёты собираются неделями. Веб-интерфейс кастомной CRM решает это — но только если он спроектирован под ваши бизнес-процессы, а не под шаблон.
Наша команда за 6 лет реализовала более 15 CRM-систем для B2B-компаний, поэтому мы знаем типовые ошибки и как их избежать. Мы разрабатываем веб-интерфейсы CRM на React и Laravel с нуля: от прототипа до продакшена. В статье разберём типовую архитектуру, ключевые сущности и подводные камни.
Основная задача — не накодить кнопок, а выстроить логику: кто видит какие сделки, как движется воронка, какие уведомления критичны. Без этого даже красивый интерфейс бесполезен. Проект начинается с аудита текущих процессов — за 2-3 дня мы собираем требования и предлагаем архитектуру.
Один из наших клиентов — дистрибьютор электроники — терял до 15% лидов из-за того, что менеджеры не видели историю звонков. Решение: интерфейс с автоматическим логированием всех активностей. Через месяц после внедрения отсев снизился до 3%.
Почему кастомная CRM выгоднее готовых решений?
Готовые CRM (amoCRM, Bitrix24) предлагают стандартный набор функций, но их адаптация под специфику бизнеса часто требует дорогих доработок и ограничивает гибкость. Кастомная разработка на React и Laravel позволяет:
- Оптимизировать воронку под конкретные этапы продаж: например, для дистрибьютора — стадии "Запрос КП", "Согласование цены", "Отгрузка".
- Интегрировать любое внешнее ПО: телефонию, 1С, мессенджеры, email-рассылки.
- Масштабировать функционал без оглядки на лицензионные ограничения.
При этом стоимость владения кастомной CRM уже через год оказывается ниже, чем ежемесячная плата за коробочное решение с аналогичным набором интеграций. По нашим данным, компании экономят до 40% на лицензиях при переходе на собственную разработку.
Типовой набор сущностей и их реализация
В кастомной CRM выделяют пять основных сущностей. Для каждой мы реализуем отдельный модуль с CRUD, фильтрацией и связями.
| Сущность | Назначение | Пример полей |
|---|---|---|
| Контакты | Физические лица с историей взаимодействий | name, email, phone, source |
| Компании | Юридические лица, к которым привязаны контакты | name, inn, address |
| Сделки | Потенциальные и активные продажи со статусами | amount, stage_id, responsible_id |
| Задачи | Поручения с дедлайнами, привязанные к сделкам/контактам | title, due_date, assignee |
| Активности | Лог коммуникаций (звонки, письма, встречи) | type, body, happened_at |
Кастомная разработка на React и Laravel в 2-3 раза быстрее адаптируется к изменениям бизнеса, чем готовые CRM-системы. Например, добавление нового поля или этапа воронки занимает часы, а не дни.
Как мы проектируем CRM под ваш бизнес?
Процесс работы разбит на шесть этапов:
- Аналитика — интервью с ключевыми пользователями, выявление сценариев и болей. (2-3 дня)
- Прототипирование — интерактивные макеты в Figma с утверждением заказчиком. (3-5 дней)
- Разработка бэкенда — Laravel (PHP 8.3+) с RESTful API, PostgreSQL, Redis для кеша и очередей.
- Разработка фронтенда — React 18 + TypeScript, TanStack Table для таблиц, @dnd-kit для воронки.
- Интеграции — телефония (Asterisk, Miko), email (SMTP/API), мессенджеры (Telegram, WhatsApp).
- Тестирование и деплой — CI/CD, нагрузочное тестирование, мониторинг.
Для авторизации используем Laravel Policies — стандартный механизм, который позволяет гибко разграничивать доступ: менеджер видит только свои сделки, руководитель — все в своём отделе.
Архитектура базы данных и API
База данных строится на PostgreSQL с нормализованной схемой. Пример основных таблиц:
CREATE TABLE contacts ( id BIGSERIAL PRIMARY KEY, company_id BIGINT REFERENCES companies(id), name VARCHAR(255) NOT NULL, email VARCHAR(255), phone VARCHAR(50), source VARCHAR(64), responsible_id BIGINT REFERENCES users(id), created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE TABLE deals ( id BIGSERIAL PRIMARY KEY, contact_id BIGINT REFERENCES contacts(id), company_id BIGINT REFERENCES companies(id), title VARCHAR(255) NOT NULL, amount DECIMAL(14,2), currency CHAR(3) DEFAULT 'RUB', stage_id BIGINT REFERENCES pipeline_stages(id), responsible_id BIGINT REFERENCES users(id), closed_at DATE, created_at TIMESTAMPTZ DEFAULT NOW() ); API строится по RESTful-принципам. Пример ресурса сделок:
GET /api/deals?stage_id=2&responsible_id=5 POST /api/deals PATCH /api/deals/{id} DELETE /api/deals/{id} POST /api/deals/{id}/move # смена стадии { "id": 1042, "stage_id": 4, "stage": { "id": 4, "name": "Переговоры" }, "updated_at": "2024-01-15T14:22:00Z", "activity": { "id": 3891, "type": "stage_change", "body": "Стадия изменена: Квалификация → Переговоры", "user_id": 12 } } Воронка с drag-and-drop
Воронка продаж визуализируется как Kanban-доска. Используем библиотеку @dnd-kit для перетаскивания карточек между колонками. Optimistic update — ключевой приём: карточка мгновенно меняет позицию без ожидания ответа сервера. При ошибке откатываем.
import { DndContext, DragEndEvent, closestCenter } from '@dnd-kit/core'; import { SortableContext } from '@dnd-kit/sortable'; const Pipeline: React.FC<{ stages: Stage[]; deals: Deal[] }> = ({ stages, deals }) => { const moveDeal = useMutation({ mutationFn: ({ dealId, stageId }) => api.patch(`/deals/${dealId}/move`, { stage_id: stageId }), onMutate: async ({ dealId, stageId }) => { await queryClient.cancelQueries({ queryKey: ['deals'] }); const prev = queryClient.getQueryData(['deals']); queryClient.setQueryData(['deals'], (old: Deal[]) => old.map(d => d.id === dealId ? { ...d, stage_id: stageId } : d) ); return { prev }; }, onError: (_, __, context) => queryClient.setQueryData(['deals'], context?.prev), }); return ( <DndContext collisionDetection={closestCenter} onDragEnd={moveDeal.mutate}> <div className="flex gap-4 overflow-x-auto p-4"> {stages.map(stage => ( <KanbanColumn key={stage.id} stage={stage} deals={deals.filter(d => d.stage_id === stage.id)} /> ))} </div> </DndContext> ); }; Управление правами доступа
CRM требует гранулярных прав. В Laravel это реализуется через Policy-классы и глобальные скоупы модели.
class DealPolicy { public function view(User $user, Deal $deal): bool { if ($user->hasRole('admin')) return true; if ($user->hasRole('team_lead')) { return $deal->responsible->team_id === $user->team_id; } return $deal->responsible_id === $user->id; } } class Deal extends Model { public function scopeVisibleTo(Builder $query, User $user): Builder { if ($user->hasRole('admin')) return $query; if ($user->hasRole('team_lead')) { return $query->whereHas('responsible', fn($q) => $q->where('team_id', $user->team_id) ); } return $query->where('responsible_id', $user->id); } } Что входит в разработку кастомной CRM
В стоимость проекта включены:
- Техническое задание и интерактивный прототип интерфейса
- Разработка бэкенда (Laravel + PostgreSQL + Redis)
- Разработка фронтенда (React 18 + TypeScript)
- Интеграция с телефонией (Asterisk, Miko), email (SMTP/API), мессенджерами
- Документация API и пользовательская инструкция
- Обучение сотрудников (2-3 часа)
- Гарантийная поддержка 3 месяца после запуска
Дополнительно можем подключить мобильную версию (PWA или React Native) и настроить BI-аналитику.
Сроки и стоимость реализации
MVP с воронкой, карточками контактов/сделок, задачами и базовой аналитикой занимает 4–6 недель. Добавление интеграций (телефония, email), расширенных отчётов и ролей — ещё 3–4 недели. Мобильная версия — дополнительно 3–4 недели.
Стоимость рассчитывается индивидуально после анализа ваших бизнес-процессов. Свяжитесь с нами, чтобы обсудить проект и получить предварительную оценку. Закажите демо нашей CRM-архитектуры на ваших данных — это займёт не больше часа.







