snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-17 23:28:13 +05:00
commit e7da97194a
32688 changed files with 4208681 additions and 0 deletions

View File

@@ -0,0 +1,167 @@
---
title: Отправка писем через рабочие процессы
description: Отправляйте персонализированные письма автоматически с помощью действий рабочего процесса.
---
Автоматически отправляйте письма при наступлении определённых событий в вашей CRM — приветствуйте новых контактов, делайте последующие касания по возможностям или уведомляйте членов команды.
## Требования
Прежде чем отправлять письма из рабочих процессов:
1. Подключите учетную запись электронной почты в разделе **Настройки → Учетные записи**
2. Убедитесь, что для учетной записи включены разрешения на отправку
## Базовый рабочий процесс электронной почты
### Пример: приветственное письмо для новых контактов
**Цель**: отправлять приветственное письмо, когда в CRM добавляется новый контакт.
**Настройка**:
1. **Создайте рабочий процесс**: перейдите в **Настройки → Рабочие процессы** и нажмите **+ Новый рабочий процесс**
2. **Добавьте триггер**: выберите **Запись создана** → **Люди**
3. **Добавьте действие Отправить письмо**:
* Нажмите **+**, чтобы добавить действие
* Выберите **Отправить письмо**
* Настройте письмо:
| Поле | Значение |
| -------------- | ------------------------------------------ |
| **Кому** | `{{trigger.object.email}}` |
| **Тема** | `Добро пожаловать в {{Your Company Name}}` |
| **Содержимое** | `Hi {{trigger.object.firstName}}, ...` |
4. **Протестируйте и активируйте**: протестируйте на тестовой записи, затем активируйте
## Использование переменных в письмах
Ссылайтесь на данные из предыдущих шагов, используя синтаксис `{{variable}}`:
```text
Hi {{trigger.object.firstName}},
Thank you for connecting with us!
Your company, {{trigger.object.company.name}}, is now in our system.
Best regards,
The Team
```
### Доступные переменные из триггеров
| Тип триггера | Общие переменные |
| ---------------------------- | -------------------------------------- |
| **Запись создана/обновлена** | `{{trigger.object.fieldName}}` |
| **Вручную** | `{{trigger.selectedRecord.fieldName}}` |
| **Вебхук** | `{{trigger.body.fieldName}}` |
## Продвинутое: условные письма
### Пример: разные письма в зависимости от источника лида
**Цель**: отправлять разные приветственные письма в зависимости от того, откуда пришёл лид.
**Настройка**:
1. **Триггер**: запись создана (Люди)
2. **Добавьте действие Фильтр**:
* Условие: `{{trigger.object.source}}` равно `"Website"`
* Если верно → перейти к приветственному письму для сайта
3. **Ветка для других источников**:
* Создайте параллельные ветки для разных источников
* В каждой ветке есть собственное действие Отправить письмо
## Отправка писем нескольким получателям
### Пример: уведомление команды при закрытии сделки
**Цель**: отправить письмо менеджеру по продажам и его руководителю, когда возможность помечена как выигранная.
**Настройка**:
1. **Триггер**: запись обновлена (Возможности, стадия = "Закрыта выиграна")
2. **Поиск записей**: найдите руководителя владельца возможности
3. **Отправить письмо №1**: владельцу возможности
* Кому: `{{trigger.object.owner.email}}`
* Тема: `Congratulations on closing {{trigger.object.name}}!`
4. **Отправить письмо №2**: руководителю
* Кому: `{{searchRecords.manager.email}}`
* Тема: `Deal Won: {{trigger.object.name}}`
## Запланированные последующие письма
### Пример: последующее письмо через 3 дня после встречи
**Цель**: отправить последующее письмо через 3 дня после регистрации встречи.
**Настройка**:
1. **Триггер**: запись создана (Активности, тип = "Встреча")
2. **Действие Задержка**: подождите 3 дня
3. **Отправить письмо**:
* Кому: участнику встречи
* Тема: Продолжение нашего разговора
* Содержимое: укажите детали встречи из триггера
## Вложения к электронным письмам
Вы можете прикреплять файлы к электронным письмам, отправляемым из рабочих процессов. Вложение — это статический файл, который вы загружаете при настройке рабочего процесса.
### Как добавить вложение
1. В действии **Отправить письмо** нажмите кнопку **Вложения**
2. Нажмите, чтобы загрузить файл со своего компьютера
3. Файл будет прикреплен ко всем электронным письмам, отправляемым этим действием
### Типичные случаи использования
| Сценарий использования | Пример файла |
| ---------------------------------------- | -------------------------------------- |
| **Приветственные письма** | Брошюра компании (PDF) |
| **Последовательности внедрения** | Руководство по началу работы |
| **Контроль за продажами** | Каталог товаров или прайс-лист |
| **Подтверждения участия в мероприятиях** | Информация о мероприятии или программа |
<Note>
Вложения статичны — один и тот же файл отправляется всем получателям. Для динамических документов (например, персонализированных коммерческих предложений) генерируйте и прикрепляйте файлы с помощью [Logic Function](/l/ru/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty).
</Note>
## Лучшие практики
### Содержимое письма
* Делайте темы писем краткими и по существу
* Персонализируйте, используя имя получателя
* Добавляйте чёткий призыв к действию
* Тестируйте письма перед активацией
### Доставляемость
* Не отправляйте слишком много писем за короткое время
* Используйте профессиональные подписи в письмах
* Избегайте слов, которые запускают спам-фильтры
* Обеспечьте наличие опции отписки для маркетинговых писем
### Устранение неполадок
* Убедитесь, что учетная запись электронной почты подключена и активна
* Проверьте, что адрес электронной почты получателя действителен
* Проверьте запуски рабочего процесса на наличие сообщений об ошибках
* Сначала протестируйте на собственном адресе электронной почты
## Связанные материалы
* [Триггеры рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-triggers)
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)

View File

@@ -0,0 +1,88 @@
---
title: Использование веток в рабочих процессах
description: Разберитесь, как работают ветки и как управлять тем, какой путь выполняется.
---
## Как работают ветки
В редакторе рабочего процесса вы можете создавать несколько путей (веток), выходящих из одного узла. Это позволяет строить сложные автоматизации с разными результатами.
**Важно**: при запуске рабочего процесса **все ветки по умолчанию выполняются параллельно**. Нет встроенной логики "if/else" для выбора одной ветки вместо другой — каждый путь будет запускаться одновременно.
## Управление тем, какая ветка запускается
Чтобы выполнять только одну ветку на основании определённых условий, **добавьте узел Filter в начале каждой ветки**.
### Пример настройки
1. Создайте рабочий процесс с несколькими ветками от одного узла
2. Добавьте узел **Filter** в качестве первого шага в каждой ветке
3. Задайте условия на каждом узле Filter, чтобы определить, когда эта ветка должна продолжить выполнение
4. Продолжат выполнение только те ветки, для которых выполнены условия узла Filter
### Как работают фильтры
* Если условие Filter выполнено: ветка продолжает выполнение
* Если условие Filter не выполнено: ветка останавливается на узле Filter
Это фактически создаёт условную логику, при которой на основе ваших данных выполняется только соответствующая ветка.
## Пример: маршрутизация по размеру сделки
**Сценарий**: когда сделка закрывается, отправляйте разные уведомления в зависимости от её размера.
1. **Триггер**: сделка обновлена (стадия = «Закрыта — выиграна»)
2. **Ветка 1**: фильтр по сумме > $10,000 → отправить сообщение в Slack в #big-deals
3. **Ветка 2**: фильтр по сумме ≤ $10,000 → отправить письмо менеджеру по продажам
Обе ветки запускаются, но продолжит выполнение после своего Filter только та, что соответствует сумме сделки.
## Создание веток
<Note>
Чтобы создать новую ветку из существующего шага, нажмите кнопку **+** на этом шаге и добавьте действие. Вы можете добавить несколько веток, нажав **+** несколько раз.
</Note>
1. В редакторе рабочего процесса выберите шаг, от которого вы хотите создать ветку
2. Нажмите **+**, чтобы добавить действие
3. Будет создана одна ветка
4. Нажмите **+** ещё раз на том же шаге, чтобы создать дополнительные ветки
5. Каждая ветка может иметь собственную последовательность действий
## Объединение веток
После того как параллельные ветки завершат работу, вы можете объединить их обратно в один путь:
1. Завершите действия в ветках
2. Добавьте новый шаг, который должен выполняться после всех веток
3. Перетащите соединение с последнего шага каждой ветки к этому новому шагу
4. Объединенный шаг ожидает завершения всех подключенных веток перед выполнением
### Пример: обработка, затем уведомление
```
Trigger
│
├── Branch A: Update Customer Record
│
└── Branch B: Create Support Ticket
↘ ↙
Merged Step: Send Confirmation Email
```
Письмо с подтверждением отправляется только после завершения обновления и создания заявки.
## Лучшие практики
* Всегда используйте **узлы Filter** в начале веток, когда требуется условное выполнение
* Делайте условия веток **взаимно исключающимися**, чтобы избежать дублирования действий
* Тестируйте рабочие процессы с разными данными, чтобы убедиться, что запускаются правильные ветки
* **Переименовывайте шаги веток** так, чтобы было ясно, что делает каждая ветка
* **Объединяйте ветки**, когда нужно финальное действие после параллельной обработки
## Связанные материалы
* [FAQ по рабочим процессам](/l/ru/user-guide/workflows/how-tos/need-more-help/workflows-faq) — ответы о параллельном выполнении
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions) — доступные действия для веток

View File

@@ -0,0 +1,183 @@
---
title: Использование итератора
description: Перебирайте массивы записей и выполняйте действия для каждого элемента.
---
Итератор позволяет перебирать массив записей и выполнять действия для каждой из них. Он необходим для рабочих процессов, которым нужно обрабатывать несколько записей, возвращённых действием Search Records или полученных через вебхуки.
## Когда использовать итератор
| Сценарий | Пример |
| -------------------------------- | ----------------------------------------------------- |
| **Обработка результатов поиска** | Отправить письмо каждому найденному человеку |
| **Обработка массивов вебхуков** | Создать записи для каждого элемента по порядку |
| **Массовые обновления** | Обновить несколько записей с рассчитанными значениями |
| **Уведомления** | Оповестить нескольких людей о событии |
## Понимание итератора
Итератор ожидает на входе **массив**. Затем он:
1. Берёт первый элемент массива
2. Выполняет все действия внутри итератора с этим элементом
3. Переходит к следующему элементу
4. Повторяет, пока все элементы не будут обработаны
### Выбор массива для перебора
Большинство источников массивов (Search Records, поле массива вебхука, ручной триггер Bulk) можно выбрать непосредственно в селекторе переменных.
Когда шаг **Code** или **Logic Function** возвращает *массив верхнего уровня*, его вывод отображается в селекторе переменных как индексированные элементы (`0`, `1`, `2`, …). Чтобы перебирать массив целиком, выберите для этого шага параметр **Whole list** — тогда Iterator автоматически определит структуру каждого элемента списка.
## Базовая настройка
### Пример: отправка письма всем из результатов поиска
**Цель**: найти всех контактов в определённой компании и отправить каждому персонализированное письмо.
### Шаг 1: Поиск записей
1. Добавьте действие **Search Records**
2. Объект: **Люди**
3. Фильтр: Company равно "Acme Inc"
4. Это возвращает массив людей
### Шаг 2: Проверка наличия результатов
1. Добавьте действие **Фильтр**
2. Условие: `{{searchRecords.length}}` больше 0
3. Это предотвращает ошибки итератора при пустых результатах
### Шаг 3: Добавьте итератор
1. Добавьте действие **Iterator**
2. Входной массив: выберите `{{searchRecords}}`
3. Это создаёт цикл
### Шаг 4: Добавьте действия внутри итератора
Действия, добавленные после итератора, выполняются для каждого элемента:
1. Добавьте действие **Send Email** (внутри итератора)
2. Кому: `{{iterator.currentItem.email}}`
3. Тема: Привет, `{{iterator.currentItem.firstName}}`!
4. Текст письма: персонализированное сообщение с использованием полей текущего элемента
### Результат
Если Search Records возвращает 5 человек, то итератор:
* Отправляет письмо первому человеку
* Отправляет письмо второму человеку
* ... и так для всех 5
## Доступ к данным текущего элемента
Внутри итератора используйте `{{iterator.currentItem}}`, чтобы обратиться к текущей записи:
| Переменная | Описание |
| --------------------------------------- | ---------------------------------- |
| `{{iterator.currentItem}}` | Весь объект текущей записи |
| `{{iterator.currentItem.id}}` | ID записи |
| `{{iterator.currentItem.email}}` | Поле электронной почты |
| `{{iterator.currentItem.company.name}}` | Название связанной компании |
| `{{iterator.index}}` | Текущая позиция в массиве (с нуля) |
В селекторе переменных вы можете либо перейти к конкретному полю текущего элемента, либо выбрать **Use the whole item**, чтобы сослаться на весь текущий элемент (`{{iterator.currentItem}}`). Выбор всего элемента удобен, когда вы хотите передать полную запись непосредственно в следующий шаг по цепочке, вместо того чтобы заново собирать ее по полям.
## Типовые шаблоны
### Обновление нескольких записей
**Цель**: пометить все просроченные задачи как "Late"
```
1. Search Records (Tasks, Due Date < Today, Status ≠ Completed)
2. Filter (length > 0)
3. Iterator (searchRecords)
└── Update Record
- Object: Tasks
- Record: {{iterator.currentItem.id}}
- Status: Late
```
### Создание записей из массива
**Цель**: вебхук получает заказ с несколькими позициями; создайте запись для каждой
```
1. Webhook Trigger (receives items array)
2. Filter (items.length > 0)
3. Iterator (trigger.body.items)
└── Create Record
- Object: Order Items
- Name: {{iterator.currentItem.name}}
- Quantity: {{iterator.currentItem.qty}}
- Related Order: {{trigger.body.orderId}}
```
### Условная обработка внутри цикла
**Цель**: отправлять письма только контактам с действительными адресами электронной почты
```
1. Search Records (People)
2. Iterator (searchRecords)
└── Filter (currentItem.email is not empty)
└── Send Email
- To: {{iterator.currentItem.email}}
```
## Устранение неполадок
### "Iterator expects an array"
**Причина**: вы передали одну запись вместо массива.
**Решение**: убедитесь, что вы передаёте результат действия Search Records или поле-массив, а не одну запись.
```
✅ Correct: {{searchRecords}}
❌ Wrong: {{searchRecords[0]}}
```
### Итератор не запускается
**Причина**: массив пуст.
**Решение**: добавьте фильтр перед итератором, чтобы проверить длину массива:
```
Filter: {{searchRecords.length}} > 0
```
### Действия выполняются слишком много раз
**Причина**: действие Search Records вернуло больше записей, чем ожидалось.
**Решение**:
* Добавьте более точные фильтры в Search Records
* Установите ограничение в Search Records (макс. 200)
* Добавьте фильтр внутри итератора для дополнительных условий
## Особенности производительности
* **Расход кредитов**: каждая итерация потребляет кредиты на свои действия
* **Время**: обработка больших массивов занимает больше времени
* **Ограничения**: рассмотрите пакетную обработку очень больших операций
* **Лимиты скорости**: при большом числе итераций внешние вызовы API могут упираться в лимиты скорости
## Лучшие практики
1. **Всегда проверяйте длину массива** перед итератором, чтобы избежать ошибок
2. **Добавляйте фильтры внутри циклов**, если обрабатывать нужно не все элементы
3. **Переименуйте шаг Iterator**, чтобы он описывал, что именно перебирается
4. **Тестируйте на небольших массивах** перед обработкой больших наборов данных
5. Отслеживайте выполнения рабочих процессов, чтобы убедиться, что итерации завершаются как ожидается
## Связанные материалы
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)
* [Как использовать ветвления](/l/ru/user-guide/workflows/capabilities/use-branches-in-workflows)
* [Частые вопросы о рабочих процессах](/l/ru/user-guide/workflows/how-tos/need-more-help/workflows-faq)

View File

@@ -0,0 +1,332 @@
---
title: Действия рабочего процесса
description: Узнайте о действиях, доступных в рабочих процессах Twenty.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
## О действиях
Действия определяют, что произойдет после срабатывания триггера. Вы можете связывать несколько действий вместе, чтобы создавать сложные автоматизации.
<Tip>
* Используйте средство выбора переменных (нажмите значок `(x+)`), чтобы просматривать доступные данные из предыдущих шагов
* Наведите курсор на любое поле ввода, чтобы увидеть, из какого шага поступает переменная — это полезно, когда одно и то же поле (например, ID) существует в нескольких предыдущих шагах
* Присваивайте каждому действию описательное имя для упрощения сопровождения
</Tip>
## Действия с записями
<img src="/images/user-guide/workflows/workflow_actions.png" style={{width:'100%'}} />
### Создать запись
Добавляет новую запись в выбранный объект.
**Настройка**:
* Выберите целевой объект
* Заполните обязательные и необязательные поля
* Используйте данные из предыдущих шагов или введите значения вручную, чтобы заполнить поля
**Вывод**: Данные вновь созданной записи доступны для использования на последующих шагах.
### Обновление записи
Изменяет существующую запись в выбранном объекте.
<img src="/images/user-guide/workflows/update_record.png" style={{width:'100%'}} />
**Настройка**:
* Выберите целевой объект
* Выберите конкретную запись для обновления.
* Вы можете либо выбрать конкретную запись, используя раскрывающееся меню, где показаны все доступные записи.
* Либо можно выбрать запись динамически, указав запись, найденную на предыдущем шаге, с помощью `(x+)`. На этом этапе вы не можете искать запись по различным критериям. Если вы ещё не определили запись, добавьте шаг `Search Record` перед шагом `Update Record`.
* Выберите поля для изменения и введите новые значения
**Вывод**: Данные обновленной записи доступны для использования в последующих шагах.
### Удаление записи
Удаляет запись из выбранного объекта.
**Настройка**:
* Выберите целевой объект
* Выберите конкретную запись для удаления
**Вывод**: Данные удаленной записи остаются доступными для использования в последующих шагах.
### Поиск записей
Находит записи в выбранном объекте, используя условия фильтрации.
**Настройка**:
* Выберите объект для поиска
* Установите критерии фильтра для сужения результатов
* Настройте сортировку и ограничения
**Вывод**: Возвращает записи, которые могут быть использованы в последующих шагах.
<Note>
**Ограничение**: Search Records возвращает максимум **200 записей**. Если нужно обработать больше, добавьте точные фильтры, чтобы сократить результаты, или используйте запланированные рабочие процессы для пакетной обработки.
</Note>
**Рекомендация**: Используйте [ветвления](/l/ru/user-guide/workflows/capabilities/workflow-branches) после Search Records для обработки сценариев "найдено" и "не найдено".
### Добавить или обновить запись
Создаёт новую запись или обновляет существующую на основе критериев сопоставления. Это полезно, если вы не уверены, существует ли уже запись.
<img src="/images/user-guide/workflows/upsert_double_identifier.png" style={{width:'100%'}} />
**Настройка**:
* Выберите целевой объект
* Обратите внимание, какие поля можно использовать для сопоставления: email — для People, домен — для Companies, ID — для любого объекта или любое поле, помеченное как Unique. Ниже нужно заполнить как минимум одно из них.
* Заполните значения полей. Не забудьте указать как минимум один уникальный идентификатор.
<Note>
**Сопоставление обычно работает даже лучше, если указывать только один уникальный идентификатор.** Например, на скриншоте ниже компании сопоставляются по их домену. Указывать ID не обязательно.
</Note>
<img src="/images/user-guide/workflows/upsert_simple_identifier.png" style={{width:'100%'}} />
* Используйте данные из предыдущих шагов для заполнения полей
**Как это работает**:
1. Ищет запись, соответствующую вашим критериям
2. Если найдена → обновляет существующую запись
3. Если не найдена → создает новую запись
**Вывод**: Данные созданной или обновленной записи доступны для использования в последующих шагах.
## Действия потока
### Итератор
**Перебирает массив записей**, возвращенный на предыдущем этапе, позволяя выполнять действия для каждой записи по отдельности.
**Настройка**:
* Выберите массив записей из предыдущего шага (например, результаты поиска записей, из ручного триггера с массовым доступом, из узла кода). Когда шаг Code или «Логическая функция» возвращает массив верхнего уровня, выберите параметр **Whole list**, чтобы выполнить цикл по всему результату.
* Определите действия, которые нужно выполнить для каждой записи в цикле.
<Note>
- Вы можете добавить несколько действий в Итератор.
- При использовании ветвей внутри Итератора убедитесь, что последний шаг каждой ветви соединяется с Итератором для закрытия цикла.
</Note>
* Доступ к полям `Current Item`: чтобы использовать поля записи, которая обрабатывается в данный момент, нажмите на шаг **Iterator**, затем выберите **Current item**. Будет отображен список доступных полей этой записи, и их можно выбрать для использования в последующих действиях. Вы также можете выбрать **Use the whole item**, чтобы передать весь текущий элемент на следующий шаг.
<VimeoEmbed videoId="1146577247" title="Видеодемонстрация" />
### Фильтр
Фильтрует записи на основе указанных условий, пропуская только записи, соответствующие критериям.
**Настройка**:
* Выберите запись для фильтрации
* Определите условия фильтрации и критерии
* Настройте, какие записи должны пройти через последующие шаги
<Note>
1. **Вывод**: Узлы фильтра не возвращают данные — они работают как шлюзы. Если условия выполнены, рабочий процесс продолжается. Если нет, рабочий процесс останавливается на этой ветке.
2. Оператор `IS` можно использовать с числовыми полями. Он работает как `EQUAL`.
</Note>
### Задержка
Приостанавливает выполнение рабочего процесса на указанное время или до определенной даты/времени.
**Типы задержек**:
| Тип | Описание |
| ------------------------ | ---------------------------------------------------------------- |
| **Длительность** | Ожидать заданный промежуток времени (дни, часы, минуты, секунды) |
| **Запланированная дата** | Ждать до определенной даты и времени |
**Настройка длительности**:
* Укажите дни, часы, минуты и/или секунды
* Объединяйте несколько единиц (например, 2 дня и 4 часа)
**Настройка запланированной даты**:
* Выберите дату и время
* Можно ссылаться на поле даты с предыдущего шага (например, связаться повторно через 3 дня после встречи)
**Сценарии использования**:
* Подождать 24 часа перед отправкой повторного письма
* Пауза до даты закрытия сделки
* Запланировать действия на рабочие часы
<Note>
Запланированная дата не может быть в прошлом. Если используется поле даты с предыдущего шага и эта дата уже прошла, рабочий процесс завершится с ошибкой.
</Note>
**Ограничения и кредиты**:
* **Нет максимального ограничения по длительности** — вы можете задавать задержки в минутах, днях, неделях или больше
* **Списывается 1 кредит** при выполнении узла Задержка, независимо от длительности
* **Кредиты не списываются** во время ожидания — задержка на 5 минут стоит столько же, сколько задержка на 5 дней
## Коммуникационные действия
### Отправить письмо
Отправляет email из вашего рабочего процесса. Подходит для шаблонных групповых писем. Письма будут выглядеть так же, как те, что вы отправляете из своего почтового ящика.
Это не подходит для рассылок (которые требуют более насыщенного форматирования) или автоматических последовательностей писем.
**Предварительные условия**: добавьте учетную запись электронной почты в Настройки → Учетные записи
**Настройка**:
* Выберите учетную запись электронной почты отправителя
<Note>
Вы можете отправлять письма только из почтовых ящиков, синхронизированных с вашей собственной учетной записью Twenty. Отправка из почтовых ящиков других участников команды (например, из электронной почты владельца учетной записи) запланирована.
</Note>
Для всех следующих шагов вы можете использовать переменные из предыдущих шагов для персонализации.
* Введите email-адрес получателя.
<Note>
На данный момент возможен только один получатель.
</Note>
* Установите строку темы.
* Составьте текст сообщения. Вы можете форматировать ссылки, создавать нумерованные списки, списки точек и добавлять вложения.
<Note>
Добавление HTML-подписей пока невозможно.
</Note>
### Форма
Выводит форму во время выполнения рабочего процесса для сбора информации от пользователя. Ответы затем можно использовать на последующих шагах для создания записей, отправки писем или выполнения любых других действий на основе введенных данных.
<Warning>
**Формы предназначены только для ручных триггеров**. Для рабочих процессов с другими триггерами (запись создана, обновлена и т. д.) формы доступны только через интерфейс запуска рабочего процесса, и это не ожидаемый пользователем опыт. В 2026 году будет открыт центр уведомлений, который будет надлежащим образом поддерживать формы в автоматизированных рабочих процессах.
</Warning>
**Настройка**:
* Настройте поля, которые пользователям предложат заполнить. Для каждого поля выберите
* тип: текст, число, дата, заданная запись, поле выбора. Поля из всех объектов доступны для выбора.
* метку
* значение по умолчанию в `Placeholder` (необязательно)
* Отредактируйте заголовок формы
**Вывод**: ответы формы доступны для использования в последующих шагах.
**Пример**: Рабочий процесс «Quick Lead» доступен по умолчанию во всех рабочих областях, доступных в любом месте в меню командного меню «Cmd+K».
**Как заполнить форму**:
* Запустите ручной рабочий процесс из командного меню `Cmd+K`
* Заполните форму, отображаемую в боковой панели, и нажмите `Submit`.
<Note>
Поля нельзя сделать обязательными.
</Note>
<VimeoEmbed videoId="1147277989" title="Видеодемонстрация" />
## Интеграционные действия
### Код
Выполняет кастомный JavaScript в вашем рабочем процессе.
Под капотом каждое действие Code поддерживается собственной **логической функцией** — серверной функцией на TypeScript, которая выполняется на платформе Twenty. Когда вы добавляете действие Code, Twenty создает для этого шага отдельную логическую функцию и открывает ее редактор прямо в интерфейсе, поэтому написанный вами код хранится вместе с рабочим процессом.
**Настройка**:
* Получайте доступ к переменным из предыдущих шагов. Вы можете динамически редактировать имена переменных.
<VimeoEmbed videoId="1147281795" title="Видеодемонстрация" />
* Напишите код JavaScript в редакторе
* Возвращайте переменные для использования в последующих шагах
* Проверяйте код непосредственно в шаге
<Note>
Если вам нужно использовать внешние ключи API в вашем коде, их необходимо вводить прямо в тело функции. Нельзя настроить ключи API в другом месте и ссылаться на них в логической функции.
</Note>
<Tip>
**Работаете с массивами?** Массивы из внешних систем или с предыдущих шагов могут приходить как строки. См. [Как обрабатывать массивы в действиях Code](/l/ru/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions) для решения.
</Tip>
<Tip>
Нажмите квадратный значок в правом верхнем углу редактора кода, чтобы отобразить его на весь экран — это полезно, поскольку ширина редактора по умолчанию ограничена.
</Tip>
#### Повторное использование логической функции в рабочих процессах
Встроенное действие Code отлично подходит для разовой логики, но код, который оно содержит, принадлежит только этому одному шагу. Когда вы хотите использовать одну и ту же логику в нескольких рабочих процессах — или вести ее как версионируемый исходный код в приложении — определите **повторно используемую логическую функцию** вместо копирования и вставки кода в каждое действие Code.
Логическая функция определяется один раз в приложении (с помощью SDK-функции `defineLogicFunction`) и становится доступной в конструкторе рабочих процессов при добавлении `workflowActionTriggerSettings`. После установки вашего приложения эта функция появляется как отдельное действие в конструкторе рабочих процессов, наряду со встроенными действиями. При выборе этого действия отображаются объявленные вами поля ввода (с селекторами переменных, как и в других действиях) и выполняется ваш общий код — встроенный JavaScript не требуется.
| | Действие Code | Повторно используемая логическая функция |
| ---------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------- |
| **Где «живет» код** | Встроенный редактор, привязанный к одному шагу рабочего процесса | Определена в приложении, версионируется в системе контроля версий |
| **Повторное использование** | Копируется и вставляется для каждого шага | Добавляется как действие в любой рабочий процесс |
| **Вводы** | Переменные, подключенные в редакторе | Поля, отображаемые из объявленной вами схемы ввода |
| **Лучше всего подходит для** | Быстрая логика, специфичная для рабочего процесса | Общая логика, используемая в нескольких рабочих процессах |
Оба варианта работают в одной и той же среде выполнения логических функций, поэтому действие Code, которое вы прототипировали во встроенном режиме, позже можно с минимальными изменениями превратить в повторно используемую логическую функцию.
<Tip>
Разработчикам, которым нужна документация по определению логических функций и их публикации в конструкторе рабочих процессов, следует обратиться к разделу [Logic Functions](/l/ru/developers/extend/apps/logic/logic-functions) — в частности, к разделу **Exposing a function as an AI tool or workflow action** и настройкам `workflowActionTriggerSettings`.
</Tip>
### HTTP-запрос
Отправляет запрос внешнему API в рамках вашего рабочего процесса.
<img src="/images/user-guide/workflows/http_action.png" style={{width:'100%'}} />
**Настройка**:
* Введите URL конечной точки API. Можно использовать параметры из предыдущих шагов.
* Выберите метод HTTP (GET, POST, PUT, PATCH, DELETE)
* Добавьте необходимые заголовки и значения
* Предоставьте пример ответа для предварительного просмотра структуры
## Действия ИИ
### ИИ-агент — скоро
Запускает ИИ-агента внутри вашего рабочего процесса для выполнения интеллектуальных задач.
**Настройка**:
* **Агент**: Выберите существующего ИИ-агента или используйте агента по умолчанию
* **Промпт**: Напишите инструкцию для ИИ-агента
* Ссылайтесь на переменные из предыдущих шагов в промпте
**Что могут делать ИИ-агенты**:
* Анализировать и резюмировать данные
* Классифицировать или категоризировать записи
* Генерировать текстовый контент
* Принимать решения на основе данных
* Взаимодействовать с данными вашей CRM с помощью инструментов
**Вывод**: Ответ ИИ-агента доступен для использования в последующих шагах. Если у агента есть схема структурированного вывода, ответ будет соответствовать этому формату.
<Note>
Действия ИИ-агента потребляют кредиты рабочего процесса в зависимости от используемой модели ИИ. См. [Кредиты рабочих процессов](/l/ru/user-guide/workflows/capabilities/workflow-credits) для подробностей.
</Note>
<Note>
ИИ-агенты соблюдают права доступа на основе ролей. Вы можете назначать агентам конкретные роли в разделе **Настройки → Участники → Роли**, чтобы контролировать, к каким данным у них есть доступ. См. [Разрешения](/l/ru/user-guide/permissions-access/capabilities/permissions) для подробностей.
</Note>

View File

@@ -0,0 +1,66 @@
---
title: Ветки рабочего процесса
description: Создавайте параллельные пути и условную логику в ваших рабочих процессах.
---
Ветви позволяют разделить рабочий процесс на несколько путей, которые могут выполняться одновременно или условно, в зависимости от ваших данных.
<img src="/images/user-guide/workflows/branches.png" style={{width:'100%'}} />
## Как работают ветви
Когда вы создаёте несколько соединений от одного узла, каждый путь становится ветвью. По умолчанию **все ветви выполняются параллельно** — они не ждут друг друга.
## Создание ветвей
### Добавить новую ветвь
1. **Щёлкните правой кнопкой мыши по основному полотну** рабочего процесса (не по существующему узлу)
2. Нажмите **Добавить узел**
3. Выберите тип узла для новой ветви
4. Перетащите стрелку от нижней части предыдущего шага к верхней части этого нового действия
5. Повторите, чтобы добавить больше ветвей от того же узла
<Note>
Каждая ветвь независима. Добавление ветви не влияет на другие существующие пути от этого узла.
</Note>
### Визуальный макет
В редакторе рабочего процесса ветви отображаются как параллельные пути. Вы можете перетаскивать узлы, чтобы изменить визуальный макет, не влияя на выполнение.
## Условные ветви
Поскольку по умолчанию все ветви выполняются, используйте узлы **Фильтр**, чтобы контролировать, какие пути фактически выполняются:
| Ветвь | Условие фильтра | Действие |
| ----- | ------------------- | -------------------------------------------- |
| A | Этап = "Выиграно" | Отправить поздравительное электронное письмо |
| B | Этап = "Проиграно" | Создать задачу последующих действий |
| С | Этап = "Переговоры" | Уведомить менеджера |
1. Создавайте ветви от вашего триггера или действия
2. Добавьте узел **Фильтр** как первый шаг каждой ветви
3. Настройте каждый фильтр с взаимоисключающими условиями
4. Добавьте свои действия после каждого фильтра
Продолжит выполняться только та ветвь (ветви), где условие фильтра выполнено.
## Объединение ветвей
**Ветви не объединяются автоматически.** Каждая ветвь выполняется независимо, пока не завершится. У вас полная свобода в том, как с этим поступать:
* **Вариант 1: Оставить ветви раздельно**
Каждая ветвь самостоятельно обрабатывает свои последующие действия. Это самый простой подход, когда ветвям не нужно сходиться.
* **Вариант 2: Объединить ветви вручную**
При создании рабочего процесса вы можете вручную подключить несколько ветвей к одному последующему действию. Просто перетащите стрелки от конца каждой ветви к общему узлу.
<Note>
Хотя вы можете использовать узел [Задержка](/l/ru/user-guide/workflows/capabilities/workflow-actions#delay) для приостановки выполнения, его пока нельзя настроить на ожидание "пока не завершится другая ветвь".
</Note>
## Связанные материалы
* [Как использовать ветви в рабочих процессах](/l/ru/user-guide/workflows/capabilities/use-branches-in-workflows) - Пошаговое руководство
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions) - Доступные действия, включая Фильтр

View File

@@ -0,0 +1,75 @@
---
title: Кредиты рабочих процессов
description: Разберитесь с потреблением и управлением кредитами рабочих процессов.
---
Кредиты рабочих процессов обеспечивают работу ваших автоматизаций в Twenty. Понимание принципов их работы помогает оптимизировать затраты и эффективно управлять бюджетом автоматизации.
## Распределение кредитов
Кредиты рабочих процессов начисляются по вашему платёжному циклу, а не по уровню плана:
| Платёжный цикл | Кредиты |
| ------------------------ | ------------------ |
| **Ежемесячная подписка** | 5 кредитов в месяц |
| **Годовая подписка** | 50 кредитов в год |
<Note>
5 кредитов в месяц — щедрый лимит для стандартных автоматизаций. Большинство команд не превысят этот лимит при типичном использовании рабочих процессов. Дополнительные кредиты в основном нужны для продвинутых действий Code и рабочих процессов на базе ИИ.
</Note>
## Как работает потребление кредитов
Кредиты расходуются при выполнении рабочих процессов, а не при их создании. Каждое действие рабочего процесса потребляет кредиты в зависимости от своей сложности:
### Потребление кредитов по типу действия
* **Основные внутренние операции**: Очень низкое потребление кредитов
* Поиск записей
* Создать запись
* Обновление записи
* Удаление записи
* Действия с формой
* **Сложные операции**: Более высокое потребление кредитов
* Действия с кодом (выполнение JavaScript)
* HTTP-запросы к внешним сервисам
* **Функции ИИ**: Более высокое потребление кредитов
* Действия ИИ-агента потребляют кредиты в зависимости от используемой модели ИИ
* Более сложные запросы и более длинные ответы расходуют больше кредитов
* **Действия Delay**: Минимальное потребление кредитов
* Узел Delay потребляет **1 кредит** при выполнении
* **Во время ожидания кредиты не потребляются**
* Задержка на 5 минут стоит столько же, сколько задержка на 5 дней
### Списание в реальном времени
Кредиты списываются в режиме реального времени при выполнении рабочих процессов. Это означает, что:
* Черновые рабочие процессы не потребляют кредиты
* Только активные, выполняемые рабочие процессы используют ваш кредитный лимит
* Неудавшиеся рабочие процессы все равно потребляют кредиты за выполненные шаги
## Управление кредитами
### Проверить использование кредитов
1. Перейдите в **Настройки → Оплата**
2. Просмотрите текущее потребление кредитов и оставшийся баланс
3. Отслеживайте шаблоны использования, чтобы оптимизировать ваши рабочие процессы
### Приобретение дополнительных кредитов
Если вам нужно больше кредитов, чем в вашем лимите плана:
1. Перейдите в **Настройки → Оплата**
2. Нажмите на опцию, чтобы приобрести дополнительные кредиты. Доступны пакеты разного размера.
3. Кредиты добавляются к вашему текущему балансу
## Лучшие практики
* **Пакетная обработка**: Используйте массовые операции и действия с Итератором эффективно, чтобы сократить количество отдельных вызовов действий
* **Оптимизация ручного запуска**: Для ручных триггеров выберите вариант `Bulk`, чтобы обработать несколько записей за один запуск рабочего процесса
* Оптимизируйте действия с кодом для повышения эффективности

View File

@@ -0,0 +1,93 @@
---
title: Запуски рабочего процесса
description: Отслеживайте и управляйте запусками рабочих процессов.
---
## О запусках
**Запуск** — это запись выполнения рабочего процесса. Каждый раз, когда срабатывает триггер рабочего процесса — будь то событие записи, расписание, ручное действие или вебхук — создаётся новый запуск.
## Просмотр запусков
### В редакторе рабочего процесса
1. Откройте рабочий процесс, который хотите отслеживать
2. Нажмите на панель **Запуски** справа
3. Посмотрите список последних запусков со статусами
### В разделе «Запуски рабочего процесса»
1. Перейдите в **Запуски рабочего процесса** в боковой панели
2. Просматривайте запуски по всем рабочим процессам
3. Фильтруйте по статусу, рабочему процессу или дате
## Статусы запусков
| Статус | Описание |
| --------------- | ---------------------------------------------------------------------------------------- |
| **Выполняется** | Рабочий процесс выполняется |
| **Завершено** | Рабочий процесс успешно завершён |
| **Не удалось** | Рабочий процесс столкнулся с ошибкой и остановился |
| **Ожидание** | Рабочий процесс приостановлен (например, ожидает действия «Задержка» или отправки формы) |
## Сведения о запуске
Нажмите на любой запуск, чтобы просмотреть:
* **Статус**: текущее состояние запуска
* **Начато**: время начала запуска
* **Длительность**: сколько времени занял запуск
* **Данные триггера**: входные данные, запустившие рабочий процесс
* **Результаты шагов**: данные, возвращённые каждым шагом
* **Сообщения об ошибках**: если запуск не удался, что пошло не так
## Пошаговое выполнение
Каждый запуск показывает прохождение по вашему рабочему процессу:
1. Смотрите, какие шаги завершились успешно
2. Определите, где произошли сбои
3. Просматривайте данные, передаваемые между шагами
4. Отлаживайте проблемы, изучая входные и выходные данные шагов
## Обработка ошибок
Если запуск не удался:
1. Откройте запуск со сбоем
2. Найдите шаг, вызвавший сбой
3. Посмотрите подробности в сообщении об ошибке
Распространённые проблемы:
* Отсутствуют обязательные поля
* Недопустимый формат данных
* Ошибки внешнего API
* Проблемы с правами доступа
## Повторный запуск рабочих процессов
Если запуск не удался, вы можете:
* Исправьте основную проблему и дождитесь следующего срабатывания триггера
* Для ручных рабочих процессов запустите снова с теми же или обновлёнными данными
* Проверьте логику рабочего процесса, чтобы предотвратить будущие сбои
## Рекомендации по производительности
### Управление историей запусков
* Запуски сохраняются для истории
* Очень старые запуски могут архивироваться автоматически
* Экспортируйте данные запусков, если нужно сохранить их для учёта
### Лучшие практики мониторинга
* Регулярно проверяйте запуски после активации новых рабочих процессов
* Анализируйте неудавшиеся запуски, чтобы выявить закономерности
## Связанные материалы
* [Триггеры рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-triggers)
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)
* [Устранение неполадок рабочего процесса](/l/ru/user-guide/workflows/how-tos/need-more-help/workflow-troubleshooting)

View File

@@ -0,0 +1,136 @@
---
title: Триггеры рабочего процесса
description: Узнайте о различных триггерах, которые запускают ваши рабочие процессы.
---
## О триггерах
Рабочие процессы всегда начинаются с единственного триггера, который определяет, когда должна выполняться автоматизация.
<img src="/images/user-guide/workflows/workflow_triggers.png" style={{width:'100%'}} />
<Tip>
**Поддерживаются расширенные объекты!** Помимо стандартных объектов CRM (люди, компании, сделки), вы также можете запускать рабочие процессы и выполнять действия с:
* Участники рабочего пространства
* События календаря
* Сообщения (электронные письма)
* Задачи, заметки и многие другие системные объекты
Это открывает возможности мощной автоматизации, например уведомление участников команды при создании событий календаря или автоматическую обработку входящих писем.
</Tip>
## Запись создана
Запускает рабочий процесс, когда новая запись создается в выбранном объекте (Люди, Компании, Возможности или любой пользовательский объект).
**Настройка**: Выберите тип объекта для отслеживания новых записей.
<Note>
* Этот триггер отлично подходит для записей, созданных CSV, почтового ящика и синхронизации календаря или API.
* **Не рекомендуется для записей, создаваемых вручную**: с этим триггером рабочие процессы запускаются сразу после создания записи. Поскольку двадцать пользовательский интерфейс предлагает автосохранение на лету (нет режима редактирования, а затем проверка для сохранения записей), рабочий процесс будет срабатывать перед вводом пользователем всех полей.
Чтобы запускать этот рабочий процесс для записей, создаваемых вручную, рекомендуется вместо этого использовать триггер `Record is created or updated`.
</Note>
## Запись обновлена
Запускает рабочий процесс, когда в существующую запись вносятся изменения.
**Настройка**:
* Выберите тип объекта
* При необходимости укажите, какие поля отслеживать на предмет изменений
## Запись обновлена или создана
Запускает рабочий процесс, когда запись либо создается, либо обновляется в выбранном объекте.
**Почему это важно**: Этот триггер особенно полезен, потому что записи, созданные разными методами, ведут себя по-разному:
* **Импорт через API/CSV**: Записи создаются сразу со всеми заполненными полями
* **Ручное создание**: Записи создаются сначала, затем поля добавляются в последующих обновлениях
**Настройка**:
* Выберите тип объекта для отслеживания
* При необходимости укажите, какие поля отслеживать на предмет изменений
* Рабочий процесс будет запускаться как при первоначальном создании, так и при последующих обновлениях
## Запись удалена
Запускает рабочий процесс при удалении записи из объекта.
**Настройка**: Выберите тип объекта для отслеживания удалений.
## Ручной триггер
Запускает рабочий процесс, когда он инициируется пользователем. Доступ к этому триггеру можно получить через меню `Cmd+K` или с помощью настраиваемой кнопки, которая будет отображаться на верхней панели навигации после выбора записей.
<img src="/images/user-guide/workflows/add_to_newsletter_button.png" style={{width:'100%'}} />
**Настройка доступности**: Выберите, как рабочий процесс должен обрабатывать выбор записи:
* **Глобально**: Для запуска этого рабочего процесса запись не требуется. Рабочий процесс запускается из командного меню «Cmd+K» где угодно (из любого объекта) и не использует запись(и) в качестве входа.
Для доступности **сингл** и **Bulk**, выбранная запись (ы) будет передана вашему рабочему процессу. Это настроено для данного объекта, и можно выбрать несколько записей перед запуском рабочего процесса.
* **Сингл**: Рабочий процесс будет работать от начала до конца столько раз, сколько будут выбраны записи.
<Note>
**Мягкий лимит: 100 запусков в минуту**. При превышении этого значения рабочие процессы остаются в статусе "Not Started" и обрабатываются постепенно — либо фоновой задачей, либо когда другой рабочий процесс попадает в очередь. Это означает, что вы можете выбрать более 100 записей с помощью одного триггера; выполнение просто будет медленнее.
</Note>
* **Bulk**: Рабочий процесс будет работать один раз, представляя весь список записей в качестве входных данных. Это означает, что рабочий процесс должен содержать [действие Iterator](/l/ru/user-guide/workflows/capabilities/workflow-actions#iterator).
<Note>
Это более продвинутый вариант и лучше всего подходит для тех, кто хочет оптимизировать количество запусков рабочего процесса.
</Note>
<img src="/images/user-guide/workflows/manual_trigger.png" style={{width:'100%'}} />
**Дополнительная настройка**:
* Выберите целевой объект (для одиночной и групповой доступности)
* Выберите значок команды для запуска рабочего процесса
* Настройте размещение в навигационной панели (Закреплено или Не закреплено)
**Методы доступа**:
* Меню `Cmd+K` для поиска и запуска ручных рабочих процессов
* Пользовательская кнопка в верхней навигационной панели (если настроено)
## Триггер по времени: по расписанию
Запускает рабочий процесс на регулярной основе, которую вы определяете.
**Настройка**:
* Выберите единицу времени (минуты, часы, дни)
* Введите значение или используйте пользовательские cron-выражения для сложного планирования
<Note>
**Часовой пояс**: Запланированные рабочие процессы выполняются в **UTC**. При указании часов для ежедневных расписаний переводите ваше местное время в UTC.
</Note>
## Внешний триггер: вебхук
Запускает рабочий процесс при получении GET или POST-запроса от внешнего сервиса.
<img src="/images/user-guide/workflows/webhook_trigger.png" style={{width:'100%'}} />
**Настройка**:
* Рабочий процесс предоставляет уникальный URL вебхука — скопируйте его и добавьте в вашу внешнюю систему в качестве вызываемой конечной точки.
* Для запросов POST определите ожидаемую структуру тела, чтобы система Twenty понимала, какие данные ожидать. Добавьте здесь поля, которые вы получите и которые понадобятся ниже в вашем рабочем процессе.
* Настройте аутентификацию (скоро будет).
## Выбор подходящего триггера
| Сценарий использования | Рекомендуемый триггер |
| -------------------------------------- | ---------------------------- |
| Новые лиды требуют обработки | Запись создана |
| Изменения данных требуют синхронизации | Запись обновлена |
| Импорт/ручная обработка данных | Запись обновлена или создана |
| Очистка после удаления | Запись удалена |
| Действие, инициированное пользователем | Запуск вручную |
| Регулярные отчеты | По расписанию |
| Внешняя интеграция | Вебхук или по расписанию |

View File

@@ -0,0 +1,84 @@
---
title: Версии рабочего процесса
description: Управление версиями и черновиками рабочего процесса.
---
## О версиях
Каждый раз, когда вы активируете рабочий процесс, создается новая версия. Это позволяет отслеживать изменения со временем и при необходимости возвращаться к предыдущим конфигурациям.
## Статусы версий
| Статус | Описание |
| ----------------- | ---------------------------------------- |
| **Черновик** | Редактируется, еще не опубликован |
| **Активный** | Активная версия, реагирующая на триггеры |
| **Деактивирован** | Ранее активная, но остановлена вручную |
| **Архивирован** | Прошлые версии, сохраненные для истории |
## Работа с черновиками
Когда вы редактируете активный рабочий процесс, ваши изменения сохраняются как **черновик**. Активная версия продолжает работать, пока вы работаете над обновлениями.
По завершении редактирования вы можете:
* **Активировать**: опубликовать черновик как новую активную версию (предыдущая версия будет архивирована)
* **Удалить**: удалить черновик и сохранить текущую активную версию
## История версий
### Просмотр прошлых версий
1. Откройте рабочий процесс
2. Нажмите вкладку **Версии**
3. Просмотрите все предыдущие версии с временными метками
### Восстановление версии
1. Найдите версию, которую хотите восстановить
2. Нажмите **Использовать как черновик**
3. Версия копируется в новый черновик
4. Внесите все необходимые изменения
5. Активируйте, когда будете готовы
## Лучшие практики
### Управление версиями
* Активируйте только когда готовы к использованию в продакшене
* Вносите между версиями только значимые изменения
* Документируйте существенные изменения в названиях или описаниях рабочих процессов
* Тестируйте в режиме черновика перед активацией
### Откат изменений
* Если новая версия вызывает проблемы, восстановите предыдущую версию
* Используйте историю версий, чтобы отслеживать, что изменилось
* Всегда тестируйте восстановленные версии перед активацией
## Распространенные рабочие процессы
### Быстрое редактирование
1. Внесите небольшие изменения в активный рабочий процесс
2. Протестируйте в режиме черновика
3. Активируйте новую версию
### Существенная переработка
1. Используйте предыдущую версию в качестве отправной точки
2. Вносите существенные изменения в черновике
3. Тщательно протестируйте все сценарии
4. Активируйте, когда будете уверены
### Откат
1. Определите проблему в текущей версии
2. Найдите последнюю рабочую версию в истории
3. Нажмите **Использовать как черновик**
4. Активируйте, чтобы восстановить прежнее поведение
## Связанные материалы
* [Начало работы с рабочими процессами](/l/ru/user-guide/workflows/overview)
* [Запуски рабочих процессов](/l/ru/user-guide/workflows/capabilities/workflow-runs)

View File

@@ -0,0 +1,82 @@
---
title: Работа с массивами в действиях Code
description: Узнайте, как правильно обрабатывать входные массивы в действиях Code в рабочих процессах.
---
При работе с массивами в действиях Code вы можете столкнуться с двумя распространёнными проблемами:
1. **Массивы, передаваемые как строки** — данные из внешних систем или предыдущих шагов приходят строкой, а не как настоящий массив
2. **Невозможно выбрать отдельные элементы** — можно выбрать только весь массив, а не конкретные поля внутри него
Обе проблемы решаются с помощью узла Code.
## Разбор массивов из строк
Массивы часто передаются между шагами рабочего процесса как строки или JSON, а не как нативные массивы. Это происходит, когда:
* Получение данных из внешних API через HTTP Request
* Обработка полезной нагрузки вебхуков
* Передача данных между шагами рабочего процесса
**Решение**: Добавьте этот шаблон в начало вашего действия Code:
```javascript
export const main = async (params: {
users: any;
}): Promise<object> => {
const { users } = params;
// Handle input that may come as a string or an array
const usersFormatted = typeof users === "string" ? JSON.parse(users) : users;
// Now you can safely work with usersFormatted as an array
return {
users: usersFormatted.map((user) => ({
...user,
activityStatus: String(user.activityStatus).toUpperCase(),
})),
};
};
```
Ключевая строка `typeof users === "string" ? JSON.parse(users) : users` проверяет, является ли входное значение строкой, парсит его при необходимости или использует напрямую, если это уже массив.
## Извлечение отдельных полей из массивов
Вебхук может вернуть массив вида `answers: [...]`, но на последующих шагах рабочего процесса вы сможете выбрать только **весь массив** — не отдельные элементы внутри него.
**Решение**: Добавьте узел Code, чтобы извлечь нужные поля и вернуть их в виде структурированного объекта:
```javascript
export const main = async (params: {
answers: any;
}): Promise<object> => {
const { answers } = params;
// Handle input that may come as a string or an array
const answersFormatted = typeof answers === "string"
? JSON.parse(answers)
: answers;
// Extract specific fields from the array
const firstname = answersFormatted[0]?.text || "";
const name = answersFormatted[1]?.text || "";
return {
answer: {
firstname,
name
}
};
};
```
Узел Code возвращает структурированный объект вместо массива. На последующих шагах теперь можно выбирать отдельные поля, такие как `answer.firstname` и `answer.name`, в селекторе переменных.
<Tip>
**Хотите перебирать массив в цикле вместо извлечения полей?** Когда шаг Code или Logic Function возвращает массив верхнего уровня, вы можете передать его напрямую в [Iterator](/l/ru/user-guide/workflows/capabilities/use-iterator): выберите параметр шага **Whole list** в качестве входных данных Iterator, а затем обращайтесь к каждому элементу с помощью `{{iterator.currentItem}}` внутри цикла. В этом случае вам не нужно преобразовывать массив в объект.
</Tip>
<Tip>
Нажмите квадратный значок в правом верхнем углу редактора кода, чтобы отобразить его на весь экран — это полезно, поскольку ширина редактора по умолчанию ограничена.
</Tip>

View File

@@ -0,0 +1,182 @@
---
title: Импортируйте данные о товарах в Twenty
description: Синхронизируйте данные каталога товаров из хранилища данных с вашей CRM по расписанию.
---
Используйте этот шаблон, чтобы поддерживать синхронизацию Twenty с данными о товарах из вашего хранилища данных (например, Snowflake, BigQuery, PostgreSQL).
## Структура рабочего процесса
1. **Триггер**: По расписанию
2. **Code**: Запрос к вашему хранилищу данных
3. **Code** (необязательно): Отформатируйте данные как массив
4. **Iterator**: Цикл по каждому товару
5. **Upsert Record**: Создать или обновить в Twenty
<img src="/images/user-guide/workflows/create_users_workflow.png" style={{width:'100%'}} />
## Шаг 1: Запланируйте триггер
Настройте запуск рабочего процесса с частотой, соответствующей требованиям к актуальности данных:
* Каждые 5 минут для синхронизации, близкой к реальному времени
* Каждый час для менее критичных данных
* Ежедневно для пакетных обновлений
## Шаг 2: Выполните запрос к вашему хранилищу данных
Добавьте действие **Code** для получения последних данных:
```javascript
export const main = async () => {
const intervalMinutes = 10; // Match your schedule frequency
const cutoffTime = new Date(Date.now() - intervalMinutes * 60 * 1000).toISOString();
// Replace with your actual data warehouse connection
const response = await fetch("https://your-warehouse-api.com/query", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
query: `
SELECT id, name, sku, price, stock_quantity, updated_at
FROM products
WHERE updated_at >= '${cutoffTime}'
`
})
});
const data = await response.json();
return { products: data.results };
};
```
<Tip>
Отфильтруйте по `updated_at >= last X minutes`, чтобы получать только недавно изменённые записи. Это делает синхронизацию эффективной.
</Tip>
## Шаг 3: Отформатируйте данные (необязательно)
Если ваше хранилище возвращает данные в формате, требующем преобразования, добавьте ещё одно действие **Code**. К распространённым преобразованиям относятся преобразование типов, переименование полей и очистка данных.
### Пример: данные пользователей с булевыми и статусными полями
```javascript
export const main = async (params: {
users: any;
}): Promise<object> => {
const { users } = params;
const usersFormatted = typeof users === "string" ? JSON.parse(users) : users;
// Convert string "true"/"false" to actual booleans
const toBool = (v: any) => v === true || v === "true";
return {
users: usersFormatted.map((user) => ({
...user,
activityStatus: String(user.activityStatus).toUpperCase(),
isActiveLast30d: toBool(user.isActiveLast30d),
isActiveLast7d: toBool(user.isActiveLast7d),
isActiveLast24h: toBool(user.isActiveLast24h),
isTwenty: toBool(user.isTwenty),
})),
};
};
```
### Пример: данные о товарах с преобразованием типов
```javascript
export const main = async (params: { products: any }) => {
const products = typeof params.products === "string"
? JSON.parse(params.products)
: params.products;
return {
products: products.map(product => ({
externalId: product.id,
name: product.name,
sku: product.sku,
price: parseFloat(product.price), // String → Number
stockQuantity: parseInt(product.stock_quantity),
isActive: product.status === "active" // String → Boolean
}))
};
};
```
### Пример: форматирование даты и валюты
```javascript
export const main = async (params: { deals: any }) => {
const deals = typeof params.deals === "string"
? JSON.parse(params.deals)
: params.deals;
return {
deals: deals.map(deal => ({
...deal,
// Convert Unix timestamp to ISO date
closedAt: deal.closed_timestamp
? new Date(deal.closed_timestamp * 1000).toISOString()
: null,
// Ensure amount is a number (remove currency symbols)
amount: parseFloat(String(deal.amount).replace(/[^0-9.-]/g, "")),
// Normalize stage names
stage: deal.stage?.toLowerCase().replace(/_/g, " ")
}))
};
};
```
### Распространённые преобразования
| Исходный формат | Целевой формат | Код |
| -------------------- | ------------------ | ---------------------------------------- |
| `"true"` / `"false"` | `true` / `false` | `v === true \|\| v === "true"` |
| `"123.45"` | `123.45` | `parseFloat(value)` |
| `"active"` | `"ACTIVE"` | `value.toUpperCase()` |
| `1704067200` (Unix) | Дата в формате ISO | `new Date(v * 1000).toISOString()` |
| `"$1,234.56"` | `1234.56` | `parseFloat(v.replace(/[^0-9.-]/g, ""))` |
| `null` / `undefined` | `""` | `value \|\| ""` |
## Шаг 4: Переберите товары
Добавьте действие **Iterator**:
* Вход: `{{code.products}}`
Это проходит по каждому товару в массиве.
## Шаг 5: Выполните Upsert для каждой записи
Внутри итератора добавьте действие **Upsert Record**:
| Настройка | Значение |
| -------------------- | --------------------------------------------- |
| **Объект** | Ваш пользовательский объект Product |
| **Сопоставление по** | Внешний ID или SKU (уникальный идентификатор) |
| **Название** | `{{iterator.item.name}}` |
| **SKU** | `{{iterator.item.sku}}` |
| **Цена** | `{{iterator.item.price}}` |
<Tip>
Используйте **Upsert** (обновление или создание) вместо построения отдельных веток для создания и обновления. Так быстрее реализовать и проще отлаживать.
</Tip>
## Примеры случаев использования
| Источник | Данные |
| ----------------------------------- | ------------------------------------------ |
| **ERP-система** | Каталог товаров, цены, запасы |
| **Платформа электронной коммерции** | Заказы, клиенты, обновления товаров |
| **Хранилище данных** | Агрегированные метрики, обогащённые данные |
| **Система управления запасами** | Уровни запасов, оповещения о дозаказах |
## Связанные материалы
* [Триггеры рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-triggers)
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)
* [Обработка массивов в действиях Code](/l/ru/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions)

View File

@@ -0,0 +1,130 @@
---
title: Импорт ответов Typeform в Twenty
description: Обработайте полезную нагрузку вебхука Typeform, чтобы создавать лиды из отправок формы.
---
Для стандартной настройки вебхука см. [Настройка триггера вебхука](/l/ru/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger). В этой статье рассматривается специальная обработка, требуемая для пользовательской структуры полезной нагрузки Typeform.
### Шаг 1: Создайте рабочий процесс с вебхуком
1. Перейдите в **Настройки → Рабочие процессы**
2. Нажмите **+ Новый рабочий процесс**
3. Выберите **Webhook** в качестве триггера
4. Скопируйте URL вебхука
### Шаг 2: Настройте Typeform
1. В Typeform откройте свою форму
2. Перейдите в **Connect → Webhooks**
3. Вставьте URL вебхука Twenty
4. Сохранить
### Шаг 3: Разберитесь в полезной нагрузке Typeform
Typeform отправляет вложенную структуру JSON. Вот упрощенный пример:
```json
{
"event_type": "form_response",
"form_response": {
"form_id": "abc123",
"submitted_at": "2025-01-15T10:30:00Z",
"answers": [
{
"text": "Jane",
"type": "text",
"field": { "id": "field1", "type": "short_text", "title": "First Name" }
},
{
"text": "Smith",
"type": "text",
"field": { "id": "field2", "type": "short_text", "title": "Last Name" }
},
{
"text": "Acme Corp",
"type": "text",
"field": { "id": "field3", "type": "short_text", "title": "Company" }
},
{
"email": "jane@acme.com",
"type": "email",
"field": { "id": "field4", "type": "email", "title": "Email" }
},
{
"type": "choice",
"field": { "id": "field5", "type": "dropdown", "title": "Team Size" },
"choice": { "label": "10-50" }
}
]
}
}
```
Ключевые моменты:
* Данные формы находятся в `form_response`
* **Ответы возвращаются в виде массива**, а не именованных полей
* Каждый ответ включает тип поля и его заголовок для справки
### Шаг 4: Извлеките поля из массива ответов
Поскольку `answers` — это массив, на последующих шагах можно выбрать только весь массив, а не отдельные поля. Добавьте действие **Code** для извлечения нужных полей:
```javascript
export const main = async (params: {
answers: any;
}): Promise<object> => {
const { answers } = params;
// Handle input that may come as a string or an array
const answersFormatted = typeof answers === "string"
? JSON.parse(answers)
: answers;
// Extract fields by position or by finding the field type
const firstName = answersFormatted[0]?.text || "";
const lastName = answersFormatted[1]?.text || "";
const company = answersFormatted[2]?.text || "";
const email = answersFormatted.find(a => a.type === "email")?.email || "";
const teamSize = answersFormatted.find(a => a.type === "choice")?.choice?.label || "";
return {
contact: {
firstName,
lastName,
company,
email,
teamSize
}
};
};
```
На последующих шагах теперь можно выбирать отдельные поля, такие как `contact.firstName` и `contact.email`, в селекторе переменных.
<Tip>
Для получения подробностей об обработке массивов в действиях Code см. [Обработка массивов в действиях Code](/l/ru/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions).
</Tip>
### Шаг 5: Создайте запись
Добавьте действие **Создать запись**:
| Поле | Значение |
| --------------------- | --------------------------------------------------------- |
| **Объект** | Люди |
| **Имя** | `{{code.contact.firstName}}` |
| **Фамилия** | `{{code.contact.lastName}}` |
| **Электронная почта** | `{{code.contact.email}}` |
| **Компания** | Найдите или создайте на основе `{{code.contact.company}}` |
### Шаг 6: Протестируйте и активируйте
1. Отправьте тестовый ответ в Typeform
2. Проверьте запуск рабочего процесса, чтобы убедиться, что данные были получены
3. Активируйте рабочий процесс
## Связанные материалы
* [Настройка триггера вебхука](/l/ru/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger)
* [Обработка массивов в действиях Code](/l/ru/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions)

View File

@@ -0,0 +1,263 @@
---
title: Создание PDF в Twenty
description: Создайте рабочий процесс для создания и прикрепления PDF (например, коммерческого предложения) к записи.
---
Автоматически создавайте или получайте PDF и прикрепляйте его к записи в Twenty. Это часто используют для создания коммерческих предложений, счетов или отчетов, связанных с компаниями, сделками или другими объектами.
## Обзор
В этом рабочем процессе используется **Ручной триггер**, чтобы пользователи могли по запросу создавать PDF для любой выбранной записи. За обработку отвечает **Логическая функция**:
1. Загрузка PDF по URL (из сервиса генерации PDF)
2. Загрузка файла в Twenty
3. Создание вложения, связанного с записью
## Требования
Прежде чем настраивать рабочий процесс:
1. **Создайте ключ API**: перейдите в **Настройки → APIs** и создайте новый ключ API. Вам понадобится этот токен для логической функции.
2. **Настройте сервис генерации PDF** (необязательно): если вы хотите динамически генерировать PDF (например, коммерческие предложения), используйте такие сервисы, как Carbone, PDFMonkey или DocuSeal, чтобы создать PDF и получить URL для скачивания.
## Пошаговая настройка
### Шаг 1: Настройте триггер
1. Перейдите в **Рабочие процессы** и создайте новый рабочий процесс
2. Выберите **Ручной триггер**
3. Выберите объект, к которому нужно прикреплять PDF (например, **Company** или **Opportunity**)
<Tip>
С ручным триггером, пользователи могут запускать этот рабочий процесс, используя кнопку, которая появляется в правом верхнем углу после выбора записи, которая запускает рабочий процесс для создания и прикрепления PDF.
</Tip>
### Шаг 2: Добавьте логическую функцию
1. Добавить действие **код** (логическая функция)
2. Создайте новую функцию с кодом ниже
3. Настройте входные параметры
#### Входные параметры
| Параметр | Значение |
| ----------- | ----------------------- |
| `companyId` | `{{trigger.object.id}}` |
<Note>
Если прикрепляете к другому объекту (Person, Opportunity и т. д.), переименуйте параметр соответствующим образом (например, `personId`, `opportunityId`) и обновите логическую функцию.
</Note>
#### Код логической функции
```typescript
export const main = async (
params: { companyId: string },
) => {
const { companyId } = params;
// Replace with your Twenty GraphQL endpoints (/metadata for metadata and files or /graphql for your records)
// Cloud: https://api.twenty.com/graphql
// Self-hosted: https://your-domain.com/graphql
const metadataGraphqlEndpoint = 'https://api.twenty.com/metadata';
const dataGraphqlEndpoint = 'https://api.twenty.com/graphql';
// Replace with your API key from Settings → APIs
const authToken = 'YOUR_API_KEY';
// Replace with your PDF URL
// This could be from a PDF generation service or a static URL
const pdfUrl = 'https://your-pdf-service.com/generated-quote.pdf';
const filename = 'quote.pdf';
// Step 1: Download the PDF file
const pdfResponse = await fetch(pdfUrl);
if (!pdfResponse.ok) {
throw new Error(`Failed to download PDF: ${pdfResponse.status}`);
}
const pdfBlob = await pdfResponse.blob();
const pdfFile = new File([pdfBlob], filename, { type: 'application/pdf' });
const fieldMetadataIdQuery = `
query FindUploadFileFieldMetadataId {
objects {
edges {
node {
nameSingular
fieldsList {
id
name
}
}
}
}
}
`;
// Step 2: Find a fieldMetadataId of "Attachment file" field in Attachments object with GraphQL API
const response = await fetch(metadataGraphqlEndpoint, {
method: 'POST',
headers: {
Authorization: `Bearer ${authToken}`
},
body: {
query: fieldMetadataIdQuery,
}
});
const result = await response.json();
const uploadFileFieldMetadataId = result.data.objects.edges.find(object => object.node.nameSingular === 'attachment').node.fieldsList.find(field => field.name === 'file').id;
// Step 3: Upload the file via GraphQL multipart upload
const uploadMutation = `
mutation UploadFilesFieldFile($file: Upload!, $fieldMetadataId: String!) {
uploadFilesFieldFile(file: $file, fieldMetadataId: $fieldMetadataId) {
id
}
}
`;
const uploadForm = new FormData();
uploadForm.append('operations', JSON.stringify({
query: uploadMutation,
variables: { file: null, fieldMetadataId: uploadFileFieldMetadataId },
}));
uploadForm.append('map', JSON.stringify({ '0': ['variables.file'] }));
uploadForm.append('0', pdfFile);
const uploadResponse = await fetch(metadataGraphqlEndpoint, {
method: 'POST',
headers: { Authorization: `Bearer ${authToken}` },
body: uploadForm,
});
const uploadResult = await uploadResponse.json();
if (uploadResult.errors?.length) {
throw new Error(`Upload failed: ${uploadResult.errors[0].message}`);
}
const fileId = uploadResult.data?.uploadFilesFieldFile?.id;
if (!fileId) {
throw new Error('No file id returned from upload');
}
// Step 4: Create the attachment linked to the company
const attachmentMutation = `
mutation CreateOneAttachment($data: AttachmentCreateInput!) {
createAttachment(data: $data) {
id
name
}
}
`;
const attachmentResponse = await fetch(dataGraphqlEndpoint, {
method: 'POST',
headers: {
Authorization: `Bearer ${authToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: attachmentMutation,
variables: {
data: {
name: filename,
targetCompanyId: companyId,
file: [
{
fileId: fileId,
label: filename
}
]
},
},
}),
});
const attachmentResult = await attachmentResponse.json();
if (attachmentResult.errors?.length) {
throw new Error(`Attachment creation failed: ${attachmentResult.errors[0].message}`);
}
return attachmentResult.data?.createAttachment;
};
```
### Шаг 3: Настройте под ваш сценарий
#### Чтобы прикреплять к другому объекту
Замените `targetCompanyId` на соответствующее поле:
| Объект | Имя поля |
| ----------------------- | -------------------------- |
| Компания | `targetCompanyId` |
| Контакт | `targetPersonId` |
| Сделка | `targetOpportunityId` |
| Пользовательский объект | `targetYourCustomObjectId` |
Обновите и параметр функции, и объект `variables.data` в мутации вложения.
#### Чтобы использовать динамический URL PDF
Если используете сервис генерации PDF, вы можете:
1. Сначала выполните действие HTTP Request, чтобы сгенерировать PDF
2. Передайте возвращенный URL PDF в логическую функцию как параметр
```typescript
export const main = async (
params: { companyId: string; pdfUrl: string; filename: string },
) => {
const { companyId, pdfUrl, filename } = params;
// ... rest of the function
};
```
### Шаг 4: Протестируйте и активируйте
1. Сохраните рабочий процесс
2. Перейдите к записи Company
3. Нажмите меню **⋮** и выберите ваш рабочий процесс
4. Проверьте раздел **Attachments** в записи, чтобы убедиться, что PDF прикреплен
5. Активируйте рабочий процесс
## Совмещение с сервисами генерации PDF
Для создания динамических коммерческих предложений или счетов:
### Пример: Сгенерировать коммерческое предложение → Прикрепить PDF
| Шаг | Действие | Назначение |
| --- | ------------------------ | ------------------------------------------ |
| 1 | Ручной триггер (Company) | Пользователь запускает на записи |
| 2 | Поиск записи | Получить сведения о сделке или позициях |
| 3 | HTTP-запрос | Вызвать API генерации PDF с данными записи |
| 4 | Логическая функция | Скачать и прикрепить сгенерированный PDF |
### Популярные сервисы генерации PDF
* **Carbone** — генерация документов на основе шаблонов
* **PDFMonkey** — динамическое создание PDF из шаблонов
* **DocuSeal** — платформа автоматизации документооборота
* **Documint** — генерация документов, ориентированная на API
Каждый сервис предоставляет API, который возвращает URL-адрес PDF, который затем можно передать в логическую функцию.
## Устранение неполадок
| Проблема | Решение |
| ----------------------------- | ----------------------------------------------------------------------------- |
| "Не удалось скачать PDF" | Проверьте, что URL PDF доступен и возвращает корректный PDF |
| "Не удалось загрузить" | Убедитесь, что ваш ключ API действителен и имеет права на запись |
| "Не удалось создать вложение" | Убедитесь, что имя поля идентификатора объекта соответствует целевому объекту |
## Связанные материалы
* [Триггеры рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-triggers)
* [Логические функции](/l/ru/user-guide/workflows/capabilities/workflow-actions#code)
* [Создание коммерческого предложения или счета в Twenty](/l/ru/user-guide/workflows/how-tos/connect-to-other-tools/generate-quote-or-invoice-from-twenty)

View File

@@ -0,0 +1,144 @@
---
title: Создайте коммерческое предложение или счёт из Twenty
description: Автоматически создавайте счёта во внешних инструментах при закрытии сделок.
---
Автоматически отправляйте данные по сделке в вашу систему выставления счетов (Stripe, QuickBooks, Xero и т. д.). когда сделка выиграна.
## Структура рабочего процесса
1. **Триггер**: Запись обновлена (объект 'Сделка')
2. **Фильтр**: Этап = Закрыто — выиграно
3. **Поиск записи**: Получить сведения о компании
4. **Код** (необязательно): Сформатировать полезную нагрузку
5. **HTTP-запрос**: Отправить в систему выставления счетов
## Шаг 1: Настройте триггер
1. Создайте новый рабочий процесс
2. Выберите триггер **Запись обновлена**
3. Выберите **Сделка** в качестве объекта
## Шаг 2: Отфильтруйте по «Закрыто — выиграно»
Добавьте действие **Фильтр**, чтобы продолжать только когда сделка выиграна:
| Настройка | Значение |
| ------------ | ---------------------------------------- |
| **Поле** | Этап |
| **Условие** | Равно |
| **Значение** | `CLOSED_WON` (или название вашего этапа) |
<Tip>
Триггер срабатывает при любом обновлении Сделки. Фильтр гарантирует, что рабочий процесс продолжится только когда этап изменится на «Закрыто — выиграно».
</Tip>
## Шаг 3: Получите сведения о компании
Запись Сделки может не содержать всех полей Компании, которые нужны для счёта. Добавьте действие **Поиск записи**:
| Настройка | Значение |
| -------------------- | --------------------------------------- |
| **Объект** | Компания |
| **Сопоставление по** | ID равен `{{trigger.object.companyId}}` |
Это вернёт полную запись Компании с платёжным адресом, налоговым идентификатором и т. д.
## Шаг 4: Сформатируйте полезную нагрузку (необязательно)
Если ваша система выставления счетов ожидает определённый формат, добавьте действие **Код**:
```javascript
export const main = async (params: {
opportunity: any;
company: any;
}): Promise<object> => {
const { opportunity, company } = params;
return {
invoice: {
// Customer info from Company
customer_name: company.name,
customer_email: company.email || "",
billing_address: {
line1: company.address?.street || "",
city: company.address?.city || "",
postal_code: company.address?.postalCode || "",
country: company.address?.country || ""
},
tax_id: company.taxId || null,
// Invoice details from Opportunity
amount: opportunity.amount,
currency: opportunity.currency || "USD",
description: `Invoice for ${opportunity.name}`,
due_days: 30,
// Reference back to Twenty
metadata: {
opportunity_id: opportunity.id,
company_id: company.id
}
}
};
};
```
## Шаг 5: Отправьте в систему выставления счетов
Добавьте действие **HTTP-запрос**:
| Настройка | Значение |
| -------------- | --------------------------------------------------- |
| **Метод** | POST |
| **URL-адрес** | Конечная точка API вашей системы выставления счетов |
| **Заголовки** | `Authorization: Bearer YOUR_API_KEY` |
| **Содержимое** | `{{code.invoice}}` или сопоставьте поля напрямую |
### Пример: счёт Stripe
```
POST https://api.stripe.com/v1/invoices
Headers:
Authorization: Bearer sk_live_xxx
Content-Type: application/x-www-form-urlencoded
Body:
customer: {{company.stripeCustomerId}}
collection_method: send_invoice
days_until_due: 30
```
### Пример: счёт QuickBooks
```
POST https://quickbooks.api.intuit.com/v3/company/{realmId}/invoice
Headers:
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
Body: {{code.invoice}}
```
## Итоговая сводка рабочего процесса
| Шаг | Действие | Назначение |
| --- | ------------------------- | --------------------------------------------------------- |
| 1 | Триггер: запись обновлена | Срабатывает при любых изменениях в Сделке |
| 2 | Фильтр | Продолжать только если Этап = Закрыто — выиграно |
| 3 | Поиск записи | Получить полные сведения о компании для выставления счёта |
| 4 | Код | Сформатировать данные для API выставления счетов |
| 5 | HTTP-запрос | Создать счёт во внешней системе |
## Советы
* **Сохраняйте внешние ID**: Сохраните идентификатор счёта, возвращённый API, обратно в Сделку с помощью действия **Обновить запись**
* **Обработка ошибок**: Добавьте ветку, чтобы отправлять уведомление, если HTTP-запрос завершится ошибкой
* **Сначала протестируйте**: Используйте песочницу/тестовый режим вашей системы выставления счетов перед запуском в рабочую среду
## Связанные материалы
* [Сгенерировать PDF из Twenty](/l/ru/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty) — прикрепляйте сгенерированные PDF-файлы к записям
* [Триггеры рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-triggers)
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)
* [Автоматизации «Закрыто — выиграно»](/l/ru/user-guide/workflows/how-tos/crm-automations/closed-won-automations)

View File

@@ -0,0 +1,170 @@
---
title: Настройка триггера вебхука
description: Получайте данные из внешних сервисов, чтобы запускать рабочие процессы.
---
Триггеры вебхуков позволяют внешним сервисам запускать ваши рабочие процессы, отправляя данные на уникальный URL. Используйте их для подключения форм, сторонних приложений и пользовательских интеграций.
## Когда использовать вебхуки
| Сценарий использования | Пример |
| ------------------------------- | ------------------------------------------ |
| **Веб-формы** | Отправки формы обратной связи создают лиды |
| **Сторонние приложения** | Платеж в Stripe → создать запись клиента |
| **Пользовательские интеграции** | Ваше приложение → автоматизация Twenty |
| **Инструменты без кода** | Подключения Zapier, Make, n8n |
## Пошаговая настройка
### Шаг 1: Создайте рабочий процесс
1. Перейдите в **Настройки → Рабочие процессы**
2. Нажмите **+ Новый рабочий процесс**
3. Дайте ему имя (например, "Отправка формы на сайте")
### Шаг 2: Настройте триггер вебхука
1. Нажмите на блок триггера
2. Выберите **Вебхук**
3. Вы получите уникальный URL вебхука, например:
```
https://api.twenty.com/webhooks/workflow/abc123...
```
4. Скопируйте этот URL — он понадобится вашему внешнему сервису
### Шаг 3: Определите ожидаемую структуру данных
Для **POST**-запросов определите ожидаемую структуру тела:
1. Нажмите **Определить ожидаемое тело**
2. Введите пример JSON, который ваш сервис будет отправлять:
```json
{
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"company": "Acme Inc",
"message": "Interested in your product"
}
```
3. Нажмите **Сохранить** — это создаст переменные, которые можно использовать на следующих шагах
### Шаг 4: Добавьте действия
Теперь добавьте действия, которые используют данные вебхука:
**Пример: Создать запись Person**
1. Добавьте действие **Создать запись**
2. Выберите объект **People**
3. Сопоставьте поля:
| Поле | Значение |
| ----------------- | --------------------------------------------------------- |
| Имя | `{{trigger.body.firstName}}` |
| Фамилия | `{{trigger.body.lastName}}` |
| Электронная почта | `{{trigger.body.email}}` |
| Компания | Найдите или создайте на основе `{{trigger.body.company}}` |
### Шаг 5: Протестируйте вебхук
Перед активацией протестируйте вебхук:
**С помощью cURL**:
```bash
curl -X POST https://api.twenty.com/webhooks/workflow/abc123... \
-H "Content-Type: application/json" \
-d '{"firstName":"Test","lastName":"User","email":"test@example.com"}'
```
**С помощью Postman или аналогов**:
1. Создайте POST-запрос на URL вашего вебхука
2. Установите заголовок Content-Type в `application/json`
3. Добавьте тестовое тело JSON
4. Отправьте и проверьте запуски рабочего процесса
### Шаг 6: Активируйте
После тестирования нажмите **Активировать**, чтобы сделать рабочий процесс активным.
## Обработка разных структур данных
### Вложенные данные
Если ваш вебхук отправляет вложенные данные:
```json
{
"contact": {
"name": "John Doe",
"email": "john@example.com"
},
"source": "website"
}
```
Обращайтесь так: `{{trigger.body.contact.email}}`
### Массивы
Если данные включают массивы:
```json
{
"items": [
{"name": "Product A", "qty": 2},
{"name": "Product B", "qty": 1}
]
}
```
То, как обрабатывать массивы, зависит от сценария использования:
**Неизвестное количество элементов → Используйте Iterator**
Если нужно обработать каждый элемент массива (например, создать запись для каждого), добавьте действие **Code** для разбора массива, затем используйте **Iterator**:
```javascript
export const main = async (params: { items: any }) => {
const items = typeof params.items === "string"
? JSON.parse(params.items)
: params.items;
return { items };
};
```
Затем используйте Iterator для перебора: `{{code.items}}`
**Известные/конкретные поля → Извлеките в именованные поля**
Если массив содержит конкретные поля, к которым нужно обращаться по отдельности (например, ответы формы, где позиция 0 — всегда "имя", позиция 1 — всегда "фамилия"), добавьте действие **Code** для их извлечения:
```javascript
export const main = async (params: { items: any }) => {
const items = typeof params.items === "string"
? JSON.parse(params.items)
: params.items;
return {
product: {
name: items[0]?.name || "",
qty: items[0]?.qty || 0
}
};
};
```
Теперь вы можете выбирать `product.name` и `product.qty` по отдельности на следующих шагах.
<Tip>
Для получения подробностей об обработке массивов см. [Обработка массивов в действиях Code](/l/ru/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions).
</Tip>
## Связанные материалы
* [Триггеры рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-triggers)
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)

View File

@@ -0,0 +1,121 @@
---
title: Автоответ на входящие письма
description: Создайте рабочий процесс, который использует ИИ для сортировки входящих писем и автоматически отправляет ответы в рамках цепочки.
---
Отвечайте на входящие письма за секунды — а не за часы. Этот рабочий процесс использует ИИ-агента, чтобы отфильтровать шум (рассылки, спам, автоответы) и подготовить персонализированный ответ на реальные сообщения. Затем ИИ отправляет его как ответ в цепочке внутри исходной переписки.
## Как работают цепочки писем
Каждое письмо содержит скрытый заголовок `Message-ID` — уникальный отпечаток, назначаемый почтовым сервером отправителя. Когда вы отвечаете на электронное письмо, ваш почтовый клиент устанавливает заголовок `In-Reply-To`, ссылающийся на этот отпечаток. Именно так Gmail, Outlook и другие клиенты группируют сообщения в цепочки.
В Twenty этот отпечаток хранится как `headerMessageId` в объекте Message. Ваш рабочий процесс получает его и передает в поле In-Reply-To действия Send Email.
## Создание рабочего процесса
### Шаг 1: Создайте новый рабочий процесс
Перейдите в **Настройки → Рабочие процессы** и нажмите **+ New Workflow**.
### Шаг 2: Настройте триггер на входящие сообщения
Выберите **When a Record is Created** и укажите **Messages**.
Каждый раз, когда письмо попадает в Twenty, этот триггер срабатывает.
<img src="/images/user-guide/workflows/auto-reply/workflow-overview.png" style={{width:'100%'}} />
### Шаг 3: Найдите, кто отправил письмо
Добавьте действие **Search Records**.
Адрес отправителя находится не в самом сообщении — он в связанной записи Message Participant.
| Поле | Значение |
| ---------- | ---------------------------------- |
| **Объект** | Участники сообщения |
| **Фильтр** | Message **равно** `{{trigger.id}}` |
| **Фильтр** | Role **равно** From |
| **Лимит** | 1 |
Это дает вам адрес электронной почты отправителя в `handle` и его имя в `displayName`.
<img src="/images/user-guide/workflows/auto-reply/find-sender.png" style={{width:'100%'}} />
### Шаг 4: Классификация и черновик ответа с помощью ИИ
Добавьте действие **AI Agent**. Этот шаг делает две вещи: решает, заслуживает ли письмо ответа, и, если да, составляет ответ.
Используйте подсказку вроде:
```
You are an email triage assistant for a sales team. Read the following
inbound email and decide if it deserves a reply.
Subject: {{trigger.subject}}
Body: {{trigger.text}}
From: {{Find Sender.first.displayName}} ({{Find Sender.first.handle}})
If this email is spam, a newsletter, an automated notification, or
otherwise does not need a human reply, respond with exactly: SKIP
Otherwise, write a short, professional reply (3-4 sentences max) that:
- Acknowledges their specific message
- Lets them know someone from the team will follow up shortly
- Is warm but not overly casual
Respond with only the reply text, no subject line or greeting prefix.
```
AI Agent выводит свой ответ в поле `response`, к которому могут обращаться следующие шаги.
<img src="/images/user-guide/workflows/auto-reply/ai-triage.png" style={{width:'100%'}} />
### Шаг 5: Ветвление по решению ИИ
Добавьте действие **If/Else**, чтобы проверить, решил ли ИИ ответить или пропустить.
| Поле | Значение |
| -------------- | ---------------------------------------------- |
| **Условие** | AI Agent `response` **не содержит** `SKIP` |
| **Если верно** | Перейти к Send Email |
| **Иначе** | Ничего не делать (рабочий процесс завершается) |
Спам, рассылки и автоматически сгенерированные сообщения отбрасываются. Все остальное переходит к следующему шагу.
<img src="/images/user-guide/workflows/auto-reply/should-reply.png" style={{width:'100%'}} />
### Шаг 6: Отправьте ответ в цепочке
Добавьте действие **Send Email** в ветке "Если верно". Нажмите **Advanced options**, затем **Add In-Reply-To**.
| Поле | Значение |
| --------------- | -------------------------------------- |
| **Кому** | `{{Find Sender.first.handle}}` |
| **Тема** | `Re: {{trigger.subject}}` |
| **Текст** | `{{AI Triage & Draft Reply.response}}` |
| **In-Reply-To** | `{{trigger.headerMessageId}}` |
Поле In-Reply-To делает это письмом-ответом, а не новой перепиской. Получатель увидит его в той же цепочке под исходным письмом в Gmail, Outlook или любом другом клиенте.
<img src="/images/user-guide/workflows/auto-reply/send-email.png" style={{width:'100%'}} />
<Tip>
**In-Reply-To** ожидает `message.headerMessageId` из триггера — это уникальный отпечаток письма, а не адрес получателя. Если оставить его пустым, письмо всё равно отправится, но как отдельное сообщение.
</Tip>
<Warning>
Gmail использует тему письма, чтобы группировать сообщения в цепочки. Тема письма **должна** начинаться с `Re:` (включая двоеточие и пробел), чтобы Gmail показывал ответ внутри исходной цепочки. Без этого ответ будет отображаться как отдельная переписка — даже если заголовок In-Reply-To установлен правильно.
</Warning>
### Шаг 7: Протестируйте и активируйте
Нажмите **Test**, затем проверьте ваш почтовый клиент. Ответ должен появиться под исходным сообщением.
Активируйте, когда будете довольны результатом.
## Идеи для развития
* **Отвечать только VIP-адресатам** — добавьте ветку, которая проверяет домен отправителя или то, существует ли он как Контакт в Twenty
* **Маршрутизация по намерению** — используйте отдельные подсказки для AI Agent, чтобы обрабатывать запросы по продажам иначе, чем обращения в поддержку
* **Обогатите данные перед ответом** — добавьте шаг Search Records, чтобы передать компанию отправителя или историю сделок в подсказку ИИ для более персонализированных ответов

View File

@@ -0,0 +1,179 @@
---
title: Автоматизации для стадии «Закрыта — выиграна»
description: Автоматизируйте действия после выигрыша, когда возможность закрывается.
---
Когда сделка закрывается, нужно сделать несколько вещей: обновить статус компании, уведомить участников команды, создать задачи по онбордингу. Автоматизируйте всё это одним рабочим процессом.
## Проблема
Когда возможность переходит в стадию «Закрыта — выиграна»:
* Тип компании должен измениться с «Потенциальный клиент» на «Клиент»
* Нужно создать задачи по онбордингу
* Команду Customer Success необходимо уведомить
* Менеджеру по продажам нужно подтверждение
Вручную это занимает много времени и приводит к ошибкам.
## Решение
Создайте рабочий процесс, который автоматически выполнит все действия после выигрыша.
## Полная настройка рабочего процесса
### Шаг 1: Создайте рабочий процесс
1. Перейдите в **Настройки → Рабочие процессы**
2. Нажмите **+ Новый рабочий процесс**
3. Назовите его "Deal Won - Post-Win Automation"
### Шаг 2: Настройте триггер
1. Выберите **Запись обновлена**
2. Выберите **Возможности**
3. В разделе "Поля для отслеживания" выберите **Стадия**
### Шаг 3: Добавьте фильтр по стадии
1. Добавьте действие **Фильтр**
2. Условие: `{{trigger.object.stage}}` равно «Закрыта выиграна»
### Шаг 4: Обновите тип компании
1. Добавьте действие **Обновить запись**
2. Настроить:
| Поле | Значение |
| ---------------------- | ------------------------------- |
| **Объект** | Компании |
| **Запись** | `{{trigger.object.company.id}}` |
| **Тип** | Клиент |
| **Дата первой сделки** | `{{trigger.object.closedAt}}` |
| **Владелец аккаунта** | `{{trigger.object.owner.id}}` |
### Шаг 5: Создайте задачу по онбордингу
1. Добавьте действие **Создать запись**
2. Настроить:
| Поле | Значение |
| ------------------------- | ---------------------------------------------------------------------------------------------------- |
| **Объект** | Задачи |
| **Заголовок** | `Onboarding: {{trigger.object.name}}` |
| **Исполнитель** | Сотрудник команды Customer Success |
| **Срок исполнения** | Через 3 дня |
| **Приоритет** | Высокий |
| **Связанная компания** | `{{trigger.object.company.id}}` |
| **Связанная возможность** | `{{trigger.object.id}}` |
| **Описание** | `New customer onboarding for {{trigger.object.company.name}}. Deal value: {{trigger.object.amount}}` |
### Шаг 6: Уведомите команду Customer Success
1. Добавьте действие **Send Email**
2. Настроить:
| Поле | Значение |
| --------- | -------------------------------------------------- |
| **Кому** | customer-success@yourcompany.com |
| **Тема** | `🎉 New Customer: {{trigger.object.company.name}}` |
| **Текст** | См. пример ниже |
**Пример текста письма**:
```
Hi CS Team,
We have a new customer!
Company: {{trigger.object.company.name}}
Deal: {{trigger.object.name}}
Value: {{trigger.object.amount}}
Sales Rep: {{trigger.object.owner.name}}
Close Date: {{trigger.object.closedAt}}
An onboarding task has been created automatically.
Let's give them a great start!
```
### Шаг 7: Отправьте подтверждение менеджеру по продажам
1. Добавьте ещё одно действие **Send Email**
2. Настроить:
| Поле | Значение |
| --------- | ------------------------------------------------------------------------------------------ |
| **Кому** | `{{trigger.object.owner.email}}` |
| **Тема** | `✅ Deal Closed: {{trigger.object.name}}` |
| **Текст** | Поздравляем! Ваша сделка обработана. Команда Customer Success уведомлена, онбординг начат. |
### Шаг 8: Протестируйте и активируйте
1. Протестируйте, переместив тестовую возможность в стадию «Закрыта выиграна»
2. Проверить:
* Тип компании изменился на «Клиент»
* Задача по онбордингу создана
* Команда Customer Success получила письмо
* Менеджер по продажам получил подтверждение
3. Активируйте, когда будете готовы
## Обработка статуса «Закрыта проиграна»
Создайте аналогичный рабочий процесс для проигранных сделок:
### Триггер
* Запись обновлена (Возможности, стадия = «Закрыта проиграна»)
### Действия
1. **Создать запись**: Задача «Анализ проигранной сделки»
2. **Обновить запись**: Добавьте причину проигрыша в запись компании
3. **Send Email**: Уведомите менеджера о проигранной сделке
## Продвинутое: многошаговый онбординг
Для сложного онбординга создайте несколько задач:
```javascript
export const main = async (params) => {
const tasks = [
{ title: "Welcome call", daysFromNow: 1, assignee: "CS" },
{ title: "Send onboarding materials", daysFromNow: 2, assignee: "CS" },
{ title: "Technical setup", daysFromNow: 5, assignee: "Support" },
{ title: "30-day check-in", daysFromNow: 30, assignee: "CS" }
];
return { tasks };
};
```
Используйте **Iterator** для создания каждой задачи из массива.
## Идеи для настройки
### Держите другие инструменты в актуальном состоянии
* Создайте клиента в биллинговой системе с помощью **HTTP Request**
### Условные действия
Используйте действия **Фильтр**, чтобы:
* Разный онбординг для Enterprise и SMB
* Разные исполнители в зависимости от региона
* Пропускать уведомления для небольших сделок
### Включить информацию о сделке
Используйте действие **Code** для форматирования:
* Документы с краткой сводкой по сделке
* Заметки для передачи команде Customer Success
* Пользовательские контрольные списки онбординга
## Связанные материалы
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)
* [Отправка писем из рабочих процессов](/l/ru/user-guide/workflows/capabilities/send-emails-from-workflows)

View File

@@ -0,0 +1,136 @@
---
title: Выявление застойных возможностей
description: Автоматически уведомляйте менеджеров, когда возможности не обновлялись.
---
Поддерживайте воронку продаж в порядке, уведомляя менеджеров, когда возможности застаиваются. Этот рабочий процесс проверяет возможности, которые не обновлялись в течение указанного числа дней.
## Проблема
Возможности без обновлений приводят к:
* Остывающие сделки
* Ненадежные прогнозы
* Потеря выручки
## Решение
Создайте запланированный рабочий процесс, который находит застойные возможности и отправляет их менеджерам электронные письма.
## Пошаговая настройка
### Шаг 1: Создайте рабочий процесс
1. Перейдите в **Настройки → Рабочие процессы**
2. Нажмите **+ Новый рабочий процесс**
3. Назовите его «Оповещение о застойных возможностях»
### Шаг 2: Настройте триггер
1. Выберите **On a Schedule**
2. Установите ежедневный запуск (например, каждый день в 8:00)
### Шаг 3: Поиск застойных возможностей
1. Добавьте действие **Search Records**
2. Настроить:
| Поле | Значение |
| ---------- | ------------------------------------------ |
| **Объект** | Возможности |
| **Фильтр** | Updated At раньше, чем (сегодня - 7 дней) |
| **Фильтр** | Stage не "Closed Won" AND не "Closed Lost" |
| **Лимит** | 100 |
### Шаг 4: Проверьте, есть ли найденные записи
1. Добавьте действие **Фильтр**
2. Условие: `{{searchRecords.length}}` больше 0
3. Если застойных возможностей нет, рабочий процесс останавливается здесь
### Шаг 5: Сформатируйте оповещение (действие Code)
Добавьте действие **Code** для форматирования письма:
```javascript
export const main = async (params) => {
const opportunities = params.opportunities;
// Group opportunities by owner
const byOwner = {};
opportunities.forEach(opp => {
const ownerEmail = opp.owner?.email || 'unassigned';
if (!byOwner[ownerEmail]) {
byOwner[ownerEmail] = [];
}
byOwner[ownerEmail].push({
name: opp.name,
amount: opp.amount,
lastUpdated: opp.updatedAt,
stage: opp.stage
});
});
// Format summary for manager
let summary = "Stale Opportunities Report\n\n";
Object.entries(byOwner).forEach(([owner, opps]) => {
summary += `${owner}: ${opps.length} stale opportunities\n`;
opps.forEach(opp => {
summary += ` - ${opp.name} (${opp.stage})\n`;
});
summary += "\n";
});
return {
summary,
totalCount: opportunities.length
};
};
```
### Шаг 6: Отправьте письмо с оповещением
Добавьте действие **Send Email**:
| Поле | Значение |
| --------- | ---------------------------------------------------------------- |
| **Кому** | sales-manager@yourcompany.com |
| **Тема** | `🚨 {{code.totalCount}} застойных возможностей требуют внимания` |
| **Текст** | `{{code.summary}}` |
### Шаг 7: Протестируйте и активируйте
1. Нажмите **Test** для запуска рабочего процесса
2. Проверьте, что письмо содержит корректные данные
3. Активируйте, когда будете готовы
## Варианты настройки
### Измените порог застоя
Измените фильтр Search Records, чтобы заменить 7 дней на предпочитаемый вами период:
* 3 дня для быстрых продаж
* 14 дней для корпоративных сделок
* 30 дней для длинных циклов продаж
### Оповещайте отдельных менеджеров по продажам
Вместо одного письма менеджеру используйте **Iterator** для отправки персонализированных писем каждому менеджеру по продажам о его застойных сделках.
### Добавьте эскалацию
Создайте несколько рабочих процессов с возрастающим уровнем серьезности:
1. День 7: письмо менеджеру по продажам
2. День 14: письмо менеджеру по продажам и руководителю
3. День 21: создать задачу для руководителя о необходимости вмешаться
### Добавьте в Slack
Используйте **HTTP Request**, чтобы отправлять сообщения в Slack через вебхук вместо электронной почты или в дополнение к ней.
## Связанные материалы
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)
* [Отправка писем из рабочих процессов](/l/ru/user-guide/workflows/capabilities/send-emails-from-workflows)

View File

@@ -0,0 +1,69 @@
---
title: Отображение количества полученных электронных писем
description: Создайте рабочий процесс для автоматического подсчета и отображения количества электронных писем, полученных от каждого контакта.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<img src="/images/user-guide/workflows/workflow-number-of-emails-received.png" style={{width:'100%'}} />
## Обзор
Этот рабочий процесс срабатывает каждый раз при получении нового электронного письма и обновляет пользовательское поле в записи контакта значением общего количества писем от этого отправителя.
## Требования
Прежде чем настраивать этот рабочий процесс, создайте пользовательское поле в объекте **Контакты**:
1. Перейдите в **Настройки → Модель данных → Контакты**
2. Добавьте новое поле **Число**
3. Назовите его, например, "Количество писем, полученных от этого контакта"
## Пошаговая настройка
<VimeoEmbed videoId="1148700336" title="Видеодемонстрация" />
### Шаг 1: Настройте триггер
1. Перейдите в **Рабочие процессы** и создайте новый рабочий процесс
2. Выберите **Запись создана** в качестве триггера
3. Выберите **Участники сообщений** (доступно в разделе «Дополнительные объекты»)
<Note>
Участник сообщения — это комбинация идентификатора сообщения и идентификатора контакта, создающая одну уникальную запись на каждое сообщение. Это проще отслеживать, чем работать напрямую с объектом Messages, потому что мы можем получить доступ к полю `handle`, которое содержит адрес электронной почты отправителя (или получателя).
</Note>
### Шаг 2: Фильтр по роли
1. Добавьте действие **Фильтр**
2. Задайте условие: **Role** равно **FROM**
Это гарантирует, что вы считаете только сообщения, отправленные этим контактом, а не сообщения, отправленные ему.
### Шаг 3: Поиск всех участников сообщений с тем же handle
1. Добавьте действие **Search Records**
2. Выберите **Участники сообщений** в качестве объекта
3. Добавьте фильтры: **Handle** равен значению handle из триггера (адрес электронной почты отправителя) и **Role** равно **FROM**
4. Увеличьте **Limit** с 1 до **200** (максимум)
Это позволит найти все письма с этого адреса электронной почты, чтобы получить общее количество.
<Note>
Действие Search Records ограничено возвратом максимум 200 записей. Однако, поскольку вы используете только значение `totalCount` (а не отдельные записи), этот шаг вернет общее количество писем, отправленных этим контактом.
</Note>
### Шаг 4: Обновите запись контакта с помощью действия Create or Update Record
1. Добавьте действие **Create or Update Record**
2. Выберите **Контакты** в качестве объекта
3. Найдите контакт, сопоставив его адрес электронной почты со значением `handle` из участника сообщения
4. Установите значение вашего пользовательского поля "Количество полученных писем" равным `{{searchRecords.totalCount}}`
Значение `totalCount` из действия Search Records представляет общее количество писем, полученных от этого контакта.
## Связанные материалы
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)
* [Как создать пользовательские поля](/l/ru/user-guide/data-model/how-tos/customize-your-data-model)
* [Действие Search Records](/l/ru/user-guide/workflows/capabilities/workflow-actions#search-records)

View File

@@ -0,0 +1,170 @@
---
title: Отображение данных связанных записей
description: Показывайте данные из связанных записей (например, информацию о компании в сделках) с помощью рабочих процессов.
---
Отображайте данные из связанных записей прямо в ваших записях — например, показывайте численность сотрудников из компании в её сделках. Этот обходной путь с рабочим процессом полезен, пока вложенные поля не поддерживаются нативно.
## Типичные случаи использования
| Источник | Назначение | Поля для копирования |
| ----------- | ----------- | -------------------------------------------------------- |
| Компания | Возможность | Отрасль, размер компании, ARR |
| Контакт | Возможность | Email, телефон, должность |
| Возможность | Компания | Сумма последней сделки, дата последней выигранной сделки |
## Базовое копирование полей
### Пример: копирование Email контакта в сделку
**Цель**: При указании контактного лица в сделке скопируйте его email в сделку для быстрого доступа.
### Предварительное условие
Создайте целевые поля в **Settings → Data Model → Opportunities** перед созданием рабочего процесса:
* Email контакта (тип: Email)
* Телефон контакта (тип: Phone)
### Настройка
1. **Триггер**: Запись обновлена (Сделки, поле Контактное лицо)
2. **Фильтр**: Проверьте, что поле Контактное лицо не пустое
3. **Поиск записей**: найдите связанного контакта
* Объект: Контакты
* Фильтр: ID равен `{{trigger.object.pointOfContact.id}}`
4. **Обновление записи**:
* Объект: Сделки
* Запись: `{{trigger.object.id}}`
* Email контакта: `{{searchRecords[0].email}}`
* Телефон контакта: `{{searchRecords[0].phone}}`
## Копирование нескольких полей
### Пример: синхронизация данных компании со всеми связанными сделками
**Цель**: Когда данные компании меняются, обновляйте все связанные сделки.
### Настройка
1. **Триггер**: Запись обновлена (Компании)
* Поля: отрасль, размер компании, годовой доход
2. **Поиск записей**: найдите все сделки для этой компании
* Объект: Сделки
* Фильтр: ID компании равен `{{trigger.object.id}}`
3. **Итератор**: цикл по каждой сделке
4. **Обновление записи** (внутри итератора):
* Объект: Сделки
* Запись: `{{iterator.currentItem.id}}`
* Отрасль компании: `{{trigger.object.industry}}`
* Размер компании: `{{trigger.object.companySize}}`
* ARR компании: `{{trigger.object.annualRevenue}}`
## Копирование при создании записи
### Пример: предзаполнение сделки данными компании
**Цель**: При создании сделки, связанной с компанией, автоматически копируйте ключевую информацию о компании.
### Предварительное условие
Создайте целевые поля в **Settings → Data Model → Opportunities**:
* Отрасль компании (тип: Text)
* Размер компании (тип: Number)
### Настройка
1. **Триггер**: Запись создана (Сделки)
* Фильтр: поле Компания не пустое
2. **Поиск записей**: получите сведения о связанной компании
* Объект: Компании
* Фильтр: ID равен `{{trigger.object.company.id}}`
3. **Обновление записи**:
* Объект: Сделки
* Запись: `{{trigger.object.id}}`
* Отрасль компании: `{{searchRecords[0].industry}}`
* Размер компании: `{{searchRecords[0].employees}}`
<Note>
**Ограничение для задач и заметок**: связи в задачах и заметках жёстко заданы как многие-ко-многим и пока недоступны в триггерах или действиях рабочего процесса. Чтобы получить доступ к этим связям, вместо этого используйте [API](/l/ru/developers/extend/api).
</Note>
## Двунаправленная синхронизация
### Пример: синхронизация основного контакта
**Цель**: Когда в компании меняется основной контакт, обновляйте контакт. Когда контакт становится основным, обновляйте компанию.
### Рабочий процесс 1: Компания → Контакт
1. **Триггер**: Запись обновлена (Компании, поле Основной контакт)
2. **Обновление записи**: установите у контакта "Is Primary Contact" в значение true
3. **Поиск записей**: найдите предыдущего основного контакта
4. **Обновление записи**: установите у предыдущего контакта "Is Primary Contact" в значение false
### Рабочий процесс 2: Контакт → Компания
1. **Триггер**: Запись обновлена (Контакты, Is Primary Contact = true)
2. **Обновление записи**: установите у компании поле Primary Contact на этого контакта
<Note>
Осторожнее с двунаправленными синхронизациями, чтобы избежать бесконечных циклов. Используйте фильтры, чтобы проверять, действительно ли значение изменилось, прежде чем обновлять.
</Note>
## Использование кода для сложного сопоставления
### Пример: преобразование данных при копировании
**Цель**: Скопировать и отформатировать номер телефона из контакта в сделку.
```javascript
export const main = async (params) => {
const { phone } = params;
if (!phone) return { formattedPhone: null };
// Remove non-numeric characters
const digits = phone.replace(/\D/g, '');
// Format as (XXX) XXX-XXXX
const formatted = digits.length === 10
? `(${digits.slice(0,3)}) ${digits.slice(3,6)}-${digits.slice(6)}`
: phone;
return { formattedPhone: formatted };
};
```
## Лучшие практики
### Избегайте циклов
* Не создавайте рабочие процессы, которые бесконечно запускают друг друга
* Используйте конкретные условия для полей
* Добавляйте проверки, что значение действительно изменилось
### Обработка отсутствующих данных
* Всегда проверяйте, что исходная запись существует, перед копированием
* Задавайте значения по умолчанию для необязательных полей
* Используйте фильтры, чтобы пропускать случаи, когда исходное поле пусто
### Производительность
* Выполняйте пакетные обновления при копировании во множество записей
* Используйте запланированные рабочие процессы для массовых операций синхронизации
* Рассмотрите использование Итератора для обновления множества записей
## Связанные материалы
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)
* [Триггеры рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-triggers)

View File

@@ -0,0 +1,202 @@
---
title: Формульные поля
description: Создавайте формульные поля с помощью рабочих процессов, пока не появится встроенная поддержка.
---
В Twenty пока нет собственной поддержки формульных полей (планируется в 2026), но того же результата можно добиться с помощью рабочих процессов. Это обходное решение позволяет автоматически вычислять и заполнять значения полей — от простых конкатенаций до сложной бизнес-логики.
## Типичные случаи использования
| Сценарий использования | Пример формулы |
| ---------------------- | ------------------------------------ |
| **Полное имя** | Имя + " " + Фамилия |
| **Ожидаемая сумма** | Сумма × Вероятность |
| **Дней до срока** | Срок - Сегодня |
| **Дней на этапе** | Сегодня - Дата входа в этап |
| **Оценка лида** | Баллы на основе нескольких критериев |
<Tip>
Полный пример отслеживания времени на этапах воронки см. в разделе [Как отслеживать, сколько времени возможности находятся на каждом этапе](/l/ru/user-guide/views-pipelines/how-tos/track-time-in-stage).
</Tip>
## Базовая формула: конкатенация
### Пример: автозаполнение полного имени
**Цель**: автоматически объединять имя и фамилию в поле полного имени.
### Настройка
1. **Триггер**: запись обновлена или создана (Люди)
2. **Фильтр**: проверьте, что изменились имя или фамилия
3. **Действие кода**:
```javascript
export const main = async (params) => {
const { firstName, lastName } = params;
const fullName = [firstName, lastName]
.filter(Boolean)
.join(' ');
return { fullName };
};
```
4. **Обновить запись**: установить Полное имя равным `{{code.fullName}}`
## Числовая формула: ожидаемая сумма
### Пример: рассчитать ожидаемую выручку
**Цель**: умножить сумму возможности на вероятность, чтобы получить ожидаемую сумму.
Полный рабочий процесс см. в разделе [Как показать ожидаемую сумму в воронке](/l/ru/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline).
### Настройка
1. **Триггер**: запись обновлена (Возможности, поле Сумма ИЛИ Вероятность)
2. **Действие кода**:
```javascript
export const main = async (params) => {
const { amount, probability } = params;
const expectedAmount = (amount || 0) * (probability || 0) / 100;
return { expectedAmount };
};
```
3. **Обновить запись**: установить Ожидаемую сумму равной `{{code.expectedAmount}}`
## Формула даты: вычисление дней
### Пример: дни до срока задачи
**Цель**: вычислить, сколько дней осталось до срока задачи.
### Настройка
1. **Триггер**: запись обновлена или создана (Задачи, поле Срок)
2. **Действие кода**:
```javascript
export const main = async (params) => {
const { dueDate } = params;
if (!dueDate) {
return { daysUntilDue: null };
}
const due = new Date(dueDate);
const today = new Date();
const diffTime = due - today;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
return { daysUntilDue: diffDays };
};
```
3. **Обновить запись**: установить поле «Дни до срока» равным `{{code.daysUntilDue}}`
<Note>
Отрицательные значения означают просроченные задачи. Это поле можно использовать для фильтрации или сортировки задач по срочности.
</Note>
## Условная формула: оценка лида
### Пример: расчет оценки лида на основе критериев
**Цель**: оценивать лидов на основе размера компании, отрасли и вовлеченности.
### Настройка
1. **Триггер**: запись обновлена (Люди или Компании)
2. **Действие кода**:
```javascript
export const main = async (params) => {
const { companySize, industry, hasEmail, hasPhone, source } = params;
let score = 0;
// Company size scoring
if (companySize === 'Enterprise') score += 30;
else if (companySize === 'Mid-Market') score += 20;
else if (companySize === 'SMB') score += 10;
// Industry scoring
const targetIndustries = ['Technology', 'Finance', 'Healthcare'];
if (targetIndustries.includes(industry)) score += 25;
// Contact info scoring
if (hasEmail) score += 10;
if (hasPhone) score += 15;
// Source scoring
if (source === 'Referral') score += 20;
else if (source === 'Website') score += 10;
return { leadScore: score };
};
```
3. **Обновить запись**: установить Оценку лида равной `{{code.leadScore}}`
## Текстовая формула: извлечение домена
### Пример: извлечение домена из email
**Цель**: автоматически извлечь и сохранить домен email.
### Настройка
1. **Триггер**: запись обновлена (Люди, поле Email)
2. **Действие кода**:
```javascript
export const main = async (params) => {
const { email } = params;
if (!email) return { domain: null };
const domain = email.split('@')[1]?.toLowerCase();
return { domain };
};
```
3. **Обновить запись**: установить поле Домен равным `{{code.domain}}`
## Лучшие практики
### Производительность
* Запускайте только при изменении релевантных полей
* Используйте фильтры, чтобы пропускать записи, которым не требуется вычисление
* Избегайте сложных вычислений в высоконагруженных рабочих процессах
### Обработка ошибок
* Проверяйте значения null/undefined перед вычислениями
* Используйте значения по умолчанию при отсутствии данных
* Возвращайте понятные сообщения об ошибках при сбое вычислений
### Тестирование
* Тестируйте на крайних случаях (пустые поля, нулевые значения)
* Проверьте вычисления вручную перед активацией
* Отслеживайте выполнения рабочих процессов на предмет неожиданных результатов
## Связанные материалы
* [Как показать ожидаемую сумму в воронке](/l/ru/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline)
* [Как отслеживать время на этапе](/l/ru/user-guide/views-pipelines/how-tos/track-time-in-stage)
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)

View File

@@ -0,0 +1,127 @@
---
title: Уведомление коллег о заметке для проверки
description: Отправляйте автоматические уведомления по email при назначении коллеги на проверку заметки.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<Note>
**Прямое упоминание коллег в заметках пока не поддерживается.** Эта функция запланирована на вторую половину 2026 года. Тем временем этот рабочий процесс предоставляет практичный обходной путь.
</Note>
## Проблема
Вы хотите упомянуть коллегу в заметке, чтобы коллеге пришло уведомление о необходимости её проверить. На данный момент Twenty не поддерживает @упоминания в заметках или задачах.
## Обходной путь
Вместо упоминания в тексте заметки вы можете:
1. Создайте поле связи **Reviewer** в объекте «Заметки»
2. Назначайте проверяющего при создании или редактировании заметки
3. Привлечь рабочий процесс автоматически отправить письмо рецензенту со ссылкой на заметку
<Warning>
**Ограничение**: этот подход позволяет назначать по одному проверяющему на поле. Если нужно уведомить нескольких людей, создайте дополнительные поля связи (например, Reviewer 1, Reviewer 2). Это плохо масштабируется, если нужно упомянуть сразу многих участников.
</Warning>
## Пошаговая настройка
### Шаг 1: Создайте поле Reviewer
<img src="/images/user-guide/workflows/reviewer-field-for-note.png" style={{width:'100%'}} />
1. Перейдите в **Настройки → Модель данных → Заметки**
2. Нажмите **Добавить поле**
3. Настроить:
| Настройка | Значение |
| -------------------- | --------------------------------------------------- |
| **Название** | Reviewer |
| **Тип** | Связь |
| **Связанный объект** | Участник рабочего пространства |
| **Тип связи** | Много заметок → Один участник рабочего пространства |
4. Сохраните поле
### Шаг 2: Создайте рабочий процесс
<VimeoEmbed videoId="1151897168" title="Видеодемонстрация" />
#### Создайте триггер рабочего процесса
1. Перейдите в основной вид и создайте новый рабочий процесс
2. Выберите **Record Updated** в качестве триггера
3. Выберите **Note** в качестве объекта и укажите поле Reviewer.
4. Это сработает каждый раз, когда у заметки обновляется проверяющий.
#### Добавьте фильтр, чтобы проверить, что проверяющий только что назначен
Добавьте действие **Фильтр**, чтобы рабочий процесс продолжался только когда назначен проверяющий:
| Условие | Значение |
| ------------ | -------- |
| **Reviewer** | не пусто |
#### Найдите участника рабочего пространства
1. Добавьте действие **Search Records**
2. Выберите **Workspace Member** (в разделе расширенных объектов)
3. Добавьте фильтр:
| Условие | Значение |
| ------- | --------------------------- |
| **ID** | равно `{{note.reviewerId}}` |
Это извлечёт полную запись участника рабочего пространства, включая адрес email и имя.
#### Отправьте уведомление по email
1. Добавьте действие **Send Email**
2. Настроить:
| Поле | Значение |
| --------- | --------------------------------------------------- |
| **Кому** | Search Record → First Workspace Member → User Email |
| **Тема** | Заметка требует вашей проверки |
| **Текст** | См. пример ниже |
**Пример текста письма:**
```text
Hi {{searchRecord.firstWorkspaceMember.name.firstName}},
You've been assigned to review a note.
View the note here:
https://yourSubDomain.twenty.com/object/note/{{recordIsUpdated.id}}
Best,
Twenty
```
<Note>
Замените `yourSubDomain` на фактический поддомен рабочего пространства Twenty.
</Note>
#### Протестируйте и активируйте
1. Нажмите **Test**, чтобы запустить рабочий процесс вручную
2. Создайте или отредактируйте заметку и назначьте проверяющего
3. Проверьте почтовый ящик проверяющего на письмо-уведомление
4. Активируйте рабочий процесс
## Использование
После настройки ваша команда сможет использовать этот рабочий процесс, выполняя следующее:
1. Создание новой заметки (или редактирование существующей)
2. Выбор коллеги в поле **Reviewer**
3. Коллега автоматически получает письмо с прямой ссылкой на заметку
## Связанные материалы
* [Триггеры рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-triggers)
* [Отправка писем из рабочих процессов](/l/ru/user-guide/workflows/capabilities/send-emails-from-workflows)
* [Как создать пользовательские поля](/l/ru/user-guide/data-model/how-tos/create-custom-fields)
* [Создание полей связи](/l/ru/user-guide/data-model/how-tos/create-relation-fields)

View File

@@ -0,0 +1,106 @@
---
title: Отправка уведомлений по электронной почте о задачах со сроком выполнения
description: Автоматически уведомляйте членов команды об их предстоящих или просроченных задачах.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<img src="/images/user-guide/workflows/email_tasks_due.png" style={{width:'100%'}} />
Отправляйте ежедневные напоминания по электронной почте каждому члену команды о задачах, срок выполнения которых сегодня.
## Обзор
Этот рабочий процесс запускается по расписанию и:
1. Получает всех участников рабочего пространства
2. Последовательно обрабатывает каждого участника
3. Находит их задачи, срок выполнения которых сегодня
4. Форматирует и отправляет персонализированное письмо
## Пошаговая настройка
<VimeoEmbed videoId="1147463514" title="Видеодемонстрация" />
### Шаг 1: Настройте триггер
1. Перейдите в **Настройки → Рабочие процессы** и создайте новый рабочий процесс
2. Выберите **On a Schedule** в качестве триггера
3. Используйте выражение cron для ежедневного запуска в 8:00: `0 8 * * *`
### Шаг 2: Поиск всех участников рабочего пространства
1. Добавьте действие **Search Records**
2. Выберите **Workspace Members** (в разделе расширенных объектов)
3. Фильтры не требуются — это вернет всех участников
### Шаг 3: Добавьте итератор
1. Добавьте действие **Iterator**
2. Укажите в качестве входного массива участников рабочего пространства из предыдущего шага
3. Все действия внутри итератора выполнятся один раз для каждого участника
### Шаг 4: Поиск задач со сроком на сегодня (внутри итератора)
1. Внутри итератора добавьте действие **Search Records**
2. Выберите **Задачи** в качестве объекта
3. Добавьте фильтры:
* **Assignee** = текущий участник рабочего пространства (из итератора)
* **Due Date** = сегодня
### Шаг 5: Отформатируйте задачи в текст письма (внутри итератора)
Добавьте действие **Code**, чтобы отформатировать задачи в удобочитаемый список со ссылками:
```javascript
export const main = async (params: {
tasksDue?: Array<{ id: string; title: string }> | null | string;
}) => {
const tasksDue =
typeof params.tasksDue === "string"
? JSON.parse(params.tasksDue)
: params.tasksDue;
if (!Array.isArray(tasksDue) || tasksDue.length === 0) {
return {
formattedTasks: "No tasks due today."
};
}
const formattedTasks = tasksDue
.map(
t =>
`${t.title}\nhttps://yourSubDomain.twenty.com/object/task/${t.id}`
)
.join("\n\n");
return { formattedTasks };
};
```
<Note>
Замените `yourSubDomain` на фактический поддомен рабочего пространства Twenty.
</Note>
### Шаг 6: Отправьте письмо (внутри итератора)
1. Добавьте действие **Send Email** (по-прежнему внутри итератора)
2. Настроить:
| Поле | Значение |
| --------- | ---------------------------------------------------------------------------------------- |
| **Кому** | `{{iterator.currentItem.userEmail}}` (электронная почта участника рабочего пространства) |
| **Тема** | Ваши задачи на сегодня |
| **Текст** | `{{code.formattedTasks}}` |
### Шаг 7: Протестируйте и активируйте
1. Нажмите **Test**, чтобы запустить рабочий процесс вручную
2. Проверьте входящие на наличие писем
3. Активируйте рабочий процесс
## Связанные материалы
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)
* [Отправка писем из рабочих процессов](/l/ru/user-guide/workflows/capabilities/send-emails-from-workflows)
* [Обработка массивов в действиях Code](/l/ru/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions)

View File

@@ -0,0 +1,21 @@
---
title: Профессиональные услуги
description: Получите профессиональную помощь в создании сложных рабочих процессов и автоматизации от команды Twenty и сертифицированных партнеров.
---
## Когда вам нужна профессиональная помощь?
Рассмотрите профессиональные услуги для:
* Сложные интеграции с несколькими системами
* Продвинутая бизнес-логика и правила автоматизации
* Рабочие процессы массовой обработки данных
* Разработка пользовательского API
* Обучение команды и оптимизация рабочих процессов
* Когда у вас нет внутренних ресурсов
## Варианты услуг
### Партнеры по внедрению
Работайте с сертифицированными партнерами для расширенных настроек. Свяжитесь с нами по адресу [contact@twenty.com](mailto:contact@twenty.com), чтобы связаться с нашими [партнерами по внедрению](https://twenty.com/partners).

View File

@@ -0,0 +1,170 @@
---
title: Устранение неполадок рабочего процесса
description: Распространённые проблемы с рабочими процессами и способы их решения.
---
## Распространенные проблемы и решения
### Рабочий процесс не запускается
**Симптомы**: Ваш рабочий процесс не запускается, когда вы этого ожидаете.
**Возможные причины**:
1. **Рабочий процесс не активирован**: Убедитесь, что для рабочего процесса установлен статус "Active", а не "Draft"
2. **Условия триггера не выполняются**: Убедитесь, что триггер соответствует ожидаемому событию
3. **Поле не отслеживается**: Для триггеров "Record is Updated" убедитесь, что нужное поле отслеживается
4. **Разрешения**: Проверьте, есть ли у вас разрешение на запуск рабочих процессов
**Решения**:
* Проверьте статус рабочего процесса в списке рабочих процессов
* Протестируйте с конкретным действием, которое должно его запустить
* Проверьте конфигурацию триггера
* Свяжитесь с администратором по поводу разрешений
### Триггер рабочего процесса срабатывает слишком рано (пустые поля)
**Симптомы**: При ручном создании записи в интерфейсе рабочий процесс срабатывает до того, как вы успеете заполнить все поля. Рабочий процесс запускается с преимущественно пустыми значениями полей.
**Почему это происходит**: Twenty сохраняет всё в режиме реального времени — нет раздельных режимов "edit" и "read". Когда вы создаёте запись, она сохраняется сразу, что вызывает событие "Record is created" до того, как вы успеете заполнить дополнительные поля.
**Когда "Record is created" работает хорошо**:
* Записи, созданные через вызовы API (поля заполняются одним запросом)
* Записи, созданные через импорт
* Автоматическое создание записей из других рабочих процессов
**Решение**: Для записей, созданных вручную в интерфейсе, используйте в качестве триггера **"Record is created or updated"**. Таким образом:
* Рабочий процесс запускается после того, как пользователь завершил заполнение и сохранение полей
* Вы получаете полные данные, а не пустые значения
<Tip>
Если вы хотите, чтобы рабочий процесс запускался только один раз для каждой записи, добавьте действие Filter, чтобы проверить поле, например `createdAt equals updatedAt` (первое сохранение), или используйте пользовательское поле-флажок, чтобы отслеживать, запускался ли рабочий процесс ранее.
</Tip>
### Сбои действий
**Симптомы**: Рабочий процесс запускается, но некоторые действия завершаются с ошибкой.
**Возможные причины**:
1. **Отсутствующие данные**: Обязательные поля пустые
2. **Недопустимые ссылки**: Переменные из предыдущих шагов отсутствуют
3. **Ошибки API**: Внешние сервисы возвращают ошибки
4. **Проблемы с разрешениями**: Действие требует разрешений, которых у вас нет
**Решения**:
* Проверьте сведения о запуске рабочего процесса на наличие сообщений об ошибках
* Убедитесь, что все обязательные поля заполнены
* Протестируйте соединения с API отдельно
* Проверьте разрешения ролей
### Ошибки HTTP-запросов
**Симптомы**: Действия HTTP Request завершаются с ошибкой или возвращают неожиданные результаты.
**Распространённые коды ошибок**:
* **400**: Некорректный запрос — проверьте формат тела запроса
* **401**: Неавторизовано — проверьте ключ API
* **403**: Запрещено — проверьте разрешения API
* **404**: Не найдено — проверьте URL конечной точки
* **429**: Слишком много запросов — реализуйте ограничение частоты
* **500**: Ошибка сервера — проблема внешнего сервиса
**Решения**:
* Проверьте URL конечной точки API
* Проверьте заголовки аутентификации
* Сначала протестируйте вызов API вне Twenty
* Добавьте обработку ошибок в действиях Code
### Ошибки действий Code
**Симптомы**: Код JavaScript не выполняется.
**Распространённые проблемы**:
1. **Синтаксические ошибки**: Опечатки или недопустимый JavaScript
2. **Неопределённые переменные**: Обращение к переменным, которые не существуют
3. **Ошибки типов**: Операции с некорректными типами данных
4. **Тайм-ауты**: Код выполняется слишком долго
**Решения**:
* Используйте проверку в встроенном редакторе кода
* Сначала протестируйте логику кода в консоли JavaScript
* Добавьте операторы console.log для отладки
* Упростите сложные операции
### Письма не отправляются
**Симптомы**: Действие Send Email не доставляет письма.
**Возможные причины**:
1. **Не подключена учетная запись электронной почты**: Проверьте Settings → Accounts
2. **Недопустимый адрес электронной почты**: Адрес электронной почты получателя имеет неверный формат
3. **Ограничения на отправку**: Достигнуты лимиты скорости у провайдера электронной почты
4. **Спам-фильтры**: Письма блокируются
**Решения**:
* Проверьте подключение учетной записи электронной почты
* Проверьте адреса электронной почты получателей
* Проверьте лимиты провайдера электронной почты
* Проверьте содержимое письма на наличие признаков спама
## Отладка рабочих процессов
### Использование запусков рабочего процесса
1. Перейдите в редактор рабочего процесса
2. Откройте панель **Runs**
3. Найдите запуск со сбоем
4. Нажмите, чтобы увидеть подробности по шагам
5. Просмотрите сообщения об ошибках и выходные данные
### Тестирование отдельных шагов
1. Для действий Code используйте кнопку **Test**
2. Для HTTP-запросов протестируйте конечную точку отдельно
3. Создайте тестовые записи, чтобы запускать рабочие процессы
4. Используйте ручные триггеры для контролируемого тестирования
### Распространённые шаблоны отладки
**Добавьте логирование**:
Используйте действия Code, чтобы записывать промежуточные значения для отладки.
**Изолируйте шаги**:
Тестируйте каждый шаг отдельно, чтобы выявить сбои.
**Проверьте поток данных**:
Убедитесь, что каждый шаг получает ожидаемые входные данные.
## Рекомендации по предотвращению проблем
### Перед активацией
* Тщательно протестируйте в режиме черновика
* Проверьте все подключения к API
* Внимательно проверьте условия триггера
* Задокументируйте ожидаемое поведение
### Во время разработки
* Используйте понятные названия шагов
* Добавьте комментарии в действиях Code
* Тестируйте с реалистичными данными
* Планируйте обработку крайних случаев
### После активации
* Внимательно отслеживайте первые запуски
* Настройте оповещения о сбоях
* Регулярно просматривайте историю запусков
* По возможности делайте рабочие процессы простыми

View File

@@ -0,0 +1,236 @@
---
title: Часто задаваемые вопросы о рабочих процессах
description: Часто задаваемые вопросы о рабочих процессах в Twenty.
---
<AccordionGroup>
<Accordion title="Почему я не могу активировать рабочий процесс?">
Скорее всего, это проблема с правами доступа. Вам нужен доступ к рабочим процессам, чтобы создавать и активировать их.
**Решение**: Свяжитесь с администратором рабочего пространства, чтобы он предоставил вам доступ к рабочим процессам в разделе **Настройки → Участники → Роли**.
Если вы совсем не видите раздел "Рабочие процессы" в боковой панели, это подтверждает, что проблема в правах доступа.
</Accordion>
<Accordion title="Почему я не вижу кнопку ручного рабочего процесса в верхней панели навигации?">
Ручные рабочие процессы отображаются в панели навигации только при правильной настройке:
1. Рабочий процесс должен быть **активирован** (не в режиме черновика)
2. Размещение в панели навигации должно быть установлено в значение **Pinned**
3. Для триггеров Single/Bulk вы должны находиться на правильной странице объекта
**Как проверить**: Откройте рабочий процесс → нажмите на триггер → убедитесь, что "Navbar placement" установлено в значение "Pinned".
Вы всегда можете получить доступ к ручным рабочим процессам через **Cmd + K** (или **Ctrl + K**) независимо от настроек панели навигации.
</Accordion>
<Accordion title="В чем разница между ручными триггерами Single, Global и Bulk?">
| Тип | Требуемые записи | Запуски рабочего процесса |
| --- | ---------------- | ------------------------- |
\| **Global** | Нет | Один раз, без входной записи |
\| **Single** | Одна или несколько выбранных | По одному разу для каждой выбранной записи |
\| **Bulk** | Одна или несколько выбранных | Один раз, со всеми записями в виде массива |
* **Global**: Используйте, когда рабочему процессу не нужен контекст записи (например, для создания отчета)
* **Single**: Используйте, когда нужно обрабатывать каждую выбранную запись независимо (например, отправлять отдельные письма)
* **Bulk**: Используйте, когда нужно обрабатывать записи вместе или оптимизировать использование кредитов (требуется действие Iterator)
См. [Триггеры рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-triggers) для подробностей.
</Accordion>
<Accordion title="Я не вижу узла If/Else. Что мне делать?">
Явный узел If/Else пока недоступен, но находится в нашем плане развития.
**Текущее решение**: Создайте несколько веток от вашего шага, каждая начинается с действия **Filter**:
```
Step 1
│
├── Branch A: Filter (condition = true) → Actions...
│
└── Branch B: Filter (condition = false) → Actions...
```
Только ветка, в которой условие фильтра выполняется, будет выполнять последующие действия.
См. пошаговое руководство [Как использовать ветки](/l/ru/user-guide/workflows/capabilities/workflow-branches).
</Accordion>
<Accordion title="Ветки рабочего процесса выполняются параллельно?">
**Да**, ветки по умолчанию выполняются параллельно.
Если вы хотите, чтобы выполнялась только одна ветка:
* Добавьте действие **Filter** в начало каждой ветки
* Задайте противоположные условия (например, Ветка A: status = "Open", Ветка B: status ≠ "Open")
Ветки, не прошедшие свое условие фильтра, прекращают выполнение, остальные продолжают.
</Accordion>
<Accordion title="Можно ли объединить ветки обратно?">
**Да**. После завершения параллельных веток вы можете добавить шаг, к которому будут подключены обе ветки.
В редакторе рабочего процесса:
1. Завершите действия в ветках
2. Добавьте новый шаг после веток
3. Перетащите соединения с конца каждой ветки к этому новому шагу
Объединенный шаг выполнится после завершения всех подключенных веток.
</Accordion>
<Accordion title="Каково ограничение на результаты Search Records?">
**Search Records возвращает максимум 200 записей.**
Если нужно обработать больше:
* Добавьте более точные фильтры, чтобы сократить результаты
* Используйте запланированные рабочие процессы для пакетной обработки
* Рассмотрите использование API для массовых операций
Для большинства рабочих процессов 200 записей достаточно. Если вы регулярно упираетесь в этот предел, подумайте о переработке вашей автоматизации.
</Accordion>
<Accordion title="Можно ли добавлять получателей в копию (CC) в письмах рабочего процесса?">
**Пока нет.** Поля CC и BCC для действия Send Email в нашем плане развития.
**Текущее решение**: Добавьте несколько действий Send Email, чтобы отправить дополнительным получателям, или используйте HTTP Request для отправки через внешний почтовый сервис, поддерживающий CC.
</Accordion>
<Accordion title="Как использовать выходные данные из предыдущих шагов?">
Каждое действие формирует выходные данные, которые можно использовать в последующих шагах.
**Как ссылаться на данные предыдущих шагов**:
* Используйте выбор переменных при настройке поля
* Или введите `{{stepName.fieldName}}` напрямую
**Примеры**:
* Данные триггера: `{{trigger.object.email}}`
* Результаты поиска: `{{searchRecords[0].name}}`
* Вывод кода: `{{code.calculatedValue}}`
Наведите курсор на любое поле в настройках действия, чтобы увидеть доступные переменные из предыдущих шагов.
</Accordion>
<Accordion title="Почему Iterator завершается с ошибкой?">
**Iterator требует массив на входе.** Частые проблемы:
1. **Входные данные не являются массивом**: Убедитесь, что вы передаете результаты из Search Records или другого действия, возвращающего массив
2. **Массив пуст**: Добавьте фильтр перед Iterator, чтобы проверить `{{searchRecords.length}} > 0`
3. **Выбрана неверная переменная**: Убедитесь, что вы выбираете сам массив, а не одну запись
**Правильная настройка**:
1. Search Records (возвращает массив)
2. Filter: length > 0
3. Iterator: выберите `{{searchRecords}}`
4. Действия внутри Iterator используют `{{iterator.currentItem.fieldName}}`
</Accordion>
<Accordion title="Каково максимальное время выполнения для действий Code?">
Действия Code (функции бизнес-логики) имеют **тайм-аут по умолчанию в 5 минут** (300 секунд).
Максимально настраиваемый тайм-аут — **15 минут** (900 секунд).
Если ваш код превышает этот предел, действие завершится ошибкой тайм-аута.
**Советы по предотвращению тайм-аутов**:
* Разбейте большие операции на небольшие части с помощью Iterator
* Избегайте тяжелых вычислений; используйте внешние сервисы через HTTP Request для ресурсоемкой обработки
* Оптимизируйте ваш код, чтобы сократить время выполнения
* Если требуется более длительная обработка, рассмотрите использование запланированных рабочих процессов, которые обрабатывают данные пакетами
</Accordion>
<Accordion title="Как понимать запуски рабочего процесса?">
Запуски рабочего процесса показывают историю выполнения и помогают диагностировать проблемы.
**Доступ к запускам**:
* В редакторе рабочего процесса → панель **Запуски** справа
* Или перейдите в **Запуски рабочего процесса** в боковой панели
**Понимание запуска**:
* **Статус**: Running, Completed, Failed, Waiting
* **Шаги**: Просматривайте, какие шаги выполнились и их вывод
* **Ошибки**: Нажмите на шаги с ошибкой, чтобы увидеть сообщения об ошибках
* **Данные**: Просматривайте входные/выходные данные на каждом шаге
См. [Запуски рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-runs) для подробностей.
</Accordion>
<Accordion title="Почему я не вижу свои запуски рабочего процесса?">
Запуски рабочего процесса могут сразу завершаться с ошибкой из-за ограничений скорости. Twenty устанавливает жесткий лимит в 5 000 запусков в час на рабочее пространство.
Если вы превышаете этот лимит, рабочие процессы сразу помечаются как завершившиеся с ошибкой и не появляются в вашем списке запусков.
**Распространенные сценарии, при которых достигается этот лимит**:
* Выбор более 5 000 записей с ручным триггером Single
* Одновременная работа нескольких рабочих процессов в вашем рабочем пространстве
* Высокочастотные автоматические триггеры (например, Record Updated на загруженном объекте)
**Решения**:
* Используйте триггеры **Bulk** вместо Single, чтобы обработать много записей за один запуск
* Разнесите во времени крупные пакетные операции
* Используйте фильтры, чтобы снизить частоту срабатываний
* Запускайте «тяжелые» рабочие процессы в часы низкой нагрузки
</Accordion>
<Accordion title="Каковы ограничения скорости для рабочих процессов?">
В Twenty есть два лимита скорости для обеспечения стабильности системы:
| Лимит | Значение | Поведение |
| ----- | -------- | --------- |
\| **Мягкий лимит** | 100 запусков/мин | Запуски ставятся в очередь со статусом "Not Started", обрабатываются постепенно |
\| **Жесткий лимит** | 5 000 запусков/час | Запуски сразу завершаются с ошибкой |
**Мягкий лимит (100/мин)**: Ваши рабочие процессы не будут завершаться с ошибкой — они просто ждут в очереди и обрабатываются со временем. Вы можете запускать более 100 записей; выполнение будет медленнее.
**Жесткий лимит (5 000/ч)**: Он применяется ко всему вашему рабочему пространству. Если суммарно все ваши рабочие процессы превышают 5 000 запусков за час, дополнительные запуски сразу завершатся с ошибкой.
**Советы, чтобы не выходить за пределы лимитов**:
* Используйте триггеры Bulk с Iterator вместо Single для больших пакетов
* Объединяйте связанные автоматизации в меньшее количество рабочих процессов
* Используйте запланированные рабочие процессы, чтобы распределить нагрузку во времени
</Accordion>
<Accordion title="Можно ли повторно запустить неудавшийся запуск рабочего процесса?">
**Нет, в настоящий момент нет функции автоматического повтора.**
Если запуск рабочего процесса завершился с ошибкой, вам нужно:
1. Просмотрите ошибку в **Настройки → Рабочие процессы → [Ваш рабочий процесс] → Запуски**
2. Исправьте проблему (данные, конфигурация или внешний сервис)
3. Повторно запустите рабочий процесс вручную для затронутой записи (записей)
**Советы по снижению количества ошибок**:
* Добавьте узлы **Filter** для проверки данных перед действиями
* Используйте **Search Records**, чтобы проверить, существуют ли связанные записи
* Тщательно протестируйте на нескольких записях перед массовыми операциями
Функция автоматического повтора находится в нашем плане развития для будущего релиза.
</Accordion>
<Accordion title="Нужно ли деактивировать рабочие процессы перед импортом CSV?">
**Да, если ваши рабочие процессы запускаются при создании или обновлении записей.**
При импорте данных через CSV каждая созданная или обновленная запись может запускать рабочие процессы. Крупный импорт (тысячи записей) может:
* Достигнуть лимита 5 000 запусков/час
* Потреблять значительное количество кредитов рабочего процесса
* Отправить неожиданные письма или уведомления
* Создать дублирующиеся задачи или записи
**Перед массовым импортом**:
1. Перейдите в **Настройки → Рабочие процессы**
2. Определите рабочие процессы, запускаемые объектом, который вы импортируете
3. **Деактивируйте** их временно
4. Выполните импорт CSV
5. **Повторно активируйте** рабочие процессы по завершении
**Альтернатива**: Если вам нужно, чтобы рабочие процессы запускались на импортируемых данных, импортируйте небольшими партиями, чтобы укладываться в лимиты скорости.
</Accordion>
<Accordion title="Как привести в порядок схему моего рабочего процесса?">
Если ваше полотно рабочего процесса выглядит беспорядочно, с разбросанными узлами, вы можете автоматически упорядочить его:
1. Щелкните правой кнопкой мыши в любом месте полотна рабочего процесса
2. Нажмите **Упорядочить рабочий процесс**
Это автоматически переупорядочит все узлы в чистое, упорядоченное расположение.
</Accordion>
</AccordionGroup>

View File

@@ -0,0 +1,81 @@
---
title: Рабочие процессы
description: Узнайте, как создавать автоматизации в Twenty.
---
## Почему важны Workflows
Twenty был создан, чтобы обеспечить максимальную гибкость для своих пользователей. Вместо того чтобы заставлять вас адаптировать бизнес-процессы под жесткие, заранее созданные функции, рабочие процессы позволяют строить автоматизации, формирующие CRM-систему, которая наилучшим образом поддерживает ваши уникальные варианты использования.
Workflows - это встроенная функция Twenty для создания этих автоматизаций. Они дают вам строительные блоки, чтобы создать именно то, что нужно вашему бизнесу, именно тогда, когда это необходимо.
## Что я могу сделать с Workflows?
Мы рекомендуем создавать автоматизации для двух основных целей:
1. **Внутренние автоматизации для облегчения повседневной работы вашей команды**: Уменьшите количество ручных записей и повторяющихся задач, которые замедляют работу вашей команды.
2. **Добавьте и извлеките данные из Twenty**: Подключите Twenty через API вызовы и вебхуки к вашей базе данных и другим инструментам.
## Создание первого рабочего процесса
### Шаг 1: Создайте новый рабочий процесс
1. Перейдите к **Рабочим процессам**, расположенным ниже других объектов
2. Нажмите **+ Новая запись**
3. Назовите рабочий процесс
### Шаг 2: Добавьте триггер
Каждый рабочий процесс начинается с триггера. Выберите один из вариантов:
* **События записей**: когда запись создается, обновляется или удаляется
* **Расписание**: запуск в определенное время (ежедневно, еженедельно и т. д.)
* **Вручную**: запускается действием пользователя
* **Вебхук**: запускается вебхуком
<img src="/images/user-guide/workflows/workflow_triggers.png" style={{width:'100%'}} />
### Шаг 3: Добавьте действия
После триггера добавьте одно или несколько действий:
* **Создать запись**: добавляет новые записи в любой объект
* **Обновить запись**: изменяет данные существующей записи
* **Удалить запись**: удаляет записи из объектов
* **Найти записи**: находит записи, соответствующие критериям
* **Создать/обновить запись**: создает или обновляет запись на основе совпадающих критериев
* **Итератор**: перебирает массивы записей
* **Фильтр**: определяет, какие записи проходят дальше
* **Задержка**: ожидает перед продолжением (длительность или запланированная дата)
* **Отправить письмо**: отправляет письма через подключенную учетную запись
* **Код**: запускает пользовательский JavaScript
* **HTTP-запрос**: выполняет вызовы внешних API
* **Форма**: получает ввод от пользователей в интерфейсе Twenty во время выполнения
* **ИИ-агент** (скоро): выполняет интеллектуальные задачи ИИ
<img src="/images/user-guide/workflows/workflow_actions.png" style={{width:'100%'}} />
### Шаг 4: Протестируйте и активируйте
1. Используйте кнопку **Test**, чтобы запустить рабочий процесс с тестовыми данными
2. Проверьте результаты, чтобы убедиться, что все работает как ожидается
3. Переведите рабочий процесс в состояние **Active**, когда будете готовы
## Лучшие практики рабочих процессов
* **Редактировать названия шагов**: переименуйте шаги рабочего процесса, чтобы четко описать, что делает каждый из них. Это поможет с поддержкой и упростит передачу коллегам
* **Использование данных предыдущих шагов**: Вы можете использовать поля из записей, возвращаемых любым предыдущим шагом в вашем рабочем процессе
* **Начинайте с простого**: Начинайте с базовых рабочих процессов и добавляйте сложность по мере того, как вы становитесь более уверенными в системе
* **Планируйте прежде, чем строить**: Составьте карту логики своего рабочего процесса перед началом построения, чтобы избежать застревания на полпути
## Следующие шаги
* [Триггеры рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-triggers)
* [Действия рабочего процесса](/l/ru/user-guide/workflows/capabilities/workflow-actions)
* [Автоматизации CRM](/l/ru/user-guide/workflows/how-tos/crm-automations/closed-won-automations)
## Нужна помощь?
<Tip>
Нужна помощь с автоматизацией ваших рабочих процессов? [Найти сертифицированного партнера](https://twenty.com/partners/list?categories=SOLUTIONING\&ref=docs-workflows) для разработки и создания автоматизации сквозного конца.
</Tip>