В процессе автоматизации учета часто возникает задача контроля данных перед проведением документов. Типичный пример — необходимость убедиться, что в системе установлен курс валюты на текущий день, особенно если расчеты ведутся в иностранных валютах, а оплата происходит в рублях. Без актуального курса суммы в регистрах могут отразиться некорректно, что приведет к ошибкам в финансовой отчетности.
В этой статье мы подробно разберем, как написать эффективный программный код для проверки курсов валют, проанализируем типичные ошибки начинающих разработчиков и рассмотрим лучшие практики реализации подобных проверок в конфигурациях на базе 1С:Предприятие 8.3 (таких как УТ 11, ERP 2.5 или КА 2).
Представим ситуацию: пользователь хочет, чтобы при проведении документа СписаниеБезналичныхДС система проверяла наличие курса валюты. При этом должны соблюдаться следующие условия:
Рассмотрим первую попытку реализации, которую часто можно встретить на форумах. Основная проблема здесь заключается в неправильном обращении к результатам запроса и неверном использовании функций.
Проанализируем ситуацию, когда Отказ срабатывает всегда. Это происходит, если разработчик пытается вызвать функцию как переменную или не учитывает, что СрезПоследних без параметров может вернуть старые данные, которые не актуальны на текущий момент. Кроме того, использование метода Количество() в выборке не всегда надежно, если запрос может вернуть NULL при определенных типах соединений.
Для того чтобы проверка работала корректно, нам необходимо соединить данные договора (для проверки условий оплаты) и данные регистра сведений КурсыВалют. Посмотрим на оптимизированный вариант запроса, который эффективно решает эту задачу:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| КурсыВалютСрезПоследних.Курс КАК Курс
|ИЗ
| РегистрСведений.КурсыВалют.СрезПоследних(
| &ДатаСреза,
| Валюта В
| (ВЫБРАТЬ
| ДоговорыКонтрагентов.ВалютаВзаиморасчетов КАК ВалютаВзаиморасчетов
| ИЗ
| Справочник.ДоговорыКонтрагентов КАК ДоговорыКонтрагентов
| ГДЕ
| ДоговорыКонтрагентов.Ссылка = &СсылкаДоговора
| И ДоговорыКонтрагентов.ПорядокОплаты = ЗНАЧЕНИЕ(Перечисление.ПорядокОплатыПоСоглашениям.РасчетыВВалютеОплатаВРублях))) КАК КурсыВалютСрезПоследних";
Запрос.УстановитьПараметр("ДатаСреза", КонецДня(Объект.Дата));
Запрос.УстановитьПараметр("СсылкаДоговора", Объект.Договор);
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
// Если результат пустой, значит либо условия по договору не совпали,
// либо курса на указанную дату нет.
КонецЕсли;
В данном примере мы используем вложенный запрос для фильтрации валюты договора. Это позволяет избежать лишних соединений таблиц в основной части запроса и повышает производительность. Обратите внимание на параметр &ДатаСреза — крайне важно передавать туда именно дату документа (или текущую дату), приведенную к концу дня, чтобы виртуальная таблица СрезПоследних отработала корректно.
Теперь разберем, как встроить эту проверку в обработчик проведения ОбработкаПроведения. Мы должны не просто сообщить об ошибке, но и установить флаг Отказ = Истина, чтобы транзакция проведения была отменена.
Рассмотрим пошагово алгоритм действий в коде:
Процедура ОбработкаПроведения(Отказ, РежимПроведения)
// 1. Проверяем, заполнена ли валюта и не является ли она рублем
Если Объект.Валюта = Константы.ВалютаРегламентированногоУчета.Получить() Тогда
Возврат;
КонецЕсли;
// 2. Выполняем проверку курса через запрос
// (Используем текст запроса из примера выше)
// ... установка параметров запроса ...
Результат = Запрос.Выполнить();
Если Результат.Пустой() Тогда
Отказ = Истина;
ТекстСообщения = СтрШаблон("На дату %1 не задан курс валюты %2! Проведение невозможно.",
Формат(Объект.Дата, "ДЛФ=D"), Объект.Валюта);
// Используем стандартный метод БСП для вывода сообщения
ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщения, , "Объект.Валюта");
КонецЕсли;
КонецПроцедуры
Проанализируем несколько моментов, которые часто упускают из виду:
1. Актуальность курса против наличия записи
Виртуальная таблица СрезПоследних возвращает последнюю доступную запись в регистре. Если курс доллара был загружен три дня назад, а сегодня — нет, запрос все равно вернет значение трехдневной давности. Если политика компании требует наличия курса именно на сегодня, в запрос необходимо добавить условие:
ГДЕ КурсыВалютСрезПоследних.Период = &НачалоДня
2. Кратность валюты
Курс валюты в 1С всегда работает в паре с кратностью. Например, курс может быть 80, а кратность 100. При проверке курса рекомендуется также проверять, что Кратность в регистре заполнена и больше нуля, иначе при расчетах возникнет ошибка деления на ноль.
3. Использование типовых функций (БСП)
Если вы работаете в современной конфигурации, нет необходимости писать запросы к регистру КурсыВалют вручную. В составе Библиотеки Стандартных Подсистем (БСП) есть общий модуль РаботаСКурсамиВалют. Выясним, как упростить код с его помощью:
// Получаем актуальный курс через типовой механизм
СтруктураКурса = РаботаСКурсамиВалют.ПолучитьКурсВалюты(Объект.Валюта, Объект.Дата);
Если СтруктураКурса.Курс = 0 Тогда
// Курс не найден или равен 0
Отказ = Истина;
КонецЕсли;
Использование типовых методов предпочтительнее, так как они автоматически учитывают особенности расчета валют (например, если курс одной валюты рассчитывается на основании курса другой).
Мы проанализировали процесс создания проверки курса валют при проведении документов. Помните, что качественная проверка должна быть не только функциональной, но и информативной для пользователя. Всегда указывайте в сообщении об ошибке, какой именно валюты не хватает и на какую дату. Это сэкономит время бухгалтера и снизит нагрузку на службу технической поддержки.
Рекомендуется размещать подобные проверки в процедуре ОбработкаПроверкиЗаполнения или ПередЗаписью, если вы хотите предотвратить сохранение некорректных данных еще до момента начала проведения документа. Это обеспечит более "отзывчивый" интерфейс и лучший пользовательский опыт (UX).