При работе в системе 1С:Предприятие 7.7, особенно в конфигурации «Торговля и склад 9.2», разработчики часто сталкиваются с необходимостью анализа истории цен. Периодические реквизиты — это мощный инструмент, но их использование требует понимания того, как система хранит и выбирает данные. Типичная задача: получить не просто какую-то цену из диапазона, а именно последнюю актуальную запись за выбранный период (например, перед изменением или самую свежую в месяце).
В этой статье мы подробно разберем, почему стандартная выборка может возвращать не те данные, которые вы ожидаете, и рассмотрим несколько способов решения задачи получения последней цены.
Рассмотрим ситуацию, описанную пользователем. При использовании объекта Периодический и метода ВыбратьЗначения(ДатаНач, ДатаКон), система по умолчанию обходит записи в хронологическом порядке (от старых к новым). Если в октябре цена менялась 1-го, 15-го и 25-го числа, то при первом вызове ПолучитьЗначение() вы получите цену на 1-е число. Если ваша цель — получить цену на 25-е число, обычный цикл «Пока...» без дополнительных условий будет неэффективен или потребует полного перебора всех записей до конца.
Самый простой и эффективный способ получить последнее значение в заданном интервале — заставить систему читать историю цен «с конца». Для этого у объекта Периодический существует метод ОбратныйПорядок().
Разберем алгоритм реализации этого метода по шагам:
Периодический.Посмотрим на пример кода:
спрЦен = СоздатьОбъект("Справочник.Цены");
спрЦен.ИспользоватьВладельца(ТекущаяНоменклатура);
// Находим нужный тип цен (например, Розничная)
Если спрЦен.НайтиПоРеквизиту("ТипЦен", ВыбранныйТипЦен, 0) = 1 Тогда
Пер = СоздатьОбъект("Периодический");
Пер.ИспользоватьОбъект("Цена", спрЦен.ТекущийЭлемент());
// Устанавливаем обратный порядок выборки
Пер.ОбратныйПорядок(1);
Пер.ВыбратьЗначения(ДатаНач, ДатаКон);
Если Пер.ПолучитьЗначение() = 1 Тогда
ПоследняяЦенаВПериоде = Пер.Значение;
ДатаИзменения = Пер.ДатаЗначения;
Сообщить("Последняя цена: " + ПоследняяЦенаВПериоде + " от " + ДатаИзменения);
КонецЕсли;
КонецЕсли;
Этот метод хорош тем, что он работает очень быстро: нам не нужно просматривать всю историю, мы сразу берем верхнюю запись из отсортированного списка.
Если нам не принципиально, была ли цена физически изменена именно в этот период, а нам просто нужно знать, какая цена действовала на момент окончания периода, можно воспользоваться методом Получить(). Этот метод возвращает состояние реквизита на конкретную дату.
Проанализируем пример:
ЦенаНаКонец = спрЦен.Цена.Получить(ДатаКон);
Важный нюанс: Метод Получить(ДатаКон) вернет значение, актуальное на эту секунду. Если последняя запись в истории была сделана год назад, он вернет её. Если же вы хотите найти именно факт изменения внутри периода (как в Решении №1), метод Получить() может не подойти, так как он не скажет вам, когда именно цена стала такой.
Часто задача звучит иначе: «найти цену, которая была до того, как установили текущую». Для этого нужно сначала найти дату последнего изменения, а затем получить значение на один день (или секунду) раньше.
Проанализируем ситуацию по шагам:
ОбратныйПорядок(1).Пер.ДатаЗначения.Получить() на дату ДатаИзменения - 1.Пример кода для получения старой цены:
Пер = СоздатьОбъект("Периодический");
Пер.ИспользоватьОбъект("Цена", СпрЦен.ТекущийЭлемент());
Пер.ОбратныйПорядок(1);
Пер.ВыбратьЗначения(ДатаНач, ДатаКон);
Если Пер.ПолучитьЗначение() = 1 Тогда
ДатаПоследнегоИзменения = Пер.ДатаЗначения;
ТекущаяЦена = Пер.Значение;
// Получаем цену, которая была ДО этого изменения
ЦенаДоИзменения = спрЦен.Цена.Получить(ДатаПоследнегоИзменения - 1);
Разница = ТекущаяЦена - ЦенаДоИзменения;
Сообщить("Цена изменилась с " + ЦенаДоИзменения + " на " + ТекущаяЦена);
КонецЕсли;
Работа со справочниками и периодическими реквизитами в циклах может сильно замедлять систему. Рассмотрим несколько советов по оптимизации, упомянутых экспертами:
1. Избегайте лишних обращений к базе через ТекущийЭлемент()
В 1С 7.7 обращение к спрНом.ТекущийЭлемент() внутри цикла заставляет систему лишний раз обращаться к объекту базы данных. Правильнее один раз присвоить значение переменной и использовать её.
Неправильно:
спрЦен.ИспользоватьВладельца(спрНом.ТекущийЭлемент);
Правильно:
ТекНом = спрНом.ТекущийЭлемент();
спрЦен.ИспользоватьВладельца(ТекНом);
2. Используйте константы для типов цен
Поиск типа цен по наименованию НайтиПоНаименованию("Розничная") — это плохая практика. Наименование может измениться пользователем, и код «сломается». В типовой конфигурации «Торговля и склад 9.2» предусмотрена константа для хранения ссылки на основной розничный тип цен.
Используйте: Константа.РозничныйТипЦен.
3. Групповая обработка данных
Если у вас в справочнике десятки тысяч товаров, перебор через ВыбратьЭлементы() будет долгим. В таких случаях программисты часто прибегают к использованию объекта Запрос (черный запрос 1С 7.7), который позволяет получить срезы последних значений гораздо быстрее на уровне SQL-движка (если используется SQL-версия базы).
Мы проанализировали способы получения последней цены в 1С 7.7. Главный вывод: для поиска последней записи в истории изменений за период всегда используйте объект Периодический с установленным методом ОбратныйПорядок(1). Это гарантирует точность данных и высокую скорость работы алгоритма. Не забывайте про оптимизацию обращений к объектам внутри циклов, чтобы ваша обработка работала стабильно даже на больших объемах данных.