Timemanmobile 26.0.0–26.400.0: окно «Начало рабочего дня» и рабочие отчёты в мобильном Битрикс24
Новый модуль timemanmobile («Мобильный учёт рабочего времени») пришёл в коробочный Битрикс24 сразу пятью версиями: 26.0.0, 26.100.0, 26.200.0, 26.300.0 и 26.400.0. Он добавляет в мобильное приложение окно «Начало рабочего дня», рабочие отчёты, экран «Рабочие дни» и плашку рабочего дня. Все версии ставятся одним обновлением, поэтому в разборе итоговое состояние 26.400.0, а в скобках указана версия, где функция появилась.
Новое
Проверьте, что стоят mobile, mobileapp и timeman (26.0.0)
include.php требует все три модуля через Loader::requireModule(), так что Loader::includeModule('timemanmobile') без любого из них бросит исключение, а без timeman модуль не установится (ошибка TIMEMANMOBILE_MODULE_INSTALL_ERROR_TIMEMAN). Свои JN-компоненты и расширения модуль кладёт в /bitrix/mobileapp/timemanmobile/ и отдаёт этот путь приложению через событие mobileapp:onJNComponentWorkspaceGet.
Окно «Начало рабочего дня» включает руководитель отдела (26.200.0)
Окно появилось в 26.100.0 и открывается само при переходах в приложении: переключении вкладок, закрытии карточки CRM, задачи, события календаря или диалога мессенджера. Сотрудник выбирает рабочий день или выходной, а если такие типы отсутствий есть на портале, ещё отгул, отпуск или больничный. Предвыбранный вариант с 26.400.0 определяет сервер по отсутствиям на сегодня и графику сотрудника. После старта дня окно предлагает открыть план на день и рекомендации CoPilot в чате с ботом бизнес-процессов.
Все экшены окна закрыты атрибутом #[ShiftsEnabled] из timeman (26.200.0). Запрос пройдёт, только если у отдела сотрудника включена настройка «Отмечать рабочие смены» (NodeSettingsType::WelcomeBox в humanresources). Руководитель отдела или его заместитель переключает её в настройках экрана «Рабочие дни» сразу для всех отделов, которыми управляет, вместе с их подотделами. Руководители подотделов могут выключить её у себя.
Кроме настройки, все экшены окна требуют доступа к учёту времени с телефона (canUseTimeman() и canManageWorktimeOnMobile() из RecordProvider timeman), иначе вернут access_denied или canOpen: false. Само окно откроется, только если день закрыт и его можно открыть. Экстранет-пользователь получит 403 от #[IntranetOnly].
С 26.400.0 момент показа тоже решает сервер. Расписание хранит timeman: границы периода, лимиты показов и отказов, интервал повтора, общий для всех устройств сотрудника. Клиент получает его в блоке schedule ответа getWorkdayIntentBoxData, по нему планирует следующий запрос, а о показе и исходе (dismiss или targetAction) сообщает экшенами registerShow и registerOutcome. Серверную часть, RecordIntent, мы разбирали в timeman 26.100.0, тогда у неё ещё не было клиента.
Отгул, отпуск или больничный из окна ставится одной записью на целевую смену из RecordIntentProvider::getSchedule() timeman. Параметр $shifts у timemanmobile.WorkdayIntent.addAbsence остался в сигнатуре, но на интервал не влияет. Если целевой смены нет, отсутствие не создаётся, ответ при этом успешный, с absences: [].
Включите рабочие отчёты флагом (26.100.0)
В приложении есть дневной отчёт, полный отчёт с отправкой руководителю, отчёты подчинённых с одобрением и обсуждением, а при закрытии дня открывается шторка с итогами. С 26.200.0 карточка отчёта бывает двух видов, GPT и «робот», и есть режим без ИИ. Руководитель включает отчёты для своих отделов настройкой «Включить план на день и отчёты» (26.200.0).
На портале отчёты по умолчанию выключены флагами из секции feature-flags в .settings.php модуля. Это WorkReportsFeature на опции timemanmobile:work_reports_enabled и WorkReportsWithoutAiFeature на timemanmobile:work_reports_without_ai_enabled (26.200.0), обе опции по умолчанию N. Реестр Bitrix\Mobile\Config\Feature собирает такие классы у всех установленных модулей, а на неизвестный класс отвечает заглушкой, которая всегда выключена. Включённый флаг сработает, только если стоят stafftrack и stafftrackmobile и включён CheckInFeature из stafftrackmobile.
<?php declare(strict_types=1);
use Bitrix\Main\Loader;
use Bitrix\Mobile\Config\Feature;
use Bitrix\TimemanMobile\Public\Features\WorkReportsFeature;
use Bitrix\TimemanMobile\Public\Features\WorkReportsWithoutAiFeature;
Loader::requireModule('timemanmobile');
$withoutAi = false;
$flag = $withoutAi ? new WorkReportsWithoutAiFeature() : new WorkReportsFeature();
$flag->enable(); // WorkReportsWithoutAiFeature::enable() включает и WorkReportsFeature
if (Feature::isDisabled(WorkReportsFeature::class)) {
echo 'Опция записана, но нет stafftrack/stafftrackmobile или выключен CheckInFeature', PHP_EOL;
}
В режиме без ИИ блок settings в ответах контроллеров отчётов фиксирован: isReportsSettingTurnedOn равен true, isReportGenerationAvailable и hasAiReportAccess равны false.
Экран «Рабочие дни» и плашка дня (26.200.0)
Экран показывает календарь записей рабочего времени, отчётов и отсутствий с легендой, списком и фильтром по сотруднику, там же настройки смен. Данные отдаёт timemanmobile.WorkDays.getData(userId, dateFrom, dateTo): до 500 записей за период, отчёты к ним, отсутствия и карточку пользователя. Записи и отчёты придут, если их разрешает UserPermissionsManager::canReadWorktime($userId) и доступен инструмент worktime. Отсутствия и карточку проверяет canViewUser(). Себя видно всегда, интранет-пользователю видны все, экстранет-пользователю только участники его групп и публичные пользователи. С 26.300.0 дни до регистрации сотрудника не помечаются пропущенными.
У плашки рабочего дня есть меню «старт / перерыв / завершение», эти команды приложение шлёт напрямую в timeman.V2.Record.start|pause|continue|stop. В 26.400.0 добавилась панель рабочего дня, а состояние дня приложение обновляет по pull-событиям timeman.
Какие экшены есть в модуле
Префикс у всех timemanmobile.:
WorkdayIntent:getWorkdayIntentBoxData,addAbsence,registerShow,registerOutcome;WorkDays:getData,getSettings,enableWorkShiftOption,disableWorkShiftOption;WorkReport:getSettings,enableWorkReportOption,disableWorkReportOption;Report.getUserReportsиFullReport:getUserReports,getReportToSend,getReportSettings,getSubordinateUserReports.
Report и FullReport, кроме getReportSettings, передают вызов в контроллеры timeman V2 через $this->forward(). Модуль берёт экшен timeman целиком, с правами, фильтрами и привязкой параметров, и меняет только форму ответа. Report.getUserReports, FullReport.getReportToSend и getSubordinateUserReports добавляют блок settings, а последний ещё и разворачивает страницу отчётов в плоский список с hasMore и nextOffset.
Экшены enable* и disable* права не проверяют, а timeman сам записывает настройку только в отделы, где текущий пользователь руководитель или заместитель, и во все их подотделы.
Повесьте свой фильтр на экшен атрибутом
#[IntranetOnly] в модуле — это класс с FilterAttributeInterface, который отдаёт фильтр. На метод он вешается одной строкой, без configureActions(). В WorkDays::getData аргументы проверяет #[PositiveNumber] прямо в сигнатуре, без Request-DTO. В своём модуле так можно закрыть экшен опцией и отсечь нулевой userId ещё до вызова метода.
<?php declare(strict_types=1);
// lib/Infrastructure/Controller/Filter/OptionEnabledFilter.php
namespace Acme\Worklog\Infrastructure\Controller\Filter;
use Bitrix\Main\Config\Option;
use Bitrix\Main\Engine\ActionFilter\Base;
use Bitrix\Main\Error;
use Bitrix\Main\Event;
use Bitrix\Main\EventResult;
final class OptionEnabledFilter extends Base
{
public function __construct(
private readonly string $moduleId,
private readonly string $optionName,
) {
parent::__construct();
}
public function onBeforeAction(Event $event): ?EventResult
{
if (Option::get($this->moduleId, $this->optionName, 'N') === 'Y') {
return null;
}
$this->addError(new Error('Функция выключена', 403));
return new EventResult(EventResult::ERROR, null, null, $this);
}
}
// lib/Infrastructure/Controller/Filter/OptionEnabled.php
namespace Acme\Worklog\Infrastructure\Controller\Filter;
use Attribute;
use Bitrix\Main\Engine\ActionFilter\Attribute\FilterAttributeInterface;
use Bitrix\Main\Engine\ActionFilter\FilterType;
#[Attribute(Attribute::TARGET_METHOD)]
final class OptionEnabled implements FilterAttributeInterface
{
public function __construct(
private readonly string $moduleId,
private readonly string $optionName,
) {}
public function getFilters(): array
{
return [new OptionEnabledFilter($this->moduleId, $this->optionName)];
}
public function getType(): FilterType
{
// EnablePrefilter дописывает фильтр к getDefaultPreFilters(),
// Prefilter заменил бы их целиком, вместе с Authentication и Csrf
return FilterType::EnablePrefilter;
}
}
// lib/Infrastructure/Controller/Timesheet.php
namespace Acme\Worklog\Infrastructure\Controller;
use Acme\Worklog\Infrastructure\Controller\Filter\OptionEnabled;
use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Validation\Rule\PositiveNumber;
final class Timesheet extends Controller
{
#[OptionEnabled('acme.worklog', 'timesheet_enabled')]
public function getAction(
#[PositiveNumber] int $userId,
#[PositiveNumber] int $dateFrom,
#[PositiveNumber] int $dateTo,
): array {
return ['userId' => $userId, 'period' => [$dateFrom, $dateTo]];
}
}
Мелочи и находки
addAbsence()не сверяет тип отсутствия со списком вариантов окна. Тип приходит с клиента и уходит вAbsenceProvider::addUserAbsence()как есть, а отсутствие ставится только текущему пользователю (26.100.0).- С 26.400.0 обработчик
mobileapp:onJNComponentWorkspaceGetописан в декларативномinstall/migrations/events.php, иinstallDB()применяет его черезCModule::installMigrations(). JN-файлы апдейтер копирует поinstallDirectoriesMappingизmigration_config.json. Пригодится как образец для своего модуля без таблиц. - При сменном графике, где в ближайшие сутки несколько смен, окно после выбора отсутствия всё ещё показывает шаг «Выбор смены», но сервер выбранные смены не читает и ставит отсутствие на целевую смену из расписания timeman (26.400.0).