Booking 26.500.100: addList отвечает ошибкой, когда не создал ни одной брони, а четыре колонки дат стали DATETIME
В коробочный Битрикс24 пришёл booking 26.500.100 с двумя правками. AJAX-экшен booking.api_v1.Booking.addList теперь возвращает ошибку, если не создал ни одной брони. Апдейтер переводит четыре колонки дат с TIMESTAMP на DATETIME, и в install/migrations/tables.php они объявлены через datetime(). Экшен важен тем, кто создаёт брони пачкой из своего JS, колонки — тем, кто читает эти поля или описывает таблицы своего модуля в tables.php.
Новое
Сверяйте, сколько броней создал addList
Раньше экшен молча пропускал бронь, если Booking::mapFromArray() не смог собрать её из полей, если ресурсов больше одного, а мультиресурсные брони на портале недоступны, или если AddBookingCommand вернул ошибку. Ответ всё равно был успешным, в худшем случае с пустым списком. Теперь ошибки собираются. Если не создалась ни одна бронь, экшен отдаёт их в ответе, и промис BX.ajax.runAction отклоняется. Если создалась хотя бы одна, ответ успешный, но в нём только созданные брони, а ошибки по остальным не приходят. Так сделано нарочно, потому что штатный клиент модуля при ошибке отбрасывает данные ответа и уже созданные брони пропали бы вместе с ними.
Какие брони не прошли, ответ не скажет, поэтому сравнивайте длину массивов. Экшен принимает только JSON-тело (JsonController с фильтром ContentType::JSON), так что в примере json, а не data:
const bookingList = collectBookings(); // поля те же, что у booking.api_v1.Booking.add
BX.ajax.runAction('booking.api_v1.Booking.addList', { json: { bookingList } })
.then(({ data }) => {
if (data.length < bookingList.length)
{
showWarning(`Создано ${data.length} из ${bookingList.length}`);
}
renderBookings(data);
})
.catch((response) => {
const messages = (response.errors ?? []).map((error) => error.message);
showError(messages.join('\n') || 'Не удалось создать брони');
});
Штатный bookingService.addList() ловит ошибку и возвращает пустой массив, так что в интерфейсе модуля ничего не меняется.
Пишите datetime() в tables.php своего модуля
С main 26.800.0 Table::checkNoTimestampColumns() считает timestamp-колонки устаревшими, потому что PostgreSQL не поддерживает семантику MySQL TIMESTAMP. Проверка срабатывает только в режиме разработки, когда апдейтер лежит в dev/updates/current/updater.php. Там такая колонка в create() или alter() вызывает исключение с кодом 1103 и текстом «use datetime instead». При установке модуля через installMigrations() и в выпущенных апдейтерах проверки нет, сам timestamp() не помечен @deprecated.
CModule::installMigrations() находит install/migrations/tables.php и в /local/modules, так что своему модулю хватит вызвать его в InstallDB(), как делает booking, а в UnInstallDB() — uninstallMigrations($dropTables). На MySQL timestamp('X') без notNull() рендерится голым TIMESTAMP, и при explicit_defaults_for_timestamp=OFF MySQL сделает колонку NOT NULL. DATETIME от этой настройки не зависит.
<?php declare(strict_types=1);
use Bitrix\Main\UpdateSystem\Migration;
use Bitrix\Main\UpdateSystem\Migration\CreateTableBuilder;
Migration::getInstance()->table('b_vendor_visit')->create(function (CreateTableBuilder $table): void {
$columns = $table->addColumn();
$columns->int('ID')->unsigned()->notNull()->autoincrement();
$columns->int('BOOKING_ID')->unsigned()->notNull();
$columns->datetime('VISITED_AT');
$columns->datetime('CREATED_AT')->notNull()->defaultCurrentTimestamp();
$table->addPrimaryKey('ID');
$table->addIndex('IX_VENDOR_VISIT_BOOKING_ID', ['BOOKING_ID']);
});
Колонкам с defaultCurrentTimestamp() booking оставил timestamp(), но этот метод есть и у datetime().
БД
В tables.php с timestamp() на datetime() переведены b_booking_resource_data.DELETED_AT, b_booking_booking.CONFIRMED_AT и DELETED_AT, b_booking_booking_message.NEXT_RETRY_AT. Существующие таблицы меняет апдейтер 26.500.100. На нашем эталоне (MySQL) он выполнил ALTER TABLE … MODIFY COLUMN … DATETIME для всех четырёх колонок, теперь они datetime NULL DEFAULT NULL. Смена типа перестраивает таблицу, так что на большой b_booking_booking обновление может идти дольше.
Потом апдейтер обнуляет значения, которые противоречат флагам: CONFIRMED_AT при IS_CONFIRMED = 'N', DELETED_AT при IS_DELETED = 'N' в обеих таблицах, NEXT_RETRY_AT у сообщений со STATUS <> 'failed'. Если ваш код читает эти поля, у таких записей там теперь NULL.
«Проверка системы» сверяет таблицы с tables.php. После обновления декларация и схема по этим колонкам совпадают, и SC_ERR_FIELD_DIFFERS по ним не будет. На PostgreSQL оба типа рендерятся как timestamp, схема там не меняется.