Как скопировать товары из Заказа покупателя в Реализацию товаров и услуг в 1С:УПП 1.3?

Программист 1С v8.3 (Обычные формы) Управленческий учет IT и автоматизация бизнеса
← На главную

При доработке типовых конфигураций 1С, особенно таких масштабных систем, как «Управление производственным предприятием» (УПП 1.3), разработчики часто сталкиваются с задачей заполнения документов на основании других объектов. Классический пример — создание документа РеализацияТоваровУслуг на основании ЗаказПокупателя.

В этой статье мы подробно разберем, как правильно написать запрос к табличной части документа-основания, перенести данные в новый документ, избежать распространенных ошибок кодинга и обойти методологические «подводные камни», связанные с учетом остатков и резервов в УПП.

Постановка задачи и анализ базовой ошибки

Рассмотрим ситуацию: нам необходимо программно скопировать данные из табличной части Товары документа ЗаказПокупателя в аналогичную табличную часть создаваемого документа РеализацияТоваровУслуг. Нам нужно написать процедуру, которая будет вызываться, например, в обработчике ОбработкаЗаполнения.

Начинающие разработчики часто совершают концептуальную ошибку в тексте запроса, пытаясь выбрать данные из табличной части самого создаваемого документа реализации, а не документа-основания. Посмотрим на ошибочный вариант запроса:


// ОШИБОЧНЫЙ ЗАПРОС: выборка идет из документа реализации!
Запрос.Текст = 
"ВЫБРАТЬ
|	Товары.Номенклатура,
|	Товары.Количество
|ИЗ
|	Документ.РеализацияТоваровУслуг.Товары КАК Товары";

Проанализируем этот код. Этот запрос пытается прочитать данные из еще не записанного (или вообще из всех существующих) документа реализации. Чтобы получить данные из источника, запрос должен быть адресован к табличной части документа-основания (в нашем случае — к ЗаказПокупателя), а в качестве условия необходимо передать конкретную ссылку на этот документ.

Решение №1: Исправленный запрос к документу-основанию и построчный перенос

Для корректного получения данных мы должны обратиться к таблице Документ.ЗаказПокупателя.Товары и установить фильтр по реквизиту Ссылка. Разберем правильную реализацию этого механизма по шагам.

Рассмотрим готовый программный код процедуры переноса данных:


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

Разбор критических ошибок в коде автора темы

Если обратить внимание на итоговый код автора в Сообщении 8, то там присутствует грубая опечатка:


НоваяСтрока.Номенклатура = Выборка.НомерСтроки;
НоваяСтрока.Номенклатура = Выборка.Номенклатура;

В первой строчке переменной с типом «СправочникСсылка.Номенклатура» ошибочно присваивается числовой номер строки. Во второй строчке эта ошибка перезаписывается правильной ссылкой, однако такой код засоряет систему, замедляет работу и может вызвать предупреждения при проверке конфигурации. Пишите код аккуратно!

Оптимизация кода с помощью функции ЗаполнитьЗначенияСвойств()

Для того чтобы сделать код более компактным, надежным и избавиться от необходимости вручную прописывать каждую связь «приемник-источник», мы можем использовать встроенную функцию платформы 1С ЗаполнитьЗначенияСвойств(). Она автоматически сопоставляет имена реквизитов в выборке запроса и в строке табличной части документа реализации.

Посмотрим, насколько лаконичнее становится обход выборки:


Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
	НоваяСтрока = Товары.Добавить();
	// Платформа сама найдет совпадающие поля и перенесет их значения
	ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
	
	// Вручную заполняем только те реквизиты, которых не было в выборке Заказа
	НоваяСтрока.ЗаказПокупателя = ДокументОснование;
КонецЦикла;

Решение №2: Использование метода Загрузить() и его риски

В ходе обсуждения на форуме прозвучало предложение полностью отказаться от цикла выборки и загрузить данные напрямую через Таблицу Значений (ТЗ):


ТЗТовары = РезультатЗапроса.Выгрузить();
Товары.Загрузить(ТЗТовары);

Выясним преимущества и недостатки этого метода.

Преимущества:

Недостатки и опасности метода:

Методологические риски принудительного копирования строк в УПП

В сообщении №7 автор признается, почему он решил переписать стандартное заполнение: «Заполнить по заказу покупателя документ реализация товаров и услуг не переносит все товары из табличной части (что-то с остатками, программа хочет каких-то настроек)».

Внимание! Это критическая методологическая ошибка. Давайте разберемся, почему стандартный типовой механизм УПП «выкидывал» часть товаров при заполнении.

При вводе Реализации на основании Заказа система производит сложный анализ регистров накопления:

  1. «Товары на складах» — проверяется фактическое физическое наличие товара на выбранном складе.
  2. «Товары в резерве на складах» — проверяется, зарезервирован ли этот товар под данный конкретный Заказ Покупателя.
  3. «Заказы покупателей» — анализируется остаток по самому заказу (сколько штук еще осталось отгрузить с учетом ранее созданных реализаций).

Если программа «не переносит» какие-то товары, это означает одно из трех:

К чему приведет «принудительный» обход контроля?

Если мы жестко скопируем строки кодом напрямую, мы столкнемся со следующими проблемами:

Как решить проблему правильно (без «костылей» в коде)?

Если бизнес-процесс компании действительно требует выписки накладных до фактического поступления товаров, правильнее использовать штатные настройки системы, а не ломать логику кодом:

  1. Снятие неактуальных резервов: Регулярно проводите очистку резервов с помощью документа Закрытие заказов покупателей.
  2. Ордерная схема учета: Настройте склад как ордерный. Тогда финансовый документ РеализацияТоваровУслуг будет выписываться без жесткого контроля остатков, а фактическое списание и контроль физического наличия на складе возьмет на себя расходный ордер, проводимый кладовщиком.
  3. Корректное указание счетов учета: При программном добавлении строк в Реализацию обязательно вызывайте типовую процедуру УПП для заполнения счетов учета (например, 41.01, 90.01.1 и т.д.):
    
    // Пример вызова заполнения счетов в УПП
    ЗаполнениеУчетныхДанных.ЗаполнитьСчетаУчетаВТЧ(НоваяСтрока, "Товары", ЭтотОбъект);
    
    Без этого документ не сформирует проводки по бухгалтерскому и налоговому учету.

Рассмотрев все варианты, мы пришли к выводу, что простое копирование строк — это лишь вершина айсберга. При работе с УПП всегда важно помнить о движениях по регистрам и сохранении целостности партионного учета!

← На главную