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