Как правильно перенести функцию расчета стажа в общий модуль и использовать ее в отчетах

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

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

Проблема контекста: Почему «ЭтотОбъект» не работает в общем модуле

Разберем ситуацию, с которой сталкиваются многие начинающие программисты. В модуле формы или модуля объекта мы привыкли обращаться к данным напрямую через ЭтотОбъект или просто по именам реквизитов. Однако общий модуль — это независимая единица. Он «не знает», из какой формы его вызвали и какие данные там сейчас введены пользователем.

Проанализируем типичную ошибку: попытку обратиться к Объект.ДатаПриема внутри общего модуля без предварительной передачи этого самого объекта в качестве параметра. Программа выдаст ошибку «Переменная не определена», так как контекст формы не передается в общий модуль автоматически. Чтобы решить эту задачу, нам необходимо использовать механизм декаплинга (отвязки логики от конкретного объекта).

Способ 1: Передача ссылки на объект или самого объекта

Первый и самый простой способ — передать весь объект или ссылку на него в качестве параметра функции. Рассмотрим пример кода в общем модуле:


Функция РассчитатьСтажОбъекта(ДанныеОбъекта) Экспорт
    
    ДатаНачала = ДанныеОбъекта.ДатаПриема;
    ДатаКонца  = ТекущаяДата(); // Или дата увольнения из объекта
    
    // Логика расчета...
    Возврат Результат;
    
КонецФункции

Важный нюанс: Если вы передаете Ссылка, то внутри функции вам придется обращаться к базе данных, чтобы получить реквизиты. Если передаете ЭтотОбъект из формы, данные берутся из памяти. Однако передача целого объекта считается «тяжелой» операцией. Правильнее передавать только те данные, которые действительно нужны для расчета.

Способ 2: Передача примитивных типов (Оптимальный подход)

Проанализируем ситуацию: для расчета стажа нам по сути нужны только две даты — дата начала и дата окончания периода. Выясним, почему передача простых дат лучше, чем передача объекта целиком:

  1. Универсальность: Функцию можно вызвать откуда угодно: из документа, из консоли запросов, из обработки или из отчета, где вообще нет никакого «Объекта».
  2. Производительность: Передача двух значений типа Дата происходит мгновенно и не нагружает память.
  3. Тестируемость: Такую функцию легче проверять, подставляя произвольные даты.

Рассмотрим, как будет выглядеть такая функция в общем модуле (например, с именем КадровыйУчетКлиентСервер):


Функция СформироватьСтрокуСтажа(ДатаПриема, ДатаУвольнения = Неопределено) Экспорт
    
    Если ДатаУвольнения = Неопределено Тогда
        ДатаКонца = ТекущаяДата();
    Иначе
        ДатаКонца = ДатаУвольнения;
    КонецЕсли;
    
    // Используем встроенные механизмы или математический расчет
    // Для примера упрощенный расчет лет:
    ВсегоМесяцев = (Год(ДатаКонца) - Год(ДатаПриема)) * 12 + (Месяц(ДатаКонца) - Месяц(ДатаПриема));
    Лет = Цел(ВсегоМесяцев / 12);
    Месяцев = ВсегоМесяцев % 12;
    
    Результат = Строка(Лет) + " лет, " + Строка(Месяцев) + " мес.";
    Возврат Результат;
    
КонецФункции

Возврат данных: Использование структур вместо строк

Часто в Сообщениях на форумах советуют возвращать сразу готовую строку (например, "5 лет 2 дня"). Это удобно для вывода на экран, но неудобно, если в другом месте программы вам нужно получить только число лет для какого-то условия. Посмотрим на профессиональный подход — использование Структура.

Разберем пример кода, который возвращает детализированные данные:


Функция РасчетСтажаДетально(ДатаНачала, ДатаОкончания) Экспорт
    
    Результат = Новый Структура;
    Результат.Вставить("Лет", 0);
    Результат.Вставить("Месяцев", 0);
    Результат.Вставить("Дней", 0);
    
    // Здесь должен быть алгоритм расчета разницы дат
    // ... логика вычисления ...
    
    Возврат Результат;
    
КонецФункции

Теперь в форме вы сможете написать: ДанныеСтажа = ОбщийМодуль.РасчетСтажаДетально(ДатаП, ДатаУ); и обратиться к любому полю: Сообщить(ДанныеСтажа.Лет);.

Особенности использования функции в отчетах (СКД)

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

Посмотрим, как вызвать функцию в схеме компоновки данных. Перейдите на закладку «Вычисляемые поля». В колонке «Выражение» напишите полное имя функции:

ИмяВашегоОбщегоМодуля.СформироватьСтрокуСтажа(ДатаПриема, ДатаУвольнения)

Алгоритмические тонкости: Правило «+1 день»

Выясним важный юридический нюанс при расчете стажа. В кадровом учете день увольнения считается рабочим днем. Если сотрудник принят 1-го числа и уволен 31-го того же месяца, обычная разность дат в 1С даст 30 дней. Но фактически он отработал полный месяц (31 день). Поэтому в алгоритмах расчета стажа к дате окончания периода часто добавляют одну секунду или один день:


// Корректировка для включения последнего дня в расчет
ДатаКонцаДляРасчета = ДатаУвольнения + 86400; // Добавляем сутки в секундах

Использование типовых механизмов (БСП)

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

Резюме для разработчика

Рассмотрев все варианты, подведем итог правильного переноса кода:

  1. Создаем (или выбираем) общий модуль с серверными флагами.
  2. Создаем в нем функцию с ключевым словом Экспорт.
  3. В качестве параметров передаем только значимые данные (даты), а не весь объект.
  4. Внутри функции инициализируем переменные (например, Лет = 0;), так как контекста формы нет.
  5. Возвращаем результат (строку для простых задач или структуру для сложных).
  6. Используем имя ИмяМодуля.ИмяФункции() для вызова из любого места системы.

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

← На главную