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