Как в запросе 1С получить количество выходных и рабочих дней между двумя датами

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

При разработке отчетов и механизмов расчета в системе 1С:Предприятие разработчики регулярно сталкиваются с задачей вычисления количества рабочих или выходных дней за определенный период. Стандартным и наиболее правильным решением является использование производственного календаря (например, из библиотеки стандартных подсистем — БСП) или специализированных регистров сведений. Однако на практике бывают ситуации, когда производственный календарь в базе данных не заполнен, отсутствует в конфигурации или его использование невозможно по техническим причинам.

В этой статье мы подробно разберем, как решить задачу подсчета выходных дней чисто средствами языка запросов 1С. Мы рассмотрим несколько эффективных подходов, проанализируем причины возникновения типичных ошибок (таких как дублирование записей при соединении) и найдем способы оптимизации производительности запроса.

Причина проблемы и постановка задачи

Представим сценарий: у нас есть основная таблица документов или хозяйственных операций (например, ВТ_ОснТаб), содержащая две даты — дату прихода товара на фотостудию (ДатаПриходаНаФотостудию) и дату выполнения фотографии (ДатаФото). Нам необходимо вычислить точное количество рабочих дней, прошедших между этими событиями. Для этого нам нужно знать общее количество календарных дней в интервале и вычесть из него количество выходных дней (суббот и воскресений).

Для реализации этой задачи нам потребуется механизм, который позволит определить, какие дни внутри произвольного периода являются выходными, и сопоставить их с каждой строкой нашей исходной таблицы.

---

Способ 1. Генерация виртуального календаря и оптимизированное левое соединение

Посмотрим на классический и самый гибкий метод — генерацию динамической таблицы дат (виртуального календаря) прямо «на лету» внутри запроса с последующим соединением с основной таблицей. Для изучения различных техник формирования числовых и календарных диапазонов можно использовать руководство по выборке последовательностей дат и чисел в запросе.

Для начала разберем, как генерируется последовательность чисел, которая затем преобразуется в даты. Мы используем декартово произведение (полное соединение по условию ИСТИНА) нескольких небольших таблиц, содержащих цифры от 0 до 9. Объединив четыре такие таблицы, мы можем получить непрерывную последовательность чисел от 0 до 9999. Этого диапазона (почти 27 лет) более чем достаточно для любых прикладных задач.

Разберем по шагам создание временной таблицы дат с признаком выходного дня:


// Шаг 1: Генерируем последовательность чисел и преобразуем их в даты начиная с даты начала выборки
ВЫБРАТЬ
    ДОБАВИТЬКДАТЕ(&НачалоПериодаВыборки, ДЕНЬ, ВложенныйЗапрос.НомерДняИзДиапазона) КАК Период
ПОМЕСТИТЬ ВТ_ВсеДаты
ИЗ
    (ВЫБРАТЬ
        ТаблицаТысяч.Цифра * 1000 + ТаблицаСотен.Цифра * 100 + ТаблицаДесятков.Цифра * 10 + ТаблицаЕдиниц.Цифра КАК НомерДняИзДиапазона
    ИЗ
        (ВЫБРАТЬ 0 КАК Цифра ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК ТаблицаТысяч
        ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ 0 КАК Цифра ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК ТаблицаСотен
        ПО (ИСТИНА)
        ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ 0 КАК Цифра ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК ТаблицаДесятков
        ПО (ИСТИНА)
        ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ 0 КАК Цифра ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК ТаблицаЕдиниц
        ПО (ИСТИНА)
    ГДЕ
        ТаблицаТысяч.Цифра * 1000 + ТаблицаСотен.Цифра * 100 + ТаблицаДесятков.Цифра * 10 + ТаблицаЕдиниц.Цифра <= РАЗНОСТЬДАТ(&НачалоПериодаВыборки, &КонецПериодаВыборки, ДЕНЬ)) КАК ВложенныйЗапрос
;

// Шаг 2: Классифицируем каждый день на рабочий и выходной (суббота - 6, воскресенье - 7)
ВЫБРАТЬ
    ВТ_ВсеДаты.Период КАК Период,
    ВЫБОР
        КОГДА ДЕНЬНЕДЕЛИ(ВТ_ВсеДаты.Период) В (6, 7) ТОГДА 1
        ИНАЧЕ 0
    КОНЕЦ КАК ЭтоВыходной
ПОМЕСТИТЬ ВТ_КалендарьВыходных
ИЗ
    ВТ_ВсеДаты КАК ВТ_ВсеДаты
;

Мы получили готовую временную таблицу ВТ_КалендарьВыходных. Теперь нам нужно соединить ее с нашей основной таблицей ВТ_ОснТаб. Если мы попытаемся выполнить левое соединение напрямую с последующей группировкой по всем полям исходной таблицы (как часто делают разработчики), мы неизбежно столкнемся с серьезной проблемой производительности и дублированием строк.

Выясним причину дублирования записей. Если в исходной таблице ВТ_ОснТаб есть строки с неуникальным набором ключевых полей, то группировка СГРУППИРОВАТЬ ПО сожмет эти строки, исказив итоговый результат. Кроме того, прямое соединение таблиц по условию МЕЖДУ заставляет СУБД строить огромные промежуточные выборки в памяти, что приводит к зависанию запроса на больших объемах данных.

Чтобы избежать этих проблем, применим паттерн оптимизации уникальных пар дат. Мы сначала выделим уникальные пары дат из нашей таблицы, рассчитаем для них количество рабочих/выходных дней, а затем соединим этот результат с исходной таблицей. Посмотрим на реализацию этого алгоритма:


// Шаг 3: Выбираем только уникальные пары дат для расчетов
ВЫБРАТЬ РАЗЛИЧНЫЕ
    ВТ_ОснТаб.ДатаПриходаНаФотостудию КАК ДатаНачала,
    ВТ_ОснТаб.ДатаФото КАК ДатаОкончания
ПОМЕСТИТЬ ВТ_УникальныеПериоды
ИЗ
    ВТ_ОснТаб КАК ВТ_ОснТаб
ГДЕ
    ВТ_ОснТаб.ДатаПриходаНаФотостудию НЕ ЕСТЬ NULL
    И ВТ_ОснТаб.ДатаФото НЕ ЕСТЬ NULL
;

// Шаг 4: Подсчитываем выходные и рабочие дни только для уникальных периодов
ВЫБРАТЬ
    ВТ_УникальныеПериоды.ДатаНачала КАК ДатаНачала,
    ВТ_УникальныеПериоды.ДатаОкончания КАК ДатаОкончания,
    СУММА(ВТ_КалендарьВыходных.ЭтоВыходной) КАК КоличествоВыходных,
    СУММА(ВЫБОР КОГДА ВТ_КалендарьВыходных.ЭтоВыходной = 0 ТОГДА 1 ИНАЧЕ 0 КОНЕЦ) КАК КоличествоРабочих
ПОМЕСТИТЬ ВТ_РассчитанныеДни
ИЗ
    ВТ_УникальныеПериоды КАК ВТ_УникальныеПериоды
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_КалендарьВыходных КАК ВТ_КалендарьВыходных
        ПО ВТ_КалендарьВыходных.Период МЕЖДУ ВТ_УникальныеПериоды.ДатаНачала И ВТ_УникальныеПериоды.ДатаОкончания
СГРУППИРОВАТЬ ПО
    ВТ_УникальныеПериоды.ДатаНачала,
    ВТ_УникальныеПериоды.ДатаОкончания
;

// Шаг 5: Соединяем результат с исходной таблицей без искажения структуры данных
ВЫБРАТЬ
    ВТ_ОснТаб.ГруппаНоменклатура,
    ВТ_ОснТаб.Номенклатура,
    ВТ_ОснТаб.ХарактеристикаНоменклатуры,
    ВТ_ОснТаб.ДатаПриходаНаФотостудию,
    ВТ_ОснТаб.ДатаФото,
    ЕСТЬNULL(ВТ_РассчитанныеДни.КоличествоВыходных, 0) КАК ВыходныхДней,
    ЕСТЬNULL(ВТ_РассчитанныеДни.КоличествоРабочих, 0) КАК РабочихДней
ИЗ
    ВТ_ОснТаб КАК ВТ_ОснТаб
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_РассчитанныеДни КАК ВТ_РассчитанныеДни
        ПО ВТ_ОснТаб.ДатаПриходаНаФотостудию = ВТ_РассчитанныеДни.ДатаНачала
        И ВТ_ОснТаб.ДатаФото = ВТ_РассчитанныеДни.ДатаОкончания
;

Благодаря этому подходу мы полностью обезопасили себя от дублирования записей, сохранили абсолютно все строки исходной таблицы в первозданном виде и значительно ускорили время выполнения запроса СУБД.

---

Способ 2. Математический метод расчета выходных (без временных таблиц дат)

Если вам требуется максимально быстрое решение без создания виртуальных календарей и тяжелых операций JOIN (которые могут снижать производительность на миллионных выборках), можно применить математический метод расчета выходных дней прямо в строке запроса. Для вычисления конкретных дней недели без вспомогательных таблиц можно использовать метод расчета количества дней недели в интервале одним запросом.

Идея метода основана на делении общего количества дней периода на 7 (количество дней в неделе) для вычисления полных недель и анализе остатка дней с помощью конструкции ВЫБОР КОГДА в зависимости от дня недели, на который выпадает стартовая дата.

Однако в языке запросов 1С отсутствуют стандартные математические функции получения остатка от деления (%) и выделения целой части (ЦЕЛ). Для преодоления этого ограничения мы используем функцию ВЫРАЗИТЬ(Число КАК ЧИСЛО(10, 0)). Важно помнить, что функция ВЫРАЗИТЬ использует алгоритм банковского округления (округляет к ближайшему четному числу, например, 2.5 округлится до 2, а 3.5 — до 4).

Рассмотрим математическую модель расчета количества выходных дней для небольших периодов времени (в пределах одной-двух недель), которая наиболее часто применяется на практике для экспресс-аналитики:


ВЫБРАТЬ
    ВТ_ОснТаб.Номенклатура,
    ВТ_ОснТаб.ДатаПриходаНаФотостудию,
    ВТ_ОснТаб.ДатаФото,
    // Общее количество дней в периоде
    РАЗНОСТЬДАТ(ВТ_ОснТаб.ДатаПриходаНаФотостудию, ВТ_ОснТаб.ДатаФото, ДЕНЬ) + 1 КАК ВсегоКалендарныхДней,
    
    // Математический расчет количества выходных дней
    ВЫБОР
        // Если период меньше недели, анализируем день недели начала и окончания
        КОГДА РАЗНОСТЬДАТ(ВТ_ОснТаб.ДатаПриходаНаФотостудию, ВТ_ОснТаб.ДатаФото, ДЕНЬ) < 7
            ТОГДА ВЫБОР
                КОГДА ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаПриходаНаФотостудию) <= ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаФото)
                    ТОГДА ВЫБОР 
                        КОГДА 6 МЕЖДУ ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаПриходаНаФотостудию) И ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаФото) И 7 МЕЖДУ ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаПриходаНаФотостудию) И ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаФото) ТОГДА 2
                        КОГДА 6 МЕЖДУ ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаПриходаНаФотостудию) И ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаФото) ИЛИ 7 МЕЖДУ ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаПриходаНаФотостудию) И ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаФото) ТОГДА 1
                        ИНАЧЕ 0
                    КОНЕЦ
                ИНАЧЕ // Если период переходит через границу недели
                    ВЫБОР 
                        КОГДА (ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаПриходаНаФотостудию) = 7 И ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаФото) = 6) ТОГДА 1
                        ИНАЧЕ 2
                    КОНЕЦ
            КОНЕЦ
            
        // Для периодов более 7 дней рассчитываем по базовой формуле полных недель
        ИНАЧЕ (ВЫРАЗИТЬ((РАЗНОСТЬДАТ(ВТ_ОснТаб.ДатаПриходаНаФотостудию, ВТ_ОснТаб.ДатаФото, ДЕНЬ) - 3.5) / 7 КАК ЧИСЛО(10, 0))) * 2 
              + (ВЫБОР КОГДА ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаПриходаНаФотостудию) В (6, 7) ИЛИ ДЕНЬНЕДЕЛИ(ВТ_ОснТаб.ДатаФото) В (6, 7) ТОГДА 1 ИНАЧЕ 0 КОНЕЦ)
    КОНЕЦ КАК КоличествоВыходныхМатематически
ИЗ
    ВТ_ОснТаб КАК ВТ_ОснТаб

Важное примечание: Математический метод идеален по скорости выполнения, однако он не учитывает официальные государственные праздники и переносы рабочих дней. Если вам критически важна абсолютная точность с учетом производственного календаря вашей страны, этот метод следует использовать с осторожностью.

---

Способ 3. Оптимизация виртуального календаря по границам выборки

В первом способе мы жестко передавали параметры начала и окончания генерации дат (&НачалоПериодаВыборки и &КонецПериодаВыборки). Но что делать, если отчет строится за неопределенный период времени и у нас нет явных параметров дат, а есть только записи в таблицах?

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

Рассмотрим пример определения границ диапазона динамически:


// Шаг 1: Получаем минимальную и максимальную дату из исходной таблицы
ВЫБРАТЬ
    МИНИМУМ(ВТ_ОснТаб.ДатаПриходаНаФотостудию) КАК МинимальнаяДата,
    МАКСИМУМ(ВТ_ОснТаб.ДатаФото) КАК МаксимальнаяДата
ПОМЕСТИТЬ ВТ_ГраницыПериода
ИЗ
    ВТ_ОснТаб КАК ВТ_ОснТаб
;

// Шаг 2: Генерируем даты строго в рамках полученных границ
ВЫБРАТЬ
    ДОБАВИТЬКДАТЕ(ВТ_ГраницыПериода.МинимальнаяДата, ДЕНЬ, ВложенныйЗапрос.НомерДняИзДиапазона) КАК Период
ПОМЕСТИТЬ ВТ_Дни
ИЗ
    ВТ_ГраницыПериода КАК ВТ_ГраницыПериода
    ПОЛНОЕ СОЕДИНЕНИЕ (
        // Здесь находится наш генератор чисел от 0 до 999
        ВЫБРАТЬ aa.a * 100 + bb.b * 10 + cc.c КАК НомерДняИзДиапазона
        ИЗ
            (ВЫБРАТЬ 0 КАК a ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК aa
            ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ 0 КАК b ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК bb ПО (ИСТИНА)
            ПОЛНОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ 0 КАК c ОБЪЕДИНИТЬ ВЫБРАТЬ 1 ОБЪЕДИНИТЬ ВЫБРАТЬ 2 ОБЪЕДИНИТЬ ВЫБРАТЬ 3 ОБЪЕДИНИТЬ ВЫБРАТЬ 4 ОБЪЕДИНИТЬ ВЫБРАТЬ 5 ОБЪЕДИНИТЬ ВЫБРАТЬ 6 ОБЪЕДИНИТЬ ВЫБРАТЬ 7 ОБЪЕДИНИТЬ ВЫБРАТЬ 8 ОБЪЕДИНИТЬ ВЫБРАТЬ 9) КАК cc ПО (ИСТИНА)
    ) КАК ВложенныйЗапрос
    ПО (ИСТИНА)
ГДЕ
    ВложенныйЗапрос.НомерДняИзДиапазона <= РАЗНОСТЬДАТ(ВТ_ГраницыПериода.МинимальнаяДата, ВТ_ГраницыПериода.МаксимальнаяДата, ДЕНЬ)
;

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

---

Важный нюанс: Очистка времени в датах

При сопоставлении дат по условию МЕЖДУ крайне важно помнить о наличии составляющей времени в полях типов Дата. Например, если дата прихода на фотостудию содержит значение 15.10.2023 18:30:00, а дата фотографии — 16.10.2023 09:00:00, то прямое сравнение со стандартной датой календаря (где время всегда равно 00:00:00) может выдать некорректные результаты или пропустить один из крайних дней интервала.

Правило разработки: Перед проведением любых календарных расчетов всегда приводите сравниваемые даты к началу дня. В языке запросов 1С для этого используется функция НАЧАЛОПЕРИОДА.

Пример подготовки дат перед расчетами:


ВЫБРАТЬ
    НАЧАЛОПЕРИОДА(ВТ_ОснТаб.ДатаПриходаНаФотостудию, ДЕНЬ) КАК ДатаНачалаБезВремени,
    НАЧАЛОПЕРИОДА(ВТ_ОснТаб.ДатаФото, ДЕНЬ) КАК ДатаОкончанияБезВремени
ПОМЕСТИТЬ ВТ_ОчищенныеДаты
ИЗ
    ВТ_ОснТаб КАК ВТ_ОснТаб
;

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

---

Способ 4. Реализация расчета через Систему компоновки данных (СКД)

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

Рассмотрим этот способ по шагам:

  1. Создадим в конфигурации общий неглобальный модуль (например, с именем РаботаСДатамиСервер). Установим у него свойства Сервер, Вызов сервера и Вызов внешнего соединения.
  2. Напишем экспортную функцию расчета рабочих дней. В коде на встроенном языке мы можем использовать любые циклы, стандартные календари и любые другие алгоритмы, которые пишутся гораздо проще, чем на чистом языке запросов:

Функция ПолучитьКоличествоРабочихДней(Знач ДатаНачала, Знач ДатаОкончания) Экспорт
    
    Если Не ЗначениеЗаполнено(ДатаНачала) Или Не ЗначениеЗаполнено(ДатаОкончания) Тогда
        Возврат 0;
    КонецЕсли;
    
    РабочихДней = 0;
    ТекущаяДата = НачалоДня(ДатаНачала);
    КонечнаяДата = НачалоДня(ДатаОкончания);
    
    Пока ТекущаяДата <= КонечнаяДата Цикл
        ДеньНедели = ДеньНедели(ТекущаяДата);
        // Если день не суббота и не воскресенье
        Если ДеньНедели <> 6 И ДеньНедели <> 7 Тогда
            РабочихДней = РабочихДней + 1;
        КонецЕсли;
        ТекущаяДата = ДобавитьМесяц(ТекущаяДата, 0) + 86400; // Переход на следующий день
    КонецЦикла;
    
    Возврат РабочихДней;
    
КонецФункции
  1. Откроем Схему компоновки данных нашего отчета. На вкладке Вычисляемые поля добавим новую строку:
    • Путь к данным: КоличествоРабочихДней
    • Выражение: РаботаСДатамиСервер.ПолучитьКоличествоРабочихДней(ДатаПриходаНаФотостудию, ДатаФото)
    • Заголовок: Количество рабочих дней

Система компоновки данных при формировании отчета автоматически построчно вызовет функцию общего модуля, передав туда даты из полей текущей строки схемы, и выведет корректный результат в отчет. Данный метод невероятно удобен, прост в отладке и полностью застрахован от дублирования строк в СУБД.

---

Резюме и рекомендации

Каждый из рассмотренных способов имеет свои преимущества и оптимальные сценарии использования:

← На главную