Как запретить установку даты документа Поступление доп. расходов раньше даты документа-основания в 1С:УПП?

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

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

Зачем нужна такая проверка?

Прежде чем переходить к коду, выясним причину жестких требований к датам. В 1С:УПП при проведении документа ПоступлениеДопРасходов система должна распределить сумму затрат на партии товаров, созданные документом ПоступлениеТоваровУслуг. Если дата «допки» будет меньше даты «поступления», возникнет логическая коллизия: документ пытается увеличить стоимость партии, которая на этот момент времени в системе еще не существует. Это приводит к некорректному заполнению регистров накопления и сложностям при закрытии месяца.

Вариант 1. Простой обход табличной части (Цикл)

Рассмотрим самый очевидный способ — перебор строк табличной части документа в обработчике ПередЗаписью. Этот метод прост в реализации и понятен начинающим разработчикам.

Проанализируем пример кода:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Пропускаем проверку, если документ помечен на удаление
    Если ЭтотОбъект.ПометкаУдаления Тогда
        Возврат;
    КонецЕсли;

    Для Каждого СтрокаТЧ Из Товары Цикл
        // Проверяем, заполнено ли поле документа партии
        Если ЗначениеЗаполнено(СтрокаТЧ.ДокументПартии) Тогда
            // Сравниваем даты
            Если СтрокаТЧ.ДокументПартии.Дата > ЭтотОбъект.Дата Тогда
                Отказ = Истина;
                Сообщить("Ошибка: Дата документа партии (" + СтрокаТЧ.ДокументПартии + ") позже даты текущего документа!");
                Прервать; // Достаточно одной ошибки, чтобы отменить запись
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;

КонецПроцедуры

Преимущества: простота написания и отладки.

Недостатки: низкая производительность при большом количестве строк в табличной части. При обращении через точку СтрокаТЧ.ДокументПартии.Дата система выполняет отдельный запрос к базе данных для каждой строки, что может существенно замедлить запись документа.

Вариант 2. Использование запроса (Оптимальный подход)

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

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


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|	ПоступлениеДопРасходовТовары.НомерСтроки КАК НомерСтроки,
|	ПоступлениеДопРасходовТовары.ДокументПартии КАК ДокументПартии
|ИЗ
|	Документ.ПоступлениеДопРасходов.Товары КАК ПоступлениеДопРасходовТовары
|ГДЕ
|	ПоступлениеДопРасходовТовары.Ссылка = &Ссылка
|	И ПоступлениеДопРасходовТовары.ДокументПартии.Дата > &ДатаДокумента";

Запрос.УстановитьПараметр("Ссылка", Ссылка);
// Важно: берем Дату объекта, так как в базе она может быть еще старой
Запрос.УстановитьПараметр("ДатаДокумента", Дата);

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

Если Не РезультатЗапроса.Пустой() Тогда
    Выборка = РезультатЗапроса.Выбрать();
    Пока Выборка.Следующий() Цикл
        Сообщить("Строка №" + Выборка.НомерСтроки + ": Дата поступления " + Выборка.ДокументПартии + " больше даты текущего документа!");
    КонецЦикла;
    Отказ = Истина;
КонецЕсли;

Важный нюанс: В запросе используется обращение ПоступлениеДопРасходовТовары.Ссылка = &Ссылка. Однако в обработчике ПередЗаписью, если документ еще ни разу не был записан, Ссылка может быть пустой. Кроме того, запрос обращается к данным в базе данных, а не к тем, которые пользователь только что ввел на экране.

Вариант 3. Проверка через передачу таблицы значений в запрос

Чтобы проверка была максимально точной и учитывала изменения, внесенные пользователем до момента нажатия кнопки «Записать», рекомендуется передавать саму табличную часть объекта в запрос как параметр. Это «эталонный» способ для подобных задач.

Рассмотрим пошагово реализацию этого метода:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)

    // Если идет загрузка данных, проверку можно пропустить
    Если ОбменДанными.Загрузка Тогда
        Возврат;
    КонецЕсли;

    Запрос = Новый Запрос;
    // Передаем таблицу товаров текущего объекта в запрос
    Запрос.УстановитьПараметр("ТаблицаТовары", Товары.Выгрузить());
    Запрос.УстановитьПараметр("ТекущаяДата", Дата);

    Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТЧ.НомерСтроки КАК НомерСтроки,
    |	ТЧ.ДокументПартии КАК ДокументПартии
    |ПОМЕСТИТЬ ВТ_Товары
    |ИЗ
    |	&ТаблицаТовары КАК ТЧ
    |ГДЕ
    |	ТЧ.ДокументПартии <> ЗНАЧЕНИЕ(Документ.ПоступлениеТоваровУслуг.ПустаяСсылка)
    |;
    |
    |ВЫБРАТЬ
    |	ВТ.НомерСтроки,
    |	ВТ.ДокументПартии
    |ИЗ
    |	ВТ_Товары КАК ВТ
    |ГДЕ
    |	ВТ.ДокументПартии.Дата > &ТекущаяДата";

    Результат = Запрос.Выполнить();
    Если Не Результат.Пустой() Тогда
        Отказ = Истина;
        Выборка = Результат.Выбрать();
        Пока Выборка.Следующий() Цикл
            ТекстОшибки = СтрШаблон("Ошибка в строке %1: документ партии имеет дату позже текущей!", Выборка.НомерСтроки);
            Сообщить(ТекстОшибки);
        КонецЦикла;
    КонецЕсли;

КонецПроцедуры

Тонкости использования Момента Времени

Проанализируем ситуацию, когда оба документа созданы в один и тот же день. В 1С дата включает в себя время. Если ПоступлениеТоваровУслуг создано в 12:00:00, а ПоступлениеДопРасходов в 11:59:59, простая проверка дат может не сработать визуально (оба документа от одного числа), но для системы это критично.

В некоторых случаях правильно сравнивать не просто даты, а МоментВремени(). Объект МоментВремени включает в себя дату, время и ссылку на документ, что позволяет однозначно определить последовательность даже в рамках одной секунды. Однако в запросах напрямую сравнивать моменты времени нельзя — там используются именно даты.

Где лучше разместить проверку?

Рассмотрим альтернативные места для размещения кода:

  1. ОбработкаПроверкиЗаполнения: Это более современный подход (для платформы 8.2 и выше). Если вынести логику сюда, система сама подсветит красным строки с ошибками. Но стоит помнить, что этот обработчик в УПП на обычных формах используется реже, чем ПередЗаписью.
  2. ОбработкаПроведения: Самый строгий вариант. Даже если пользователь ухитрится записать документ, он не сможет его провести. Поскольку именно проведение влияет на учет, это гарантирует чистоту данных.
  3. Подписка на событие: Если вы не хотите вносить изменения в типовую конфигурацию (оставляя её на поддержке), создайте подписку на событие ПередЗаписью для документа ПоступлениеДопРасходов. Логика кода останется такой же, как в примере с запросом.

Проблема обратного изменения

Выясним еще одну важную деталь: что мешает пользователю сначала правильно записать «Доп. расходы», а потом открыть старое «Поступление товаров» и изменить его дату на более позднюю?

Чтобы закрыть эту «дыру» в безопасности, необходимо реализовать проверку и в обратную сторону. В документе ПоступлениеТоваровУслуг при изменении даты нужно проверять наличие подчиненных документов ПоступлениеДопРасходов и контролировать, чтобы новая дата не стала больше их дат. Это реализуется с помощью критериев отбора или запроса по табличным частям всех «Доп. расходов», ссылающихся на данное поступление.

Итоги

Мы проанализировали несколько способов контроля дат. Наиболее сбалансированным по производительности и удобству является Вариант 3 (запрос по переданной таблице значений). Он позволяет:

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

← На главную