Aller au contenu principal

Définition de table Model-Driven

Identifiant du schéma : model-driven-entity@1 Schéma lisible par machine : model-driven-entity.schema.json

La représentation canonique, en texte brut, d’une table Model-Driven Apps. C’est la référence selon laquelle une table est validée — la même définition que produit le concepteur, que soumet un appel d’API, que génère un assistant IA et que transporte une importation de solution. La validation s’applique de manière identique quelle que soit l’origine : un fichier écrit à la main ou généré, conforme à ce schéma, peut être importé en toute sécurité.

Structure​

Une définition est un objet JSON comportant ces membres :

ChampTypeObligatoireRemarques
logicalNamestringouiPréfixé par l’éditeur, en snake-case minuscule (p. ex. par_invoice).
displayNamestringouiLibellé au singulier (p. ex. Invoice).
displayCollectionNamestringouiLibellé au pluriel (p. ex. Invoices).
iconstringnonClé d’icône issue du jeu d’icônes de la plateforme, affichée à côté de la table dans la navigation d’une application (§4.4). À omettre pour le glyphe par défaut.
publisherPrefixstringouiPréfixe court en minuscules (p. ex. par).
primaryFieldstringouiNom logique du champ principal (nom) ; doit figurer dans fields.
primaryImagestringnonNom logique de l’image de l’enregistrement (§6.16) — l’image affichée à côté d’un enregistrement dans les grilles et les recherches. Doit désigner un champ de type Image dans fields. À omettre pour une table sans image.
secondaryFieldstring | nullnonNom logique du champ affiché comme texte secondaire de l’enregistrement — la seconde ligne, plus petite, sous son nom partout où les enregistrements sont listés sans grille, comme dans un panneau de recherche (§6.13.5). Doit désigner un champ de fields qui n’est ni le champ principal ni une colonne File/Image.
hierarchyParentFieldstring | nullnonNom logique de la recherche qui fait de cette table une hiérarchie — le champ contenant le parent de chaque enregistrement, pointant vers cette même table (la catégorie parente d’une catégorie, l’unité parente d’une unité). Doit désigner un champ de fields de type Lookup dont la targetEntity est cette table. Il n’accorde rien et n’impose rien : la table s’affiche toujours sous forme de grille tant qu’aucune vue n’imbrique ses enregistrements. Il sert à ce que le fait d’être une hiérarchie soit une propriété des données plutôt que d’une seule vue — le niveau supérieur d’une vue arborescente utilise par défaut ce champ comme lien enfant.
personFieldstringnullnon
constraintsarraynonDes règles appliquées par la base de données, et non par l’application (voir ci-dessous) — une colonne unique, ou une période qui ne peut pas en chevaucher une autre dans la même portée.
fieldsarrayouiUne ou plusieurs définitions de champ (voir ci-dessous).
relationshipsarraynonDes relations un-à-plusieurs reposant sur des champs de recherche.
isAuditEnabledbooleannonActive l’audit des modifications pour la table (section 6.5). Vaut false par défaut.
statesarraynonLes états dans lesquels peut se trouver un enregistrement de cette table (§6.14) — son cycle de vie. Voir ci-dessous. Absent dans un document écrit avant l’existence des états, ce qui permet à un importateur de le distinguer d’une table qui n’en a aucun.
reasonsarraynonLes raisons, chacune appartenant à exactement un état. Voir ci-dessous.
statusValueBaseintegernonLe plancher de la plage à partir de laquelle sont numérotées les options d’état et de raison ajoutées à cette table — la base des valeurs d’option de l’éditeur propriétaire (§6.14). Les valeurs Actif/Inactif créées d’office se situent hors de cette plage, à 0 et 1 : chaque table les possède et elles signifient partout la même chose, elles n’ont donc besoin d’aucun espace de noms.
nextStatusValueintegernonLe niveau le plus haut atteint par les statuts de la table — la prochaine valeur qu’elle attribuera. Stocké plutôt que déduit des options, pour la même raison que pour un groupe d’options : le numéro d’une option supprimée est retiré, et le recalculer l’attribuerait à la prochaine nouvelle option alors que des enregistrements peuvent encore le contenir.

Champ​

ChampTypeObligatoireRemarques
logicalNamestringouiEn snake-case minuscule.
displayNamestringoui
dataTypestringouiL’un de Text, MultilineText, Integer, Decimal, Boolean, DateTime, Lookup, PolymorphicLookup, File, Image, OptionSet, State, Reason.
isRequiredbooleannon
isPrimarybooleannonVrai pour l’unique champ principal.
maxLengthintegernonPour Text (1–4000).
precision, scaleintegernonPour Decimal (scale ≤ precision ≤ 38).
targetEntitystringconditionnelObligatoire lorsque dataType vaut Lookup ; le nom logique de la table référencée.
targetEntitiesstring[]conditionnelObligatoire lorsque dataType vaut PolymorphicLookup ; les tables vers lesquelles une valeur peut pointer, dans l’ordre du sélecteur. Au moins deux, sans répétition.
isAuditExcludedbooleannonExclut le champ de l’audit des modifications lorsque la table est auditée (section 6.5). Vaut false par défaut.

Les champs PolymorphicLookup sont provisionnés sous forme de deux colonnes : la colonne uuid propre au champ, qui contient l’id de l’enregistrement, et une colonne sœur <column>_type de type text, qui contient le nom logique de la table cible. Aucune ne porte de clé étrangère — une même colonne ne peut pas référencer plusieurs tables — l’intégrité relève donc du chemin d’écriture : il vérifie que la cible existe, et une cible supprimée ensuite se lit comme une référence non résolue.

Sur le réseau, les deux moitiés voyagent sous la forme d’un seul scalaire, <table>:<id>, de sorte que les values d’un enregistrement restent une table plate associant le nom logique du champ à un scalaire, et que la colonne discriminante n’apparaisse jamais comme un champ. C’est la convention existante de la plateforme pour une référence autodescriptive (recordfile:<id>, webresource:par_/app.js).

C’est le nom logique de la table qui est stocké, et non un nombre, car un discriminant numérique serait un index dans targetEntities — réordonner les cibles dans le concepteur réorienterait alors silencieusement chaque ligne stockée vers une autre table. Les colonnes de propriétaire d’un enregistrement utilisent au contraire une énumération numérique, ce qui est cohérent et non contradictoire : leurs cinq valeurs sont des concepts de la plateforme fixés dans le code, et non une liste qu’un créateur modifie.

Chaque entrée de targetEntities reçoit sa propre relation sous-jacente, ce qui permet au formulaire de la table référencée de proposer un onglet Associés pour ces enregistrements.

Les champs File / Image sont provisionnés sous forme d’une colonne texte contenant une référence qualifiée par un schéma — recordfile:<id> pour un fichier conservé dans le schéma propre de l’environnement, ou objectstore:<key> pour un fichier assez volumineux pour avoir débordé vers le stockage d’objets. Les octets sont téléversés vers le point de terminaison de fichier propre au champ plutôt qu’écrits via l’API d’enregistrement, et sont servis sous l’autorisation propre à l’enregistrement. Lequel des deux magasins contient un fichier est déterminé par sa taille, et par rien de ce qu’un créateur définit.

Relation​

ChampTypeObligatoireRemarques
logicalNamestringouiEn snake-case minuscule.
typestringouiOneToMany.
referencedEntitystringouiLe côté « un ».
referencingEntitystringouiLe côté « plusieurs » (qui porte la recherche).
lookupFieldstringouiLe champ de recherche de la table référençante sur lequel repose la relation.

État et raison​

Un état indique où en est un enregistrement dans son cycle de vie ; une raison précise de quelle variante de cet état il s’agit. Ils voyagent avec la table parce qu’ils font partie de ce que le créateur a conçu.

🔴 Ce ne sont pas un groupe d’options. Active sur une facture et Active sur un contact ne sont pas le même choix, et les modéliser comme une seule liste partagée ferait qu’en renommer un renommerait l’autre.

État :

ChampTypeObligatoireRemarques
valueintegerouiLa valeur stockée, conservée lors d’une importation : les enregistrements de l’environnement source la contiennent déjà, Dev et Test doivent donc s’accorder sur sa signification.
displayNamestringouiLe libellé.
colorstringnonLa couleur dans laquelle son badge est dessiné.
iconstringnonLe glyphe de son badge.
defaultReasonintegernonLaquelle des raisons de cet état reçoit un enregistrement qui y entre. Absent signifie la première, ce qui rend ce réglage facultatif.

Raison :

ChampTypeObligatoireRemarques
stateintegerouiLa value de l’état auquel appartient cette raison. Indexée par valeur plutôt que par id de ligne, car une valeur est ce que stocke un enregistrement et ce qui survit à une exportation.
valueintegerouiLa valeur stockée.
displayNamestringouiLe libellé.
color · iconstringnonComme pour un état.

L’appartenance est tout l’enjeu : Won et Lost sont toutes deux des raisons pour lesquelles une affaire est Inactive, et les proposer alors qu’elle est Active n’aurait aucun sens.

Exemple​

{
"logicalName": "par_invoice",
"displayName": "Invoice",
"displayCollectionName": "Invoices",
"publisherPrefix": "par",
"primaryField": "par_name",
"fields": [
{ "logicalName": "par_name", "displayName": "Name", "dataType": "Text", "isPrimary": true, "isRequired": true, "maxLength": 200 },
{ "logicalName": "par_total", "displayName": "Total", "dataType": "Decimal", "precision": 18, "scale": 2 },
{ "logicalName": "par_account", "displayName": "Account", "dataType": "Lookup", "targetEntity": "par_account" }
],
"relationships": [
{ "logicalName": "par_account_invoices", "type": "OneToMany", "referencedEntity": "par_account", "referencingEntity": "par_invoice", "lookupField": "par_account" }
]
}

Gestion des versions​

Les anciennes versions du schéma sont conservées à côté des nouvelles, afin que les solutions déjà déployées continuent d’être validées après l’évolution du schéma. Lorsqu’une modification incompatible est nécessaire, une nouvelle version (model-driven-entity@2) est publiée plutôt que de modifier celle-ci.