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 3s

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,121 @@
---
title: Réponse automatique aux emails entrants
description: Créez un flux de travail qui utilise l'IA pour trier les emails entrants et envoyer automatiquement des réponses en fil de discussion.
---
Répondez aux emails entrants en quelques secondes — et non en heures. Ce flux de travail utilise un Agent AI pour filtrer le bruit (newsletters, spam, réponses automatiques) et rédiger une réponse personnalisée aux vrais messages. Ensuite, Amnesty International l'envoie comme une réponse filetée dans la conversation initiale.
## Comment fonctionne le fil de discussion des emails
Chaque email comporte un en-tête caché `Message-ID` — une empreinte unique attribuée par le serveur de messagerie de l'expéditeur. Lorsque vous répondez à un e-mail, votre client de messagerie définit un en-tête `In-Reply-To` faisant référence à cette empreinte. C’est ainsi que Gmail, Outlook et tous les autres clients regroupent les messages en fils de discussion.
Dans Twenty, cette empreinte est stockée en tant que `headerMessageId` sur l’objet Message. Votre flux de travail la récupère et la transmet au champ In-Reply-To de l’action Send Email.
## Création du flux de travail
### Étape 1 : Créer un nouveau flux de travail
Accédez à **Settings -> Workflows** et cliquez sur **+ New Workflow**.
### Étape 2 : Déclencher sur les messages entrants
Choisissez **When a Record is Created** puis **Messages**.
Chaque fois qu’un e-mail arrive dans Twenty, ceci se déclenche.
<img src="/images/user-guide/workflows/auto-reply/workflow-overview.png" style={{width:'100%'}} />
### Étape 3 : Identifier l’expéditeur
Ajoutez une action **Search Records**.
L’adresse de l’expéditeur n’est pas sur le message lui-même — elle se trouve sur l’enregistrement Message Participant associé.
| Champ | Valeur |
| ---------- | -------------------------------- |
| **Objet** | Participants au message |
| **Filtre** | Message **est** `{{trigger.id}}` |
| **Filtre** | Rôle **est** From |
| **Limite** | 1 |
Vous obtenez ainsi l’adresse e-mail de l’expéditeur dans `handle` et son nom dans `displayName`.
<img src="/images/user-guide/workflows/auto-reply/find-sender.png" style={{width:'100%'}} />
### Étape 4 : Triage et ébauche une réponse avec AI
Ajoutez une action **AI Agent**. Cette seule étape fait deux choses : elle détermine si l’e-mail mérite une réponse et, le cas échéant, en rédige une.
Utilisez une invite comme :
```
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.
```
L’action AI Agent produit sa réponse dans un champ `response` auquel les étapes suivantes peuvent se référer.
<img src="/images/user-guide/workflows/auto-reply/ai-triage.png" style={{width:'100%'}} />
### Étape 5 : Bifurquer selon la décision de l’IA
Ajoutez une action **If/Else** pour vérifier si l’IA a décidé de répondre ou d’ignorer.
| Champ | Valeur |
| ------------- | ---------------------------------------------- |
| **Condition** | AI Agent `response` **ne contient pas** `SKIP` |
| **Si vrai** | Continuer vers Send Email |
| **Sinon** | Ne rien faire (le flux de travail se termine) |
Les spams, les newsletters et les messages générés automatiquement sont ignorés. Tout le reste passe à l’étape suivante.
<img src="/images/user-guide/workflows/auto-reply/should-reply.png" style={{width:'100%'}} />
### Étape 6 : Envoyer une réponse dans le fil de discussion
Ajoutez une action **Envoyer un e-mail** sur la branche « si vrai ». Cliquez sur **Options avancées**, puis sur **Ajouter In-Reply-To**.
| Champ | Valeur |
| -------------------- | -------------------------------------- |
| **À** | `{{Find Sender.first.handle}}` |
| **Objet** | `Re: {{trigger.subject}}` |
| **Corps du message** | `{{AI Triage & Draft Reply.response}}` |
| **In-Reply-To** | `{{trigger.headerMessageId}}` |
C’est le champ In-Reply-To qui fait de ce message une réponse plutôt qu’une nouvelle conversation. Le destinataire le voit regroupé sous l’e-mail d’origine dans Gmail, Outlook ou tout autre client.
<img src="/images/user-guide/workflows/auto-reply/send-email.png" style={{width:'100%'}} />
<Tip>
**In-Reply-To** attend un `message.headerMessageId` provenant du déclencheur — c’est l’empreinte unique de l’e-mail, pas une adresse de destinataire. Si vous le laissez vide, l’e-mail sera quand même envoyé, simplement comme un message autonome.
</Tip>
<Warning>
Gmail utilise l’objet pour regrouper les messages en fils de discussion. L’objet **doit** commencer par `Re:` (y compris les deux-points et l’espace) pour que Gmail affiche la réponse dans le fil d’origine. Sans cela, la réponse apparaîtra comme une conversation distincte — même si l’en-tête In-Reply-To est correctement défini.
</Warning>
### Étape 7 : Tester et activer
Cliquez sur **Test**, puis vérifiez votre client de messagerie. La réponse devrait apparaître imbriquée sous le message d’origine.
Activez lorsque vous en êtes satisfait.
## Idées pour aller plus loin
* **Ne répondre qu’aux VIP** — ajoutez une branche qui vérifie le domaine de l’expéditeur ou s’il existe en tant que Contact dans Twenty
* **Orienter selon l’intention** — utilisez des invites AI Agent distinctes pour traiter différemment les demandes commerciales et les demandes d’assistance
* **Enrichir avant de répondre** — ajoutez une étape Search Records pour intégrer l’entreprise de l’expéditeur ou l’historique d’opportunités dans l’invite d’IA afin d’obtenir des réponses plus personnalisées

View File

@@ -0,0 +1,179 @@
---
title: Automatisations « Closed Won »
description: Automatisez les activités post-gain lorsque des opportunités sont clôturées.
---
Lorsqu'une affaire se clôture, plusieurs actions doivent avoir lieu : mettre à jour le statut de l'entreprise, notifier les membres de l'équipe, créer des tâches d'intégration. Automatisez tout cela avec un seul flux de travail.
## Le problème
Lorsqu'une opportunité passe à "Closed Won":
* Le type d'entreprise doit passer de "Prospect" à "Customer"
* Des tâches d'intégration doivent être créées
* L'équipe Customer Success doit être informée
* Le commercial a besoin d'une confirmation
Faire cela manuellement est chronophage et sujet aux erreurs.
## La solution
Créez un flux de travail qui gère automatiquement toutes les activités post-gain.
## Configuration complète du flux de travail
### Étape 1 : Créer le flux de travail
1. Allez dans **Paramètres → Flux de travail**
2. Cliquez sur **+ Nouveau flux de travail**
3. Nommez-le "Affaire gagnée - Automatisation post-gain"
### Étape 2 : Configurer le déclencheur
1. Sélectionnez **L'enregistrement est mis à jour**
2. Choisissez **Opportunités**
3. Dans "Champs à surveiller", sélectionnez **Étape**
### Étape 3 : Ajouter un filtre d'étape
1. Ajoutez l'action **Filtre**
2. Condition : `{{trigger.object.stage}}` est égal à "Closed Won"
### Étape 4 : Mettre à jour le type d'entreprise
1. Ajoutez l'action **Mettre à jour l'enregistrement**
2. Configurer:
| Champ | Valeur |
| ------------------------------- | ------------------------------- |
| **Objet** | Entreprises |
| **Enregistrement** | `{{trigger.object.company.id}}` |
| **Type** | Client |
| **Date de la première affaire** | `{{trigger.object.closedAt}}` |
| **Propriétaire du compte** | `{{trigger.object.owner.id}}` |
### Étape 5 : Créer une tâche d'intégration
1. Ajoutez l'action **Créer un enregistrement**
2. Configurer:
| Champ | Valeur |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| **Objet** | Tâches |
| **Titre** | `Onboarding: {{trigger.object.name}}` |
| **Assigné(e)** | Membre de l'équipe Customer Success |
| **Date d'échéance** | Dans 3 jours |
| **Priorité** | Élevée |
| **Entreprise associée** | `{{trigger.object.company.id}}` |
| **Opportunité associée** | `{{trigger.object.id}}` |
| **Description** | `Intégration d'un nouveau client pour {{trigger.object.company.name}}. Valeur de l'affaire : {{trigger.object.amount}}` |
### Étape 6 : Informer Customer Success
1. Ajoutez l'action **Envoyer un e-mail**
2. Configurer:
| Champ | Valeur |
| -------------------- | -------------------------------------------------- |
| **À** | customer-success@yourcompany.com |
| **Objet** | `🎉 New Customer: {{trigger.object.company.name}}` |
| **Corps du message** | Voir l'exemple ci-dessous |
**Exemple de corps de l'e-mail** :
```
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!
```
### Étape 7 : Confirmer au commercial
1. Ajoutez une autre action **Envoyer un e-mail**
2. Configurer:
| Champ | Valeur |
| -------------------- | ------------------------------------------------------------------------------------------------------------------ |
| **À** | `{{trigger.object.owner.email}}` |
| **Objet** | `✅ Deal Closed: {{trigger.object.name}}` |
| **Corps du message** | Félicitations ! Votre affaire a été traitée. L'équipe Customer Success a été informée et l'intégration a commencé. |
### Étape 8 : Tester et activer
1. Testez en déplaçant une opportunité de test vers "Closed Won"
2. Vérifier:
* Type d'entreprise modifié en "Customer"
* Tâche d'intégration créée
* L'équipe CS a reçu l'e-mail
* Le commercial a reçu la confirmation
3. Activez lorsque vous êtes prêt
## Gestion de "Closed Lost"
Créez un flux de travail similaire pour les affaires perdues :
### Déclencheur
* Enregistrement mis à jour (Opportunités, Étape = "Closed Lost")
### Actions
1. **Créer un enregistrement** : Tâche pour "Analyse d'affaire perdue"
2. **Mettre à jour l'enregistrement** : Ajouter la raison de perte à l'enregistrement de l'entreprise
3. **Envoyer un e-mail** : Informer le responsable de l'affaire perdue
## Avancé : intégration en plusieurs étapes
Pour une intégration complexe, créez plusieurs tâches :
```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 };
};
```
Utilisez **Iterator** pour créer chaque tâche à partir du tableau.
## Idées de personnalisation
### Maintenez vos autres outils à jour
* Créez un client dans le système de facturation avec une action **HTTP Request**
### Actions conditionnelles
Utilisez des actions **Filtre** pour :
* Intégration différente pour entreprises vs PME
* Assignés différents selon la région
* Ignorer les notifications pour les petites affaires
### Inclure les détails de la transaction
Utilisez l'action **Code** pour formater :
* Documents de synthèse de l'affaire
* Notes de passation pour l'équipe CS
* Listes de contrôle d'intégration personnalisées
## Articles connexes
* [Actions de flux de travail](/l/fr/user-guide/workflows/capabilities/workflow-actions)
* [Envoyer des e-mails depuis des flux de travail](/l/fr/user-guide/workflows/capabilities/send-emails-from-workflows)

View File

@@ -0,0 +1,136 @@
---
title: Détecter les opportunités stagnantes
description: Avertissez automatiquement les responsables lorsque les opportunités n'ont pas été mises à jour.
---
Maintenez la santé de votre pipeline en alertant les responsables lorsque des opportunités deviennent stagnantes. Ce flux de travail vérifie les opportunités qui n'ont pas été mises à jour depuis un nombre de jours défini.
## Le problème
Des opportunités laissées sans mise à jour entraînent :
* Affaires qui se refroidissent
* Prévisions peu fiables
* Perte de revenus
## La solution
Créez un flux de travail planifié qui identifie les opportunités stagnantes et envoie un e-mail à leurs responsables.
## Configuration pas à pas
### Étape 1 : Créer le flux de travail
1. Allez dans **Paramètres → Flux de travail**
2. Cliquez sur **+ Nouveau flux de travail**
3. Nommez-le "Alerte d'opportunités stagnantes"
### Étape 2 : Configurer le déclencheur
1. Sélectionnez **Selon un calendrier**
2. Définissez l'exécution quotidienne (par ex., chaque jour à 8 h)
### Étape 3 : Rechercher les opportunités stagnantes
1. Ajoutez l'action **Rechercher des enregistrements**
2. Configurer:
| Champ | Valeur |
| ---------- | ------------------------------------------------------- |
| **Objet** | Opportunités |
| **Filtre** | Updated At est antérieur à (today - 7 days) |
| **Filtre** | Stage n'est pas "Closed Won" ET n'est pas "Closed Lost" |
| **Limite** | 100 |
### Étape 4 : Vérifier si des résultats ont été trouvés
1. Ajoutez l'action **Filtre**
2. Condition : `{{searchRecords.length}}` est supérieur à 0
3. S'il n'y a pas d'opportunités stagnantes, le flux de travail s'arrête ici
### Étape 5 : Formater l'alerte (action Code)
Ajoutez une action **Code** pour formater l'e-mail :
```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
};
};
```
### Étape 6 : Envoyer l'e-mail d'alerte
Ajoutez l'action **Envoyer un e-mail** :
| Champ | Valeur |
| -------------------- | ---------------------------------------------------------------------------- |
| **À** | sales-manager@yourcompany.com |
| **Objet** | `🚨 {{code.totalCount}} opportunités stagnantes nécessitent votre attention` |
| **Corps du message** | `{{code.summary}}` |
### Étape 7 : Tester et activer
1. Cliquez sur **Test** pour exécuter le flux de travail
2. Vérifiez que l'e-mail contient les bonnes données
3. Activez lorsque vous êtes prêt
## Options de personnalisation
### Modifier le seuil de stagnation
Modifiez le filtre Rechercher des enregistrements pour changer de 7 jours à la période de votre choix :
* 3 jours pour les ventes à cadence élevée
* 14 jours pour les affaires grands comptes
* 30 jours pour les cycles de vente longs
### Alerter les commerciaux individuellement
Au lieu d'un seul e-mail au responsable, utilisez **Iterator** pour envoyer des e-mails personnalisés à chaque commercial au sujet de ses propres affaires stagnantes.
### Ajouter une escalade
Créez plusieurs flux de travail avec une sévérité croissante :
1. Jour 7 : e-mail au commercial
2. Jour 14 : e-mail au commercial + au responsable
3. Jour 21 : créer une tâche pour que le responsable intervienne
### Inclure dans Slack
Utilisez **HTTP Request** pour publier vers un webhook Slack à la place d'un e-mail ou en complément.
## Articles connexes
* [Actions de workflows](/l/fr/user-guide/workflows/capabilities/workflow-actions)
* [Envoyer des e-mails depuis des workflows](/l/fr/user-guide/workflows/capabilities/send-emails-from-workflows)

View File

@@ -0,0 +1,69 @@
---
title: Afficher le nombre d'e-mails reçus
description: Créez un workflow pour compter et afficher automatiquement le nombre d'e-mails reçus de chaque contact.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<img src="/images/user-guide/workflows/workflow-number-of-emails-received.png" style={{width:'100%'}} />
## Vue d'ensemble
Ce workflow se déclenche chaque fois qu'un nouvel e-mail est reçu et met à jour un champ personnalisé sur l'enregistrement Person avec le nombre total d'e-mails provenant de cet expéditeur.
## Prérequis
Avant de configurer ce workflow, créez un champ personnalisé sur l'objet **People** :
1. Allez dans **Paramètres → Modèle de données → People**
2. Ajoutez un nouveau champ **Nombre**
3. Nommez-le par exemple "Nombre d'e-mails reçus de cette personne"
## Configuration pas à pas
<VimeoEmbed videoId="1148700336" title="Démonstration vidéo" />
### Étape 1 : Configurer le déclencheur
1. Allez dans **Workflows** et créez un nouveau workflow
2. Sélectionnez **Record is Created** comme déclencheur
3. Choisissez **Message Participants** (disponible sous Advanced objects)
<Note>
Un Message Participant est une combinaison d'un ID de message et d'un ID de personne, créant un enregistrement unique par message. Ceci est plus facile à suivre que les Messages directement, car nous pouvons accéder au champ `handle`, qui contient l'adresse e-mail de l'expéditeur (ou du destinataire).
</Note>
### Étape 2 : Filtrer sur le rôle
1. Ajoutez l'action **Filtre**
2. Définissez la condition : **Role** est égal à **FROM**
Cela garantit que vous ne comptez que les messages envoyés par cette personne, et non les messages qui lui sont envoyés.
### Étape 3 : Rechercher tous les Message Participants avec le même handle
1. Ajoutez une action **Search Records**
2. Sélectionnez **Message Participants** comme objet
3. Ajoutez des filtres : **Handle** est égal au handle du déclencheur (l'adresse e-mail de l'expéditeur) et **Role** est égal à **FROM**
4. Augmentez **Limit** de 1 à **200** (le maximum)
Cela trouve tous les messages provenant de cette adresse e-mail afin d'obtenir le nombre total.
<Note>
L'action Search Records est limitée à retourner 200 enregistrements maximum. Cependant, comme vous utilisez uniquement la valeur `totalCount` (et non les enregistrements individuels), cette étape renverra le nombre total d'e-mails envoyés par cette personne.
</Note>
### Étape 4 : Mettre à jour l'enregistrement Person avec une action Create or Update Record
1. Ajoutez une action **Créer ou mettre à jour un enregistrement**
2. Sélectionnez **Personnes** comme objet
3. Recherchez la personne en faisant correspondre son e-mail au `handle` du participant au message
4. Définissez votre champ personnalisé "Nombre d'e-mails reçus" sur `{{searchRecords.totalCount}}`
La valeur `totalCount` de l'action Rechercher des enregistrements représente le nombre total d'e-mails reçus de cette personne.
## Articles connexes
* [Actions de workflows](/l/fr/user-guide/workflows/capabilities/workflow-actions)
* [Comment créer des champs personnalisés](/l/fr/user-guide/data-model/how-tos/customize-your-data-model)
* [Action Search Records](/l/fr/user-guide/workflows/capabilities/workflow-actions#search-records)

View File

@@ -0,0 +1,170 @@
---
title: Afficher les données des enregistrements liés
description: Afficher les données provenant d'enregistrements liés (p. ex., informations sur l'entreprise dans les Opportunités) à l'aide de workflows.
---
Afficher les données d'enregistrements liés directement sur vos enregistrements — par exemple, afficher l'effectif d'une Entreprise dans ses Opportunités. Cette solution de contournement par workflow est utile jusqu'à ce que les champs imbriqués soient disponibles nativement.
## Cas d'utilisation courants
| Source | Destination | Champs à copier |
| ----------- | ----------- | --------------------------------------------------------- |
| Entreprise | Opportunité | Secteur, Taille de l'entreprise, ARR |
| Personne | Opportunité | E-mail, Téléphone, Titre du poste |
| Opportunité | Entreprise | Montant du dernier contrat, Date du dernier contrat gagné |
## Copie de champ simple
### Exemple : Copier l'e-mail du contact vers l'Opportunité
**Objectif** : Lors de la définition d'un Point de contact sur une opportunité, copier son e-mail dans l'opportunité pour un accès facile.
### Prérequis
Créez les champs de destination dans **Paramètres → Modèle de données → Opportunités** avant de créer le workflow :
* E-mail du contact (type : E-mail)
* Téléphone du contact (type : Téléphone)
### Installation
1. **Déclencheur** : Enregistrement mis à jour (Opportunités, champ Point de contact)
2. **Filtre** : Vérifiez que le Point de contact n'est pas vide
3. **Rechercher des enregistrements** : Trouver la personne liée
* Objet : Personnes
* Filtre : ID égal à `{{trigger.object.pointOfContact.id}}`
4. **Mettre à jour un enregistrement** :
* Objet : Opportunités
* Enregistrement : `{{trigger.object.id}}`
* E-mail du contact : `{{searchRecords[0].email}}`
* Téléphone du contact : `{{searchRecords[0].phone}}`
## Copier plusieurs champs
### Exemple : Synchroniser les informations de l'entreprise vers toutes les Opportunités liées
**Objectif** : Lorsque les détails de l'entreprise changent, mettre à jour toutes les opportunités liées.
### Installation
1. **Déclencheur** : Enregistrement mis à jour (Entreprises)
* Champs : Secteur, Taille de l'entreprise, Chiffre d'affaires annuel
2. **Rechercher des enregistrements** : Trouver toutes les opportunités pour cette entreprise
* Objet : Opportunités
* Filtre : ID de l'entreprise égal à `{{trigger.object.id}}`
3. **Iterator** : Parcourir chaque opportunité
4. **Mettre à jour un enregistrement** (dans l'Iterator) :
* Objet : Opportunités
* Enregistrement : `{{iterator.currentItem.id}}`
* Secteur de l'entreprise : `{{trigger.object.industry}}`
* Taille de l'entreprise : `{{trigger.object.companySize}}`
* ARR de l'entreprise : `{{trigger.object.annualRevenue}}`
## Copier lors de la création d'un enregistrement
### Exemple : Préremplir l'Opportunité avec les données de l'entreprise
**Objectif** : Lors de la création d'une opportunité liée à une entreprise, copier automatiquement les informations clés de l'entreprise.
### Prérequis
Créez les champs de destination dans **Paramètres → Modèle de données → Opportunités** :
* Secteur de l'entreprise (type : Texte)
* Taille de l'entreprise (type : Nombre)
### Installation
1. **Déclencheur** : Enregistrement créé (Opportunités)
* Filtre : L'entreprise n'est pas vide
2. **Rechercher des enregistrements** : Récupérer les détails de l'entreprise liée
* Objet : Entreprises
* Filtre : ID égal à `{{trigger.object.company.id}}`
3. **Mettre à jour un enregistrement** :
* Objet : Opportunités
* Enregistrement : `{{trigger.object.id}}`
* Secteur de l'entreprise : `{{searchRecords[0].industry}}`
* Taille de l'entreprise : `{{searchRecords[0].employees}}`
<Note>
**Limitation concernant les tâches et les notes** : Les relations sur les tâches et les notes sont codées en dur en plusieurs-à-plusieurs et ne sont pas encore disponibles dans les déclencheurs ou actions de workflows. Pour accéder à ces relations, utilisez plutôt l'[API](/l/fr/developers/extend/api).
</Note>
## Synchronisation bidirectionnelle
### Exemple : Maintenir le contact principal synchronisé
**Objectif** : Lorsque le contact principal d'une entreprise change, mettre à jour le contact. Lorsqu'une personne devient contact principal, mettre à jour l'entreprise.
### Workflow 1 : Entreprise → Personne
1. **Déclencheur** : Enregistrement mis à jour (Entreprises, champ Contact principal)
2. **Mettre à jour un enregistrement** : Définir le champ "Is Primary Contact" de la personne sur true
3. **Rechercher des enregistrements** : Trouver le contact principal précédent
4. **Mettre à jour un enregistrement** : Définir le "Is Primary Contact" de l'ancien contact sur false
### Workflow 2 : Personne → Entreprise
1. **Déclencheur** : Enregistrement mis à jour (Personnes, Is Primary Contact = true)
2. **Mettre à jour un enregistrement** : Définir le contact principal de l'entreprise sur cette personne
<Note>
Soyez prudent avec les synchronisations bidirectionnelles pour éviter les boucles infinies. Utilisez des filtres pour vérifier si la valeur a réellement changé avant la mise à jour.
</Note>
## Utiliser du code pour des mappages complexes
### Exemple : Transformer les données lors de la copie
**Objectif** : Copier et mettre en forme le numéro de téléphone de la personne vers l'opportunité.
```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 };
};
```
## Meilleures pratiques
### Évitez les boucles
* Ne créez pas de workflows qui se déclenchent mutuellement à l'infini
* Utilisez des conditions spécifiques sur les champs
* Ajoutez des vérifications pour voir si la valeur a réellement changé
### Gérer les données manquantes
* Vérifiez toujours que l'enregistrement source existe avant de copier
* Fournissez des valeurs par défaut pour les champs facultatifs
* Utilisez des filtres pour ignorer lorsque le champ source est vide
### Performances
* Regroupez les mises à jour lors de la copie vers de nombreux enregistrements
* Utilisez des workflows planifiés pour les opérations de synchronisation en masse
* Envisagez d'utiliser Iterator pour les mises à jour de plusieurs enregistrements
## Articles connexes
* [Actions de workflows](/l/fr/user-guide/workflows/capabilities/workflow-actions)
* [Déclencheurs de workflows](/l/fr/user-guide/workflows/capabilities/workflow-triggers)

View File

@@ -0,0 +1,202 @@
---
title: Champs de formule
description: Créez des champs de formule à l'aide des workflows jusqu'à ce que la prise en charge native soit disponible.
---
Vingt ne supporte pas encore les champs de formules natives (à venir en 2026), mais vous pouvez obtenir le même résultat en utilisant les workflows. Cette solution de contournement vous permet de calculer et de renseigner automatiquement les valeurs de champs — des concaténations simples à une logique métier complexe.
## Cas d'utilisation courants
| Cas d'utilisation | Exemple de formule |
| -------------------------- | ---------------------------------------- |
| **Nom complet** | Prénom + " " + Nom de famille |
| **Montant attendu** | Montant × Probabilité |
| **Jours avant l'échéance** | Date d'échéance - Aujourd'hui |
| **Jours dans l'étape** | Aujourd'hui - Date d'entrée dans l'étape |
| **Score de lead** | Points basés sur plusieurs critères |
<Tip>
Pour un exemple complet de suivi du temps dans les étapes du pipeline, voir [Suivre la durée de présence des opportunités dans chaque étape](/l/fr/user-guide/views-pipelines/how-tos/track-time-in-stage).
</Tip>
## Formule de base : concaténation
### Exemple : remplissage automatique du nom complet
**Objectif** : Combiner automatiquement le prénom et le nom de famille dans un champ de nom complet.
### Installation
1. **Déclencheur** : Enregistrement mis à jour ou créé (Personnes)
2. **Filtre** : Vérifier que le prénom ou le nom de famille a changé
3. **Action de code** :
```javascript
export const main = async (params) => {
const { firstName, lastName } = params;
const fullName = [firstName, lastName]
.filter(Boolean)
.join(' ');
return { fullName };
};
```
4. **Mettre à jour l'enregistrement** : Définir le nom complet sur `{{code.fullName}}`
## Formule numérique : montant attendu
### Exemple : calculer le revenu attendu
**Objectif** : Multiplier le montant de l'opportunité par la probabilité pour obtenir le montant attendu.
Voir [Comment afficher le montant attendu dans le pipeline](/l/fr/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline) pour le workflow complet.
### Configuration
1. **Déclencheur** : Enregistrement mis à jour (Opportunités, champ Montant OU Probabilité)
2. **Action de code** :
```javascript
export const main = async (params) => {
const { amount, probability } = params;
const expectedAmount = (amount || 0) * (probability || 0) / 100;
return { expectedAmount };
};
```
3. **Mettre à jour l'enregistrement** : Définir le montant attendu sur `{{code.expectedAmount}}`
## Formule de date : calcul des jours
### Exemple : jours avant l'échéance de la tâche
**Objectif** : Calculer le nombre de jours restants avant la date d'échéance d'une tâche.
### Installation
1. **Déclencheur** : Enregistrement mis à jour ou créé (Tâches, champ Date d'échéance)
2. **Action de code** :
```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. **Mettre à jour l'enregistrement** : Définir les jours avant l'échéance sur `{{code.daysUntilDue}}`
<Note>
Des valeurs négatives indiquent des tâches en retard. Vous pouvez utiliser ce champ pour filtrer ou trier les tâches par urgence.
</Note>
## Formule conditionnelle : score de lead
### Exemple : calculer le score de lead en fonction de critères
**Objectif** : Noter les leads selon la taille de l'entreprise, le secteur d'activité et l'engagement.
### Installation
1. **Déclencheur** : Enregistrement mis à jour (Personnes ou Entreprises)
2. **Action de code** :
```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. **Mettre à jour l'enregistrement** : Définir le score de lead sur `{{code.leadScore}}`
## Formule texte : extraction de domaine
### Exemple : extraire le domaine à partir de l'email
**Objectif** : Extraire et stocker automatiquement le domaine de l'email.
### Installation
1. **Déclencheur** : Enregistrement mis à jour (Personnes, champ Email)
2. **Action de code** :
```javascript
export const main = async (params) => {
const { email } = params;
if (!email) return { domain: null };
const domain = email.split('@')[1]?.toLowerCase();
return { domain };
};
```
3. **Mettre à jour l'enregistrement** : Définir le champ Domaine sur `{{code.domain}}`
## Meilleures pratiques
### Performances
* Ne déclenchez que lors des modifications de champs pertinentes
* Utilisez des filtres pour ignorer les enregistrements qui n'ont pas besoin de calcul
* Évitez les calculs complexes dans les workflows à fort volume
### Gestion des erreurs
* Vérifiez les valeurs null/undefined avant les calculs
* Utilisez des valeurs par défaut lorsque des données manquent
* Renvoyez des messages d'erreur clairs lorsque les calculs échouent
### Tests
* Testez avec des cas limites (champs vides, valeurs zéro)
* Vérifiez manuellement les calculs avant l'activation
* Surveillez les exécutions de workflows pour détecter des résultats inattendus
## Articles connexes
* [Comment afficher le montant attendu dans le pipeline](/l/fr/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline)
* [Comment suivre le temps par étape](/l/fr/user-guide/views-pipelines/how-tos/track-time-in-stage)
* [Actions de workflows](/l/fr/user-guide/workflows/capabilities/workflow-actions)

View File

@@ -0,0 +1,127 @@
---
title: Notifier les collègues d'une note à relire
description: Envoyez des notifications par e-mail automatiques lorsque vous assignez un collègue pour relire une note.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<Note>
**La mention directe des collègues dans les notes n'est pas encore prise en charge.** Cette fonctionnalité est prévue pour le second semestre 2026. En attendant, ce workflow constitue une solution de contournement pratique.
</Note>
## Le défi
Vous souhaitez mentionner un collègue dans une note afin qu'il reçoive une notification pour la relire. À ce jour, Twenty ne prend pas en charge les @mentions dans les notes ou les tâches.
## La solution de contournement
Au lieu de mentionner dans le texte de la note, vous pouvez :
1. Créez un champ de relation **Relecteur** sur l'objet Notes
2. Attribuez le relecteur lors de la création ou de la modification de la note
3. Avoir un flux de travail envoyer automatiquement un e-mail au réviseur avec un lien vers la note
<Warning>
**Limitation** : Cette approche permet d'attribuer un seul relecteur par champ. Si vous devez notifier plusieurs personnes, créez des champs de relation supplémentaires (par ex. : Relecteur 1, Relecteur 2). Cela ne s'adaptera pas bien si vous devez mentionner de nombreux collaborateurs à la fois.
</Warning>
## Configuration pas à pas
### Étape 1 : Créer le champ Relecteur
<img src="/images/user-guide/workflows/reviewer-field-for-note.png" style={{width:'100%'}} />
1. Allez dans **Paramètres → Modèle de données → Notes**
2. Cliquez sur **Ajouter un champ**
3. Configurer :
| Paramètre | Valeur |
| -------------------- | -------------------------------------------------- |
| **Name** | Relecteur |
| **Type** | Relation |
| **Objet lié** | Membre de l'espace de travail |
| **Type de relation** | Plusieurs notes → Un membre de l'espace de travail |
4. Enregistrez le champ
### Étape 2 : Créer le workflow
<VimeoEmbed videoId="1151897168" title="Démonstration vidéo" />
#### Créer le déclencheur du workflow
1. Allez dans la vue principale et créez un nouveau workflow
2. Sélectionnez **Record Updated** comme déclencheur
3. Choisissez **Note** comme objet et indiquez le champ Relecteur.
4. Cela se déclenchera chaque fois que le relecteur d'une note est mis à jour.
#### Ajouter un filtre pour vérifier qu'un relecteur vient d'être attribué
Ajoutez une action **Filtre** pour garantir que le workflow ne continue que lorsqu'un relecteur est attribué :
| Condition | Valeur |
| ------------- | -------------- |
| **Relecteur** | n'est pas vide |
#### Rechercher le membre de l'espace de travail
1. Ajoutez une action **Search Records**
2. Sélectionnez **Membre de l'espace de travail** (dans les objets avancés)
3. Ajoutez un filtre :
| Condition | Valeur |
| --------- | ---------------------------- |
| **ID** | égal à `{{note.reviewerId}}` |
Cela récupère l'enregistrement complet du membre de l'espace de travail, y compris son adresse e-mail et son nom.
#### Envoyer l'e-mail de notification
1. Ajoutez l'action **Envoyer un e-mail**
2. Configurer :
| Champ | Valeur |
| -------------------- | --------------------------------------------------- |
| **À** | Search Record → First Workspace Member → User Email |
| **Objet** | Une note nécessite votre relecture |
| **Corps du message** | Voir l'exemple ci-dessous |
**Exemple de corps de l'e-mail :**
```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>
Remplacez `yourSubDomain` par le sous-domaine réel de votre espace de travail Twenty.
</Note>
#### Tester et activer
1. Cliquez sur **Test** pour exécuter le workflow manuellement
2. Créez ou modifiez une note et attribuez un relecteur
3. Vérifiez la boîte de réception du relecteur pour l'e-mail de notification
4. Activez le flux de travail
## Utilisation
Une fois configuré, votre équipe peut utiliser ce workflow de la manière suivante :
1. Créer une nouvelle note (ou en modifier une existante)
2. Sélectionner un collègue dans le champ **Relecteur**
3. Le collègue reçoit automatiquement un e-mail avec un lien direct vers la note
## Articles connexes
* [Déclencheurs de workflows](/l/fr/user-guide/workflows/capabilities/workflow-triggers)
* [Envoyer des e-mails depuis des flux de travail](/l/fr/user-guide/workflows/capabilities/send-emails-from-workflows)
* [Créer des champs personnalisés](/l/fr/user-guide/data-model/how-tos/create-custom-fields)
* [Créer des champs de relation](/l/fr/user-guide/data-model/how-tos/create-relation-fields)

View File

@@ -0,0 +1,106 @@
---
title: Envoyer des alertes par e-mail pour les tâches arrivant à échéance
description: Informer automatiquement les membres de l'équipe de leurs tâches à venir ou en retard.
---
import { VimeoEmbed } from '/snippets/vimeo-embed.mdx';
<img src="/images/user-guide/workflows/email_tasks_due.png" style={{width:'100%'}} />
Envoyer des rappels quotidiens par e-mail à chaque membre de l'équipe concernant ses tâches arrivant à échéance aujourd'hui.
## Vue d'ensemble
Ce workflow s'exécute selon un calendrier et :
1. Récupère tous les membres de l'espace de travail
2. Parcourt chaque membre
3. Trouve ses tâches arrivant à échéance aujourd'hui
4. Formate et envoie un e-mail personnalisé
## Configuration pas à pas
<VimeoEmbed videoId="1147463514" title="Démonstration vidéo" />
### Étape 1 : Configurer le déclencheur
1. Allez dans **Paramètres → Workflows** et créez un nouveau workflow
2. Sélectionnez **Selon un calendrier** comme déclencheur
3. Utilisez une expression cron pour tous les jours à 8:00 : `0 8 * * *`
### Étape 2 : Rechercher tous les membres de l'espace de travail
1. Ajoutez une action **Rechercher des enregistrements**
2. Sélectionnez **Membres de l'espace de travail** (dans les objets avancés)
3. Aucun filtre nécessaire — cela renvoie tous les membres
### Étape 3 : Ajouter un itérateur
1. Ajoutez une action **Itérateur**
2. Définissez le tableau d'entrée sur les membres de l'espace de travail de l'étape précédente
3. Toutes les actions à l'intérieur de l'itérateur s'exécuteront une fois par membre
### Étape 4 : Rechercher les tâches arrivant à échéance aujourd'hui (dans l'itérateur)
1. À l'intérieur de l'itérateur, ajoutez une action **Rechercher des enregistrements**
2. Sélectionnez **Tâches** comme objet
3. Ajoutez des filtres :
* **Assigné** = membre actuel de l'espace de travail (depuis l'itérateur)
* **Date d'échéance** = aujourd'hui
### Étape 5 : Mettre en forme les tâches dans le corps de l'e-mail (dans l'itérateur)
Ajoutez une action **Code** pour mettre en forme les tâches en une liste lisible avec des liens :
```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>
Remplacez `yourSubDomain` par le sous-domaine réel de votre espace de travail Twenty.
</Note>
### Étape 6 : Envoyer un e-mail (dans l'itérateur)
1. Ajoutez une action **Envoyer un e-mail** (toujours dans l'itérateur)
2. Configurer:
| Champ | Valeur |
| -------------------- | ------------------------------------------------------------------------------ |
| **À** | `{{iterator.currentItem.userEmail}}` (e-mail du membre de l'espace de travail) |
| **Objet** | Vos tâches arrivant à échéance aujourd'hui |
| **Corps du message** | `{{code.formattedTasks}}` |
### Étape 7 : Tester et activer
1. Cliquez sur **Test** pour exécuter le workflow manuellement
2. Vérifiez les boîtes de réception pour les e-mails
3. Activez le workflow
## Articles connexes
* [Actions de workflows](/l/fr/user-guide/workflows/capabilities/workflow-actions)
* [Envoyer des e-mails depuis des workflows](/l/fr/user-guide/workflows/capabilities/send-emails-from-workflows)
* [Gérer les tableaux dans les actions Code](/l/fr/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions)