При автоматизации складского учета и розничной торговли часто возникает необходимость контролировать процесс сканирования товаров. Типичный сценарий: оператор сканирует штрихкод номенклатуры, система заполняет табличную часть товарами и предлагает ввести серийные номера. После того как фактическое количество совпало со счетным, программа задает вопрос: "Продолжить ввод этой номенклатуры?". Если пользователь отвечает "Нет", ввод по этой позиции должен быть заблокирован.
Однако на практике разработчики часто сталкиваются с тем, что при повторном случайном сканировании заблокированного товара процедура обработки штрихкода запускается заново, сбрасывая введенные данные или начиная новый подсчет. В этой статье мы подробно разберем, почему так происходит, проанализируем ошибки в области видимости переменных и напишем надежный алгоритм блокировки сканирования для конфигурации «1С:Управление торговлей 10» (на обычных формах).
Рассмотрим типичную ошибку реализации, с которой часто сталкиваются начинающие разработчики. Попытка объявить переменную блокировки внутри процедуры обработки события сканирования выглядит следующим образом:
// ОШИБОЧНЫЙ ПОДХОД
Процедура ВнешнееСобытие(Источник, Событие, Данные)
// ... получение штрихкода и поиск номенклатуры ...
Если НайденныеСтроки[0].КоличествоФакт = НайденныеСтроки[0].КоличествоСчет Тогда
Если Вопрос("КоличествоФакт равно КоличествоСчет. Продолжить?" , РежимДиалогаВопрос.ДаНет) = КодВозвратаДиалога.Да Тогда
ВводСерийныхНомеров = Истина;
Иначе
ВводСерийныхНомеров = Ложь;
НайденныеСтроки.Доступность = Ложь; // Вызовет ошибку!
ПровереннаяНоменклатура = ТекущаяНоменклатура; // Сбросится при следующем сканировании
КонецЕсли;
КонецЕсли;
КонецПроцедуры
В приведенном коде скрыты сразу две серьезные проблемы, которые препятствуют правильной работе программы:
ПровереннаяНоменклатура: Поскольку переменная инициализируется внутри процедуры ВнешнееСобытие, она является локальной. При завершении процедуры эта переменная уничтожается. Когда вы сканируете следующий штрихкод, процедура вызывается заново, переменная создается с нуля и, естественно, не хранит информацию о предыдущей блокировке.Доступность у массива: Метод НайтиСтроки() возвращает объект типа Массив. У массива нет свойства Доступность. Строка кода НайденныеСтроки.Доступность = Ложь; приведет к критической ошибке выполнения (Runtime Error). Блокировать ввод нужно не через свойства интерфейса, а на уровне логики выполнения кода.Давайте разберем по шагам, как правильно реализовать эту задачу, используя несколько различных подходов.
Чтобы переменная не сбрасывала свое значение при каждом новом сканировании, ее область видимости должна быть расширена до уровня модуля формы. Для этого мы объявим переменную в самом верху модуля и инициализируем ее как коллекцию СписокЗначений при открытии формы.
Рассмотрим пошагово реализацию этого метода:
Шаг 1. Объявление и инициализация переменной
Перейдем в самый верх модуля формы документа или обработки и добавим объявление переменной вне процедур:
Перем ЗаблокированнаяНоменклатура; // Переменная модуля формы, доступная во всех процедурах
Теперь инициализируем ее в предопределенной процедуре ПриОткрытии:
Процедура ПриОткрытии()
// Инициализируем список, который будет хранить заблокированные товары
ЗаблокированнаяНоменклатура = Новый СписокЗначений;
КонецПроцедуры
Шаг 2. Проверка блокировки в начале процедуры сканирования
Главный секрет успешной блокировки — осуществлять проверку в самом начале процедуры ВнешнееСобытие, еще до того, как начнется обработка табличных частей и поиск серийных номеров. Если отсканированный товар находится в нашем списке заблокированных, мы просто прерываем выполнение алгоритма.
Процедура ВнешнееСобытие(Источник, Событие, Данные)
// Проверяем, что событие пришло от сканера штрихкодов
ПолученныеДанные = СокрЛП(Данные);
// Вызываем стандартную функцию поиска номенклатуры по штрихкоду
// (в УТ 10.3/10.4 это обычно ОбработкаСканирования Штрихкода)
НайденныйТовар = ПолучитьНоменклатуруПоШтрихкоду(ПолученныеДанные);
Если ЗначениеЗаполнено(НайденныйТовар) Тогда
// КЛЮЧЕВАЯ ПРОВЕРКА: Проверяем, не заблокирован ли этот товар ранее
Если ЗаблокированнаяНоменклатура.НайтиПоЗначению(НайденныйТовар) <> Неопределено Тогда
Предупреждение("Ввод по номенклатуре '" + НайденныйТовар + "' завершен и заблокирован!");
Возврат; // Полностью прерываем дальнейшую обработку сканирования!
КонецЕсли;
КонецЕсли;
// ... Далее идет ваш стандартный код обработки сканирования ...
КонецПроцедуры
Шаг 3. Добавление товара в список блокировки при отказе пользователя
Теперь отредактируем логику обработки диалога с вопросом. Если пользователь отвечает "Нет", мы добавляем текущую номенклатуру в наш список:
// Код проверки совпадения количества
Если СтрокаТЧ.КоличествоФакт = СтрокаТЧ.КоличествоСчет Тогда
Режим = РежимДиалогаВопрос.ДаНет;
Ответ = Вопрос("Количество совпадает. Продолжить ввод серийных номеров?", Режим);
Если Ответ = КодВозвратаДиалога.Да Тогда
// Разрешаем ввод серийных номеров
ВводСерийныхНомеров = Истина;
Иначе
// Блокируем дальнейший ввод
ВводСерийныхНомеров = Ложь;
// Добавляем номенклатуру в заблокированные, чтобы при следующем сканировании сработал Возврат
ЗаблокированнаяНоменклатура.Добавить(СтрокаТЧ.Номенклатура);
Сообщить("Ввод номенклатуры '" + СтрокаТЧ.Номенклатура + "' успешно заблокирован.");
КонецЕсли;
КонецЕсли;
При работе с торговым оборудованием существует важный нюанс. Если сканер штрихкодов работает в режиме эмуляции клавиатуры или штрихкоды сканируются очень быстро, то в момент показа модального окна Вопрос() сканер может продолжать передавать данные в буфер. Символы штрихкода могут симулировать нажатие клавиши Enter, что приведет к автоматическому закрытию диалогового окна с непредсказуемым результатом.
Чтобы избежать этой проблемы, рекомендуется временно отключать обработку сигналов торгового оборудования перед показом вопроса и включать ее обратно сразу после получения ответа от пользователя.
Проанализируем пример кода с программным управлением сканером:
// 1. Отключаем обработку событий от торгового оборудования
Попытка
ПолучитьСерверТО().ОтключитьКлиента(ЭтаФорма);
Исключение
// Обработка исключения, если сервер ТО недоступен
КонецПопытки;
// 2. Задаем вопрос пользователю в безопасном режиме
Режим = РежимДиалогаВопрос.ДаНет;
Ответ = Вопрос("Количество совпадает. Продолжить ввод?", Режим);
Если Ответ = КодВозвратаДиалога.Нет Тогда
ЗаблокированнаяНоменклатура.Добавить(ТекущаяНоменклатура);
КонецЕсли;
// 3. Включаем сканер обратно для продолжения работы
Попытка
ПолучитьСерверТО().ПодключитьКлиента(ЭтаФорма);
Исключение
Сообщить("Не удалось переподключить сканер штрихкодов!", СтатусСообщения.Важное);
КонецПопытки;
Использование переменной модуля формы отлично работает, пока открыта форма документа. Однако, если пользователь случайно закроет документ, а затем откроет его снова, переменная модуля формы очистится, и блокировка пропадет. Если вам необходимо гарантировать сохранность состояния блокировки даже после перезапуска сессии или закрытия формы, лучше использовать реквизит табличной части.
Рассмотрим, как реализовать этот подход по шагам:
Товары) новую колонку с именем БлокировкаВвода (тип данных: Булево).ТолькоПросмотр = Истина, чтобы исключить ручное изменение.
Процедура ВнешнееСобытие(Источник, Событие, Данные)
// ... Код поиска номенклатуры и строки в табличной части ...
Если СтрокаТабличнойЧасти <> Неопределено Тогда
// Проверяем флаг блокировки непосредственно из строки ТЧ
Если СтрокаТабличнойЧасти.БлокировкаВвода Тогда
Предупреждение("Внимание! Ввод по строке '" + СтрокаТабличнойЧасти.Номенклатура + "' заблокирован!");
Возврат;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
При получении ответа "Нет" в диалоговом окне мы просто устанавливаем этот флаг в строке табличной части:
Если Ответ = КодВозвратаДиалога.Нет Тогда
СтрокаТабличнойЧасти.БлокировкаВвода = Истина;
КонецЕсли;
Преимущества данного подхода:
ПриВыводеСтроки, чтобы визуально выделять заблокированные строки (например, окрашивать их фон в светло-серый цвет), что сделает интерфейс работы оператора более наглядным и дружелюбным.Если ваша конфигурация «1С:Управление торговлей 10» запущена на современной платформе «1С:Предприятие 8.3» и в свойствах конфигурации установлено значение «Режим использования модальности = Не использовать», то вызов классической функции Вопрос() приведет к аварийному завершению работы программы.
В этом случае необходимо использовать асинхронный метод ПоказатьВопрос(). При этом логика блокировки и дальнейшего ввода переносится в отдельную процедуру-обработчик (оповещение):
// Описание процедуры-обработчика выбора ответа
&НаКлиенте
Процедура ВопросОПродолженииЗавершен(РезультатВопроса, ДополнительныеПараметры) Экспорт
Если РезультатВопроса = КодВозвратаДиалога.Нет Тогда
ТекущаяНоменклатура = ДополнительныеПараметры.Номенклатура;
// Добавляем номенклатуру в список заблокированных
ЗаблокированнаяНоменклатура.Добавить(ТекущаяНоменклатура);
КонецЕсли;
КонецПроцедуры
Вызов самого вопроса в коде сканирования будет выглядеть следующим образом:
ПараметрыОповещения = Новый ОписаниеОповещения("ВопросОПродолженииЗавершен", ЭтотОбъект, Новый Структура("Номенклатура", ТекущаяНоменклатура));
ПоказатьВопрос(ПараметрыОповещения, "Количество совпадает. Продолжить ввод?", РежимДиалогаВопрос.ДаНет);
Использование описанных методик позволит вам гибко и без ошибок настроить логику работы со сканером штрихкодов в 1С, исключив случайный повторный ввод информации пользователями.