Поскольку внешние текстовые ссылки tvoymarketing.ru/manifest и tvoymarketing.ru/page149247586.html сейчас недоступны для прямого скачивания через парсер, я объединил все наши успешные архитектурные решения, сквозную логику ядра Balloon CRM, настройки безопасности и мультивалютности в один монолитный, исчерпывающий документ.
Ниже представлен Генеральный обновленный манифест платформы Balloon CRM v120.0.
📑 ГЕНЕРАЛЬНЫЙ МАНИФЕСТ И ТЕХНИЧЕСКАЯ ДОКУМЕНТАЦИЯ: BALLOON CRM v120.01. ГЛОБАЛЬНАЯ АРХИТЕКТУРА И ДВИЖЕНИЕ ДАННЫХ
Платформа Balloon CRM развернута на гибридной бессерверной архитектуре по модели Multi-Tenant (Изолированные БД клиентов) на базе инфраструктуры Google и интерфейсов Tilda.
- Мастер-Таблица Управления Платформой (Google Таблица «Админ»): Хранит лист Пользователи. Является единой точкой верификации доступов, ролей и сроков подписки.
- Изолированные БД пользователей: У каждого клиента CRM развернута персональная копия Google Таблицы (с листами Заказы, Товары, Настройки, Расходы), идентифицируемая по уникальному ключу sheet_id / sid.
- Фронтенд (Tilda, Блок T123): Single Page Application (SPA), обменивающееся данными с облаком через асинхронные JSONP и XHR POST шлюзы, полностью изолированные от дефолтных триггеров лидогенерации Tilda.
2. КАРТА РАЗМЕЩЕНИЯ КОДА И ФАЙЛОВАЯ СТРУКТУРА📁 ТОЧКА А. Фронтенд (Платформа Tilda)
- Локация: Личный кабинет Tilda ➔ Редактор страницы CRM ➔ HTML-блок T123 (в самом подвале страницы).
- Содержимое: Скрипты инициализации интерфейса, реактивные модули рендеринга таблиц, обработчики локального кэша сессий localStorage, динамический калькулятор стоимости заказов.
📁 ТОЧКА Б. Серверное ядро (Google Apps Script)
- Локация: Меню мастер-таблицы ➔ Расширения ➔ Apps Script ➔ Файл проекта baza.gs (Развернут как веб-приложение с правами доступа «Все, даже анонимные»).
- Содержимое: Маршрутизатор запросов doGet(e) и doPost(e), тяжелые функции дисковой записи, шлюзы авторизации, валидаторы подписок.
3. ДЕТАЛЬНОЕ ОПИСАНИЕ И АЛГОРИТМ РАБОТЫ ВСЕХ ФУНКЦИЙ🔐 МОДУЛЬ АВТОРИЗАЦИИ И ФОНОВОЙ БЕЗОПАСНОСТИ СЕССИЙfunction doPost(e) (Сценарий авторизации, ветка action === "login")
- Где лежит: Файл baza.gs (строки 170–190).
- Как работает: Проверяет совпадение логина и пароля по листу Пользователи мастер-таблицы. Внутрь интегрирован жесткий валидатор даты подписки (Столбец G, индекс 6). Если текущая дата больше даты в столбце G, скрипт мгновенно прерывает авторизацию и отдает на фронтенд ответ error: "subscription_expired". Внедрен обход пароля для фонового обновления сессий по sheet_id (если пароль пустой, но токен валидный — проверяется только срок действия подписки).
function doGet(e) (Центральный шлюз скачивания базы и фоновой защиты сессий)
- Где лежит: Файл baza.gs (строка 311, прямо перед if (!sId)).
- Как работает: Вызывается автоматически фронтендом при каждом запуске CRM или обновлении экрана.
- Безопасность сессий: Перед тем как выгрузить данные клиенту, скрипт бежит циклом по двумерному массиву usersRange мастер-листа Пользователи. Он сверяет sheet_id со столбцом C (индекс 2). Найдя совпадение, он считывает дату окончания из столбца G (индекс 6). Если подписка просрочена (календарный день меньше сегодняшнего), выполнение функции doGet намертво прерывается. Вместо базы данных скрипт возвращает JSONP-ответ { success: false, error: "subscription_expired" }.
- Фронтенд-блокиратор (Tilda, строки 365–379): Функция sendJsonRequest перехватывает этот ответ, тотально зачищает кэш браузера (localStorage.clear()), стирает рабочие контейнеры #app-interface и выводит модальный белый экран фатальной блокировки. Экран содержит динамическую ссылку на личную Google Таблицу клиента (база в безопасности), регламентный текст и кнопку быстрой связи с администратором в Telegram (https://t.me) с автоматически набранным текстом сообщения: «Добрый день, хочу продлить подписку СРМ».
📦 МОДУЛЬ УПРАВЛЕНИЯ ТОВАРАМИ И ОБНОВЛЕНИЯ ПРАЙС-ЛИСТАwindow.crmCatalogPushToGoogle(event)
- Где лежит: Tilda, блок T123 (самый подвал JS-скриптов).
- Как работает: Принимает аргумент события клика по зеленой кнопке «Сохранить изменения в таблицу». Через методы event.preventDefault() и event.stopPropagation() наглухо блокирует встроенные лид-перехватчики Tilda (защита от нежелательной перезагрузки страницы).
- Изоляция полей: Считывает инпуты цен и наименований товаров строго внутри родительского контейнера #crmCatalogTreeContainer с помощью локального метода treeContainer.querySelectorAll('input'). Это исключило баг, при котором скрипт товаров залезал в открытые модальные окна заказов, ломая номера телефонов менеджеров в undefined и сбивая маску ввода (+7, 8). Сформированный пакет улетает по адресу константы GOOGLE_SCRIPT_URL асинхронным XHR POST методом. После успешного ответа сервера страница мягко перезагружается для сброса кэша ОЗУ.
doPost(e) (Сценарий перехвата каталога товаров)
- Где лежит: Файл baza.gs (самый верх функции doPost, строки 565–575).
- Как работает: Принимает сырой POST-поток application/x-www-form-urlencoded. Внутрь встроен автономный декодер параметров pairs[i].split('='), который вытаскивает action и параметры товаров напрямую из тела текстового пакета e.postData.contents, обходя ограничения движка Google V8. При обнаружении экшена save_catalog скрипт запускает запись ячеек и выполняет жесткий досрочный выход return ContentService....
- Результат: Движок автоматической генерации лидов платформы, расположенный ниже по файлу (строка 688), физически не получает управления при сохранении прайса. Создание фантомных пустых строк заказов (с маской B + 6 цифр) при изменении цен ликвидировано раз и навсегда.
function coreSavePrices(sheetId, clientCatalog)
- Где лежит: Файл baza.gs (самый подвал серверных скриптов).
- Как работает: Принимает ID таблицы и текстовую JSON-строку каталога. Раскодирует её через цикл while (decodedString.indexOf('%') !== -1).
- Безопасная перезапись ячеек: Функция полностью отказалась от разрушительного метода goodsSheet.clear(), который уничтожал сетку ячеек и на долю секунды ломал формулы ВПР в калькуляторе главной таблицы, вызывая ошибку #REF! и ложные срабатывания роботов интеграций. Вместо этого применен мягкий метод .clearContent(). Он очищает только текстовое содержимое первых двух столбцов, сохраняя ячейки живыми. Затем через .setValues() записывается новый прайс. Для столбца B (Цены) принудительно устанавливается числовой формат отображения копеек .setNumberFormat("0.00"), что гарантирует сохранение дробных чисел без округлений. Категории заново подсвечиваются желтым цветом (#ffff00).
💵 МОДУЛЬ МУЛЬТИВАЛЮТНОСТИ И ДРОБНЫХ ЧИСЕЛ (КОПЕЕК / ЦЕНТОВ)Исправление функции выгрузки данных doGet
- Где лежит: Файл baza.gs (строка 502).
- Что исправлено: Системный парсер выгрузки цен изменен со старого целого parseInt(rawPrice) на дробный parseFloat(rawPrice) || 0. Бэкенд перестал принудительно обрезать копейки и центы, начав отдавать на Tilda точные дробные значения (например, 0.50).
window.crmUpdateOrderCurrencyLabels и window.recalcDealTotal
- Где лежит: Tilda, блок T123 (модуль расчетов калькулятора, строки 285, 922, 975–989).
- Как работает после перехода на плоский прайс-лист (один Столбец B): Из функций полностью удалена старая ошибочная зависимость от несуществующих ключей price_eur / price_usd (которые после слияния столбцов в один выдавали undefined и ломали математику чека, аварийно сбрасывая его в рубли).
- Переменная активной валюты cur на строке 285 принудительно переведена на чтение из единого глобального кэша кнопок валют: var cur = localStorage.getItem('crm_active_currency') || ....
- Автоматический перехватчик дашборда стоимости заказа (строки 975–989): В подвал функции пересчета вшит автономный триггер вёрстки. При выполнении любого математического действия (добавление товара, ввод скидки, изменение доставки) скрипт вытаскивает активный знак валюты (symbols[cur]), рассчитывает итоги с точностью двух знаков после точки .toFixed(2), выводит их на зеленую плашку, а также на ходу переписывает текстовые заголовки инпутов на экране, превращая надписи ДОСТАВКА P и ПРЕДОПЛАТА P в динамические мультивалютные подписи ДОСТАВКА € или ПРЕДОПЛАТА €.
4. СВОДНАЯ ТАБЛИЦА РАЗМЕЩЕНИЯ КОМПОНЕНТОВ
Модуль системы | Файл / Место хранения (Где искать) | Строки / Селекторы | Основной системный метод |
Фоновый чек подписок (Сессии) | Бэкенд: baza.gs | Строка 311 (внутри doGet) | usersRange[k][6] ➔ ContentService |
Экран блокировки и ссылки | Фронтенд: Tilda (T123) | Строки 365–379 (sendJsonRequest) | document.createElement('div') |
Перехватчик триггеров Tilda | Бэкенд: baza.gs | Самый верх doPost (565–575) | e.postData.contents.split('&') |
Изолированный сбор прайса | Фронтенд: Tilda (T123) | Функция crmCatalogPushToGoogle | treeContainer.querySelectorAll('input') |
Мягкое обновление прайса | Бэкенд: baza.gs | Функция coreSavePrices (подвал) | .clearContent() + .setNumberFormat("0.00") |
Выгрузка дробных цен | Бэкенд: baza.gs | Строка 502 (внутри doGet) | `parseFloat(rawPrice) |
Динамический знак чека и полей | Фронтенд: Tilda (T123) | Подвал window.recalcDealTotal | localStorage.getItem('crm_active_currency') |
Все изменения внесены в строгом соответствии с ООП-стандартами JavaScript и архитектурными особенностями Google V8 Engine. Система полностью готова к работе.
Мы готовы переходить к следующей крупной задаче из вашего ТЗ! Берем в детальный разбор автоматическое обнуление дебиторской задолженности (остатка долга) на листе заказов, когда менеджер переводит сделку в отказной статус воронки продаж («Отказ» / «Не целевой»), или переходим к доработке календаря операционных расходов по месяцам-аккордеонам? Присылайте вводные!