➕ Auto-incrémenter un numéro de dossier (sans CircularRefError)

:dart: Le problème

Vous voulez créer un numéro de dossier qui s’incrémente automatiquement (1, 2, 3, 4…) à chaque nouvelle ligne, SANS obtenir l’erreur CircularRefError.


:white_check_mark: Une solution qui fonctionne

Voici une formule magique (formule d’initialisation) qui évite l’erreur circulaire :

max([r.Numero_du_dossier for r in Dossiers.all if r.id != $id and r.Numero_du_dossier], default=0) + 1

:clipboard: Exemple concret : Table « Dossiers »

Structure de la table

Numero_du_dossier Nom_du_client Date_de_signature
1 Dupont SARL 2025-01-15
2 Martin & Co 2025-01-20
3 Société Durand 2025-01-25
(auto) (nouveau client) (nouvelle date)

:hammer_and_wrench: Configuration pas à pas

Étape 1 : Créez votre table « Dossiers »

  1. Créez une nouvelle table appelée Dossiers
  2. Ajoutez 3 colonnes :
    • Numero_du_dossier (type: Numerique)
    • Nom_du_client (type: Texte)
    • Date_de_signature (type: Date)

Étape 2 : Configurez la colonne « Numero_du_dossier »

  1. Cliquez sur l’en-tête de colonne Numero_du_dossier

  2. Dans le panneau de droite, configurez la formule d’initialisation à appliquer sur les nouvelles lignes

    • Type de colonne : Numerique
  3. Dans le champ de formule qui apparaît, copiez-collez :

max([r.Numero_du_dossier for r in Dossiers.all if r.id != $id and r.Numero_du_dossier], default=0) + 1

Étape 3 : Testez !

  1. Créez une nouvelle ligne
  2. Le numéro de dossier se remplit automatiquement avec 1
  3. Remplissez le nom du client et la date
  4. Ajoutez une nouvelle ligne → le numéro passe à 2
  5. Et ainsi de suite !

:mag: Pourquoi cette formule fonctionne ?

max([r.Numero_du_dossier for r in Dossiers.all if r.id != $id and r.Numero_du_dossier], default=0) + 1

Décomposons :

Partie Signification
Dossiers.all Toutes les lignes de la table Dossiers
r.id != $id LA CLÉ : Exclut la ligne actuelle (évite CircularRefError)
r.Numero_du_dossier Récupère le numéro de chaque ligne
max(...) Trouve le plus grand numéro
default=0 Si aucun numéro n’existe, renvoie 0
+ 1 Ajoute 1 pour avoir le prochain numéro

En français : « Trouve le plus grand numéro existant (en excluant ma propre ligne) et ajoute 1 »


:gift: BONUS 1 : Commencer à un autre numéro (ex: 100)

Si vous voulez que votre numérotation commence à 100 au lieu de 1 :

max([r.Numero_du_dossier for r in Dossiers.all if r.id != $id and r.Numero_du_dossier], default=99) + 1

Changement : default=99 au lieu de default=0

Résultat :

  • Premier dossier → 100
  • Deuxième dossier → 101
  • Troisième dossier → 102
  • Etc.

:memo: Formule générale

Pour commencer à N, utilisez default=N-1 :

Vous voulez commencer à Mettez default=
1 0
50 49
100 99
1000 999

:gift: BONUS 2 : Ajouter un préfixe (ex: « DOS-2025-001 »)

Si vous voulez des numéros du type DOS-2025-001, DOS-2025-002, etc. :

Colonne de type Texte

  1. Changez le type de colonne Numero_du_dossier en Texte
  2. Utilisez cette formule :
# Trouve le dernier numéro
numeros_existants = [int(r.Numero_du_dossier.split('-')[-1]) for r in Dossiers.all 
                     if r.id != $id and r.Numero_du_dossier and r.Numero_du_dossier.startswith('DOS-')]
prochain_numero = max(numeros_existants, default=0) + 1

# Formate avec l'année en cours et un zéro padding
annee = NOW().year
return f"DOS-{annee}-{prochain_numero:03d}"

Résultat :

  • DOS-2025-001
  • DOS-2025-002
  • DOS-2025-003
  • DOS-2025-099
  • DOS-2025-100

:art: Variantes de formatage

Avec le mois

# Format : DOS-2025-01-001 (année-mois-numéro)
f"DOS-{NOW().year}-{NOW().month:02d}-{$Numero_interne:03d}"

Avec des lettres

# Format : REF-001, REF-002
f"REF-{$Numero_interne:03d}"

Avec département + année

# Format : 75-2025-001 (département Paris + année)
f"75-{NOW().year}-{$Numero_interne:03d}"

:wrench: Dépannage

Erreur « CircularRefError »

:arrow_right: Vérifiez que vous avez bien r.id != $id dans votre formule

La numérotation recommence à 1 à chaque fois

:arrow_right: Vérifiez que vous utilisez bien une formule d’initialisation, pas une formule classique

Les numéros ne sont pas consécutifs (ex: 1, 2, 5, 8…)

:arrow_right: C’est normal ! Si vous supprimez des lignes, les numéros ne sont pas réutilisés (c’est une bonne pratique)


1 « J'aime »

Hello :slight_smile:
Pour info il est aussi possible d’utiliser la fonction PREVIOUS() pour aller chercher la valeur de la ligne précédente, avec la formule finale :

PREVIOUS(rec, order_by=None).Nom_colonne + 1

En termes de performances, elle sera plus adaptée si la table Dossiers contient beaucoup de données, car le Dossiers.all va chercher toutes les lignes de la table Dossiers, et ce pour chaque cellule calculée.

oui mais ça contraint à créer une ligne toujours au dessous de la dernière, il y a un risque de créer des doublons. Ca va dépendre des cas d’usage

Mmm comment ça ?

Tu peux avoir une seule ligne : la première ligne a la valeur 1 (PREVIOUS().A d’une colonne numérique A c’est 0)

Et il n’y a pas de risque de doublon, vu que la première est à 1 et les suivantes sont incrémentées de 1 en 1.

Ahhh, tu veux dire si besoin d’ajouter une ligne entre 2 autres lignes ?

avec previous, si je cree une nouvelle ligne au milieu d’une liste par exemple, ou au dessus au lieu d’en dessous la suite n’est pas maintenue et tu peux obtenir des doublons

C’est top, bravo !

Quel est l’avantage de cette formule par rapport à cette solution qui s’appuie sur rec.id ? Déjà j’ai l’impression que cette dernière ne permet pas de commencer à un numéro différent de 1…

T’as raison si jamais c’est une formule d’initialisation et pas une formule, on peut avoir des doublons en insérant une ligne entre 2 autres.

Et que penses-tu de celle-ci ?

max_id = Table4.lookupOne(order_by="-id").id

previous = PEEK(Table4.lookupOne(id = max_id -1).Ma_colonne)

return previous + 1

Le PEEK permet d’éviter la circular error, et le lookupOne d’éviter le .all

Et fonctionne bien avec la formule d’initialisation, tu peux mettre la valeur de départ directement dans la première cellule