Créer des tables avec Envision

Créer des tables avec Envision










Accueil » Ressources » Ici

Le moyen le plus courant de créer une table dans Envision consiste à la lire depuis un fichier plat. Il est également possible d’en créer entièrement. Les tables sont alors définies et instanciées par un script. La création d'une table peut être particulièrement utile lorsqu’aucune des tables fournies en entrée ne dispose des propriétés nécessaires à un calcul donné.


Présentation de la syntaxe

La syntaxe habituelle de création d’une table repose sur les instructions table et extend qui s’utilisent selon la syntaxe suivante :
table T = extend.range(Orders.42)
Le mot clé table ne doit pas être confondu avec le type de vignette, comme dans show table. La table T peut ensuite être utilisée dans le reste du script, exactement comme une table issue d’un fichier.
show table "Number of lines" with sum(T.1)

extend.range()

Le moyen le plus simple de créer une table consiste à utiliser l’extension de plage extend.range() : pour chaque ligne de la table d’origine, N lignes sont créées et numérotées. L’extension de plage peut servir à ajouter des lignes à une table.

La syntaxe est la suivante :
table T = extend.range(Orders.K)
T.Quantity = Orders.Quantity // projection implicite
show table "Line numbers" with T.N, T.Quantity
L’argument doit être un entier, qui peut varier d’une ligne de la table d’origine à l’autre. Le nombre de lignes ajoutées est ainsi maîtrisé.

La table T est une extension de la table passée en argument. Comme illustré ci-dessus à la ligne 2, les affectations inter-tables simples fonctionnent car chaque ligne de T est encore associée à son équivalent d’origine dans Orders. Le champ T.N est renseigné par défaut et représente le numéro de la ligne. Il démarre à 1 et augmente par incréments de 1.

Attention : la fonction extend.range() peut générer artificiellement un grand nombre de lignes. Le temps de création d'une table trop grande peut être très long, selon le quota de traitement de votre compte Lokad, et peut échouer. En pratique, si K est supérieur à 10, en moyenne, alors il se peut que extend.range() ne soit pas la solution à votre problème.

L'exemple suivant illustre comment certaines lignes d'une table peuvent être dupliquées, tout en conservant le contenu de la table d'origine.
read "/sample" all
expect Grid[*]

table G = extend.range(Grid.Min == 0 ? 2 : 1)
G.Min = Grid.Min // affinity between 'G' and 'Grid'
G.Max = Grid.Max
G.Probability = Grid.Probability
G.Probability = 0 where G.N == 2
show table "My Grid" with Id, G.Probability, G.Min, G.Max

extend.distrib()

Les distributions dans Envision offrent un algèbre puissant, qui peut être utilisé pour éviter les calculs alambiqués de probabilités sur des listes. Mais, dans certains cas, une liste brute de probabilités est tout à fait satisfaisante et même souhaitable. L’extension de distribution extend.distrib() transforme un vecteur de distributions en table, comme l’illustre la syntaxe suivante :
table T = extend.distrib(D)
show table "Distribution details" with Id, T.Min, T.Max, T.Probability
L’argument D doit être un vecteur de distributions, comme ceux produits par le moteur de prévisions probabilistes de Lokad. La table T est une extension des tables d’origine. Elle contient trois champs :

  • T.Min : limite inférieure entière inclusive du segment ;
  • T.Max : limite supérieure entière inclusive du segment ;
  • T.Probability : la somme de la distribution sur la plage inclusive.

Malgré le nom du champ, Probability, il s’agit bien de la somme de la distribution sur la plage indiquée.

Pour les distributions relativement compacts, les segments ont une longueur de 1, d’où T.Min == T.Max. Cependant, si la distribution s’étale sur des valeurs plus grandes, les segments dont la longueur est égale à 1 peuvent générer des millions de lignes, ce qui devient ingérable. Par conséquent, dans le cas-là, Envision agrège automatiquement les distributions sur des segments plus importants. Les algorithmes sont réglés pour garantir que les tables générées gardent des tailles gérables.

Extend.distrib() isole délibérément les segments nuls. Ainsi, le segment [0;0] obtient toujours sa propre ligne dans la table générée. Ce comportement est utile dans plusieurs situations, lorsque la demande nulle est un cas limite, tout comme une couverture de stock infinie, qui requiert une logique spécifique.

De plus, trois surcharges supplémentaires sont fournies pour extend.distrib() afin de mieux contrôler la granularité de la table générée.

Séparer

La première surcharge est conçue pour aider à élaborer une liste de priorités d’achat en prenant en compte le niveau de stock actuel. La syntaxe est la suivante :
table T = extend.distrib(D, S)
Le premier argument D est tel que défini ci-dessus. Le deuxième argument S doit être un nombre entier. Lorsque ce second argument est présent, la table générée inclut deux lignes consacrées aux deux segments [0;0] et [1;S]. Les autres segments sont générés automatiquement à partir de S+1 comme indiqué ci-dessus. La valeur par défaut de cet argument lorsque celui-ci n'est pas spécifié est zéro.

En pratique, l’argument S est fréquemment défini par la somme du stock disponible et du stock en commande. En effet, lors du réapprovisionnement, seules les probabilités de demande supérieure au niveau de stock actuel doivent être prises en compte.

Multiplier

La seconde surcharge est conçue pour des situations qui impliquent des « multiples de commande ». Dans ces cas-là, la table doit faire des itérations sur des segments de taille donnée. La syntaxe est la suivante :
table T = extend.distrib(D, S, M)
Les arguments D et S sont tels que définis ci-dessus. Le troisième argument M doit être un nombre entier. Il représente la longueur de segment obligatoire. Ainsi la table contient la liste de segments [0;0], [1;S], [S+1;S+M] [S+M+1;S+2M] … Si M vaut zéro, la fonction détermine la taille des segments automatiquement.

En pratique, si la longueur de segment imposée est 1, des problèmes de performance peuvent apparaître car la table peut devenir très volumineuse. Envision peut alors avoir recours à un « multiple » de M. Ainsi, la logique des multiples de commande continuera à fonctionner, tout en conservant un nombre raisonnable de lignes à générer.

D’une manière générale, nous vous conseillons de ne pas utiliser cette surcharge si vous ne devez pas gérer des multiples de commande. Si vous devez l’utiliser, nous vous suggérons d’indiquer zéro pour l’argument L des articles qui ne sont pas soumis à un tel multiple.

Atteindre

La troisième surcharge est conçue pour des situations dans lesquelles des quantités minimales de commande doivent être prises en compte. Dans ce cas, la table doit faire assez d'itérations pour atteindre les valeurs souhaitées. La syntaxe est la suivante :
table T = extend.distrib(D, S, M, R)
Les arguments D, S et M sont définis ci-dessus. Le quatrième argument R doit être un entier non négatif. Il représente la valeur maximale à atteindre. Ainsi, à partir d'un moment T.Max est supérieur ou égal à R. La valeur par défaut de cet argument lorsque celui-ci n'est pas spécifié est zéro.

En pratique, cet argument est utilisé pour faire face à d'importantes contraintes de quantité minimale de commande qui nécessitent des itérations de la table éloignées les unes des autres pour couvrir les valeurs visées.

De manière générale, nous vous conseillons de ne pas utiliser cette surcharge, sauf si des quantités minimales de commande doivent être atteintes. Lorsque vous l'utilisez, faites en sorte que R soit aussi petit que possible. Une petite valeur de R n'empêche pas la table T d'atteindre des valeurs élevées, elle garantit simplement que celles-ci sont atteintes.

extend.billOfMaterials()

Les nomenclatures entrent en jeu lorsque l’intérêt porte non pas sur les articles vendus mais sur leurs pièces ou leur composition. Dans ces cas-là, l’historique de la demande au niveau « article » doit être transformé en historique au niveau « pièce ». Ce sont les nomenclatures qui le permettent en indiquant la composition de chaque article.

La fonction d’appel extend.billOfMaterials() est conçue spécialement pour ce cas d’utilisation avec la syntaxe suivante :
table T = extend.billOfMaterials(
  Item: J.Id
  Part: B.PartId
  Quantity: B.Quantity
  DemandId: O.OrderId
  DemandValue: O.Quantity)

T.DemandDate by T.DemandId = same(O.Date) by O.OrderId // exemple d'obtention de la date

show table "Details" with Id, T.DemandDate, T.Quantity
Trois tables sont attendues, J, B et O. La table J contient généralement les articles, mais ce n’est pas obligatoire. La table B est censée être celle des nomenclatures. Elle doit être une extension de J, c’est-à-dire du type (Id, *), si J est bien la table des articles. La table O doit contenir l’historique de la demande, c’est généralement la table des commandes. Elle doit aussi être une extension de la table J.

La table résultat est une extension de la table J, avec les spécificités article en place. La table T contient deux champs déjà renseignés :
  • T.DemandId, conçu pour offrir la possibilité d'identifier dans la table T les lignes issues de la table O
  • T.Quantity, obtenu en déroulant les nomenclatures.

Il est souvent utile d'injecter une date dans la table T. Pour cela, vous pouvez utiliser l'instruction « left-by » comme illustré dans la ligne sous le bloc billOfMaterials() ci-dessus.

La fonction extend.billOfMaterials() peut prendre en charge des assemblages récursifs, c’est à dire qu’un article peut être à la fois composant et composé dans la table B. Évidemment, la fonction échoue si la table des nomenclatures contient des dépendances cycliques.

L’argument Quantity représente le nombre d’unités de B.PartId utilisées lorsqu’un assemblage identifié par J.Id est vendu.