В типовых конфигурациях 1С, таких как 1С:CRM Управление предприятием 2, механизм триггеров позволяет автоматизировать многие бизнес-процессы. Однако иногда стандартная логика не полностью соответствует нашим потребностям. Одной из таких распространенных ситуаций является автоматическое создание "Интересов" на основании "Заказов клиента". Пользователи часто сталкиваются с тем, что вместо создания нового, уникального "Интереса" для каждого нового заказа, система группирует несколько заказов в один уже существующий "Интерес". Эта проблема может быть особенно сложной для начинающих разработчиков, которые только начинают осваивать платформу 1С и специфику работы с CRM-системами.
В этой статье мы подробно рассмотрим, почему возникает такая группировка, как можно доработать или создать внешнюю обработку для решения этой задачи, и какие шаги потребуется предпринять новичку, чтобы справиться с этой непростой, но вполне выполнимой задачей.
Прежде чем перейти к решению, давайте разберем, что представляют собой триггеры в контексте 1С:CRM и почему они могут приводить к нежелательной группировке "Интересов".
Триггеры в 1С:CRM – это мощный инструмент для автоматизации бизнес-процессов. Они представляют собой набор правил, которые автоматически запускаются при наступлении определенных событий. Эти события могут быть самыми разнообразными: создание нового документа "Заказ клиента", изменение статуса "Интереса", удаление элемента справочника и так далее. Задача триггера – выполнить заранее определенное действие в ответ на это событие. Например, создать задачу ответственному менеджеру, отправить уведомление или, как в нашем случае, создать новый "Интерес" или связать заказ с существующим.
Почему "Интересы" группируются? Если новые заказы клиентов группируются в один существующий "Интерес" вместо создания отдельных новых, это обычно указывает на то, что логика триггера настроена таким образом, что она пытается найти и привязаться к уже существующему интересу. Вероятно, система использует какой-либо общий критерий для поиска, например, существующего контрагента, контактное лицо, или даже схожее описание предмета заказа. Если такой "Интерес" найден, новый "Заказ клиента" просто привязывается к нему, а не создает новый элемент справочника "Интересы".
Стандартная логика работы конфигураций 1С (особенно типовых) часто бывает очень гибкой, но не всегда подходит для всех уникальных бизнес-процессов. Кроме того, прямое изменение кода в типовой конфигурации 1С может быть проблематичным. Как было упомянуто в сообщении 9, попытки изменить код в конфигурации часто блокируют возможность ее дальнейшего обновления, что может привести к серьезным проблемам при переходе на новые версии платформы или конфигурации. Именно поэтому для внесения уникальных изменений мы рассмотрим другой подход – использование внешних обработок.
Внешние обработки являются предпочтительным решением для расширения функционала типовых конфигураций 1С без их прямого изменения. Давайте посмотрим, почему это так и как это поможет нам в решении нашей задачи.
Внешняя обработка – это отдельный файл (обычно с расширением .epf), который содержит программный код и пользовательские формы. Главное преимущество внешних обработок заключается в том, что они позволяют нам добавлять или модифицировать логику работы системы, не затрагивая при этом исходный код типовой конфигурации. Это критически важно для сохранения возможности легкого обновления конфигурации в будущем. Если мы внесем изменения непосредственно в конфигурацию, то каждое новое обновление от фирмы "1С" может привести к потере наших доработок или потребует трудоемкого процесса их повторного внесения.
В нашем случае, внешняя обработка может быть настроена на запуск по расписанию (регламентное задание) или вызываться непосредственно из кода существующего триггера (если это позволяет механизм триггеров 1С:CRM). Ее задача будет заключаться в том, чтобы взять информацию о новом "Заказе клиента" и гарантированно создать для него новый, отдельный "Интерес", а не пытаться найти и привязаться к уже существующему.
В сообщении 11 пользователь упоминает, что нашел внешнюю обработку с названием "ПривязкаЗаказовКИнтересам". Это очень важная зацепка, поскольку она указывает на то, что в системе уже есть механизм, отвечающий за связь заказов с интересами, и он, вероятно, реализован через внешнюю обработку. Доработка существующего механизма всегда проще, чем создание нового "с нуля".
Давайте рассмотрим приведенный в сообщении 11 листинг кода:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Если ЭтаФорма.Параметры <> Неопределено И ЭтаФорма.Параметры.Свойство("Настройки") Тогда
ЗаполнитьЗначенияСвойств(Объект, ЭтаФорма.Параметры.Настройки);
КонецЕсли;
Элементы.НастройкаОбменаССайтом.Видимость = ЗначениеЗаполнено(Объект.НастройкаОбменаССайтом);
КонецПроцедуры
&НаКлиенте
Процедура ЗакрытьССохранением(Команда)
Если Не ПроверитьЗаполнение Тогда
Возврат;
КонецЕсли;
ПредставлениеНастроекДействия = "Настройка обмена: " + Объект.НастройкаОбменаССайтом;
ПредставлениеНастроекДействия = ПредставлениеНастроекДействия + Символы.ПС + "Состояние: " + Объект.СостояниеНовогоИнтереса;
ПредставлениеНастроекДействия = ПредставлениеНастроекДействия + Символы.ПС + "Адресовать ответственному: " + Объект.АдресоватьОтветственному;
ПредставлениеНастроекДействия = ПредставлениеНастроекДействия + Символы.ПС + "Ответственный: " + Объект.Ответственный;
ПараметрыОбработки = Новый Структура;
ПараметрыОбработки.Вставить("НастройкаОбменаССайтом", Объект.НастройкаОбменаССайтом);
ПараметрыОбработки.Вставить("СостояниеНовогоИнтереса", Объект.СостояниеНовогоИнтереса);
ПараметрыОбработки.Вставить("АдресоватьОтветственному", Объект.АдресоватьОтветственному);
ПараметрыОбработки.Вставить("Ответственный", Объект.Ответственный);
ПараметрыОбработки.Вставить("ПредставлениеНастроекДействия", ПредставлениеНастроекДействия);
ЭтаФорма.Закрыть(ПараметрыОбработки);
КонецПроцедуры
&НаКлиенте
Процедура Отмена(Команда)
Закрыть;
КонецПроцедуры
Мы видим, что этот код содержит процедуры ПриСозданииНаСервере, ЗакрытьССохранением и Отмена. Эти процедуры характерны для формы внешней обработки, а не для ее основного модуля, где должна находиться логика обработки данных. Фактически, этот код отвечает за:
ПриСозданииНаСервере).НастройкаОбменаССайтом, СостояниеНовогоИнтереса, Ответственный) при закрытии формы (ЗакрытьССохранением).Отмена).Этот фрагмент кода не содержит логики, которая непосредственно создает или привязывает "Интересы" к "Заказам клиента". Он лишь предоставляет интерфейс для настройки того, как эта привязка должна происходить. Основная логика, скорее всего, находится в другом модуле этой же внешней обработки (например, в ее основном модуле или в модуле объекта), или вызывается триггером, который использует эти настройки.
Таким образом, наша задача заключается в том, чтобы либо найти этот основной модуль и изменить его, либо, если его нет или он слишком сложен для модификации, создать новую процедуру в этой же внешней обработке (или новой) и заставить триггер 1С:CRM вызывать именно ее.
Для того чтобы система всегда создавала новый "Интерес" для каждого нового "Заказа клиента", нам потребуется реализовать следующую логику:
Справочники.Интересы. Это гарантирует, что каждый новый заказ получит свой уникальный интерес.Наименование (можно сгенерировать из номера и даты заказа).Дата.Клиент.КонтактноеЛицо.Ответственный (можно взять из заказа, или из настроек обработки, как в примере из сообщения 11).Состояние (статус нового интереса, также можно взять из настроек обработки).Содержание (описание интереса, можно указать, что он создан по заказу).Предположим, что мы создаем процедуру, которая будет вызываться либо из триггера, либо из регламентного задания. Эта процедура будет получать ссылку на "Заказ клиента" как входной параметр. Вам потребуется интегрировать этот код в вашу внешнюю обработку (например, в модуль объекта или основной модуль внешней обработки), а затем настроить вызов этой процедуры.
// Процедура, которая будет вызываться при обработке нового заказа
// Параметр "ЗаказКлиентаСсылка" - это ссылка на документ "Заказ клиента",
// который был только что создан или изменен.
Процедура ОбработатьНовыйЗаказИСоздатьИнтерес(ЗаказКлиентаСсылка) Экспорт
// Шаг 1: Получаем объект заказа для доступа к его данным.
// Это позволяет нам читать все реквизиты заказа.
Попытка
ЗаказКлиентаОбъект = ЗаказКлиентаСсылка.ПолучитьОбъект();
Исключение
// Обрабатываем ситуацию, если по какой-то причине объект не найден
Сообщить("Ошибка получения объекта заказа по ссылке: " + ЗаказКлиентаСсылка + ". " + ОписаниеОшибки());
Возврат;
КонецПопытки;
// Проверяем, что объект заказа успешно получен
Если ЗаказКлиентаОбъект = Неопределено Тогда
Сообщить("Объект заказа " + ЗаказКлиентаСсылка + " не был получен.");
Возврат;
КонецЕсли;
// Шаг 2: Создаем новый элемент справочника "Интересы".
// Этот метод гарантирует создание нового, пустого элемента.
НовыйИнтерес = Справочники.Интересы.СоздатьЭлемент();
// Шаг 3: Заполняем основные реквизиты нового интереса из данных заказа клиента.
// Наименование интереса делаем информативным.
НовыйИнтерес.Наименование = "Интерес по заказу №" + ЗаказКлиентаОбъект.Номер + " от " + Формат(ЗаказКлиентаОбъект.Дата, "ДФ=dd.MM.yyyy");
НовыйИнтерес.Дата = ТекущаяДата(); // Дата создания интереса
НовыйИнтерес.Клиент = ЗаказКлиентаОбъект.Клиент; // Клиент из заказа
НовыйИнтерес.КонтактноеЛицо = ЗаказКлиентаОбъект.КонтактноеЛицо; // Контактное лицо из заказа
// Устанавливаем ответственного. Можно взять из заказа или из настроек обработки.
// Если в вашей внешней обработке есть форма настроек, где пользователь может указать Ответственного
// (как это видно в сообщении 11: Объект.Ответственный), то используем его.
// В данном примере мы предполагаем, что настройки доступны через некий объект "НастройкиОбработки",
// который инициализируется при запуске обработки или является ее реквизитом.
// Для простоты здесь берем ответственного из заказа.
НовыйИнтерес.Ответственный = ЗаказКлиентаОбъект.Ответственный;
// Содержание интереса может быть произвольным или формироваться по шаблону.
НовыйИнтерес.Содержание = "Сформирован автоматически по заказу клиента: "
+ ЗаказКлиентаОбъект.Номер + " от "
+ Формат(ЗаказКлиентаОбъект.Дата, "ДФ=dd.MM.yyyy")
+ ". Сумма: " + ЗаказКлиентаОбъект.СуммаДокумента + " " + ЗаказКлиентаОбъект.ВалютаДокумента;
// Устанавливаем статус нового интереса (состояние).
// Важно, чтобы это состояние соответствовало справочнику "СостоянияИнтересов" или другому механизму в вашей конфигурации.
// В примере из сообщения 11 есть "СостояниеНовогоИнтереса". Если оно доступно, используем его.
// Если нет, ищем по наименованию или устанавливаем предопределенное значение.
// Предположим, что переменная "НастройкиОбработки" содержит значения, сохраненные из формы настроек.
// Если у вас есть доступ к этим настройкам (например, через глобальные параметры или реквизиты объекта обработки)
// то используйте их. Иначе, нужно будет реализовать получение настроек.
// Например, можно сделать так:
// ТекущиеНастройки = ПолучитьНастройкиОбработки(); // Ваша функция для получения сохраненных настроек
// Если ЗначениеЗаполнено(ТекущиеНастройки.СостояниеНовогоИнтереса) Тогда
// НовыйИнтерес.Состояние = ТекущиеНастройки.СостояниеНовогоИнтереса;
// Иначе
// // Поиск состояния по умолчанию, например "Новый"
// НовыйИнтерес.Состояние = Справочники.СостоянияИнтересов.НайтиПоНаименованию("НовыйИнтерес"); // Пример
// Если НовыйИнтерес.Состояние = Неопределено Тогда
// НовыйИнтерес.Состояние = Справочники.СостоянияИнтересов.НайтиПоКоду("000000001"); // Пример кода
// КонецЕсли;
// КонецЕсли;
// Для данного примера будем считать, что нам нужно установить некое начальное состояние.
// Вам нужно будет адаптировать это под вашу конкретную конфигурацию 1С:CRM.
НовыйИнтерес.Состояние = Справочники.СостоянияИнтересов.НайтиПоНаименованию("Новый интерес");
Если НовыйИнтерес.Состояние = Неопределено Тогда
// Если "Новый интерес" не найден, попробуйте другое стандартное или первое попавшееся
// Или создайте новое состояние, если это разрешено и необходимо.
Сообщить("Предупреждение: Не найдено состояние интереса 'Новый интерес'. Проверьте справочник 'СостоянияИнтересов'.");
// Возможно, нужно взять первое попавшееся состояние, если нет других вариантов.
ВыборкаСостояний = Справочники.СостоянияИнтересов.Выбрать();
Если ВыборкаСостояний.Следующий() Тогда
НовыйИнтерес.Состояние = ВыборкаСостояний.Ссылка;
КонецЕсли;
КонецЕсли;
// Шаг 4: Записываем новый интерес.
Попытка
НовыйИнтерес.Записать();
// Теперь, если необходимо, связываем заказ с этим интересом.
// Это делается путем заполнения соответствующего реквизита в документе "Заказ клиента",
// если он там есть. Например, реквизит с именем "Интерес".
// Проверяем, существует ли такой реквизит в метаданных документа "Заказ клиента".
Если ЗаказКлиентаОбъект.Метаданные().Реквизиты.Найти("Интерес") <> Неопределено Тогда
ЗаказКлиентаОбъект.Интерес = НовыйИнтерес.Ссылка; // Присваиваем ссылку на созданный интерес
ЗаказКлиентаОбъект.Записать(); // Перезаписываем заказ, чтобы сохранить связь
КонецЕсли;
Сообщить("Для заказа " + ЗаказКлиентаСсылка + " успешно создан новый интерес: " + НовыйИнтерес.Ссылка);
Исключение
Сообщить("Ошибка при создании интереса для заказа " + ЗаказКлиентаСсылка + ": " + ОписаниеОшибки());
КонецПопытка;
КонецПроцедуры
Этот пример кода демонстрирует основные шаги. Вам потребуется адаптировать его под конкретные названия реквизитов и справочников вашей конфигурации 1С:CRM, а также учесть, как именно вы будете передавать настройки из формы (СостояниеНовогоИнтереса, Ответственный) в эту процедуру.
После того как мы разработали логику создания нового "Интереса", нам нужно обеспечить ее запуск в нужный момент. Есть два основных способа:
ОбработатьНовыйЗаказИСоздатьИнтерес().Для реализации регламентного задания вам понадобится изучить работу с объектами РегламентныеЗадания и ФоновыеЗадания в 1С. Также потребуется хранить дату последнего выполнения обработки, чтобы не обрабатывать одни и те же заказы повторно.
Выбор способа зависит от вашей конфигурации 1С:CRM и ваших навыков. Для новичка регламентное задание может быть немного проще в освоении, так как оно дает больше контроля над процессом и меньше зависит от специфических настроек CRM-триггеров.
Как упоминалось в сообщениях 6 и 12, эта задача не самая простая, но и не сверхсложная для новичка. Чтобы справиться с ней, мы рекомендуем следующие шаги:
Решение этой задачи, хоть и требует усилий от новичка, является отличным способом углубить свои знания в 1С-программировании и работе с конкретными конфигурациями. Постепенно, шаг за шагом, вы сможете не только решить текущую проблему, но и получить ценный опыт для будущих задач.