Как изменить нумерацию недель года в 1С и настроить расчет по стандарту ISO 8601?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При построении отчетов с детализацией по периодам разработчики и пользователи 1С часто сталкиваются с несоответствием номеров недель: например, календарь или внешняя система показывает 6-ю неделю года, а отчет 1С выводит уже 7-ю. Выясним причину такого поведения платформы и разберем практические способы реализации нумерации по стандарту ISO 8601.

В чем причина различий в нумерации недель

Проанализируем логику, заложенную в платформу 1С. В соответствии с международным стандартом ISO 8601 (которому соответствует и российский ГОСТ ИСО 8601), первой неделей года считается та неделя, которая содержит первый четверг года. Это эквивалентно правилу: первая неделя содержит не менее четырех дней нового года. В такой модели неделя всегда состоит строго из 7 дней, а даты 31 декабря и 1 января могут относиться к одной и той же неделе (предыдущего или нового года).

В платформе 1С встроенная функция НеделяГода(), функция языка запросов НЕДЕЛЯ(), а также стандартная периодичность СКД ПериодНеделя работают по другому принципу, аналогичному поведению функции DATEPART(week, ...) в MS SQL Server:

  1. 1 января всегда жестко относится к 1-й неделе наступившего года.
  2. 31 декабря всегда относится к последней неделе уходящего года.
  3. Если 1 января выпадает, например, на пятницу, субботу или воскресенье, эти дни выделяются платформой в отдельную 1-ю неделю года, а со следующего понедельника начинается уже 2-я неделя.

Повлиять на поведение стандартной функции глобальными настройками базы, региональными установками или параметрами конфигурации нельзя. Поэтому, если пользователь в отчете СКД выбирает стандартную группировку периода «Неделя», платформа вычисляет ее номер исключительно по своему системному алгоритму.

Способ 1. Программный расчет недели и года по стандарту ISO 8601

Рассмотрим алгоритм вычисления недели по 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.

Способ 2. Вычисляемые поля в схеме компоновки данных (СКД)

Когда требуется вывести корректный номер недели в отчете, разберем по шагам настройку схемы компоновки данных:

  1. Разместим функцию расчета в общем модуле с признаками Сервер и Внешнее соединение.
  2. В СКД на закладке Вычисляемые поля добавим новое поле, например НомерНеделиISO.
  3. В поле «Выражение» укажем вызов нашей функции: КалендарныеФункцииКлиентСервер.ПолучитьПараметрыНеделиISO(Период).НомерНедели
  4. Для формирования красивого заголовка в отчете можно добавить отдельное вычисляемое поле ПериодНеделяПредставление с выражением: КалендарныеФункцииКлиентСервер.ПолучитьПараметрыНеделиISO(Период).Представление
  5. В настройках отчета используем созданное вычисляемое поле в качестве группировки вместо стандартного поля ПериодНеделя.

Способ 3. Календарная таблица в запросе

Если отчет обрабатывает большие объемы данных, вызов функций общего модуля для каждой строки в СКД может замедлить формирование отчета. В таких случаях наиболее производительным решением является предварительно заполненная таблица календаря.

Разберем реализацию через регистр сведений:

  1. Создаем регистр сведений (или используем данные производственного календаря), где для каждой календарной даты заранее записаны реквизиты: Дата, ГодISO, НомерНеделиISO, НачалоНедели, КонецНедели.
  2. В запросе СКД соединяем основную таблицу движений или документов с таблицей регистра по полю даты:
    
    ВЫБРАТЬ
    	Продажи.Номенклатура КАК Номенклатура,
    	Продажи.СуммаОборот КАК Сумма,
    	Календарь.НомерНеделиISO КАК НомерНедели,
    	Календарь.ГодISO КАК ГодНедели
    ИЗ
    	РегистрНакопления.Продажи.Обороты(&НачалоПериода, &КонецПериода, Регистратор, ) КАК Продажи
    	ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КалендарныеПериоды КАК Календарь
    		ПО (НАЧАЛОПЕРИОДА(Продажи.Период, ДЕНЬ) = Календарь.Дата)
    
  3. В настройках СКД пользователи группируют данные по полям ГодНедели и НомерНедели, получая точные данные без потери производительности.

Способ 4. Обход проблемы через отображение дат вместо номеров

Если дорабатывать конфигурацию под ISO 8601 нет возможности или требуется исключить путаницу между системами нумерации, проанализируем альтернативный подход — отказ от номеров недель в пользу дат начала периода.

Функция НАЧАЛОПЕРИОДА(Дата, НЕДЕЛЯ) всегда возвращает точный понедельник недели. Настроив формат вывода поля в СКД, можно показывать пользователям понятный интервал вместо спорного номера:

  1. В СКД на вкладке Наборы данных для поля ПериодНеделя переходим в колонку Оформление или Выражение представления.
  2. Задаем формат отображения даты начала недели: Формат(ПериодНеделя, "ДФ=""'Неделя с' dd.MM.yyyy""")
  3. Либо формируем строковый интервал вида:
    
    Строка(Формат(ПериодНеделя, "ДФ=dd.MM")) + " - " + Строка(Формат(КонецПериода(ПериодНеделя, "НЕДЕЛЯ"), "ДФ=dd.MM"))
    

Такой формат исключает разночтения при согласовании отчетов между подразделениями и не требует пересчета системных номеров недель.

← На главную