В процессе разработки в среде 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью автоматического заполнения реквизитов документа на основании данных, хранящихся в регистрах сведений. Типичный сценарий: при выборе даты или контрагента система должна «подтянуть» актуальные сведения (например, номер посещения, категорию цен или статус пациента). Однако использование встроенных методов менеджера регистра нередко приводит к ошибке Несоответствие типов (Параметр номер "1") или к некорректному считыванию данных. Рассмотрим детально, почему возникают эти ошибки и как реализовать задачу максимально эффективно.
Проанализируем фрагмент кода, который часто становится причиной затруднений:
&НаСервере
Процедура ЗаполнитьДанныеКифАмбулатНаСервере()
Отбор = новый Структура("Период", Объект.ДатаПервичногоПосещения);
Срез = РегистрыСведений.Обращения.СрезПоследних(Объект.ДатаПервичногоПосещения, Отбор);
Объект.ПорядковыйНомерПосещения = Срез.ПорядковыйНомерПосещения;
КонецПроцедуры
В данном коде заложены сразу две критические ошибки, которые не позволят программе работать корректно. Выясним их причины.
Во-первых, использование стандартного реквизита Период в структуре отбора (второй параметр метода СрезПоследних) недопустимо. Метод СрезПоследних(Период, Отбор) работает по следующим правилам:
Поскольку Период — это стандартный реквизит, а не измерение, попытка вставить его в структуру отбора вызывает исключение «Несоответствие типов». Платформа ожидает увидеть там названия измерений (например, «Пациент», «Организация»), а встречает системное поле.
Во-вторых, метод СрезПоследних возвращает не объект записи, а ТаблицуЗначений. Даже если условиям отбора соответствует всего одна запись, результат всё равно будет таблицей. Поэтому прямое обращение через точку Срез.ПорядковыйНомерПосещения не сработает — у таблицы нет такого свойства, оно есть только у строк этой таблицы.
Для исправления вышеуказанного кода нам необходимо исключить поле Период из структуры отбора и добавить проверку на наличие данных в результирующей таблице. Проанализируем исправленный вариант:
&НаСервере
Процедура ЗаполнитьДанныеКифАмбулатНаСервере()
// Если в регистре нет измерений, отбор можно не создавать
// Если есть (например, ФизЛицо), добавляем его в структуру
Отбор = Новый Структура;
// Отбор.Вставить("Пациент", Объект.Пациент); // Пример использования
// Получаем таблицу значений (срез на указанную дату)
ТаблицаСреза = РегистрыСведений.Обращения.СрезПоследних(Объект.ДатаПервичногоПосещения, Отбор);
// Проверяем, что в таблице есть хотя бы одна строка
Если ТаблицаСреза.Количество() > 0 Тогда
// Обращаемся к первой строке таблицы (индекс 0)
Объект.ПорядковыйНомерПосещения = ТаблицаСреза[0].ПорядковыйНомерПосещения;
КонецЕсли;
КонецПроцедуры
В этом примере мы правильно обрабатываем результат. Однако стоит помнить, что метод СрезПоследних у менеджера регистра сведений извлекает все колонки регистра (все ресурсы, реквизиты и измерения). Если ваш регистр содержит много данных, это может негативно сказаться на производительности. Поэтому перейдем к более профессиональному способу решения задачи.
Использование механизма запросов (Query) является «золотым стандартом» разработки на платформе 1С. Это позволяет извлечь из базы данных только те поля, которые нам действительно нужны, и наложить более гибкие условия.
Разберем по шагам создание запроса для получения данных из регистра:
&НаСервере
Процедура ЗаполнитьДанныеКифАмбулатЗапросомНаСервере()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ОбращенияСрезПоследних.ПорядковыйНомерПосещения КАК НомерПосещения
|ИЗ
| РегистрСведений.Обращения.СрезПоследних(&Период, ) КАК ОбращенияСрезПоследних";
// Передаем дату документа в параметр запроса
Запрос.УстановитьПараметр("Период", Объект.ДатаПервичногоПосещения);
РезультатЗапроса = Запрос.Выполнить();
// Проверяем, пустой ли результат
Если Не РезультатЗапроса.Пустой() Тогда
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
Объект.ПорядковыйНомерПосещения = ВыборкаДетальныеЗаписи.НомерПосещения;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
Преимущества данного подхода:
ПорядковыйНомерПосещения вместо всей записи целиком.Иногда возникает ситуация, когда запись в регистр и документ создаются в одну и ту же секунду. При обычном использовании даты в качестве параметра СрезПоследних, система может «не увидеть» запись, сделанную ровно в это же время. Чтобы гарантированно включить записи за текущую секунду, проанализируем использование объекта Граница.
// Вместо простой даты используем Границу
МоментВремени = Новый Граница(Объект.ДатаПервичногоПосещения, ВидГраницы.Включая);
Запрос.УстановитьПараметр("Период", МоментВремени);
Это обеспечит получение самых свежих данных, даже если время документа и время записи в регистре совпадают до секунды.
Иногда у начинающих разработчиков возникает идея отказаться от периодичности регистра и создать собственное измерение «Дата». Рассмотрим, почему это неудачное решение:
СрезПоследних вам придется писать сложный запрос с группировками и поиском МАКСИМУМ(Дата).СрезПоследних в 1С оптимизированы на уровне СУБД для максимально быстрого поиска актуальных значений. Ручные запросы к физической таблице будут работать медленнее.Таким образом, правильным подходом будет использование периодического регистра сведений, обращение к нему через запрос к виртуальной таблице СрезПоследних с передачей даты в качестве первого параметра, и обязательная проверка результата на наличие записей перед заполнением реквизитов документа.