Формула (Formula)
Поле типа Формула автоматически вычисляет значение на основе JavaScript-выражения.
Основные характеристики
- Тип данных: Вычисляемое значение (любой тип)
- Язык: JavaScript (ES6+)
- Режим: Только для чтения
- Пересчет: Автоматический при изменении зависимых полей
- Доступ: К полям текущей записи и связанным записям
Настройки поля
При создании поля типа "Формула" доступны следующие настройки:
- Название поля: Отображаемое имя поля
- Системное имя: Уникальный идентификатор для API
- Описание: Подсказка для пользователей
- Формула: JavaScript-выражение
- Тип результата: String, Number, Boolean, Date, Array, Object
- Формат отображения: Для числовых и дат
Синтаксис формул
Доступ к полям
// Текущая запись
record.field_name
// Примеры
record.price
record.quantity
record.created_at
Математические операции
// Сложение
record.price + record.tax
// Вычитание
record.total - record.discount
// Умножение
record.price * record.quantity
// Деление
record.total / record.count
// Остаток от деления
record.number % 2
// Возведение в степень
record.base ** record.exponent
Строковые операции
// Конкатенация
record.first_name + ' ' + record.last_name
// Шаблонные строки
`${record.first_name} ${record.last_name}`
// Методы строк
record.name.toUpperCase()
record.email.toLowerCase()
record.text.substring(0, 100)
record.description.replace('old', 'new')
Условные выражения
// Тернарный оператор
record.quantity > 0 ? 'В наличии' : 'Нет в наличии'
// Множественные условия
record.score >= 90 ? 'Отлично' :
record.score >= 70 ? 'Хорошо' :
record.score >= 50 ? 'Удовлетворительно' : 'Неудовлетворительно'
// Логические операторы
record.is_active && record.is_verified ? 'Активен' : 'Неактивен'
Примеры использования
Полное имя
{
"name": "full_name",
"type": "formula",
"formula": "record.first_name + ' ' + record.last_name",
"resultType": "string"
}
Итоговая стоимость
{
"name": "total",
"type": "formula",
"formula": "record.price * record.quantity",
"resultType": "number",
"format": "currency"
}
Возраст
{
"name": "age",
"type": "formula",
"formula": "Math.floor((new Date() - new Date(record.birth_date)) / (365.25 * 24 * 60 * 60 * 1000))",
"resultType": "number"
}
Статус заказа
{
"name": "order_status",
"type": "formula",
"formula": "record.paid && record.shipped ? 'Доставлен' : record.paid ? 'Оплачен' : 'Ожидает оплаты'",
"resultType": "string"
}
Скидка в рублях
{
"name": "discount_amount",
"type": "formula",
"formula": "record.price * (record.discount_percent / 100)",
"resultType": "number",
"format": "currency"
}
Финальная цена
{
"name": "final_price",
"type": "formula",
"formula": "record.price - (record.price * (record.discount / 100))",
"resultType": "number"
}
Работа с датами
Дней до дедлайна
{
"name": "days_until_deadline",
"type": "formula",
"formula": "Math.ceil((new Date(record.deadline) - new Date()) / (24 * 60 * 60 * 1000))",
"resultType": "number"
}
Форматирование даты
{
"name": "formatted_date",
"type": "formula",
"formula": "new Date(record.created_at).toLocaleDateString('ru-RU')",
"resultType": "string"
}
Просрочено или нет
{
"name": "is_overdue",
"type": "formula",
"formula": "new Date(record.deadline) < new Date() && record.status !== 'Завершена'",
"resultType": "boolean"
}
Работа с массивами
Количество элементов
{
"name": "items_count",
"type": "formula",
"formula": "record.items.length",
"resultType": "number"
}
Сумма элементов
{
"name": "total_amount",
"type": "formula",
"formula": "record.items.reduce((sum, item) => sum + item.price, 0)",
"resultType": "number"
}
Объединение массива
{
"name": "tags_string",
"type": "formula",
"formula": "record.tags.join(', ')",
"resultType": "string"
}
Математические функции
Округление
// Округление до целого
Math.round(record.value)
// Округление вверх
Math.ceil(record.value)
// Округление вниз
Math.floor(record.value)
// Округление до 2 знаков
Math.round(record.value * 100) / 100
Минимум и максимум
// Минимальное значение
Math.min(record.price1, record.price2, record.price3)
// Максимальное значение
Math.max(record.value1, record.value2)
Абсолютное значение
Math.abs(record.difference)
Степень и корень
// Квадрат
Math.pow(record.value, 2)
// Квадратный корень
Math.sqrt(record.value)
Работа со связанными записями
Доступ к связанной записи
// Если есть связь с пользователем
record.user.name
// Если есть связь с категорией
record.category.title
Вычисления со связанными данными
{
"name": "author_info",
"type": "formula",
"formula": "`Автор: ${record.author.name} (${record.author.email})`",
"resultType": "string"
}
Сложные примеры
Прогресс выполнения
{
"name": "completion_percent",
"type": "formula",
"formula": "record.total_tasks > 0 ? Math.round((record.completed_tasks / record.total_tasks) * 100) : 0",
"resultType": "number",
"format": "percent"
}
Рейтинг
{
"name": "rating_display",
"type": "formula",
"formula": "'⭐'.repeat(Math.round(record.rating))",
"resultType": "string"
}
Инициалы
{
"name": "initials",
"type": "formula",
"formula": "(record.first_name[0] || '') + (record.last_name[0] || '')",
"resultType": "string"
}
Цветовой индикатор
{
"name": "status_color",
"type": "formula",
"formula": "record.status === 'Завершена' ? 'green' : record.status === 'В работе' ? 'blue' : 'gray'",
"resultType": "string"
}
Валидация email
{
"name": "is_valid_email",
"type": "formula",
"formula": "/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(record.email)",
"resultType": "boolean"
}
Обработка ошибок
Проверка на null/undefined
// Безопасный доступ
record.user?.name || 'Не указано'
// Значение по умолчанию
record.discount || 0
// Проверка существования
record.price ? record.price * 1.2 : 0
Try-catch (не поддерживается напрямую)
// Вместо try-catch используйте условные выражения
record.value && !isNaN(record.value) ? record.value * 2 : 0
Ограничения
- Нельзя изменять значения полей
- Нельзя выполнять асинхронные операции
- Нельзя использовать циклы (for, while)
- Ограниченный набор глобальных объектов (Math, Date, String, Number, Array)
- Максимальная длина формулы: 1000 символов
Производительность
Оптимизация формул
// ❌ Плохо (сложные вычисления)
record.items.map(i => i.price).reduce((a, b) => a + b, 0) / record.items.length
// ✅ Хорошо (простые вычисления)
record.total / record.count
Кеширование
Формулы пересчитываются только при изменении зависимых полей.
Отладка формул
Тестирование в консоли
// Создать тестовую запись
const testRecord = {
price: 1000,
quantity: 5,
discount: 10
};
// Протестировать формулу
const result = testRecord.price * testRecord.quantity * (1 - testRecord.discount / 100);
console.log(result); // 4500
Рекомендации
- Используйте формулы для вычисляемых значений
- Избегайте сложных вычислений (влияет на производительность)
- Используйте понятные имена полей
- Добавляйте комментарии в описание поля
- Проверяйте на null/undefined
- Используйте значения по умолчанию
- Тестируйте формулы перед развертыванием
Отличие от других типов
- Formula vs Number: Formula вычисляется автоматически, Number вводится вручную
- Formula vs Lookup: Formula для вычислений, Lookup для получения значений из связанных записей
- Formula vs Rollup: Formula для простых вычислений, Rollup для агрегации связанных записей