Рубрики
Примеры

Подбор по первым символам на 1С 8.3 (как было в 1С 7.7)

Сейчас расскажу про одну из самых больных тем — подбор номенклатуры в документ. Это, конечно, очень спорный момент, особенно сейчас, когда большое количество документов создается с помощью загрузки (с сайтов, из прайс-листов или каталогов, мобильных устройств, ЭДО, EDI и т.д.), но все равно есть еще компании, которые работают по-старинке, создавая документы «по-телефону», с бумажных носителей. И в этом случае на первый план выходит скорость и удобство подбора номенклатуры в документ.

Сейчас разработчики 1С всех склоняют к полнотекстовому поиску. Это достаточно удобно, но далеко не всегда. Например:

  • Полнотекстовый поиск постоянно обращается к серверу, а это задержки. Я, как ни старался настроить этот поиск, все-равно выходило около 0.2 секунды задержка после ввода символа. Оператор, когда быстро вводит, постоянно попадал на «проглатываемые» системой буквы (ввел 3 символа, пробел — система задумывается, а оператор нажимает кнопки, которые система уже не воспринимает)
  • Второй минус — когда 1С сделала отбор, а товар закончился. Покупатель просит «дайте что-то похожее». И тогда надо очистить отбор и начинается эпопея заново.
  • Иногда полнотекстовый поиск задумывается на 1-2 секунды. А это уже очень много и сильно нервирует пользователей.

Именно поэтому решил написать быстро работающий подбор и похожий по функционалу на то, что было в 1С 7.7 — по первым буквам. У нас в базе и номенклатура именуется соответственно — по «типам», т.е. вначале наименования «предопределенные» слова: мыло, смс, м.ср., шамп и т.д.

Когда первый раз занялся этим вопросом, то все оказалось достаточно просто. До версии 8.3.8.х (если мне не изменяет память) у размещенного на форме дерева значений поиск осуществлялся как раз по первым символам наименования. Было очень удобно, работало быстро и все были счастливы.

Но по каким-то причинам, разработчики 1С закрыли эту возможность и влупили туда несчастный полнотекстовый поиск. И все опять стало плохо. Обращения к серверу, отбор списка, лишние нажатия кнопок очистки поиска и т.д.

Пришлось «пораскинуть» мозгами и написать новую обработку, в которой использовать внешнюю компоненту перехвата нажатия кнопок клавиатуры. Вот как выглядит:

Простенько, без наворотов, но все пользователи довольны. И работает по принципу поиска в 1С 7.7 — при вводе символов позиционируемся на нужной строке, если введенного сочетания уже нет, то символ не вводится. При перемещении по списку введенный текст очищается.

Не могу не сказать и про минус, который есть в этой обработке. Чтобы сделать высокую скорость работы полностью убираются серверные вызовы при вводе символов и перед началом поиска создается «индекс» строк таблицы. На создание «индекса» требуется время и поэтому сделано ограничение на количество вводимых символов (чем меньше символов, тем меньше требуется времени на создание «индекса»). Методом «научного тыка» для используемой у меня конфигурации оборудования было установлено ограничение в 20 символов — на создание «индекса» 20 символов для 20+ тысяч товаров при открытии формы система тратит примерно 2 секунды. Это значение приемлемо для пользователей. У вас, возможно, при меньшей номенклатуре или более мощном компьютере (сервере) можно будет установить и большее количество индексируемых символов, но, на мой взгляд, этого количество (20 символов) вполне достаточно.

Еще один минус — использование внешней компоненты. Не помню, откуда я ее скачал (давно дело было) и кто разработчик. Компоненты, которые есть у меня в примере работают для windows (32 и 64 бита). Для других ОС не искал.

Применение описанной в этой заметке обработки вы можете увидеть, скачав демонстрационную конфигурацию.

Если у вас будут какие-либо вопросы, пожелания — пишите в комментариях. Постараюсь ответить в ближайшее время.

Рубрики
Примеры

Работа с файлами в 1С 8.3. Часть 2.

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

  • Выбор файла на клиенте. Создаем форму с реквизитом «ПолноеИмяФайла» и размещаем этот реквизит на форме. У поля ввода «ПолноеИмяФайла» добавляем отображение кнопки выбора и создаем соответствующий обработчик.

// Обработчик события на клиенте, в котором показываем диалог выбора файла
&НаКлиенте
Процедура ПолноеИмяФайлаНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
	
	ДиалогВыбораФайла = ПолучитьДиалогВыбораФайла();
	
	ВыбратьФайлАсинх(ДиалогВыбораФайла);
	
КонецПроцедуры

// Получаем диалог выбора файла
&НаКлиенте
Функция ПолучитьДиалогВыбораФайла()
	
	ДиалогВыбораФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
	ДиалогВыбораФайла.МножественныйВыбор = Ложь;
	ДиалогВыбораФайла.Заголовок = "Выберите файл...";
	ДиалогВыбораФайла.Фильтр = "Изображение (*.jpg, jpeg)|*.jpg;*.jpeg|Архив (*.zip)|*.zip|Все файлы|*.*";
	
	Возврат ДиалогВыбораФайла;
	
КонецФункции

&НаКлиенте
Асинх Функция ВыбратьФайлАсинх(ДиалогВыбораФайла)
	
	ОбещаниеВыбора = ДиалогВыбораФайла.ВыбратьАсинх();
	ВыбранныеФайлы = Ждать ОбещаниеВыбора;
	ОбработатьВыборФайла(ВыбранныеФайлы);
	
КонецФункции

// Ожидаем выбор файла пользователем
&НаКлиенте
Процедура ОбработатьВыборФайла(Результат)
	
	Если Результат = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Если Результат.Количество() > 0 Тогда
		ПолноеИмяФайла = Результат[0];
	КонецЕсли;
	
	ПолучитьДанныеФайлаАсинх();
	
КонецПроцедуры

// Получаем информацию о файле
&НаКлиенте
Асинх Процедура ПолучитьДанныеФайлаАсинх()
	
	НайденныйФайл = ждать ПолучитьВыбранныйФайлАсинх();
	Если ТипЗнч(НайденныйФайл) = Тип("Файл") Тогда
		обРазмер = Ждать НайденныйФайл.РазмерАсинх();
		ДанныеФайла = Новый Структура("Файл, ИмяФайла, Размер, Расширение", НайденныйФайл, НайденныйФайл.Имя, обРазмер, НайденныйФайл.Расширение);
	КонецЕсли;
	
	Возврат;
	
КонецПроцедуры
  • Теперь передаем файл на сервер и сохраняем в базе данных. Для этого на форме создаем команду «ЗаписатьВБазуДанных» и размещаем соответствующую команде кнопку.

// Вызываем команду, в которой вызываем асинхронную процедуру для передачи файла на сервер
&НаКлиенте
Процедура ЗаписатьВБазуДанных(Команда)
	
	ДополнительныеПараметры = Новый Структура("ПолноеИмяФайла, ИмяФайла, Размер, Расширение", ПолноеИмяФайла, ИмяФайла, РазмерФайла, РасширениеФайла);
	ПередатьФайлНаСерверАсних(ДополнительныеПараметры);
	
КонецПроцедуры

// С использованием асинхронного метода ПоместитьФайлНаСерверАсинх передаем файл на сервер, ожидам окончания операции
&НаКлиенте
Асинх Процедура ПередатьФайлНаСерверАсних(ДополнительныеПараметры)
	
	ОписаниеОповещения = Новый ОписаниеОповещения("ПрогрессПомещенияФайла", ЭтаФорма, ДополнительныеПараметры);
	ОбПомещение = ПоместитьФайлНаСерверАсинх(ОписаниеОповещения,,, ПолноеИмяФайла,);
	ПомещенныйФайл = Ждать ОбПомещение;
	ЗаписатьФайлВБазуДанных(ПомещенныйФайл.Адрес, ДополнительныеПараметры);
	
КонецПроцедуры

&НаКлиенте
Процедура ПрогрессПомещенияФайла(ПомещаемыйФайл, Помещено, ОтказОтПомещенияФайла, ДополнительныеПараметры) Экспорт
	// Можно показать прогресс выполнения или отменить передачу
КонецПроцедуры

// Получаем данные файла из временного хранилища и записываем в заранее созданный регистр сведений
&НаСервере
Процедура ЗаписатьФайлВБазуДанных(АдресВХранилище, ДополнительныеПараметры)
	
	ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресВХранилище);
	
	ЗаписьРегистра = РегистрыСведений.СохраненныеФайлы.СоздатьМенеджерЗаписи();
	ЗаписьРегистра.Идентификатор = СокрЛП(Новый УникальныйИдентификатор);
	ЗаписьРегистра.ДвоичныеДанные = Новый ХранилищеЗначения(ДвоичныеДанные);
	ЗаписьРегистра.Размер = ДополнительныеПараметры.Размер;
	ЗаписьРегистра.Расширение = ДополнительныеПараметры.Расширение;
	ЗаписьРегистра.ПолноеИмяФайла = ДополнительныеПараметры.ПолноеИмяФайла;
	ЗаписьРегистра.ИмяФайла = ДополнительныеПараметры.ИмяФайла;
	ЗаписьРегистра.ДатаВремяДобавления = ТекущаяДата();
	ЗаписьРегистра.Записать(Ложь);
	
КонецПроцедуры

Применение описанных в этой заметке методов вы можете увидеть, скачав демонстрационную конфигурацию.

Рубрики
Примеры

Работа с файлами в 1С 8.3

При работе с файлами в 1С 8.3 главное — понять принцип и разделить — какие команды выполняются на клиенте, а какие на сервере. В этой заметке опишу основные возможности — как выбрать файл, получить его параметры, как передать на сервер и сохранить в базу данных, как передать его обратно с сервера и сохранить на клиенте. Теперь пойдем по порядку.

Применение описанных в этой заметке методов вы можете увидеть, скачав демонстрационную конфигурацию.

  • Выбор файла на клиенте. Создаем форму с реквизитом «ПолноеИмяФайла» и размещаем этот реквизит на форме. У поля ввода «ПолноеИмяФайла» добавляем отображение кнопки выбора и создаем соответствующий обработчик.
// Обработчик события на клиенте, в котором показываем диалог выбора файла
&НаКлиенте
Процедура ПолноеИмяФайлаНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
	
	ДиалогВыбораФайла = ПолучитьДиалогВыбораФайла();
	
	ДополнительныеПараметры = Новый Структура;
	ОписаниеОповещения = Новый ОписаниеОповещения("ПослеВыбораФайла", ЭтаФорма, ДополнительныеПараметры);
	ДиалогВыбораФайла.Показать(ОписаниеОповещения);
	
КонецПроцедуры


// Получаем диалог выбора файла
&НаКлиенте
Функция ПолучитьДиалогВыбораФайла()
	
	ДиалогВыбораФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
	ДиалогВыбораФайла.МножественныйВыбор = Ложь;
	ДиалогВыбораФайла.Заголовок = "Выберите файл...";
	ДиалогВыбораФайла.Фильтр = "Изображение (*.jpg, jpeg)|*.jpg;*.jpeg|Архив (*.zip)|*.zip|Все файлы|*.*";
	
	Возврат ДиалогВыбораФайла;
	
КонецФункции

// Обрабатываем окончание выбора файла
&НаКлиенте
Процедура ПослеВыбораФайла(Результат, ДополнительныеПараметры) Экспорт
	ОбработатьВыборФайла(Результат);
КонецПроцедуры

&НаКлиенте
Процедура ОбработатьВыборФайла(Результат)
	
	Если Результат = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Если Результат.Количество() > 0 Тогда
		ПолноеИмяФайла = Результат[0];
	КонецЕсли;
	
	ПолучитьДанныеФайла();
	
КонецПроцедуры

// Получаем информацию о файле - имя, размер, расширение
&НаКлиенте
Функция ПолучитьДанныеФайла()
	
	Результат = Неопределено;

	Если ЗначениеЗаполнено(ПолноеИмяФайла) Тогда
		НайденныеФайлы = НайтиФайлы(ПолноеИмяФайла);
		Если НайденныеФайлы.Количество() = 1 Тогда
			Результат = НайденныеФайлы[0];
			Результат = Новый Структура("Файл, ИмяФайла, Размер, Расширение", НайденныеФайлы[0], НайденныеФайлы[0].Имя, НайденныеФайлы[0].Размер(), НайденныеФайлы[0].Расширение);
		КонецЕсли;
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции
  • Теперь передаем файл на сервер и сохраняем в базе данных. Для этого на форме создаем команду «ЗаписатьВБазуДанных» и размещаем соответствующую команде кнопку.
// Вызываем команду, в которой с помощью метода НачатьПомещениеФайлаНаСервер передаем файл на сервер
&НаКлиенте
Процедура ЗаписатьВБазуДанных(Команда)
	
	ДополнительныеПараметры = Новый Структура("ПолноеИмяФайла, ИмяФайла, Размер, Расширение", ПолноеИмяФайла, ИмяФайла, РазмерФайла, РасширениеФайла);
	ОписаниеОповещения = Новый ОписаниеОповещения("ПослеПомещенияФайлаНаСервер", ЭтаФорма, ДополнительныеПараметры);
	НачатьПомещениеФайлаНаСервер(ОписаниеОповещения,,,, ПолноеИмяФайла,);
	
КонецПроцедуры

// Обрабатываем завершение передачи файла на сервер и вызываем процедуру записи файла в базу данных
&НаКлиенте
Процедура ПослеПомещенияФайлаНаСервер(Результат, ДополнительныеПараметры) Экспорт
	
	Если Результат.ПомещениеФайлаОтменено Тогда
		Возврат;
	КонецЕсли;
	
	ЗаписатьФайлВБазуДанных(Результат.Адрес, ДополнительныеПараметры);
	
КонецПроцедуры

// Получаем данные файла из временного хранилища и записываем в заранее созданный регистр сведений
&НаСервере
Процедура ЗаписатьФайлВБазуДанных(АдресВХранилище, ДополнительныеПараметры)
	
	ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресВХранилище);
	
	ЗаписьРегистра = РегистрыСведений.СохраненныеФайлы.СоздатьМенеджерЗаписи();
	ЗаписьРегистра.Идентификатор = СокрЛП(Новый УникальныйИдентификатор);
	ЗаписьРегистра.ДвоичныеДанные = Новый ХранилищеЗначения(ДвоичныеДанные);
	ЗаписьРегистра.Размер = ДополнительныеПараметры.Размер;
	ЗаписьРегистра.Расширение = ДополнительныеПараметры.Расширение;
	ЗаписьРегистра.ПолноеИмяФайла = ДополнительныеПараметры.ПолноеИмяФайла;
	ЗаписьРегистра.ИмяФайла = ДополнительныеПараметры.ИмяФайла;
	ЗаписьРегистра.ДатаВремяДобавления = ТекущаяДата();
	ЗаписьРегистра.Записать(Ложь);
	
КонецПроцедуры
  • И теперь получение файла с сервера и его сохранение на диске. В форме списка регистра сведений, в котором хранятся файлы создаем следующий код:

// Вызываем команду, в которой открываем диалог выбора файла для сохранения
&НаКлиенте
Процедура СохранитьФайл(Команда)
	
	ТекущиеДанные = Элементы.Список.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ДиалогВыбораФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
	ДиалогВыбораФайла.Заголовок = "Выберите файл...";
	ДиалогВыбораФайла.Фильтр = СокрЛП(ТекущиеДанные.Расширение) + "|*" + СокрЛП(ТекущиеДанные.Расширение);
	
	ВыбратьРасположениеНаДиске(ДиалогВыбораФайла, ТекущиеДанные.Идентификатор);
	
КонецПроцедуры

// В данном примере АСИНХРОННО открываем диалог выбора, ожидаем завершения выбора и вызываем процедуру обработки
&НаКлиенте
Асинх Процедура ВыбратьРасположениеНаДиске(ДиалогВыбораФайла, Идентификатор)
	
	ОбещаниеВыбора = ДиалогВыбораФайла.ВыбратьАсинх();
	ВыбранныеФайлы = Ждать ОбещаниеВыбора;
	ОбработатьВыборФайла(ВыбранныеФайлы, Идентификатор);
	
КонецПроцедуры

// Проверяем, что выбор осуществлен, вызываем серверную функцию помещения файла во временное хранилище и вызываем метод НачатьПолучениеФайлаССервера для передачи файла с сервера на клиент и сохранения в выбранный ранее файл
&НаКлиенте
Процедура ОбработатьВыборФайла(Результат, Идентификатор)
	
	Если Результат = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ПолноеИмяФайла = Результат[0];
	
	АдресВХранилище = ПоместитьФайлВХранилищеНаСервере(Идентификатор);
	Если ЗначениеЗаполнено(АдресВХранилище) Тогда
		ДополнительныеПараметры = Новый Структура;
		ДополнительныеПараметры.Вставить("ПолноеИмяФайла", ПолноеИмяФайла);
		ОписаниеОповещения = Новый ОписаниеОповещения("ПослеСохраненияФайла", ЭтаФорма, ДополнительныеПараметры);
		НачатьПолучениеФайлаССервера(ОписаниеОповещения, АдресВХранилище, ПолноеИмяФайла);
	КонецЕсли;
	
КонецПроцедуры

// На сервере получаем данные из регистра сведений и помещаем их во временное хранилище
&НаСервере
Функция ПоместитьФайлВХранилищеНаСервере(Идентификатор)
	
	ЗаписьРегистра = РегистрыСведений.СохраненныеФайлы.СоздатьМенеджерЗаписи();
	ЗаписьРегистра.Идентификатор = Идентификатор;
	ЗаписьРегистра.Прочитать();
	
	АдресВХранилище = ПоместитьВоВременноеХранилище(ЗаписьРегистра.ДвоичныеДанные.Получить());
	
	Возврат АдресВХранилище;
	
КонецФункции

// Показываем пользователю сообщение о завершении операции
&НаКлиенте
Процедура ПослеСохраненияФайла(Результат, ДополнительныеПараметры) Экспорт
	
	ПоказатьПредупреждение(, "Сохранен файл " + СокрЛП(ДополнительныеПараметры.ПолноеИмяФайла));
	
КонецПроцедуры

Применение описанных в этой заметке методов вы можете увидеть, скачав демонстрационную конфигурацию.