При автоматизации учета часто возникает задача привязки данных (например, кодов ТН ВЭД, сезонных цен или коэффициентов) к определенному периоду внутри года. Основная сложность заключается в том, что стандартный тип данных Дата в платформе 1С всегда содержит год, а бизнес-логика требует, чтобы код «01.03 — 31.05» действовал ежегодно, независимо от того, какой сейчас год на календаре. Рассмотрим, как эффективно решить эту задачу, избежав типичных ошибок производительности.
Если мы создадим в регистре сведения колонки ДатаНачала и ДатаОкончания с типом Дата, нам придется каждый год переписывать записи, актуализируя год. Это неудобно и ведет к избыточности данных. Проанализируем несколько способов реализации «вечного» календаря в 1С.
Это самый простой и надежный метод. Мы договариваемся, что все даты в регистре хранятся с одним фиксированным годом (например, 2000-м или 0001-м). Это позволяет использовать стандартные функции языка запросов и сохраняет типизацию данных.
Реализация:
ДатаС и ДатаПо типа Дата.Разберем пример кода для подготовки параметра запроса:
ТекущаяДатаДокумента = Объект.Дата;
// Создаем дату с тем же днем и месяцем, но 2000-м годом
ДатаДляСравнения = Дата(2000, Месяц(ТекущаяДатаДокумента), День(ТекущаяДатаДокумента));
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТНВЭД.КодТНВЭД
|ИЗ
| РегистрСведений.КодыТНВЭД КАК ТНВЭД
|ГДЕ
| ТНВЭД.Номенклатура = &Номенклатура
| И &ДатаДляСравнения МЕЖДУ ТНВЭД.ДатаС И ТНВЭД.ДатаПо";
Запрос.УстановитьПараметр("Номенклатура", ВыбраннаяНоменклатура);
Запрос.УстановитьПараметр("ДатаДляСравнения", ДатаДляСравнения);
Если вы хотите избежать работы с типом Дата из-за его привязки к календарю, можно использовать целые числа. Числовой индекс вычисляется по формуле: Месяц * 100 + День. В этом случае 1 марта превращается в 301 (3 * 100 + 1), а 31 декабря в 1231.
Преимущества метода:
Посмотрим на пример формирования условия в запросе:
ИндексДаты = Месяц(ДатаДок) * 100 + День(ДатаДок);
// В регистре храним числа в ресурсах "ДеньНачалаИндекс" и "ДеньКонцаИндекс"
Запрос.Текст =
"ВЫБРАТЬ
| КодТНВЭД
|ИЗ
| РегистрСведений.ип_дКодыТНВЭД
|ГДЕ
| Номенклатура = &Номенклатура
| И &ИндексДаты >= ДеньНачалаИндекс
| И &ИндексДаты <= ДеньКонцаИндекс";
Особую сложность представляют периоды, которые начинаются в одном году, а заканчиваются в другом (например, с 15 декабря по 15 января). Если использовать простое сравнение МЕЖДУ, такой период не будет найден, так как 1215 больше, чем 115.
Проанализируем ситуацию: в таких случаях условие в запросе должно меняться. Вместо И (когда дата должна быть и больше начала, и меньше конца) нужно использовать ИЛИ.
Логическая схема в запросе:
"ГДЕ
| Номенклатура = &Номенклатура И
| (ВЫБОР
| КОГДА ДеньНачалаИндекс <= ДеньКонцаИндекс
| ТОГДА &ИндексДаты МЕЖДУ ДеньНачалаИндекс И ДеньКонцаИндекс
| ИНАЧЕ &ИндексДаты >= ДеньНачалаИндекс ИЛИ &ИндексДаты <= ДеньКонцаИндекс
| КОНЕЦ)"
Эта конструкция универсальна: она корректно отработает и обычные летние периоды, и «зимние» переходящие сроки.
В ходе обсуждения автор предложил вариант с хранением дат в виде строки "ДД.ММ" и последующим разбором в цикле 1С. Рассмотрим, почему этот метод не является оптимальным, но разберем его логику для понимания работы с имеющимися данными.
Автор использовал следующий алгоритм:
Пока Выборка.Следующий() разбивать строку с помощью функций Лев() и Прав().Пример кода автора:
Пока Выборка.Следующий Цикл
ДатаСМ = Прав(Выборка.С, 2); // Получаем месяц начала
ДатаСД = Лев(Выборка.С, 2); // Получаем день начала
// ... аналогично для даты окончания и текущей даты
Если ДатаМ >= ДатаСМ И ДатаМ <= ДатаПоМ Тогда
// Сложные вложенные условия для дней...
Примечание = Выборка.КодТНВЭД;
КонецЕсли;
КонецЦикла;
Внимание: Основной недостаток этого метода — производительность. Если у вас 10 000 позиций номенклатуры и для каждой нужно найти код в цикле, отчет или проведение документа будет работать крайне медленно. Старайтесь всегда переносить логику фильтрации в тело запроса (на сторону SQL-сервера).
Выясним, как сделать работу с такими данными максимально быстрой:
Индексировать для этих полей (если они не являются измерениями).СрезПоследних, подавая на вход дату с фиксированным 2000-м годом. Это позволит системе самой найти актуальную запись на временной оси.МЕЖДУ в запросе.Таким образом, для реализации сезонности в 1С лучше всего подходит метод фиксированного года или числового индекса. Это обеспечивает высокую скорость работы системы и чистоту программного кода.