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