
Предположим стоит задача организации телемаркетинга. Телемаркетинг это прежде всего общение сотрудников с клиентами по телефону. Наша задача организовать и формализовать это общение с привязкой к клиентам, заявкам или чему либо еще в зависимости от конкретной ситуации. CRM - система.
Таким образом у нас есть: сотрудник, клиент, заявка, общение. Организуем эти части в единую систему.Что есть клиент? Это набор личных данных - уникальных в совокупности для каждого клиента. И контактная информация - телефон в простейшем случае.
Для простоты будем рассматривать только телефонные контакты, хотя дело в реальности несколько сложнее, тем не менее можно сейчас пренебречь почтой и sms в силу невеликой сложности последних.
Формализуем задачу.
Клиент - личная информация + набор произвольного количества телефонов (без повторений для каждого клиента).
Телефон: номер + описание. В дальнейшем можно добавить флаг о том, является ли телефон мобильным.
Сотрудник: в рамках нашей задачи это числовой идентификатор сотрудника call-центра.
Общение: один или несколько звонков одному клиенту по одной заявке с тем или иным результатом одним из сотрудников телемаркетинга.
Простейший подход к организации записи звонков - это рассматривать каждый звонок как отдельное событие в независимости от результата. Каждый новый звонок - это новое общение. Такой подход имеет право на существование но становится неудобным в случае, если телефонов у клиента больше одного.
Давайте попробуем представить общение сотрудника с клиентом в виде некоторой "сессии общения". Эта сессия содержит ряд атомарных звонков. Рабочая ситуация, на которую опирается данный подход, типична для штатной работы телемаркетинга. Сотрудник делает звонок на один из телефонов клиента - не дозванивается, звонит на следующий - заблокирован, и наконец, звонит на очередной номер клиента - происходит общение.
Схема организации таблиц и связей.Сложностью работы с такой схемой становится выборка из базы списка заявок, организованных в порядке оцередности звонков. Например, в зависимости от времени назначенного общения. Для облегчения этой задачи давайте договоримся об организации отметок статуса звонков. Нормальный подход к таким задачам - это поле числового типа и таблица-словарь, где расшифровывается каждый возможный числовой код. Давайте для определенности организовывать статусы звонка для последующей выборки от малого к большему. Т.е. минимальные статусы для нас будут наиболее важными в обычной ситуации - назначенные звонки.
Для организации такой выборки можно использовать следующий подход. Выбрать всю нужную информацию, которая должная быть для каждого элемента списка - это делается через JOIN всех нужных таблиц с нужной информацией. Для получения времени звонка используем также JOIN, но не отдельной таблицы, а результата подзапроса. Этот подзапрос и будет получать из всех атомарных звонков в рамках одной сессии те, которые удовлеворяют, например максиминному условию: максимальное время и минимальный статус. В этом подзапросе выберем только значения минимума и максимума и id сессии общения.
Простым способом мы и не сможем правильно получить больше - т.к. при выборе большего количества столбцов нам потребуется большее количество условий в группировке GROUP BY (а эта группировка необходима, т.к. мы используем агрегатные функции для максиминного условия) - а это значит, что возможна ситуация при которой строки в результате содержат дубли по некоторым столбцам в силу того, что вся уникальность строки обеспечивается уникальностью дополнительных столбцов, которые мы ввели в GROUP BY.
Примерный запрос может быть таковым:
SELECT orders.id, profiles.id AS client_id,
orders.status AS status, calls.estimated AS call_time, calls.executed AS call_time_executed,
users.login AS specialist,
calls_data.call_status
FROM orders
LEFT JOIN profiles ON profiles.id = orders.profile
LEFT JOIN ( SELECT MIN(call_items.status) AS call_status, MAX(calls.id) AS call_id, calls.order_id FROM calls
LEFT JOIN call_items ON call_items.CALL_ID=calls.id
GROUP BY calls.order_id )
calls_data ON calls_data.order_id = orders2.id
LEFT JOIN calls ON calls.id = calls_data.call_id
LEFT JOIN users ON users.id = orders.specialist
WHERE <условия> ORDER BY calls_data.call_status asc, calls.estimated asc
В результате такого подхода мы получили список, отсортированный таким образом, что сотрудник телемаркетинга видит в начале те заявки, для корторых самое подходящее время для назначенного звонка.
Другой сложностью в "сессионном" подходе может быть организация редактирования сессии общения. Решить эту задачу можно при помощи разделения имен input-элементов для существующийх звонков ( например у них name="calls[]" ) и тех, которые мы добавляем в сессию.
Само добавление элементов в форму порльзовательского интерфейса удобнее производить через js и dhtml. Ajax в этом месте излишний. Добавление атомарных звонков на сервере можно делать в простом цикле sql-запросов в рамках одной транзакции.
$new_call_items = array_combine( $_POST['new_phones'], $_POST['new_status'] );
$db->transact();
foreach( $new_call_items as $phone_id => $status ) {
...
$db->process( $sql );
...
}
$db->commit();