При проектировании высоконагруженных систем или рабочих мест операторов (например, при сканировании товаров на складе) перед разработчиками часто встает дилемма. С одной стороны, нам нужно фиксировать каждое действие (каждый «пик» сканера) в системе для учета оборотов. С другой стороны, создавать отдельный документ на каждое сканирование кажется избыточным, так как это «замусоривает» базу данных и увеличивает её объем. Рассмотрим эту проблему подробно, проанализируем архитектурные ограничения платформы 1С:Предприятие и найдем эффективные способы решения задачи.
В исходной задаче мы сталкиваемся с желанием записывать движения в Регистр накопления (Обороты) напрямую из обработки, минуя создание документа. Давайте разберемся, почему платформа накладывает здесь строгие ограничения.
Регистр накопления в 1С — это таблица базы данных, которая жестко связана с принципом двойной записи или транзакционной целостности, обеспечиваемой документами. В структуре таблицы регистра накопления (на уровне SQL) всегда присутствует обязательное поле — ссылка на Регистратор. Без этого поля запись существовать не может.
Попытка создать набор записей регистра накопления без установки отбора по регистратору приведет к ошибке исполнения. Это сделано намеренно:
Таким образом, ответ на прямой вопрос «Можно ли писать в регистр накопления без регистратора?» — нет, технически это невозможно. Однако, наша задача — не нарушить архитектуру, а решить бизнес-проблему: посчитать количество сканирований номенклатуры, не создавая миллионы лишних документов.
Наиболее адекватным решением для задачи «посчитать, какая номенклатура сколько раз просканировалась» является использование независимого регистра сведений. В отличие от регистров накопления, регистр сведений может не иметь регистратора.
Преимущества подхода:
Давайте посмотрим, как программно реализовать запись сканирования в регистр сведений.
// Пример записи факта сканирования в независимый регистр сведений
// РС: ИсторияСканирования (Измерения: Номенклатура, Пользователь, МоментВремени; Ресурсы: Количество)
МенеджерЗаписи = РегистрыСведений.ИсторияСканирования.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Номенклатура = ТекущаяНоменклатура;
МенеджерЗаписи.Пользователь = ПараметрыСеанса.ТекущийПользователь;
// Чтобы записи были уникальными, используем Текущую дату со временем
МенеджерЗаписи.МоментВремени = ТекущаяДата();
МенеджерЗаписи.Количество = 1;
Попытка
МенеджерЗаписи.Записать();
Исключение
// Обработка ошибки, если запись с таким ключом уже есть (маловероятно при точности до секунды, но возможно)
КонецПопытки;
Для получения отчетов по такому регистру можно использовать запрос с группировкой и функцией СУММА(Количество) или КОЛИЧЕСТВО(Номенклатура).
Если вам критически важно использовать именно Регистр накопления (например, для использования виртуальных таблиц оборотов в сложных отчетах СКД, где регистр сведений будет работать медленно), рассмотрим вариант с техническим регистратором.
Идея заключается в создании одного документа (например, «Лист сканирования»), в который мы будем добавлять строки. Однако здесь есть серьезный подводный камень: блокировки.
Если все пользователи будут пытаться писать в один и тот же документ (например, «Лист сканирования за 24.02.2024»), вы получите конкуренцию за ресурсы. Когда Пользователь А сканирует товар, система блокирует набор записей регистра по этому документу. В этот момент Пользователь Б не сможет записать свое сканирование и получит ошибку таймаута блокировки.
Как сделать правильно:
ЛистСканирования, ответственным в котором является текущий пользователь.Разберем алгоритм работы с буферизацией:
// В модуле формы обработки
&НаКлиенте
Процедура ПриСканированииШтрихкода(Штрихкод, Тип)
// 1. Просто добавляем строку в таблицу на форме (это мгновенно)
НоваяСтрока = Объект.Товары.Добавить();
НоваяСтрока.Номенклатура = НайтиНоменклатуруПоШтрихкоду(Штрихкод);
НоваяСтрока.Количество = 1;
// 2. Опционально: Визуально показываем пользователю, что товар добавлен
КонецПроцедуры
&НаКлиенте
Процедура ЗавершитьРаботу()
// 3. Сбрасываем данные в базу только в конце сессии или по кнопке
СохранитьДанныеВДокументНаСервере();
КонецПроцедуры
&НаСервере
Процедура СохранитьДанныеВДокументНаСервере()
// Создаем документ-регистратор
Док = Документы.СводныйЛистСканирования.СоздатьДокумент();
Док.Дата = ТекущаяДата();
Док.Ответственный = ПараметрыСеанса.ТекущийПользователь;
// Переносим данные из обработки в документ
Для Каждого Стр Из Объект.Товары Цикл
НовСтр = Док.Товары.Добавить();
ЗаполнитьЗначенияСвойств(НовСтр, Стр);
КонецЦикла;
// Проводим документ - движения попадают в Регистр Накопления
Док.Записать(РежимЗаписиДокумента.Проведение);
// Очищаем форму
Объект.Товары.Очистить();
КонецПроцедуры
Этот метод (накопление в памяти и сброс пакетом) решает сразу две проблемы: отсутствие мусора (один документ вместо тысячи) и отсутствие блокировок (так как запись происходит редко).
Если задача стоит максимально узко — «просто знать, сколько раз сканировали этот товар за всё время» и история по дням не нужна, можно рассмотреть добавление реквизита в сам справочник Номенклатура или в регистр сведений Штрихкоды.
Однако, этот метод крайне не рекомендуется для активной работы по следующим причинам:
Номенклатура (для увеличения счетчика на +1) вы блокируете этот товар. Другие пользователи не смогут его изменить или выбрать в документах в этот момент.Давайте подытожим варианты решения задачи подсчета сканирований:
Регистр накопления + Документ на каждый чих:
Плюсы: Стандартная схема.
Минусы: Огромный объем базы, медленная работа при массовом создании документов.
Независимый регистр сведений (Рекомендуемо для статистики):
Плюсы: Быстрая запись, нет блокировок, нет лишних документов.
Минусы: Нет готовых таблиц «Обороты» (нужно писать запросы с агрегацией).
Обработка с буфером + Сводный документ (Рекомендуемо для учета):
Плюсы: Данные попадают в «правильные» регистры накопления, база чистая.
Минусы: Риск потери данных, если у пользователя выключится свет до нажатия кнопки «Сохранить».
Не пытайтесь обмануть платформу и писать в регистр накопления без регистратора. Это невозможно. Для простых задач логирования действий используйте Независимые периодические регистры сведений. Если же данные должны участвовать в полноценном товародвижении, используйте паттерн отложенной записи: накапливайте данные в форме обработки (или во временном хранилище) и создавайте один сводный документ-регистратор раз в час или в конце смены.