Как реализовать быстрый ввод данных для ссылочных типов в 1С без потери символов?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интенсивной работе операторов первичного ввода часто возникает ситуация: пользователи «вслепую» с высокой скоростью вводят последовательность кодов с клавиатуры (например, номенклатуру или контрагентов), нажимая клавиши перехода между полями. Однако интерфейс 1С на управляемых формах начинает подтормаживать, из-за чего часть вводимых цифр просто пропадает («съедается» платформой). Разберем причины такого поведения платформы и подробно рассмотрим варианты решения этой задачи.

Выясним причину потери символов

Проанализируем механику работы тонкого клиента при стандартном вводе значения ссылочного типа. Когда оператор вводит код в поле ссылочного типа и нажимает клавишу Enter или Tab, платформа 1С выполняет следующие действия:

  1. Инициирует контекстный или неконтекстный вызов сервера для выполнения поиска по введенному тексту (отрабатывают стандартные механизмы автоподбора, поиск по коду, наименованию, полнотекстовый индекс).
  2. На время отправки запроса и ожидания ответа от сервера поток пользовательского интерфейса (UI) может кратковременно блокироваться.
  3. Если оператор не смотрит на экран и продолжает непрерывно нажимать клавиши со следующими цифрами, очередь сообщений операционной системы и внутренний буфер ввода 1С могут сбрасываться или очищаться в момент обновления фокуса и значений элементов формы.

В результате следующий символ попросту игнорируется платформой. Рассмотрим эффективные архитектурные подходы, которые позволяют организовать молниеносный ввод данных без единой потери символа.

Решение 1. Паттерн «Строковый буфер» с отложенным пакетным сопоставлением

Разберем самый надежный и производительный способ для потокового ввода первички. Основная идея заключается в том, чтобы полностью изолировать оператора от ссылочных типов на момент ввода и перевести редактируемые поля в чисто строковый тип.

Переход между строковыми полями на клиенте выполняется мгновенно и без единого обращения к серверу, что гарантирует сохранение всех нажатых клавиш.

Рассмотрим реализацию по шагам:

  1. В табличной части или наборе реквизитов формы создаем строковые поля для ввода кодов (например, КодНоменклатурыСтрока).
  2. Рядом размещаем неизменяемые текстовые поля или надписи для вывода наименований и скрытые реквизиты ссылочного типа.
  3. Преобразование строковых кодов в реальные ссылки выполняем пакетно — либо по нажатию специальной кнопки (или горячей клавиши), либо при завершении строки / документа.

Посмотрим на пример пакетного получения ссылок по массиву кодов одним запросом к базе данных:


&НаСервереБезКонтекста
Функция ПолучитьСсылкиПоМассивуКодов(МассивКодов)
	
	ТаблицаРезультата = Новый ТаблицаЗначений;
	ТаблицаРезультата.Колонки.Добавить("Код", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(20)));
	ТаблицаРезультата.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
	ТаблицаРезультата.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(150)));
	
	Если МассивКодов.Количество() = 0 Тогда
		Возврат ТаблицаРезультата;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	Спр.Код КАК Код,
		|	Спр.Ссылка КАК Ссылка,
		|	Спр.Наименование КАК Наименование
		|ИЗ
		|	Справочник.Номенклатура КАК Спр
		|ГДЕ
		|	Спр.Код В(&МассивКодов)";
	
	Запрос.УстановитьПараметр("МассивКодов", МассивКодов);
	
	Выборка = Запрос.Выполнить().Выбрать();
	Пока Выборка.Следующий() Цикл
		НоваяСтрока = ТаблицаРезультата.Добавить();
		ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
	КонецЦикла;
	
	Возврат ТаблицаРезультата;
	
КонецФункции

Рассмотрим, как связать результат пакетной обработки с табличной частью документа на клиенте:


&НаКлиенте
Процедура ЗаполнитьСсылкиПоКодам()
	
	МассивКодов = Новый Массив;
	Для Каждого СтрокаТЧ Из Объект.Товары Цикл
		Если ЗначениеЗаполнено(СтрокаТЧ.КодНоменклатурыСтрока) Тогда
			МассивКодов.Добавить(СтрокаТЧ.КодНоменклатурыСтрока);
		КонецЕсли;
	КонецЦикла;
	
	Если МассивКодов.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ТаблицаСопоставления = ПолучитьСсылкиПоМассивуКодов(МассивКодов);
	
	СоответствиеСсылок = Новый Соответствие;
	СоответствиеНаименований = Новый Соответствие;
	
	Для Каждого СтрокаДанных Из ТаблицаСопоставления Цикл
		СоответствиеСсылок.Вставить(Сокращенный(СтрокаДанных.Код), СтрокаДанных.Ссылка);
		СоответствиеНаименований.Вставить(Сокращенный(СтрокаДанных.Код), СтрокаДанных.Наименование);
	КонецЦикла;
	
	Для Каждого СтрокаТЧ Из Объект.Товары Цикл
		ИскомыйКод = Сокращенный(СтрокаТЧ.КодНоменклатурыСтрока);
		НайденнаяСсылка = СоответствиеСсылок[ИскомыйКод];
		
		Если НайденнаяСсылка <> Неопределено Тогда
			СтрокаТЧ.Номенклатура = НайденнаяСсылка;
			СтрокаТЧ.НоменклатураПредставление = СоответствиеНаименований[ИскомыйКод];
		Иначе
			СтрокаТЧ.НоменклатураПредставление = "<Код не найден!>";
		КонецЕсли;
	КонецЦикла;
	
КонецПроцедуры

Решение 2. Использование клиентского кэша для мгновенной подстановки

Если список часто используемых элементов справочника ограничен (например, от нескольких сотен до десятков тысяч элементов), оптимально применить кэширование на стороне клиента. В этом случае обращение к серверу при вводе каждого кода полностью исключается.

Разберем по шагам создание клиентского кэша:

  1. При открытии формы (событие ПриСозданииНаСервере или фоновая загрузка при старте) выгружаем облегченную таблицу соответствий Код -> Ссылка.
  2. Помещаем эти данные в реквизит формы типа ФиксированноеСоответствие.
  3. В событии клиентского поля ПриИзменении выполняем мгновенный поиск по кэшу прямо в оперативной памяти клиента без вызова сервера.

Посмотрим на пример реализации клиентского кэширования:


&НаСервере
Процедура ЗагрузитьКэшНоменклатурыНаСервере()
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	Спр.Код КАК Код,
		|	Спр.Ссылка КАК Ссылка,
		|	Спр.Наименование КАК Наименование
		|ИЗ
		|	Справочник.Номенклатура КАК Спр
		|ГДЕ
		|	НЕ Спр.ПометкаУдаления";
	
	ВременноеСоответствие = Новый Соответствие;
	Выборка = Запрос.Выполнить().Выбрать();
	
	Пока Выборка.Следующий() Цикл
		СтруктураДанных = Новый Структура("Ссылка, Наименование", Выборка.Ссылка, Выборка.Наименование);
		ВременноеСоответствие.Вставить(Сокращенный(Выборка.Код), СтруктураДанных);
	КонецЦикла;
	
	КэшКодов = Новый ФиксированноеСоответствие(ВременноеСоответствие);
	
КонецПроцедуры

&НаКлиенте
Процедура ПолеВводаКодаПриИзменении(Элемент)
	
	ТекущиеДанные = Элементы.ТаблицаТовары.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ВведенныйКод = Сокращенный(ТекущиеДанные.КодНоменклатуры);
	ДанныеЭлемента = КэшКодов[ВведенныйКод];
	
	Если ДанныеЭлемента <> Неопределено Тогда
		ТекущиеДанные.Номенклатура = ДанныеЭлемента.Ссылка;
		ТекущиеДанные.НоменклатураНаименование = ДанныеЭлемента.Наименование;
	Иначе
		// Если код не найден в кэше, вызываем точечный поиск на сервере
		НайтиКодНаСервере(ВведенныйКод, ТекущиеДанные);
	КонецЕсли;
	
КонецПроцедуры

Решение 3. Оптимизация стандартного автоподбора платформы

Если переделывать форму на строковый ввод невозможно и требуется оставить стандартные поля ссылочного типа, необходимо максимально облегчить серверную часть механизма подбора.

Проанализируем настройки, которые нужно отключить или оптимизировать:

  1. Переопределение обработчика получения данных выбора: в модуле менеджера соответствующего справочника объявляем экспортную процедуру ОбработкаПолученияДанныхВыбора, отключаем стандартную логику через СтандартнаяОбработка = Ложь и выполняем предельно быстрый запрос строго по индексу поля Код с ограничением ВЫБРАТЬ ПЕРВЫЕ 1.
  2. Свойства элемента формы:
    • Отключаем ИсторияВыбораПриВводе в значение НеИспользовать (так как чтение и запись истории выбора создают дополнительную нагрузку на сервер).
    • Отключаем свойства КнопкаВыбора, КнопкаОчистки, КнопкаСпискаВыбора, АвтоВыборНезаполненного.
    • Устанавливаем РежимВыбораИзСписка в Ложь.

Посмотрим на пример переопределения обработчика в модуле менеджера справочника:


Процедура ОбработкаПолученияДанныхВыбора(ДанныеВыбора, Параметры, СтандартнаяОбработка) Экспорт
	
	СтандартнаяОбработка = Ложь;
	
	СтрокаПоиска = Сокращенный(Параметры.СтрокаПоиска);
	Если ПустаяСтрока(СтрокаПоиска) Тогда
		Возврат;
	КонецЕсли;
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	Спр.Ссылка КАК Ссылка,
		|	Спр.Наименование КАК Наименование
		|ИЗ
		|	Справочник.Номенклатура КАК Спр
		|ГДЕ
		|	Спр.Код = &Код";
	
	Запрос.УстановитьПараметр("Код", СтрокаПоиска);
	
	Результат = Запрос.Выполнить();
	Если НЕ Результат.Пустой() Тогда
		ДанныеВыбора = Новый СписокЗначений;
		Выборка = Результат.Выбрать();
		Пока Выборка.Следующий() Цикл
			ДанныеВыбора.Добавить(Выборка.Ссылка, Выборка.Наименование);
		КонецЦикла;
	КонецЕсли;
	
КонецПроцедуры

Решение 4. Консольный ввод через единое текстовое поле

Для специализированных рабочих мест (АРМ оператора) наилучшим решением является командный или консольный ввод. Оператор переходит в одно многострочное текстовое поле и вбивает коды сплошным потоком, разделяя их переводом строки (нажатием Enter):


96
1
1
я501

По нажатию одной комбинации клавиш (например, Ctrl + Enter) процедура на клиенте разбирает текст на массив строк и за один серверный вызов трансформирует всю введенную простыню кодов в строки табличной части документа. Это полностью исключает потерю фокуса и задержки в процессе набора.

Резюме: Для организации сверхбыстрого ввода по кодам в 1С наиболее надежным решением является отказ от ссылочных полей в момент набора и использование строкового буфера с последующей пакетной обработкой или клиентским кэшем. Это гарантирует сохранение каждого нажатия клавиши и максимальную скорость работы операторов.

← На главную