Как заблокировать повторный ввод номенклатуры при сканировании штрихкода в 1С:Управление торговлей 10?

Программист 1С v8.3 (Обычные формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При автоматизации складского учета и розничной торговли часто возникает необходимость контролировать процесс сканирования товаров. Типичный сценарий: оператор сканирует штрихкод номенклатуры, система заполняет табличную часть товарами и предлагает ввести серийные номера. После того как фактическое количество совпало со счетным, программа задает вопрос: "Продолжить ввод этой номенклатуры?". Если пользователь отвечает "Нет", ввод по этой позиции должен быть заблокирован.

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

Выясняем причину: почему сбрасывается блокировка и не работает код?

Рассмотрим типичную ошибку реализации, с которой часто сталкиваются начинающие разработчики. Попытка объявить переменную блокировки внутри процедуры обработки события сканирования выглядит следующим образом:


// ОШИБОЧНЫЙ ПОДХОД
Процедура ВнешнееСобытие(Источник, Событие, Данные)
    // ... получение штрихкода и поиск номенклатуры ...
    Если НайденныеСтроки[0].КоличествоФакт = НайденныеСтроки[0].КоличествоСчет Тогда
        Если Вопрос("КоличествоФакт равно КоличествоСчет. Продолжить?" , РежимДиалогаВопрос.ДаНет) = КодВозвратаДиалога.Да Тогда
            ВводСерийныхНомеров = Истина;
        Иначе
            ВводСерийныхНомеров = Ложь;
            НайденныеСтроки.Доступность = Ложь; // Вызовет ошибку!
            ПровереннаяНоменклатура = ТекущаяНоменклатура; // Сбросится при следующем сканировании
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

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

  1. Область видимости переменной ПровереннаяНоменклатура: Поскольку переменная инициализируется внутри процедуры ВнешнееСобытие, она является локальной. При завершении процедуры эта переменная уничтожается. Когда вы сканируете следующий штрихкод, процедура вызывается заново, переменная создается с нуля и, естественно, не хранит информацию о предыдущей блокировке.
  2. Попытка обращения к свойству Доступность у массива: Метод НайтиСтроки() возвращает объект типа Массив. У массива нет свойства Доступность. Строка кода НайденныеСтроки.Доступность = Ложь; приведет к критической ошибке выполнения (Runtime Error). Блокировать ввод нужно не через свойства интерфейса, а на уровне логики выполнения кода.

Давайте разберем по шагам, как правильно реализовать эту задачу, используя несколько различных подходов.

Решение 1: Использование переменной модуля формы и списка заблокированных товаров

Чтобы переменная не сбрасывала свое значение при каждом новом сканировании, ее область видимости должна быть расширена до уровня модуля формы. Для этого мы объявим переменную в самом верху модуля и инициализируем ее как коллекцию СписокЗначений при открытии формы.

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

Шаг 1. Объявление и инициализация переменной

Перейдем в самый верх модуля формы документа или обработки и добавим объявление переменной вне процедур:


Перем ЗаблокированнаяНоменклатура; // Переменная модуля формы, доступная во всех процедурах

Теперь инициализируем ее в предопределенной процедуре ПриОткрытии:


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

Шаг 2. Проверка блокировки в начале процедуры сканирования

Главный секрет успешной блокировки — осуществлять проверку в самом начале процедуры ВнешнееСобытие, еще до того, как начнется обработка табличных частей и поиск серийных номеров. Если отсканированный товар находится в нашем списке заблокированных, мы просто прерываем выполнение алгоритма.


Процедура ВнешнееСобытие(Источник, Событие, Данные)

    // Проверяем, что событие пришло от сканера штрихкодов
    ПолученныеДанные = СокрЛП(Данные);
    
    // Вызываем стандартную функцию поиска номенклатуры по штрихкоду
    // (в УТ 10.3/10.4 это обычно ОбработкаСканирования Штрихкода)
    НайденныйТовар = ПолучитьНоменклатуруПоШтрихкоду(ПолученныеДанные);
    
    Если ЗначениеЗаполнено(НайденныйТовар) Тогда
        
        // КЛЮЧЕВАЯ ПРОВЕРКА: Проверяем, не заблокирован ли этот товар ранее
        Если ЗаблокированнаяНоменклатура.НайтиПоЗначению(НайденныйТовар) <> Неопределено Тогда
            Предупреждение("Ввод по номенклатуре '" + НайденныйТовар + "' завершен и заблокирован!");
            Возврат; // Полностью прерываем дальнейшую обработку сканирования!
        КонецЕсли;
        
    КонецЕсли;

    // ... Далее идет ваш стандартный код обработки сканирования ...

КонецПроцедуры

Шаг 3. Добавление товара в список блокировки при отказе пользователя

Теперь отредактируем логику обработки диалога с вопросом. Если пользователь отвечает "Нет", мы добавляем текущую номенклатуру в наш список:


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

Решение 2: Временное отключение сканера на время показа диалога (Важно!)

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

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

Проанализируем пример кода с программным управлением сканером:


// 1. Отключаем обработку событий от торгового оборудования
Попытка
    ПолучитьСерверТО().ОтключитьКлиента(ЭтаФорма);
Исключение
    // Обработка исключения, если сервер ТО недоступен
КонецПопытки;

// 2. Задаем вопрос пользователю в безопасном режиме
Режим = РежимДиалогаВопрос.ДаНет;
Ответ = Вопрос("Количество совпадает. Продолжить ввод?", Режим);

Если Ответ = КодВозвратаДиалога.Нет Тогда
    ЗаблокированнаяНоменклатура.Добавить(ТекущаяНоменклатура);
КонецЕсли;

// 3. Включаем сканер обратно для продолжения работы
Попытка
    ПолучитьСерверТО().ПодключитьКлиента(ЭтаФорма);
Исключение
    Сообщить("Не удалось переподключить сканер штрихкодов!", СтатусСообщения.Важное);
КонецПопытки;

Решение 3: Блокировка через реквизит табличной части (Альтернативный способ)

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

Рассмотрим, как реализовать этот подход по шагам:

  1. Добавьте в табличную часть вашего документа (например, Товары) новую колонку с именем БлокировкаВвода (тип данных: Булево).
  2. На форме документа сделайте эту колонку невидимой для пользователя или установите для нее свойство ТолькоПросмотр = Истина, чтобы исключить ручное изменение.
  3. В процедуре обработки внешнего события при сканировании штрихкода выполняйте поиск строки в табличной части и проверяйте состояние флага блокировки:

Процедура ВнешнееСобытие(Источник, Событие, Данные)
    
    // ... Код поиска номенклатуры и строки в табличной части ...
    Если СтрокаТабличнойЧасти <> Неопределено Тогда
        
        // Проверяем флаг блокировки непосредственно из строки ТЧ
        Если СтрокаТабличнойЧасти.БлокировкаВвода Тогда
            Предупреждение("Внимание! Ввод по строке '" + СтрокаТабличнойЧасти.Номенклатура + "' заблокирован!");
            Возврат;
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

При получении ответа "Нет" в диалоговом окне мы просто устанавливаем этот флаг в строке табличной части:


Если Ответ = КодВозвратаДиалога.Нет Тогда
    СтрокаТабличнойЧасти.БлокировкаВвода = Истина;
КонецЕсли;

Преимущества данного подхода:

Важный нюанс: Работа в режиме без использования модальности

Если ваша конфигурация «1С:Управление торговлей 10» запущена на современной платформе «1С:Предприятие 8.3» и в свойствах конфигурации установлено значение «Режим использования модальности = Не использовать», то вызов классической функции Вопрос() приведет к аварийному завершению работы программы.

В этом случае необходимо использовать асинхронный метод ПоказатьВопрос(). При этом логика блокировки и дальнейшего ввода переносится в отдельную процедуру-обработчик (оповещение):


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

Вызов самого вопроса в коде сканирования будет выглядеть следующим образом:


ПараметрыОповещения = Новый ОписаниеОповещения("ВопросОПродолженииЗавершен", ЭтотОбъект, Новый Структура("Номенклатура", ТекущаяНоменклатура));
ПоказатьВопрос(ПараметрыОповещения, "Количество совпадает. Продолжить ввод?", РежимДиалогаВопрос.ДаНет);

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

← На главную