Автоматически создавайте или получайте PDF и прикрепляйте его к записи в Twenty. Это часто используют для создания коммерческих предложений, счетов или отчетов, связанных с компаниями, сделками или другими объектами.
Обзор
В этом рабочем процессе используется Ручной триггер, чтобы пользователи могли по запросу создавать PDF для любой выбранной записи. За обработку отвечает Логическая функция:
- Загрузка PDF по URL (из сервиса генерации PDF)
- Загрузка файла в Twenty
- Создание вложения, связанного с записью
Требования
Прежде чем настраивать рабочий процесс:
- Создайте ключ API: перейдите в Настройки → APIs и создайте новый ключ API. Этот токен понадобится для логической функции.
- Настройте сервис генерации PDF (необязательно): если вы хотите динамически генерировать PDF (например, коммерческие предложения), используйте такие сервисы, как Carbone, PDFMonkey или DocuSeal, чтобы создать PDF и получить URL для скачивания.
Пошаговая настройка
Шаг 1: Настройте триггер
- Перейдите в Рабочие процессы и создайте новый рабочий процесс
- Выберите Ручной триггер
- Выберите объект, к которому нужно прикреплять PDF (например, Company или Opportunity)
С помощью ручного триггера пользователи могут запускать этот рабочий процесс с кнопки, которая появляется в правом верхнем углу после выбора записи, чтобы сгенерировать и прикрепить PDF.
Шаг 2: Добавьте логическую функцию
- Добавьте действие Code (логическая функция)
- Создайте новую функцию с кодом ниже
- Настройте входные параметры
Входные параметры
| Параметр | Значение |
|---|
companyId | {{trigger.object.id}} |
Если прикрепляете к другому объекту (Person, Opportunity и т. д.), переименуйте параметр соответствующим образом (например, personId, opportunityId) и обновите логическую функцию.
Код логической функции
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, вы можете:
- Сначала выполните действие HTTP Request, чтобы сгенерировать PDF
- Передайте возвращенный URL PDF в логическую функцию как параметр
export const main = async (
params: { companyId: string; pdfUrl: string; filename: string },
) => {
const { companyId, pdfUrl, filename } = params;
// ... rest of the function
};
Шаг 4: Протестируйте и активируйте
- Сохраните рабочий процесс
- Перейдите к записи Company
- Нажмите меню ⋮ и выберите ваш рабочий процесс
- Проверьте раздел Attachments в записи, чтобы убедиться, что PDF прикреплен
- Активируйте рабочий процесс
Совмещение с сервисами генерации 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 действителен и имеет права на запись |
| ”Не удалось создать вложение” | Убедитесь, что имя поля идентификатора объекта соответствует целевому объекту |
Связанные материалы