timeman 26.100.0 Ломающее

Timeman 26.100.0: время считается по IANA-зоне сотрудника, у CTimeManEntry и REST другие значения

3 мин чтения Устаревших API: 3

Ломающее обновление

Удалены или изменены публичные API — прикладной код может перестать работать.

С timeman 26.100.0 учёт рабочего времени берёт зону сотрудника из IANA-идентификатора в b_user.TIME_ZONE и считает смещение UTC на момент каждого события, с переходами на летнее и зимнее время. Форматы полей и ответов остались прежними, поменялись значения: TIME_START, TIME_FINISH и DURATION у легаси CTimeManEntry, время и TZ_OFFSET в ответе timeman.status, время, которое timeman.open и timeman.close берут из TIME, результат User::obtainUtcOffset(). Сильнее всего это ударит по порталам с сотрудниками не из зоны сервера, если свой код пишет в учёт времени, строит по нему отчёты или читает рабочий день по REST.

Что сломается

Проверьте, какая зона записана у сотрудников

Зону определяет TimeHelper::getInstance()->resolveEffectiveTimeZoneId(int $userId), цепочка у него такая:

  1. Если в main часовые пояса выключены (CTimeZone::OptionEnabled() && CTimeZone::Enabled() ложно), берётся зона сервера.
  2. Для текущего пользователя берётся TIME_ZONE из его параметров, а если он пуст и включено автоопределение, зона из куки CTimeZone::getTzCookie().
  3. Для всех остальных читается только сохранённый b_user.TIME_ZONE.
  4. Если зоны нет, берётся main.default_time_zone, затем зона сервера.

Найденное значение сверяется со списком timezone_identifiers_list(DateTimeZone::ALL_WITH_BC), поэтому '+03:00', 'GMT+3' и 'MSK' отбрасываются и срабатывает запасной вариант. Результат кешируется на хит. Новая цепочка b_user.TIME_ZONE_OFFSET не читает, грид timeman.worktime.grid тоже перестал выбирать это поле.

Устаревшие getUserUtcOffset() и getUserTimezone() по-прежнему идут через CTimeZone::GetOffset(), и для сотрудника с пустым TIME_ZONE он отдаёт последнее смещение из TIME_ZONE_OFFSET. Внутри модуля их ещё вызывают WorktimeEvent::create(), формы, ShiftPlanService и контроллеры.

Сотрудник с автоопределением и пустым TIME_ZONE в своих запросах получит зону из куки, а в запросах руководителя у него окажется main.default_time_zone или зона сервера. При автоопределении main сам записывает браузерную IANA-зону в b_user.TIME_ZONE через экшен main.timezone.set, так что пустой TIME_ZONE бывает в основном у тех, кто не заходил в веб-версию и работает только через мобильное приложение или REST. Список сотрудников, чья зона не пройдёт проверку, лучше получить до обновления. Конструктор new \DateTimeZone() для этого не подходит, он принимает и '+03:00', и 'MSK', так что сверяем со списком так же, как модуль:

        <?php declare(strict_types=1);

use Bitrix\Main\UserTable;

$ianaIds = array_flip(timezone_identifiers_list(\DateTimeZone::ALL_WITH_BC));

$users = UserTable::getList([
    'select' => ['ID', 'LOGIN', 'TIME_ZONE'],
    'filter' => ['=ACTIVE' => 'Y', '=IS_REAL_USER' => 'Y'],
]);

while ($user = $users->fetch()) {
    $zone = (string)$user['TIME_ZONE'];
    if (!isset($ianaIds[$zone])) {
        // timeman возьмёт main.default_time_zone, а без неё зону сервера
        printf("%d %s: '%s'\n", $user['ID'], $user['LOGIN'], $zone);
    }
}

    

Не сравнивайте старые и новые TIME_START в CTimeManEntry

CAllTimeManEntry::CheckFields теперь пишет в TIME_START и TIME_FINISH секунды от полуночи в IANA-зоне сотрудника на момент события, по формуле (ts + getOffsetAt(user, ts)) % 86400. В 26.0.0 формула была серверной, (ts + date('Z')) % 86400. Сотрудник в Екатеринбурге (UTC+5) начинает день в 09:00 по местному, сервер живёт по Москве (UTC+3). До обновления в TIME_START попадало 25200, то есть 07:00, после попадёт 32400. Прямые вызовы CTimeManEntry::Add() и Update() без TIME_* для таких сотрудников начнут сохранять другие числа.

При UPDATE поля DATE_START и DATE_FINISH пересчитываются из новых TIME_* на дату события в зоне сотрудника, серверная разница tz_diff в этом больше не участвует. DURATION теперь равен ts_finish − ts_start − TIME_LEAKS.

Модель WorktimeRecord считает длительность так же, от абсолютных моментов: RECORDED_STOP − RECORDED_START − перерывы с ограничением от 0 до 86400 (calculateElapsedDuration()). В 26.0.0 это была разница TIME_FINISH − TIME_START. В день перехода на летнее или зимнее время старая и новая формулы разойдутся на час.

Конвертации исторических строк в диффе нет. В b_timeman_entries рядом окажутся строки до обновления, где TIME_START посчитан в зоне сервера, и строки после, где он в зоне сотрудника. У сотрудников не из зоны сервера новые значения сдвинутся на разницу зон, и отчёт по TIME_START за такой период покажет скачок в день обновления. Надёжнее строить отчёты от RECORDED_START_TIMESTAMP и RECORDED_STOP_TIMESTAMP со снимками START_OFFSET и STOP_OFFSET.

Учтите, что время в форме правки теперь в зоне сотрудника

WorktimeRecord::updateByForm() трактует введённое время в зоне сотрудника при любых настройках. Флаг useEmployeesTimezone на сохраняемый момент больше не влияет, в коде он описан как «display-only mode now». Изменилось ли время, модель проверяет по «настенным» дате и времени (isWallTimeChanged, допуск 59 секунд). Раньше сравнивались timestamp.

Руководитель в Москве, который в карточке записи сотрудника из Новосибирска с выключенным «по времени сотрудника» вводит окончание 18:00, в 26.0.0 записывал 18:00 по Москве, то есть 22:00 по Новосибирску. Теперь запишется 18:00 по Новосибирску.

StopCustomTimeWorktimeManager строит время окончания в зоне сотрудника (userId) вместо зоны редактора (editedBy).

Смещения в записи тоже считаются на момент события:

  • START_OFFSET вычисляется в defineStartTime() через getOffsetAt() на момент начала и пересчитывается при каждом переносе начала. Раньше его брали из getUserUtcOffset() при создании записи и больше не трогали.
  • STOP_OFFSET вычисляется в stopWork() на момент окончания для любого редактора. Раньше только если editedBy пуст или совпадает с сотрудником.
  • stopOffset и recordedStopTimestamp из формы учитываются только на системном пути (isSystem, автозакрытие), в остальных случаях модель их игнорирует. Комментарий называет эти поля «public loadable form field» и объясняет запрет тем, что через них можно было бы подсунуть старое или поддельное смещение.

Сверьте интеграции с timeman.status, timeman.open и timeman.close

В описании версии вендор пишет, что «формат устаревшего REST (включая TZ_OFFSET) не изменён». Значения полей, включая сам TZ_OFFSET, при этом другие.

У timeman.status:

  • TIME_START и TIME_FINISH строятся из RECORDED_START_TIMESTAMP/RECORDED_STOP_TIMESTAMP и снимков START_OFFSET/STOP_OFFSET;
  • TZ_OFFSET теперь равен START_OFFSET, раньше его считали в момент вызова как getDayStartOffset() + date('Z');
  • у приостановленного дня TIME_FINISH теперь null, раньше приходило ISO-время из TIME_FINISH, который модель ставит при паузе;
  • TIME_FINISH_DEFAULT ставится на дату начала со смещением START_OFFSET.

timeman.open с параметром TIME пересчитывает момент из ISO-строки в зону сотрудника. Раньше бралось «настенное» время строки, а смещение из ISO игнорировалось. Если интеграция отправляла 2026-10-05T09:00:00+00:00, имея в виду девять утра по Москве, день сотрудника из Москвы открывался в 09:00. Теперь это 12:00 по Москве. Вызов после полудня откроет день в 12:00, а вызов до полудня получит ошибку «Нельзя начинать день со временем, больше текущего» с кодом ALERT_WARNING. Проверка «сегодня» тоже идёт по дате в зоне сотрудника, а дата уходит в openDay() как CUSTOM_DATE.

Передавайте настоящее смещение сотрудника, например (new \DateTimeImmutable('2026-10-05 09:00', new \DateTimeZone('Europe/Moscow')))->format(DATE_ATOM).

timeman.close с TIME больше не подгоняет время через correctTimeOffset() к смещению на момент вызова. Дата сверяется с датой начала в зоне сотрудника, а ночные смены закрывает ветка модели «stop <= start → +1 день».

Если вы наследуетесь от Bitrix\Timeman\Rest, проверьте вызовы его хелперов. Удалены protected static convertTimeToISO(), correctTimeOffset(), formatDateToISO() и formatTimeToISO(), у convertTimeFromISO($isoTime) появился второй параметр $userId, и наследник, переопределивший метод со старой сигнатурой, упадёт с Fatal error «must be compatible». Добавлены convertTimestampToISO(), convertDaySecondsToISO(), formatOffsetSuffix(), getCurrentDateInUserZone() и getRecordedStartDateInUserZone().

Уберите getUserOffset() и не полагайтесь на obtainUtcOffset()

Из TimemanWorktimeGridComponent удалён public static getUserOffset($userData), его вызов закончится фатальной ошибкой. Переключатель «по времени сотрудника» в гриде теперь появляется, когда у пользователей больше одной IANA-зоны. В 26.0.0 хватало больше одного смещения.

Bitrix\Timeman\Model\User\User::obtainUtcOffset() больше не подставляет getUserUtcOffset(). Если смещение не задали через defineUtcOffset(), метод вернёт 0, то есть UTC. Он помечен @internal, вместо него предложен obtainPinnedUtcOffset(), который в этом случае вернёт null. Если коду нужен прежний запасной вариант, подставьте смещение на нужный момент через ??:

        <?php declare(strict_types=1);

use Bitrix\Timeman\Helper\TimeHelper;

/** @var \Bitrix\Timeman\Model\User\User $user */
$offset = $user->obtainPinnedUtcOffset()
    ?? TimeHelper::getInstance()->getOffsetAt((int)$user->getId(), $timestamp);

    

TimeHelper::getUserToServerOffset() лишился CPHPCache. Раньше значение жило 3153600 секунд при BX_COMP_MANAGED_CACHE (иначе сутки) под тегом USER_NAME_<id>, теперь только в пределах хита.

Обновите код, который читает схему уведомлений

CTimemanNotifySchema::OnGetNotifySchema() возвращает группу ['timeman' => ['NAME' => ..., 'NOTIFY' => [...]]]. Раньше типы уведомлений лежали прямо под ключом timeman. Если вы читаете схему сами, берите типы из ['timeman']['NOTIFY'].

Проверьте расчёты смен и рантайм-данных дня

  • CTimeManUser::getDayStartOffset() возвращает смещение сотрудника минус смещение сервера, оба на момент начала дня. isDayOpenedToday() сравнивает даты в зоне сотрудника.
  • CTimeMan::GetTimeTS() берёт смещение сервера на момент $ts. В CUserReportFull вызов date('Z') тоже заменён на TimeHelper::getInstance()->getServerOffsetAt($timestamp), им же CTimeManUser::getDayStartOffset() считает смещение сервера.
  • CTimeMan::getRuntimeInfo() заполняет INFO.DATE_START и INFO.DATE_FINISH из RECORDED_START_TIMESTAMP и buildDisplayedStopTimestamp(), в ответе появились ключи DISPLAY_START_TIMESTAMP и DISPLAY_STOP_TIMESTAMP. Pull-события WorktimeService берут dateStart и dateFinish из абсолютных полей записи.
  • Shift::buildUtcStartByUserId($userId, $shiftDateTime) строит начало смены из WORK_TIME_START на календарной дате в IANA-зоне и всегда возвращает DateTime. buildUtcEndByShiftplan() берёт WORK_TIME_END на дате смены, у ночной смены на следующей. Прежнюю формулу «начало плюс getDuration()» убрали.
  • ShiftWithDate::__construct() получил необязательный параметр ?int $userId и считает конец смены через Shift::buildUtcEndByUserId(). Без userId концом считается WORK_TIME_END в зоне $dateTimeStart.
  • TimeHelper::getTimestampByUserDate() строит полночь даты в IANA-зоне сотрудника. Раньше это была серверная полночь минус текущее смещение.

Deprecated

В Bitrix\Timeman\Helper\TimeHelper на выпил помечены два метода:

  • getUserUtcOffset($userId) отдаёт смещение «на сейчас», замена getOffsetAt($userId, $timestamp);
  • getUserTimezone($userId) заменяется на getUserDateTimeZone($userId), который возвращает настоящую DateTimeZone с правилами DST.

createTimezoneByOffset() помечен @internal. Синтетическая зона '+HH:MM' нужна только для восстановления истории по снимку смещения, это делает reconstructHistoricalDateTime($timestamp, $snapshotOffset). Перевод оставшихся вызовов getUserUtcOffset() и getUserTimezone() внутри модуля в комментариях назван этапом «P5».

Методы у TimeHelper экземплярные, объект берётся через getInstance(). Обёртка для своего модуля:

        <?php declare(strict_types=1);

namespace Vendor\Timesheet\Application\Service;

use Bitrix\Timeman\Helper\TimeHelper;

final class WorkdayClock
{
    private readonly TimeHelper $timeHelper;

    public function __construct(?TimeHelper $timeHelper = null)
    {
        $this->timeHelper = $timeHelper ?? TimeHelper::getInstance();
    }

    public function offsetAt(int $userId, int $timestamp): int
    {
        // было: $this->timeHelper->getUserUtcOffset($userId), одно смещение на все даты
        return $this->timeHelper->getOffsetAt($userId, $timestamp);
    }

    public function toUserTime(int $userId, int $timestamp): \DateTimeImmutable
    {
        // было: getUserTimezone($userId), зона без правил DST
        return (new \DateTimeImmutable('@' . $timestamp))
            ->setTimezone($this->timeHelper->getUserDateTimeZone($userId));
    }

    public function fromWallTime(int $userId, string $date, int $hours, int $minutes): int
    {
        return $this->timeHelper->buildTimestampFromWallTime($userId, $date, $hours * 3600 + $minutes * 60);
    }
}

    

Для сотрудника с зоной Europe/Berlin offsetAt() вернёт 3600 для 27 марта 2026 года и 7200 для 30 марта, getUserUtcOffset() отдал бы одно число на обе даты. Вызов fromWallTime($userId, '2026-03-29', 2, 30) попадает в час, которого в Берлине в эту ночь нет, и buildTimestampFromWallTime() сдвинет время вперёд. В повторяющийся осенний час результат определяет DateTime в PHP, в комментарии сказано, что это поведение закреплено регрессионным тестом.

Новое

Проверьте таблицу до кнопки «Обсудить отчёт»

Экшен timeman.V2.ReportDiscussion.discuss принимает reportId и вызывает ReportDiscussionService::discuss(). Сервис находит непосредственного руководителя, создаёт или переиспользует групповой чат пары с ENTITY_TYPE = TIMEMAN_REPORT_DISCUSS и ENTITY_ID = <managerId>_<employeeId> и публикует туда отчёт. Связь отчёта с чатом пишется в новую таблицу b_timeman_report_discussion через ReportDiscussionTable::merge() на SqlHelper::prepareMerge(). Рядом появились ReportPeriodPhraseFormatter для фразы с периодом отчёта и FullReportUserService::canUserReadUser().

Таблица есть только в install/db/mysql/install.sql и pgsql/install.sql, миграции для уже установленных порталов в диффе нет. Сервис проверяет её через isTableExists() и без таблицы отдаёт ошибку CHAT_CREATION_ERROR. В комментарии это названо временной страховкой до версионного апдейтера, выпуск которого «deliberately deferred». Пока апдейтер не вышел, на обновлённой коробке кнопка работать не будет. Переустановка модуля с сохранением данных таблицу тоже не создаст, потому что install.sql, как и в 26.0.0, выполняется только при отсутствии b_timeman_entries, а installMigrations() создаёт только b_timeman_entries_intent_state.

Проверить коробку после обновления:

        <?php declare(strict_types=1);

use Bitrix\Main\Application;

$exists = Application::getConnection()->isTableExists('b_timeman_report_discussion');
echo $exists
    ? 'b_timeman_report_discussion на месте'
    : 'таблицы нет, «Обсудить отчёт» вернёт CHAT_CREATION_ERROR', PHP_EOL;

    

Берите руководителя и период из триггеров БП

Три триггера бизнес-процессов отдают новые значения:

  • FullReportReadyTrigger — MANAGER;
  • FullReportSentTrigger — MANAGER, REPORT_ID, PERIOD_PHRASE и SOURCE_TYPE;
  • StopWorktimeTrigger — WORKDAY_START, DATETIME со смещением начала дня.

FullReportAiGenerator теперь передаёт в триггер «отчёт готов» непосредственного руководителя, первого из managerIds. Правки лежат в install/activities/bitrix/*trigger/, lib/V2/Internal/Integration/Bizproc/ и lib/integration/bizproc/stopworktimetrigger.php.

Учтите 12 часов автозакрытия при check-in

Если включён check-in (Stafftrack\CheckIn::isCheckInStartEnabled()), у свободного графика автозакрытие ставит стоп через 12 часов от начала вместо 9 (buildStopTimestampForCheckInAutoClose). У дня, начатого в 10:00, окончание запишется на 22:00 вместо 19:00. Закроет его агент, который для flextime теперь планируется на начало следующих местных суток после момента «стоп минус секунда», в этом примере на 00:00. До полуночи день остаётся открытым, в 26.0.0 агент срабатывал в момент стопа. AutoCloseWorktimeAgent больше не приравнивает STOP_OFFSET к START_OFFSET.

Собирайте зоны сотрудников одним запросом

Кроме getUserDateTimeZone(), getOffsetAt(), buildTimestampFromWallTime(), reconstructHistoricalDateTime() и getServerOffsetAt() в TimeHelper появились resolveEffectiveTimeZoneId(), formatSignedOffset() и resolveEffectiveTimeZoneIdFromPersisted(). Последний берёт уже загруженный TIME_ZONE и не ходит в b_user, так грид обходится без N+1. Отчёту по отделу он пригодится так же:

        <?php declare(strict_types=1);

use Bitrix\Main\UserTable;
use Bitrix\Timeman\Helper\TimeHelper;

/** @param int[] $userIds */
function resolveZones(array $userIds): array
{
    $helper = TimeHelper::getInstance();
    $zones = [];

    $rows = UserTable::getList([
        'select' => ['ID', 'TIME_ZONE'],
        'filter' => ['@ID' => $userIds],
    ]);
    while ($row = $rows->fetch()) {
        $id = (int)$row['ID'];
        $zones[$id] = $helper->resolveEffectiveTimeZoneIdFromPersisted($id, (string)$row['TIME_ZONE']);
    }

    return $zones;
}

    

Для наследников TimeHelper открыты protected-точки переопределения: isTimeZoneFeatureEnabled(), getPortalDefaultTimeZoneId(), getCurrentUserId(), getCurrentUserRuntimeTimeZoneId() и getPersistedTimeZoneId(). Ещё добавлены Shift::buildUtcEndByUserId(), WorktimeRecord::buildDisplayedStopTimestamp(), User::obtainPinnedUtcOffset(), User::obtainPinnedTimezoneName(), RecordRepository::getClosedStartTimestampsByUserAndRange(), а для обсуждения отчётов FullReportProvider::getByIdWithoutParticipants() и FullReportRepository::getByIdWithoutPayload(). У WorktimeRecord::buildRecordedStopDateTime() появился необязательный параметр $recordedStopTimestamp.

БД

  • b_timeman_report_discussion: ID, REPORT_ID, CHAT_ID, MANAGER_ID, EMPLOYEE_ID, MAIN_MESSAGE_ID, COMMENT_MESSAGE_ID, CREATED_AT. Индекс UNIQUE по (REPORT_ID, MANAGER_ID) и обычный по CHAT_ID. NULL в *_MESSAGE_ID значит, что сообщение ещё не доставлено и его можно отправить повторно.
  • b_timeman_entries_intent_state описана в install/migrations/tables.php. Одна строка на сотрудника (USER_ID UNIQUE), в ней FIRST_USE_AT, PERIOD_KEY, TARGET_START_AT, PERIOD_END_AT, счётчики SHOW_COUNT и DISMISS_COUNT, SUPPRESSED_UNTIL, флаг HAS_TARGET_ACTION, отметки LAST_REGISTERED_AT и LAST_SHOWN_AT.
  • Установка стала гибридной. InstallDB(), как и раньше, выполняет install.sql, только если нет b_timeman_entries, а после этого теперь всегда вызывает installMigrations(). UnInstallDB() вызывает uninstallMigrations($dropTables), где $dropTables истинно при savedata != 'Y'. В migration_config.json defaultTableName равен b_timeman_entries.
  • Под часовые пояса схему не трогали. Зона берётся из b_user.TIME_ZONE, в b_timeman_entries те же START_OFFSET, STOP_OFFSET, RECORDED_START_TIMESTAMP и RECORDED_STOP_TIMESTAMP, поменялось только то, что в них пишется.

Мелочи и находки

  • В релизе изменено 245 файлов, +5461/−1287. Около 5100 из этих строк приходится на 123 PHP-файла, ещё 50 файлов занимает замена png на webp.
  • Чат пары защищён от подмены. ENTITY_ID у него предсказуемый, а addUniqueChat() переиспользует любой чат с таким ключом. В комментарии сказано прямо, что пользователь мог заранее создать такой чат через публичный im.chat.add и получить туда чужой отчёт. Поэтому при создании найденный чат принимается, только если его автор и точный состав совпадают с парой (isPairChatTrusted()). Чаты, уже записанные в b_timeman_report_discussion, считаются доверенными без этой проверки. Так стоит проверять любой служебный чат с угадываемым ключом.
  • Черновики отчётов (ACTIVE=N) не может обсудить никто. Руководитель из оргструктуры без прав tm_read или tm_read_subordinate получит ACCESS_DENIED, иначе, по комментарию, «the manager could brute-force reportId».
  • В CTimeMan::buildInfoTimestamps() есть строки «the historical contract restored after 26.100.0» и «DISPLAY_*_TIMESTAMP are synonyms kept for the JS already shipped on them in 26.100.0». Похоже, в эту сборку уже вошло исправление к более ранней сборке 26.100.0.
  • Докблок getUserToServerOffset() называет удалённый кеш «~365 days», хотя константа была 3153600 секунд, это 36,5 суток.
  • Комментарии ссылаются на внутреннюю проектную документацию: ALG-01/02/03, INV-COORD, «ADR invariant», этапы P1–P5, Q-1/Q-2, API-01, LBS #6/#13, quirk #3–#5, DTO-01. Судя по «until they are migrated in P5», переход не закончен.
  • RecordIntent готовит плашку «welcome-box» с напоминанием начать рабочий день: RecordIntentService, RecordIntentStateRepository, команды RegisterShowCommand и RegisterOutcomeCommand, DTO RecordIntentSchedule, RecordIntentProvider::getSchedule(). По константам там режимы dataCollection, smart и shift, до 4 показов (в выходной фиксированного графика 1) и до 4 отказов, повтор раз в час, при сменном графике цель за 20 минут до смены, в режиме сбора данных старт в 05:00 с запасным в 09:00 и история за 7 дней. Ни контроллера, ни JS, которые вызывали бы провайдер или команды, в релизе нет, это заготовка.
  • Действия БП fillfullreportactivity, getreportactivity, savedayplanactivity и savereportactivity убирают ИИ-варианты из списка, если Bizproc\Public\Service\AiAgent\NodeAvailabilityServiceInterface::isAvailable() вернул false. Реализация в bizproc проверяет наличие модуля aiassistant и доступность ИИ в регионе портала.

Что делать

До обновления найдите в проекте getUserOffset(, obtainUtcOffset(, getUserUtcOffset(, getUserTimezone(, CTimeManEntry::Add(, CTimeManEntry::Update( и чтение OnGetNotifySchema. Интеграторов, которые открывают и закрывают день по REST, предупредите, что смещение в TIME теперь учитывается. После обновления проверьте b_timeman_report_discussion и прогоните список сотрудников с пустой или не-IANA зоной.

Устаревшие и удалённые API в этой версии

Символ Статус Чем заменять
Bitrix\Timeman\Helper\TimeHelper::getUserUtcOffset() Deprecated Bitrix\Timeman\Helper\TimeHelper::getOffsetAt()
Bitrix\Timeman\Helper\TimeHelper::getUserTimezone() Deprecated Bitrix\Timeman\Helper\TimeHelper::getUserDateTimeZone()
Bitrix\Timeman\Component\SchedulePlan\TimemanWorktimeGridComponent::getUserOffset() Удалено —

Читайте дальше

voximplant 26.800.0 Ломающее Свежее

Voximplant 26.800.0: attachRecord и finish отказывают локальным адресам, searchCrmEntities отдаёт один лид

В voximplant 26.800.0 у четырёх REST-методов внешней телефонии поменялось поведение, хотя сигнатуры остались прежними. `telephony.externalCall.attachRecord` и `telephony.externalCall.finish` с `RECORD_URL` больше не скачивают записи с приватных адресов и не ходят через прокси. `searchCrmEntities` от...

4 мин
report 26.200.100 Безопасность Свежее

Report 26.200.100: закрыта SQL-инъекция через формат имени пользователя

Релиз «Конструктора отчётов» вендор описал фразой «Усилена безопасность модуля». На деле изменилась одна строка в `CReport::getFormattedNameExpr()`, теперь метод экранирует литералы из формата имени пользователя перед вставкой в SQL. Если модуль `report` установлен, обновите его до 26.200.100. Файл...

2 мин
note 26.400.0 Ломающее Свежее

note 26.400.0: updatedAt не отражает правки в редакторе, уведомления выключены по умолчанию

В [note 26.300.0](/bitrix-updates/note/note-26-300-0) модуль «Базы знаний 2.0» получил события, историю версий и подписки. Версия 26.400.0 добавляет избранное, обратные ссылки между документами, отдельную материализацию markdown-проекции Yjs-документа, асинхронный пересчёт поиска и боковой чат Bitri...

2 мин
note 26.300.0 Заметное Свежее

note 26.300.0: у Базы знаний 2.0 появились события, история версий и подписки

Модуль note в коробочном Битрикс24 отвечает за «Базу знаний 2.0»: коллекции с деревом markdown-документов, совместное редактирование на Yjs, свои права доступа и REST v3 `note.*`. В 26.300.0 он получил первые публичные события, историю версий, подписки с уведомлениями в IM, массовые операции над дер...

4 мин
Мы используем файлы cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.
AI Домовой

AI Домовой История

на связи

пишет…
Нет истории чатов
AI Домовой

Нужна авторизация

Войдите, чтобы задавать вопросы AI Домовому.

Войти