Как правильно дозаполнять и пересчитывать табличную часть документа при записи или загрузке в 1С?

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

При интеграции 1С с внешними системами или мобильными приложениями часто возникает задача: при загрузке документа (например, заказа покупателя) необходимо программно пересчитать или дозаполнить цены и суммы в табличной части. Попытка реализовать эту логику в событии ПередЗаписью() модуля объекта может привести к тому, что при первом создании документ записывается со старыми ценами, а пересчет происходит только при повторном сохранении.

Рассмотрим подробнее причины такого поведения и проанализируем правильные способы решения этой задачи.

Выясним причину: почему пересчет не работает при первой записи нового документа

Существует три основные причины, почему обработчик ПередЗаписью() отрабатывает некорректно при первичной загрузке документа:

  1. Обращение к данным через ссылку нового объекта. В процедуре ПередЗаписью() разработчики часто пишут запросы к базе данных или обращаются к свойствам объекта через Ссылка. Однако у нового, еще не записанного документа ссылки в базе данных нет (она либо пустая, либо еще не зафиксирована в БД). Запрос к таблицам базы данных возвращает бывшие (пустые) данные. При повторной записи документ уже существует в системе, и запрос находит сохраненную строки.
  2. Использование режима загрузки данных. Если при создании документа из внешнего источника устанавливается флаг ОбменДанными.Загрузка = Истина, в типовых и кастомных конфигурациях в начале ПередЗаписью() стоит прерывание выполнения:
    
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;
    
    В этом случае код алгоритма пересчета просто пропускается платформой.
  3. Пустая дата документа. Если срез последних цен в регистре сведений выполняется на дату документа, а у нового объекта реквизит Дата еще не сформирован или равен 01.01.0001, запрос возвращает пустые цены. При повторной записи дата уже заполнена, и цены успешно извлекаются.

Способ 1. Выполнение логики пересчета в месте создания документа (Рекомендуемый)

Наиболее правильный архитектурный подход — пересчитывать цены не в событии записи самого документа, а в обработке загрузки данных с мобильного устройства или через заполнение объекта.

Если пересчет логически привязан к процессу загрузки, его следует выполнять непосредственно при формировании объекта ДокументОбъект до вызова метода Записать(), либо использовать штатный механизм Заполнить() и обработчик ОбработкаЗаполнения() в модуле объекта.

Чтобы разделить обычное интерактивное создание документа пользователем и программную загрузку, можно передавать контекст через структуру дополнительных свойств:


// В обработке загрузки данных с мобильного устройства:
НовыйДокумент = Документы.ЗаказПокупателя.СоздатьДокумент();
// Заполнение реквизитов и табличной части...

// Передаем флаг о том, что требуется спец. пересчет
НовыйДокумент.ДополнительныеСвойства.Вставить("ПересчитатьЦеныПриЗагрузке", Истина);

// Записываем документ
НовыйДокумент.Записать(РежимЗаписиДокумента.Запись);

Это исключает случайное перезаполнение цен, когда менеджер вручную редактирует и сохраняет документ в интерфейсе 1С.

Способ 2. Использование запроса по таблице значений в событии ПередЗаписью

Если логику дозаполнения необходимо сохранить именно в модуле объекта перед записью, нельзя делать запрос к табличным частям самого документа через физические таблицы базы данных. Посмотрим на пример, как правильно передать текущие незаписанные строки документа в запрос с помощью выгрузки в таблицу значений.

Разберем по шагам алгоритм работы с незаписанной табличной частью:

  1. Выгружаем табличную часть объекта в локальную таблицу значений: ТЗ = ТабличнаяЧасть.Выгрузить();.
  2. Передаем полученную таблицу значений в качестве параметра запроса &ТЗ.
  3. В тексте запроса описываем временную таблицу ПОМЕСТИТЬ тзДок ИЗ &тзДок и соединяем её с нужными справочниками или регистрами сведений (например, со срезом последних регистра цен).
  4. По результатам запроса обходим строки исходной табличной части объекта и обновляем необходимые реквизиты (Цена, Сумма).

Рассмотрим пример реализации такой процедуры в модуле объекта:


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

Процедура ПересчитатьЦеныПоДоговору()
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |    ТЗ.НомерСтроки КАК НомерСтроки,
    |    ТЗ.Номенклатура КАК Номенклатура
    |ПОМЕСТИТЬ тзДокумента
    |ИЗ
    |    &ТЗ КАК ТЗ
    |;
    |
    |////////////////////////////////////////////////////////////////////////////////
    |ВЫБРАТЬ
    |    тзДокумента.НомерСтроки КАК НомерСтроки,
    |    ЕСТЬNULL(Цены.Цена, 0) КАК НоваяЦена
    |ИЗ
    |    тзДокумента КАК тзДокумента
    |    ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&Период, ВидЦены = &ВидЦены) КАК Цены
    |        ПО тзДокумента.Номенклатура = Цены.Номенклатура
    |УПОРЯДОЧИТЬ ПО
    |    тзДокумента.НомерСтроки";
    
    Запрос.УстановитьПараметр("ТЗ", Товары.Выгрузить());
    Запрос.УстановитьПараметр("ВидЦены", ДоговорКонтрагента.ВидЦены);
    Запрос.УстановитьПараметр("Период", ?(ЗначениеЗаполнено(Дата), Дата, ТекущаяДата()));
    
    РезультатЗапроса = Запрос.Выполнить().Выбрать();
    
    // Обновляем данные непосредственно в табличной части объекта
    Для Каждого СтрИзТЧ Из Товары Цикл
        Отбор = Новый Структура("НомерСтроки", СтрИзТЧ.НомерСтроки);
        Если РезультатЗапроса.НайтиСледующий(Отбор) Тогда
            СтрИзТЧ.Цена = РезультатЗапроса.НоваяЦена;
            // Важно также пересчитывать зависимые колонки суммы и НДС
            СтрИзТЧ.Сумма = СтрИзТЧ.Количество * СтрИзТЧ.Цена;
        КонецЕсли;
    КонецЦикла;
    
КонецПроцедуры

Важные нюансы при программном изменении табличных частей

← На главную