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