Разработка обработок заполнения табличных частей — одна из наиболее частых задач программиста 1С. На первый взгляд, передача выбранного на форме значения в запрос на сервере выглядит тривиально. Однако начинающие разработчики регулярно сталкиваются с неочевидными трудностями: от ошибок адресации реквизитов формы до некорректной работы фильтров запроса и неоптимального расходования системных ресурсов. В этой статье мы подробно разберем все эти нюансы, проанализируем типичные ошибки и выработаем профессиональный подход к решению подобных задач.
Давайте подробно разберем ситуацию, когда при попытке передать параметр в запрос система выдает критическую ошибку: «Поле объекта не обнаружено (Партнер)». Проанализируем, почему это происходит.
В управляемом приложении 1С структура данных формы разделена на две принципиальные области:
Объект (например, Объект.Ссылка, Объект.Организация).Если на форме обработки вы создали реквизит Партнер на том же уровне, что и стандартный реквизит Объект, то обращаться к нему на сервере через точку от Объект нельзя. Попытка написать код Объект.Партнер приведет к аварийному завершению программы. К реквизитам формы, расположенным в корне, необходимо обращаться напрямую по их имени.
Посмотрим на пример ошибочного и правильного кода:
// ОШИБОЧНЫЙ ВАРИАНТ (вызовет ошибку "Поле объекта не обнаружено")
Запрос.УстановитьПараметр("Партнер", Объект.Партнер);
// ПРАВИЛЬНЫЙ ВАРИАНТ (прямое обращение к реквизиту формы)
Запрос.УстановитьПараметр("Партнер", Партнер);
Рассмотрим подробнее архитектуру взаимодействия клиента и сервера. В коде, представленном в начале обсуждения, процедура обработки на сервере объявлена с директивой &НаСервере:
&НаСервере
Процедура ЗаполнитКИНаСервере
// Код выполнения запроса
КонецПроцедуры
Использование директивы &НаСервере означает контекстный вызов. При каждом запуске такой процедуры платформа 1С автоматически упаковывает и передает на сервер абсолютно все данные формы — все её реквизиты, элементы управления, таблицы и служебное состояние. После выполнения процедуры весь этот объем данных (контекст) возвращается обратно на клиент. Если форма содержит много элементов или тяжелые таблицы, это приводит к существенным задержкам в работе интерфейса и перегрузке сети.
Давайте исправим эту методическую неточность. Правильным решением для простых выборок является использование внеконтекстного вызова с директивой &НаСервереБезКонтекста. В такую процедуру контекст формы не передается, а все необходимые для работы данные мы передаем явно через параметры процедуры.
Разберем по шагам, как переписать наш код по правилам оптимальной разработки:
&НаКлиенте
Процедура ЗаполнитКИ(Команда)
// Передаем значение реквизита "Партнер" на сервер в качестве параметра
ЗаполнитКИНаСервере(Партнер);
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ЗаполнитКИНаСервере(Знач ВыбранныйПартнер)
// Проверим, заполнено ли поле на форме
Если Не ЗначениеЗаполнено(ВыбранныйПартнер) Тогда
Возврат;
КонецЕсли;
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПартнерыКонтактнаяИнформация.Представление КАК Представление,
| ПартнерыКонтактнаяИнформация.Ссылка КАК Ссылка
|ИЗ
| Справочник.Партнеры.КонтактнаяИнформация КАК ПартнерыКонтактнаяИнформация
|ГДЕ
| ПартнерыКонтактнаяИнформация.Ссылка = &Партнер";
Запрос.УстановитьПараметр("Партнер", ВыбранныйПартнер);
РезультатЗапроса = Запрос.Выполнить();
// Дальнейшая обработка данных...
КонецПроцедуры
В процессе обсуждения выяснилось, что при замене параметра конкретным значением данные выбирались, а при установке отбора по партнеру результат оказывался пустым. Это классический симптом логического конфликта в условиях запроса (секция ГДЕ).
Давайте проанализируем конструкцию, которую изначально использовал автор:
И ПартнерыКонтактнаяИнформация.Представление ПОДОБНО "[+][7][9]_________"
Использование оператора ПОДОБНО с жесткой маской символов — крайне нестабильный подход при работе со строковыми полями ввода. Пользователи могут вносить номера телефонов в самых разных форматах:
+7 (999) 111-22-3389991112233+799911122337 999 111 2233Маска "[+][7][9]_________" требует, чтобы строка начиналась строго с символов «+79» и содержала ровно 9 произвольных символов после них. Любое отклонение (например, наличие скобок или пробелов) приведет к тому, что СУБД отбросит эту строку при фильтрации. Именно поэтому у автора не выбирались данные по конкретному партнеру: введенные для него номера телефонов просто не подходили под жесткий шаблон маски.
Чтобы избежать подобных проблем, в современных типовых конфигурациях 1С, использующих Библиотеку Стандартных Подсистем (БСП), в табличной части контактной информации хранятся стандартизированные поля: НомерТелефона и НомерТелефонаБезКодов. В эти поля система автоматически записывает только чистые цифры, очищенные от скобок, дефисов и пробелов. Намного надежнее выполнять поиск именно по ним:
// Более надежный вариант отбора мобильных номеров РФ в запросе
И (ПартнерыКонтактнаяИнформация.НомерТелефона ПОДОБНО "79%"
ИЛИ ПартнерыКонтактнаяИнформация.НомерТелефона ПОДОБНО "89%")
Отдельного внимания заслуживает вопрос корректного изменения контактной информации программным путем. В современных конфигурациях 1С (таких как ERP, Комплексная автоматизация, Управление торговлей, Бухгалтерия предприятия) контактная информация хранится в сложном структурированном виде.
Поле Представление служит только для показа текста пользователю на форме. Реальные же структурированные данные (страна, город, код города, номер, добавочный номер) хранятся в специальном служебном поле ЗначенияПолей в формате XML или JSON.
Важное правило: Напрямую записывать текстовую строку в поле ЗначенияПолей категорически запрещено! Это нарушает целостность данных базы, и при последующем открытии карточки партнера система либо выдаст ошибку, либо не сможет корректно распознать и отобразить номер телефона.
Для корректного преобразования обычного текста в структурированный формат и его записи следует использовать программный интерфейс БСП — функции общего модуля УправлениеКонтактнойИнформацией. Рассмотрим, как сделать это правильно:
// Определяем вид и тип контактной информации
ВидТелефона = Справочники.ВидыКонтактнойИнформации.ТелефонПартнера;
ТипКонтактнойИнформации = Перечисления.ТипыКонтактнойИнформации.Телефон;
// Шаг 1. Преобразуем строковое представление телефона в XML-структуру БСП
ОбъектXDTO = УправлениеКонтактнойИнформациейСлужебный.КонтактнаяИнформацияXDTOПоПредставлению(СтрокаТелефона, ТипКонтактнойИнформации);
СтруктурированныйXML = УправлениеКонтактнойИнформациейСлужебный.ContactInformationXDTOtoXML(ОбъектXDTO);
// Шаг 2. Записываем подготовленные структурированные данные в объект партнера
УправлениеКонтактнойИнформацией.ЗаписатьКонтактнуюИнформацию(ПартнерОбъект, СтруктурированныйXML, ВидТелефона, ТипКонтактнойИнформации);
Использование этого метода гарантирует, что номера телефонов запишутся корректно во все служебные регистры и поля, сохраняя целостность системы и обеспечивая корректную интеграцию с телефонией или механизмами отправки СМС.
Давайте посмотрим на еще один важный аспект представленного в обсуждении кода, связанный с быстродействием системы. В первом сообщении темы присутствует следующая конструкция:
Пока ВыборкаДетальныеЗаписи.Следующий Цикл
ПартнерОбъект = ВыборкаДетальныеЗаписи.Ссылка.ПолучитьОбъект;
// Здесь происходит обработка строк
КонецЦикла;
Этот код содержит критическую ошибку производительности, известную как «Получение объекта базы данных в цикле». Представим ситуацию, когда у одного партнера в базе зарегистрировано несколько телефонов. Запрос вернет нам несколько строк для одного и того же партнера. В результате метод ПолучитьОбъект() для одного и того же элемента справочника будет вызван многократно.
Каждое обращение к методу ПолучитьОбъект() инициирует полноценный запрос к СУБД для чтения всех реквизитов и табличных частей элемента, загружая оперативную память сервера и создавая избыточную нагрузку на дисковую подсистему. Если в выборке окажутся сотни или тысячи записей, обработка будет выполняться крайне медленно.
Давайте изменим логику работы. Сначала выберем из базы только уникальные (различные) ссылки на партнеров, которым требуется обновление контактной информации. Затем для каждого уникального партнера получим объект ровно один раз, обработаем всю его табличную часть в оперативной памяти и запишем изменения.
Посмотрим на оптимизированный алгоритм:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ РАЗЛИЧНЫЕ
| ПартнерыКонтактнаяИнформация.Ссылка КАК ПартнерСсылка
|ИЗ
| Справочник.Партнеры.КонтактнаяИнформация КАК ПартнерыКонтактнаяИнформация
|ГДЕ
| ПартнерыКонтактнаяИнформация.Ссылка = &Партнер";
Запрос.УстановитьПараметр("Партнер", ВыбранныйПартнер);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Получаем объект партнера строго ОДИН раз
ПартнерОбъект = Выборка.ПартнерСсылка.ПолучитьОбъект();
Модифицировано = Ложь;
// Обходим табличную часть контактной информации в памяти сервера
Для Каждого СтрокаКИ Из ПартнерОбъект.КонтактнаяИнформация Цикл
// Проверяем соответствие строки нужным условиям
Если СтрокаКИ.Вид = Справочники.ВидыКонтактнойИнформации.ТелефонПартнера Тогда
// Выполняем требуемые манипуляции с КИ
// Например, очищаем некорректные символы или обновляем представление
Модифицировано = Истина;
КонецЕсли;
КонецЦикла;
// Записываем объект только в том случае, если данные реально изменились
Если Модифицировано Тогда
Попытка
ПартнерОбъект.Записать();
Исключение
ЗаписьЖурналаРегистрации("Обновление КИ",
УровеньЖурналаРегистрации.Ошибка,,,
ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
КонецЦикла;
Этот подход позволяет сократить количество обращений к базе данных в несколько раз, сводит к минимуму риск блокировок таблиц СУБД и обеспечивает максимальную скорость работы вашей обработки даже на высоконагруженных корпоративных информационных системах.