Как я починил каталог вилл в мини-приложении Telegram и нашёл лавину скрытых ошибок

Я не планировал тратить на это весь день. Зашёл проверить, почему вилла 26 не показывается в мини-приложении Telegram, хотя её уже включили в админке. Думал — минутное дело, может опечатка в статусе, может кеш. Открываю базу, смотрю — и начинается кроличья нора. Один баг потянул второй, второй обнажил структурный бардак в каталоге вилл, там нашлись дублирующиеся записи, за ними — неработающее удаление, потом грязный раздел «Продажа», потом Casa Solar с семью «детьми» в базе, из которых только три живые. И в конце всего этого — ещё ни одна вилла не имела нормальных фотографий, потому что скрипта загрузки из Google Drive не существовало. Итого: одна «вилла 26» превратилась в восемь включённых вилл, два починенных бага в интерфейсе, три зачищенных отеля, 36 свежих фотографий и один переиспользуемый скрипт для импорта медиа. Ни одной строки SQL вручную. Всё — в диалоге с ботом. Рассказываю по порядку, потому что каждый слой этой истории — отдельный урок.

Вилла 26 не показывается: как один тумблер скрывал системный баг

История начинается банально. Менеджер говорит: «Мы включили виллу 26, но её нет в каталоге». Открываю мини-приложение Telegram — действительно нет. Захожу в админку — тумблер в положении «включено». Всё выглядит нормально, пока не залезешь внутрь.

В нашей базе данных у каждого объекта есть два разных флага. Первый — что-то вроде «is_enabled», который переключает тумблер в интерфейсе. Второй — поле «status» со значениями «active», «closed», «draft». Фильтр главной страницы каталога вилл смотрит именно на второй флаг: он показывает только объекты со статусом «active». Логика в этом есть — статус описывает бизнес-состояние объекта, а не просто настройку видимости.

Проблема в том, что тумблер «включить виллу» в админке переключал только первый флаг — «is_enabled». Статус при этом оставался «closed». Получалась абсурдная ситуация: вилла технически «включена», но каталог её не видит. Как будто ты поднял флаг, но дверь так и не открыл.

Починка заняла несколько минут: тумблер теперь атомарно ставит оба флага. При включении — и «is_enabled = true», и «status = active». При отключении — оба в обратную сторону. Никаких промежуточных состояний.

Но пока я смотрел на эту логику, понял, что баг существует не только для виллы 26. Полез проверять базу — и нашёл восемь объектов, которые годами висели в «is_enabled = true, status = closed». Восемь вилл и комнат, которые управляющая компания считала опубликованными, а каталог игнорировал. Среди них — Rose 2, Angels Home и шесть комнат Pyramids Hotel в Чангу. Включил их всех разом. Теперь они в каталоге.

Почему два флага вместо одного — это не архитектурная ошибка

Можно подумать: зачем вообще два флага, если это создаёт такие проблемы? Но логика здесь есть. «is_enabled» — это ручное управление видимостью, которое может делать менеджер. «status» — это состояние объекта в системе бронирования, которое может меняться автоматически: объект закрыт на ремонт, передан в обслуживание другой компании, временно выведен из аренды по договору. Когда у них разные источники изменений, держать их раздельно — правильно. Проблема была только в том, что тумблер в админке не знал о существовании второго флага.

Удалить нельзя: как Telegram блокирует браузерные диалоги в мини-приложениях

Раз я уже был внутри кода, решил проверить соседние функции. Попробовал удалить один из неактивных объектов через интерфейс — ткнул кнопку, подтвердил в диалоге, ничего не произошло. Попробовал ещё раз. Тишина.

Полез в код фронтенда. Картина такая: кнопка удаления вызывала стандартный браузерный диалог — «confirm(» Вы уверены? »)». В обычном браузере это работает нормально: появляется системное окошко, пользователь нажимает ОК, функция выполняется. Но мини-приложения Telegram работают иначе. WebView внутри Telegram блокирует стандартные браузерные диалоги — «alert», «confirm», «prompt» — и просто не показывает их. Молча. Без ошибки, без лога, без ничего. Пользователь нажимает, диалог не появляется, код воспринимает это как отказ и отменяет удаление.

Что интересно: в коде уже лежала правильная замена. Кто-то раньше написал функцию, которая использует нативный Telegram API — «showConfirm» из объекта «window.Telegram.WebApp». Это официальный способ показывать диалоги подтверждения внутри мини-приложений. Нативный, выглядит органично, работает везде. Просто эту функцию подключили к другим кнопкам, а к кнопке удаления — забыли.

Пять минут — и удаление заработало. Но сам факт меня зацепил: в нашем каталоге вилл люди неделями не могли удалять объекты, а никто не жаловался. Либо не пробовали, либо решили, что «так и должно быть». Это отдельная история про то, как молчаливые баги живут годами.

Telegram Mini Apps: что ещё не работает из стандартного веба

Баг с confirm — не единственная особенность. Мини-приложения Telegram — это WebView с ограничениями. Помимо диалогов, там не работают некоторые CSS-анимации, могут быть проблемы с буфером обмена без явного разрешения, история навигации ведёт себя иначе. Если вы делаете мини-приложение и что-то «не срабатывает» — проверяйте не логику, а совместимость с WebView. Половина «багов» оказываются просто несовместимым API.

Помойка в разделе «Продажа»: семь карточек одного отеля

После двух быстрых починок я уже разогрелся и решил пройтись по каталогу шире. Открыл раздел «Продажа» — и сразу стало понятно, что там давно никто не убирал.

Pyramids Hotel в Чангу висел как семь отдельных объявлений. Одна карточка — весь отель целиком с ценой несколько миллионов долларов. И рядом шесть карточек — каждая отдельная комната того же отеля, каждая по 130 000 долларов. Как будто я продаю номера поштучно, как квартиры в новостройке.

Откуда это взялось? Шесть отдельных записей — это старые листинги, которые создали раньше, до того как в системе появилась нормальная структура «отель плюс комнаты». Тогда каждую комнату добавляли как самостоятельный объект. Потом структуру переделали, отель получил свою карточку с дочерними комнатами, но старые листинги так и остались висеть рядом — никто их не убрал.

Решение: привязал все шесть старых записей как дочерние к основной карточке отеля. Цены продажи у «детей» обнулил — они теперь просто комнаты внутри объекта, а не самостоятельные лоты. Раздел «Продажа» сразу стал чище: 13 объектов вместо 19, каждый отель одной карточкой.

Интересная деталь: это случается с любым бизнесом, который мигрирует структуру данных. Старые записи не исчезают сами по себе — их нужно либо архивировать, либо привязывать к новой схеме. Если этого не делать, через год каталог вилл превращается в хаос, где покупатель видит один и тот же объект несколько раз с разными ценами и не понимает, что выбрать.

Почему дубли в каталоге убивают конверсию

С точки зрения автоматизации бизнеса на Бали это не просто эстетическая проблема. Когда потенциальный покупатель или арендатор видит один отель семь раз с разными ценами, первая реакция — недоверие. «Они сами не знают, что продают». Вторая — путаница: какая цена настоящая? Третья — он закрывает страницу. Дублирующиеся записи в каталоге вилл — это прямой урон конверсии, который не видно в аналитике, пока не начнёшь смотреть на поведение пользователей в сессиях.

Casa Solar в Чангу: семь «детей», один дубль и 385 транзакций

Это был самый сложный случай дня. Casa Solar — отель в Чангу, один из объектов в управлении. Открываю его в базе и вижу семь дочерних записей. Начинаю разбираться.

Первое, что бросается в глаза: одна из записей — полный дубль самого отеля. Те же поля, та же структура, просто другой ID. Кто-то в какой-то момент создал отель дважды, и обе версии жили параллельно. Звучит как мелочь, пока не смотришь на историю: на этот дубль было привязано 9 бронирований и 385 финансовых транзакций. Просто так его не выкинешь — потеряешь историю.

Дальше — пара записей под именами «Superior» и «Family» без цен, без описания, без фотографий. Явно тестовые заготовки, которые забыли удалить. И, наконец, реальные комнаты — но с неверным статусом «inactive», хотя они давно опубликованы и принимают гостей.

Как это разбирать без потери данных? Пошагово. Сначала перевязал все 9 бронирований и 385 транзакций с дубля на правильную запись отеля. Проверил, что история сохранилась. Потом удалил дубль. Потом разобрал тестовые заготовки. И в конце переделал оставшиеся комнаты в нормальную структуру: 6 комнат тремя парами — 2 Standard, 2 Deluxe, 2 Interior. Починил маппинг для динамических цен через eZee, чтобы обновление прилетало сразу на обе комнаты каждого типа.

После этой операции Casa Solar в системе выглядит как настоящий маленький отель: один родительский объект с чистой историей и шесть живых дочерних комнат. Не семь призраков с запутанными статусами.

Как не потерять историю при слиянии дублей в базе

Главный принцип при работе с дублями в боевой базе — никогда не удалять запись, пока не перевязал все зависимые данные. Бронирования, транзакции, отзывы, логи — всё это ссылается на ID объекта. Если удалить дубль первым, получишь «висячие» ссылки и поломанные отчёты. Правильный порядок: сначала мигрируй зависимые данные, потом проверяй целостность, потом удаляй.

В нашем случае это звучит просто, потому что объём небольшой — 9 бронирований и несколько сотен транзакций. Но принцип не меняется и при миллионе записей. Именно поэтому я не пишу SQL руками: бот читает код, видит связи между таблицами и подсказывает правильный порядок операций. То, что я бы искал полчаса в схеме, он объясняет за секунды.

Pipeline для фотографий из Google Drive: от папки к базе за пару минут

Когда структура Casa Solar была исправлена, встал следующий вопрос: у шести новых комнат нет ни одной фотографии. Каталог вилл без фоток — это не каталог вилл, это список текста. Фотографии лежат в Google Drive — у каждой виллы своя папка, внутри подпапки по комнатам: R1, R2, R3, R4, R5, R6, плюс отдельная папка для бассейна и общих зон.

Раньше это делалось вручную: скачай, переименуй, залей через интерфейс, пропиши в базе. Часа три на один отель. Я решил написать скрипт.

Сервисный аккаунт Google у нас уже был — он используется для других интеграций. Оказалось, что у него есть доступ ко всем папкам вилл. Скрипт делает следующее: получает список папок для объекта, заходит в каждую подпапку (R1–R6, бассейн), скачивает все изображения на сервер, раскладывает их в правильную директорию рядом с приложением, и прописывает каждый файл в базу данных с нужными метаданными — к какой вилле относится, какая это комната, порядок сортировки.

Запустил для Casa Solar. Через несколько минут: все 6 комнат получили реальные фотографии, у отеля в шапке появились 8 снимков бассейна и общих зон. Итого 36 фотографий загружены и привязаны без единого ручного действия.

Скрипт переиспользуемый — он принимает ID объекта как аргумент. Теперь, когда появится новая вилла или нужно будет обновить фотографии, это займёт несколько минут вместо нескольких часов. Это и есть настоящая автоматизация бизнеса: не замена людей, а устранение повторяющейся ручной работы.

Почему сервисный аккаунт Google лучше OAuth для серверных задач

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

Если у вас несколько объектов с папками в Google Drive — заведите один сервисный аккаунт с доступом ко всем папкам. Это снимает целый класс проблем с «у меня слетела авторизация». Подробнее об интеграции внешних сервисов через API я писал в статье про диагностику OAuth интеграций.

Итоговый счёт: что получилось из одного бага

Давайте подведём итог в цифрах. Зашёл починить одну виллу — вышел с:

8 включённых вилл — вилла 26 плюс семь объектов, которые годами числились «включёнными» в интерфейсе, но были невидимы для каталога из-за бага с двойными флагами. Rose 2, Angels Home, шесть комнат Pyramids Hotel — все они теперь в каталоге вилл Бали.

2 починенных бага в интерфейсе — двойной флаг статуса и заблокированный браузерный confirm в Telegram WebView. Оба существовали давно, оба незаметно ломали работу менеджеров.

3 почищенных отеля — Pyramids Hotel с семью карточками вместо одной, Casa Solar с дублирующейся записью и тестовыми заготовками, и ещё один объект с неверной структурой продажных листингов.

36 фотографий — загружены и привязаны к шести комнатам Casa Solar через новый скрипт импорта из Google Drive.

1 переиспользуемый скрипт — pipeline для загрузки фотографий из Drive, который теперь можно запускать для любой виллы.

Самое важное: ни одной строки SQL вручную. Всё — в диалоге с ботом. Он читал код, указывал строки, находил связи между таблицами, объяснял зависимости. Работу, которую я делал бы несколько дней с открытой схемой БД и постоянными вопросами, мы разобрали за один день.

Накопленный бардак: почему он появляется и как его предотвратить

История про виллу 26 — это не история про плохой код. Это история про то, как бизнес растёт быстрее, чем успевает наводить порядок в данных. Когда запускаешь первые виллы, создаёшь листинги вручную, используешь одну схему. Потом схема меняется, но старые данные не мигрируют — просто потому что некогда. Потом добавляются новые функции, они работают с новой схемой, но старые записи их игнорируют. Через год имеешь каталог, где половина объектов живёт по разным правилам.

Решение не в том, чтобы «делать правильно с самого начала» — это невозможно, потому что ты не знаешь заранее, как будет развиваться система. Решение в том, чтобы периодически делать то, что я сделал сегодня: заходить по конкретному багу и не выходить, пока не поймёшь всю картину. Один баг — это всегда входная точка в накопленный технический долг. Главное не бояться за ниточку потянуть.

Управление виллами через мини-приложение Telegram: зачем это вообще нужно

Раз уж разобрал внутренности системы, стоит объяснить контекст: почему управление виллами на Бали через мини-приложение Telegram, а не через обычный веб-интерфейс или CRM.

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

Это не просто удобство. Это разница между тем, используют ли менеджеры инструмент или нет. Если инструмент требует пять дополнительных шагов — они будут работать по старинке через сообщения и звонки. Если он встроен в привычный интерфейс — используют. Автоматизация бизнеса на Бали работает только тогда, когда люди реально ею пользуются.

Отдельный плюс: Telegram хранит авторизацию на всех устройствах. Менеджер открыл мини-приложение на телефоне — видит каталог вилл с актуальными статусами. Открыл на планшете — та же картина. Никаких «у меня слетел пароль», никаких VPN, никаких корпоративных учёток, которые нужно выдавать и отзывать.

Что мы автоматизировали в управлении виллами

Мини-приложение — это только один из слоёв. Под ним — система, которая синхронизирует бронирования через channel manager, обновляет динамические цены из eZee PMS, отправляет уведомления при заезде/выезде, ведёт P&L по каждой вилле в реальном времени. Всё это работает без участия человека в фоновом режиме. Менеджер видит в мини-приложении уже переваренные данные, а не сырой поток событий из трёх источников.

Про то, как устроена автоматизация финансов, я писал отдельно — автоматический мониторинг финансов для 16 вилл на Бали. Там подробно про P&L, транзакции и почему Excel в этом деле рано или поздно убивает.

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

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

Это не значит, что SQL не пишется вообще. Это значит, что я не пишу его вручную — с открытой схемой, держа в голове названия таблиц, типы колонок и связи между ними. Вместо этого я описываю задачу на обычном языке: «у Casa Solar в базе семь дочерних объектов, один из них — дубль самого отеля, на нём 9 бронирований и 385 транзакций, как правильно их перевязать и удалить дубль». Бот читает схему, находит нужные таблицы, видит внешние ключи, предлагает последовательность операций с объяснением почему именно такой порядок.

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

Это то, что я называю правильным использованием AI в управлении виллами: не «напиши мне функцию», а «помоги разобраться в том, что уже есть». Второе намного сложнее и намного ценнее.

Какие задачи хорошо решаются с AI, а какие — нет

Честно: AI хорошо работает с задачами, где есть структура и можно прочитать код. Разобрать схему базы, найти связи, предложить безопасный порядок миграции — отлично. Понять бизнес-контекст, который нигде не записан — плохо. «Почему мы используем два флага вместо одного» — это я знаю из головы и объяснил боту. Без этого объяснения он бы предложил «упрощённое» решение, которое сломало бы другую часть системы.

Полезная аналогия: AI — это очень хороший junior-разработчик, который быстро читает незнакомый код и хорошо помнит то, что прочитал. Но он не знает, почему в вашем бизнесе принято именно так, а не иначе. Это знание должен вносить человек.

Выводы: что делать, когда чинишь один баг и находишь десять

Эта история — хороший образец того, как работает технический долг в растущем бизнесе. Не бывает систем без накопленного бардака. Бывают системы, в которых бардак обнаружен и устранён, и системы, в которых бардак скрыт и продолжает накапливаться. Разница между ними — не в том, насколько аккуратно писали код вначале. Разница в том, делает ли кто-то регулярные «ревизии» по конкретным симптомам.

Вилла 26, которая не показывалась в каталоге вилл Бали — это был симптом. Симптом двойного флага, симптом неработающего удаления в Telegram, симптом грязных данных в разделе «Продажа», симптом незавершённой миграции структуры Casa Solar, симптом отсутствия нормального pipeline для фотографий. Если бы я починил только этот один симптом и закрыл задачу — всё остальное продолжало бы тихо гнить.

Главный урок: когда тыкаешь в один баг — следи, куда ты выходишь. Не бойся потянуть за ниточку дальше, чем планировал. Каждый слой, который вскрывается, — это проблема, которая уже влияла на бизнес, просто молча. Лучше достать её сейчас, чем ждать, пока она вырастет до размера, который будет неприятно доставать.

Ещё один вывод про автоматизацию бизнеса на Бали вообще: самые ценные скрипты — не те, которые делают что-то принципиально новое. Самые ценные — те, которые убирают повторяющееся ручное действие, которое занимало несколько часов и теперь занимает несколько минут. Pipeline для фотографий из Google Drive — три часа против пяти минут. Это и есть реальная автоматизация.

Подробнее о том, как выстроена вся система управления виллами на уровне AI-агентов, можно прочитать в статье про AI-корпорацию по управлению виллами на Бали — там про общую архитектуру и логику распределения задач между агентами.

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

Написать в Telegram