В работе с документами часто возникает необходимость в дополнительных проверках для избежания ошибок. Распространенная задача — контроль соответствия дат связанных документов. Например, важно, чтобы дата выписанного счета-фактуры совпадала с датой документа отгрузки (реализации). Давайте пошагово разберемся, как реализовать такую проверку в конфигурации "1С: Управление торговлей 11" при записи документа "Счет-фактура выданный".
Цель — добавить в систему механизм, который при попытке записать или провести счет-фактуру будет сравнивать его дату с датой документа-основания. Если даты не совпадают, система должна вывести пользователю сообщение об ошибке и отменить запись.
Самая очевидная идея — добавить код в обработчик события ПередЗаписью в модуле объекта или формы. Начнем с простого условия, которое было предложено на форуме.
Первоначальный вариант кода:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если НачалоДня(Дата) <> НачалоДня(ДокументОснование.Дата) Тогда
Сообщить("Даты документов с/ф и документа-основания разные!");
Отказ = Истина;
Возврат;
КонецЕсли;
КонецПроцедуры
При попытке использовать этот код можно столкнуться с ошибкой "Преобразование значения к типу Дата не может быть выполнено". Почему она возникает? Проблема в том, что счет-фактуру можно создать не на основании другого документа, а как самостоятельный документ. В этом случае реквизит ДокументОснование будет не заполнен, и попытка получить у него свойство .Дата приведет к ошибке.
Вывод: Перед обращением к свойствам реквизита-ссылки всегда нужно проверять, заполнена ли сама ссылка.
Усовершенствуем код, добавив проверку на заполненность документа-основания с помощью функции ЗначениеЗаполнено(). Также учтем, что при написании кода в модуле управляемой формы, для доступа к реквизитам документа необходимо обращаться к основному реквизиту формы, который по умолчанию называется Объект.
Скорректированный код для модуля формы:
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
Если ЗначениеЗаполнено(Объект.ДокументОснование) Тогда
Если НачалоДня(Объект.Дата) <> НачалоДня(Объект.ДокументОснование.Дата) Тогда
Сообщить("Даты документов с/ф и документа-основания разные!");
Отказ = Истина;
Возврат;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот код уже гораздо лучше: он проверяет наличие основания и правильно обращается к реквизитам документа через Объект. Однако и он может вызывать ошибку, но уже другую: "Поле объекта не обнаружено (Дата)". Это происходит из-за особенностей клиент-серверного взаимодействия в управляемых формах. Событие ПередЗаписью на клиенте не всегда имеет полный доступ к данным связанных объектов (в нашем случае, к дате документа-основания). Для надежного получения данных из базы необходимо выполнить серверный вызов.
Чтобы корректно получить дату документа-основания, нужно вынести логику проверки на сервер. Сделаем это, создав отдельную серверную процедуру и вызвав ее из клиентского обработчика ПередЗаписью.
ПроверкаДатНаСервере.ПередЗаписью вызываем эту серверную процедуру.Пример реализации:
&НаСервере
Процедура ПроверкаДатНаСервере(Отказ)
Если ЗначениеЗаполнено(Объект.ДокументОснование) Тогда
// На сервере мы гарантированно получаем доступ к данным объекта-основания
ДокументОснованиеСсылка = Объект.ДокументОснование;
Если НачалоДня(Объект.Дата) <> НачалоДня(ДокументОснованиеСсылка.Дата) Тогда
Сообщить("Даты документов с/ф и документа-основания разные!");
Отказ = Истина;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
// Вызываем серверную процедуру для выполнения проверки
ПроверкаДатНаСервере(Отказ);
Если Отказ Тогда
Возврат; // Если проверка не пройдена, прерываем запись
КонецЕсли;
КонецПроцедуры
Это решение является полностью рабочим. Клиентская процедура инициирует проверку, а вся логика и доступ к данным выполняются на сервере, где это наиболее эффективно и надежно. Параметр Отказ передается по ссылке, поэтому его изменение на сервере повлияет на ход выполнения клиентской процедуры.
Хотя предыдущее решение работает, его можно сделать еще лучше и эффективнее. В управляемых формах существует специальное событие ПередЗаписьюНаСервере, которое идеально подходит для подобных проверок. Его использование избавляет от необходимости делать лишний вызов "клиент-сервер" и делает код более лаконичным и производительным.
В этом обработчике нам доступен параметр ТекущийОбъект, который представляет собой записываемый объект документа со всеми его данными.
Рекомендуемый код для модуля формы документа "СчетФактураВыданный":
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Проверяем, что документ-основание указан
Если ЗначениеЗаполнено(ТекущийОбъект.ДокументОснование) Тогда
// Сравниваем даты, приведенные к началу дня для корректности
Если НачалоДня(ТекущийОбъект.Дата) <> НачалоДня(ТекущийОбъект.ДокументОснование.Дата) Тогда
// Формируем информативное сообщение для пользователя
ТекстСообщения = "Дата счета-фактуры (" + Формат(ТекущийОбъект.Дата, "ДФ=dd.MM.yyyy") +
") не совпадает с датой документа-основания (" + Формат(ТекущийОбъект.ДокументОснование.Дата, "ДФ=dd.MM.yyyy") + "). Запись отменена.";
// Используем стандартный механизм вывода сообщений
ОбщегоНазначения.СообщитьПользователю(ТекстСообщения);
// Отменяем запись документа
Отказ = Истина;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Почему этот вариант лучше:
Таким образом, пройдя через несколько этапов отладки и усовершенствования, мы пришли к элегантному и правильному с точки зрения архитектуры решению для контроля дат связанных документов. Для смежной задачи контроля цепочки документов можно использовать расширение для запрета печати реализации без введенного счета-фактуры.