При разработке отчетов в среде 1С:Предприятие 7.7 программисты часто сталкиваются с ситуацией, когда код работает корректно при одних настройках фильтров и выдает критическую ошибку при других. Одной из самых коварных ошибок является: "Операции сравнения на больше-меньше допустимы только над значениями совпадающих базовых типов". Рассмотрим подробнее, почему это происходит и как правильно организовать выборку данных по залежалому товару.
Проанализируем ситуацию. Ошибка возникает в строке Если ВыбДата > Д Тогда. В языке 1С 7.7 типизация переменных является мягкой, однако операции сравнения требуют, чтобы оба операнда принадлежали к одному базовому типу (число, строка или дата). Если в переменной ВыбДата хранится значение типа "Дата", а переменная Д по какой-то причине не была инициализирована (имеет тип Empty или Undefined), система не сможет выполнить сравнение и прервет работу программы.
Почему переменная Д оказывается пустой? Разберем по шагам логику отчета. Автор использует объект Периодический для получения даты последнего изменения цены (реквизит Прих_Цена). Если для конкретного товара в истории значений этого реквизита нет записей (например, после свертки базы или некорректного импорта остатков), цикл Пока Пер.ПолучитьЗначение() = 1 Цикл ни разу не выполнится. В результате переменная Д останется неопределенной.
Самый простой и эффективный способ избежать ошибки — гарантировать, что переменная Д всегда будет иметь тип "Дата", даже если история значений пуста. Для этого перед началом перебора периодических значений нужно присвоить ей пустое значение соответствующего типа.
Рассмотрим пример исправленного кода:
// Создаем объект для работы с типами
ПустаяДата = ПолучитьПустоеЗначение("Дата");
Пока Запрос.Группировка(1) = 1 Цикл
Тов = Запрос.Товар;
СпрТов.НайтиЭлемент(Тов);
Тов = СпрТов.ТекущийЭлемент();
Пер.ИспользоватьОбъект("Прих_Цена", Тов);
Пер.ВыбратьЗначения();
// Инициализируем переменную перед циклом
Д = ПустаяДата;
Пока Пер.ПолучитьЗначение() = 1 Цикл
Д = Пер.ДатаЗнач;
КонецЦикла;
// Теперь сравнение всегда безопасно, так как Дата сравнивается с Датой
Если ВыбДата > Д Тогда
Таб.ВывестиСекцию("Товар");
СуммаИтог = СуммаИтог + Сумма;
КонецЕсли;
КонецЦикла;
В исходном коде используется цикл для поиска последней даты изменения значения. В 1С 7.7 это не самый оптимальный метод. Если нам нужно только последнее значение на определенный момент времени, лучше использовать специализированные методы. Это не только сократит код, но и сделает его более читаемым.
Посмотрим на пример использования метода ЗначениеНаДату или оптимизированного перебора:
Периодический можно обращаться напрямую к справочнику, если реквизит определен как периодический.Проанализируем пример с оптимизацией цикла:
Пер.ИспользоватьОбъект("Прих_Цена", Тов);
// Устанавливаем обратный порядок, чтобы получить последнюю дату сразу
Пер.ОбратныйПорядок(1);
Пер.ВыбратьЗначения();
Д = ПустаяДата;
Если Пер.ПолучитьЗначение() = 1 Тогда
Д = Пер.ДатаЗнач;
КонецЕсли;
// Цикл больше не нужен, мы получили последнюю дату за одну итерацию
Рассмотрим ситуацию с точки зрения производительности. В представленном алгоритме объект Периодический создается один раз вне цикла, но методы ВыбратьЗначения() и ИспользоватьОбъект() вызываются внутри цикла по товарам. Это классическая проблема "N+1", когда для каждой строки отчета программа делает отдельный запрос к файловой базе данных (или SQL-серверу).
Рекомендация для больших баз:
ТаблицуЗначений.ОстаткиТоваров.Чтобы окончательно обезопасить свой код от подобных падений, мы можем использовать функцию ТипЗначения(). Это полезно, когда вы не уверены, какие данные придут из внешней обработки или сложного запроса.
// Тип 3 в 1С 7.7 соответствует типу "Дата"
Если (ТипЗначения(ВыбДата) = 3) И (ТипЗначения(Д) = 3) Тогда
Если ВыбДата > Д Тогда
// Логика вывода
КонецЕсли;
Иначе
Сообщить("Ошибка: Неверный тип данных для сравнения дат!");
КонецЕсли;
Подведем итог: главная причина ошибки крылась в отсутствии инициализации переменной. Всегда инициализируйте переменные, которые используются в условиях сравнения, особенно если они заполняются внутри циклов Пока ... Получить...() = 1. Использование ПолучитьПустоеЗначение("Дата") — это стандарт де-факто для обеспечения стабильности кода в 1С 7.7.