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