При построении отчетов с детализацией по периодам разработчики и пользователи 1С часто сталкиваются с несоответствием номеров недель: например, календарь или внешняя система показывает 6-ю неделю года, а отчет 1С выводит уже 7-ю. Выясним причину такого поведения платформы и разберем практические способы реализации нумерации по стандарту ISO 8601.
Проанализируем логику, заложенную в платформу 1С. В соответствии с международным стандартом ISO 8601 (которому соответствует и российский ГОСТ ИСО 8601), первой неделей года считается та неделя, которая содержит первый четверг года. Это эквивалентно правилу: первая неделя содержит не менее четырех дней нового года. В такой модели неделя всегда состоит строго из 7 дней, а даты 31 декабря и 1 января могут относиться к одной и той же неделе (предыдущего или нового года).
В платформе 1С встроенная функция НеделяГода(), функция языка запросов НЕДЕЛЯ(), а также стандартная периодичность СКД ПериодНеделя работают по другому принципу, аналогичному поведению функции DATEPART(week, ...) в MS SQL Server:
Повлиять на поведение стандартной функции глобальными настройками базы, региональными установками или параметрами конфигурации нельзя. Поэтому, если пользователь в отчете СКД выбирает стандартную группировку периода «Неделя», платформа вычисляет ее номер исключительно по своему системному алгоритму.
Рассмотрим алгоритм вычисления недели по ISO 8601 на встроенном языке. Поскольку по стандарту ISO неделя относится к тому году, в который попадает ее четверг, мы можем вычислить дату четверга текущей недели и на ее основе получить корректный номер недели и год.
Создадим экспортную функцию в общем модуле (например, КалендарныеФункцииКлиентСервер):
Функция ПолучитьПараметрыНеделиISO(Знач ИсходнаяДата) Экспорт
// Определяем четверг текущей недели
// В 1С: Понедельник = 1, Воскресенье = 7
НомерДняНедели = ДеньНедели(ИсходнаяДата);
СмещениеДоЧетверга = 4 - НомерДняНедели;
ДатаЧетверга = НачалоДня(ИсходнаяДата) + СмещениеДоЧетверга * 86400;
// Год по стандарту ISO совпадает с годом четверга этой недели
РасчетныйГодISO = Год(ДатаЧетверга);
// Вычисляем номер недели через порядковый день четверга в году
НомерНеделиISO = Цел((ДеньГода(ДатаЧетверга) - 1) / 7) + 1;
Результат = Новый Структура;
Результат.Вставить("Год", РасчетныйГодISO);
Результат.Вставить("НомерНедели", НомерНеделиISO);
Результат.Вставить("Представление", Строка(РасчетныйГодISO) + "-W" + Формат(НомерНеделиISO, "ЧЦ=2; ЧВН="));
Возврат Результат;
КонецФункции
Посмотрим на пример работы функции: если дата приходится на 1 января, которое выпало на пятницу, функция определит четверг как 31 декабря предыдущего года и вернет 53-ю неделю предыдущего года, что полностью соответствует стандарту ISO 8601.
Когда требуется вывести корректный номер недели в отчете, разберем по шагам настройку схемы компоновки данных:
НомерНеделиISO.
КалендарныеФункцииКлиентСервер.ПолучитьПараметрыНеделиISO(Период).НомерНедели
ПериодНеделяПредставление с выражением:
КалендарныеФункцииКлиентСервер.ПолучитьПараметрыНеделиISO(Период).Представление
ПериодНеделя.Если отчет обрабатывает большие объемы данных, вызов функций общего модуля для каждой строки в СКД может замедлить формирование отчета. В таких случаях наиболее производительным решением является предварительно заполненная таблица календаря.
Разберем реализацию через регистр сведений:
Дата, ГодISO, НомерНеделиISO, НачалоНедели, КонецНедели.
ВЫБРАТЬ
Продажи.Номенклатура КАК Номенклатура,
Продажи.СуммаОборот КАК Сумма,
Календарь.НомерНеделиISO КАК НомерНедели,
Календарь.ГодISO КАК ГодНедели
ИЗ
РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Регистратор, ) КАК Продажи
ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КалендарныеПериоды КАК Календарь
ПО (НАЧАЛОПЕРИОДА(Продажи.Период, ДЕНЬ) = Календарь.Дата)
ГодНедели и НомерНедели, получая точные данные без потери производительности.Если дорабатывать конфигурацию под ISO 8601 нет возможности или требуется исключить путаницу между системами нумерации, проанализируем альтернативный подход — отказ от номеров недель в пользу дат начала периода.
Функция НАЧАЛОПЕРИОДА(Дата, НЕДЕЛЯ) всегда возвращает точный понедельник недели. Настроив формат вывода поля в СКД, можно показывать пользователям понятный интервал вместо спорного номера:
ПериодНеделя переходим в колонку Оформление или Выражение представления.
Формат(ПериодНеделя, "ДФ=""'Неделя с' dd.MM.yyyy""")
Строка(Формат(ПериодНеделя, "ДФ=dd.MM")) + " - " + Строка(Формат(КонецПериода(ПериодНеделя, "НЕДЕЛЯ"), "ДФ=dd.MM"))
Такой формат исключает разночтения при согласовании отчетов между подразделениями и не требует пересчета системных номеров недель.