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