Dans diverses applications, la tâche se pose régulièrement de soutenir la logique du changement dans le temps d'un attribut d'un objet par rapport à un certain sujet (ou sujets). Par exemple, cela peut être un changement dans le prix de détail des marchandises en magasin ou des indicateurs KPI pour les employés.
Dans cet article, je montrerai quelle logique de domaine et quelles interfaces peuvent être construites pour résoudre ce problème. Je ferai immédiatement une réserve pour que cela concerne l'influence managériale de l'utilisateur sur l'attribut, et non le reflet du changement historique.
L'implémentation sera présentée sur la base de la plate-forme
lsFusion ouverte et gratuite, mais un schéma similaire peut être appliqué lors de l'utilisation de toute autre technologie.
Présentation
Pour une présentation et une compréhension plus simples de l'article, nous prenons le prix comme attribut, le produit comme objet, et l'entrepôt sera le sujet. Dans ce cas, l'intervalle minimum possible pour définir l'attribut sera la date. Ainsi, l'utilisateur pourra déterminer quel sera le prix pour une date particulière pour tout produit et entrepôt.
Le schéma d'entrée utilisateur pour les changements de prix sera similaire à celui utilisé dans les systèmes de contrôle de version classiques. Tout changement, du point de vue de la logique du domaine, sera une
validation unique, sur la base de laquelle le statut pour une certaine date sera calculé. Dans de nombreux domaines, ces commits sont appelés documents ou transactions. Dans ce cas, par ce commit, nous entendons la soi-disant liste de prix. Chaque liste de prix précisera les marchandises et les entrepôts qui y sont inclus, ainsi que la période de validité.
Le schéma décrit présente les avantages suivants:
- Atomicité . Chaque modification est publiée dans un document distinct. Par conséquent, ces documents peuvent être temporairement enregistrés, mais pas publiés. Avec une entrée erronée, il est facile d'annuler la modification entière.
- La transparence Il est facile de déterminer qui a fait le changement et quand, ainsi que d'en indiquer la raison en faisant un commentaire sur le document.
La principale différence avec le système de contrôle de version est que les validations explicites sont indépendantes les unes des autres. Ainsi, il est possible de supprimer toutes les validations à tout moment sans douleur. De plus, chaque validation peut être définie pour se terminer lorsqu'elle cesse de fonctionner, ce qui n'est bien sûr pas dans le système de contrôle de version.
Implémentation
Nous commençons la définition de la logique du domaine avec les entrepôts. Compliquons un peu la solution en combinant les entrepôts dans une hiérarchie d'un groupe de profondeur dynamique. Par quel principe cela est fait est décrit dans l'
article correspondant, donc je vais juste donner un morceau de code qui déclare les groupes et crée des formulaires pour les éditer:
Annonce du groupe d'entrepôt Exemple de hiérarchie de groupe Ensuite, déclarez les entrepôts qui peuvent être liés à l'un des groupes:
Et enfin, déclarez la logique de la marchandise:
Nous procédons directement à la création de la logique des tarifs. Tout d'abord, nous définissons la classe de
liste de prix elle-même, ainsi que sa période de validité:
Nous pensons que si aucune
date n'a été fixée, la liste de prix est infinie.
Nous ajoutons un événement qui, lors de la création de la liste de prix, inscrira automatiquement la date actuelle à partir de laquelle il commencera à fonctionner.
Le mot clé
LOCAL signifie que l'événement ne se déclenchera pas lorsque la sauvegarde est appliquée à la base de données, mais immédiatement lorsque la modification est effectuée.
Ajoutez ensuite l'utilisateur qui l'a créé et l'heure de création:
Créez maintenant un événement qui les remplira automatiquement:
Cet événement, contrairement au précédent, ne sera déclenché que lorsque vous cliquerez sur le bouton Enregistrer. C'est-à-dire lors d'une transaction de sauvegarde dans la base de données.
Ensuite, créez les lignes de liste de prix dans lesquelles les marchandises et les prix seront définis:
L'attribut
NONULL indique que la propriété
priceList doit toujours être définie et
DELETE indique que lorsque la valeur de la propriété est mise à zéro (par exemple, lors de la suppression de la liste de prix), la ligne correspondante doit être automatiquement supprimée.
Pour une utilisation future, nous créerons des propriétés qui détermineront la période de validité des lignes de liste de prix:
Nous allons maintenant lier la liste de prix aux entrepôts pour lesquels elle opèrera. Tout d'abord, ajoutez la propriété principale, ce qui sera vrai si le groupe entier d'entrepôts est inclus dans la liste de prix:
Nous calculons «l'inclusion» du groupe en tenant compte des parents sélectionnés (comme décrit dans l'article sur les hiérarchies):
Ajoutez la propriété principale, avec laquelle vous pouvez spécifier que la liste de prix agit sur un entrepôt spécifique:
Nous calculons la propriété finale, qui déterminera que la liste de prix change les prix dans l'entrepôt correspondant, en tenant compte des groupes:
Créez une propriété qui affichera les noms de tous les groupes et entrepôts sélectionnés de la liste de prix, pour un utilisateur plus pratique de visualiser la liste des listes de prix:
La dernière étape de la description de la logique du domaine calculera directement le prix actuel des marchandises dans l'entrepôt. Pour ce faire, créez une propriété qui trouve la dernière ligne de date de la liste de prix avec les marchandises, l'entrepôt et la période de validité souhaités:
Dans la logique de calcul de cette propriété, différentes variations sont possibles. Vous pouvez modifier à la fois le filtre pour les lignes de frappe (par exemple, en ajoutant une condition dans
OERE la liste de prix est publiée) et la commande. Il est à noter que l'objet lui-même, ou plutôt son identifiant interne, a été ajouté à l'ordre de sélection par le deuxième paramètre. Cela est nécessaire pour que la valeur du prix soit toujours déterminée d'une manière unique.
Sur la base de la liste de prix reçue, nous déterminons la valeur du prix et sa période de validité:
Ils seront ensuite utilisés dans les tables d'interface utilisateur.
Ensuite, nous passons à la construction de l'interface utilisateur. Tout d'abord, nous dessinons un formulaire pour modifier la liste de prix. Créez un formulaire et ajoutez-y l’en-tête du document:
Ajoutez la ligne de liste de prix au formulaire:
Ensuite, ajoutez un arbre dans lequel il y aura à la fois des groupes et des entrepôts:
Les propriétés des groupes et des entrepôts sont ajoutées simultanément à l'arborescence. La plate-forme affichera, selon l'objet, telle ou telle propriété dans l'ordre dans lequel elles sont ajoutées au formulaire.
Nous personnalisons la conception du formulaire afin que les marchandises et les entrepôts soient dessinés dans des onglets séparés:
Le formulaire d'édition ressemblera à ceci:
Reste à construire la forme de base de la gestion des prix. Il comprendra deux onglets. Le premier affichera une liste de toutes les listes de prix (similaire à la liste des commits). Le deuxième onglet affichera les prix actuels d'un entrepôt spécifique pour la date sélectionnée.
Pour implémenter le premier onglet, ajoutez au formulaire une liste de listes de prix avec des lignes pour un aperçu rapide:
Pour le deuxième onglet, nous ajoutons d'abord la date à laquelle les prix sont affichés, l'arborescence des groupes de magasins, ainsi que les magasins eux-mêmes:
La liste des entrepôts affichera tous les entrepôts qui sont les descendants du groupe sélectionné en haut.
Ensuite, ajoutez au formulaire une liste de marchandises pour lesquelles il existe des prix valides pour l'entrepôt à la date sélectionnée:
Le prix lui-même et la période de validité sont ajoutés aux colonnes. Vous pouvez également ajouter le numéro de liste de prix - alors ce tableau ressemblera à la logique des annotations dans les systèmes de contrôle de version.
Afin que l'utilisateur comprenne d'où vient un tel prix, nous ajoutons la liste des lignes de liste de prix avec les marchandises et les entrepôts appropriés:
À l'aide de l'attribut
BACKGROUND, mettez en surbrillance la ligne qui a déterminé le prix indiqué dans le tableau.
De plus, pour la commodité de l'utilisateur, nous ajouterons la possibilité d'ouvrir le formulaire d'édition de la liste de prix correspondante dans une nouvelle session immédiatement à partir de cette histoire:
Pour ce faire, vous devez spécifier l'action qui sera effectuée lorsque vous essayez de modifier une ligne en implémentant l'action de
modification intégrée. Ensuite, un bouton standard pour modifier un objet via un appel de dialogue est ajouté au formulaire de la manière standard.
Et enfin, nous formons le design final du formulaire:
Ici, le conteneur de
volet est ajouté en premier, qui se compose de deux onglets:
listes de prix et
prix . Le premier d'entre eux ajoute simplement une liste de listes de prix et de lignes. Dans le second, deux panneaux sont créés:
leftPane et
rightPane . Le panneau de gauche contient la date et les entrepôts, et le panneau de droite contient l'historique des marchandises et des prix.
Résultat
Considérez les principales options d'utilisation de la logique résultante.
Supposons que nous ayons deux listes de prix distinctes pour différents groupes de marchandises. Ensuite, en fonction de l'entrepôt sélectionné, dans l'onglet des prix, seuls les produits des listes de prix correspondantes seront affichés:
Créez maintenant une nouvelle liste de prix avec une période de validité limitée, une liste de magasins dépouillée et un nouveau prix. Dans le deuxième onglet, si nous sélectionnons une date dans la plage de la nouvelle liste de prix, nous en obtiendrons un nouveau. Dès l'expiration de la période de validité, l'ancien prix reviendra à nouveau du prix d'origine:
En utilisant le même mécanisme, vous pouvez «annuler» l'action de prix spécifiques à partir d'une certaine date. Par exemple, si vous entrez un nouveau prix sans spécifier de prix, il se trouve que le prix sera réinitialisé et les marchandises disparaîtront du filtre. Dans ce cas, lors de la suppression du document saisi, tout revient à l'ancien état:
La propriété résultante avec le prix des marchandises par l'entrepôt à la date peut être en outre utilisée dans divers événements ou d'autres formes. Par exemple, vous pouvez effectuer une tarification automatique dans une commande basée sur cette logique de tarification:
Un bon avantage dans cette logique est que lorsque vous ajoutez un nouvel entrepôt au groupe, les prix des listes de prix déjà créées lui seront automatiquement appliqués. La même chose se produit lorsque vous modifiez le groupe de l'entrepôt.
Si vous le souhaitez, vous pouvez rendre la colonne avec le prix sur l'onglet avec les prix actuels modifiable et ajouter un bouton qui créera un nouvel engagement pour les prix modifiés.
Conclusion
Dans la solution au niveau de la plateforme, ni les ouvrages de référence, ni les documents avec des chaînes, ni les registres, ni les rapports et autres abstractions inutiles ne sont utilisés. Tout se fait exclusivement sur les concepts de classes et de propriétés. Notez que cette logique assez complexe a été implémentée dans environ 150 lignes de code significatives sur lsFusion. Le mettre en œuvre dans la même formulation sur d'autres plateformes (par exemple, 1C) est une tâche beaucoup plus difficile.
Le schéma décrit ci-dessus est largement utilisé dans la
solution ERP basée sur lsFusion. Son utilisation, avec diverses modifications, prend en charge les listes de prix des fournisseurs, les prix de détail de gestion, les stocks et de nombreux autres paramètres de gestion.
Le modèle peut être compliqué en ajoutant plusieurs entités au document (par exemple, un fournisseur peut être ajouté à l'entrepôt), ainsi qu'en définissant plusieurs attributs dans un document à la fois. En particulier, vous pouvez ajouter le type de prix de l'entité et, dans la ligne de document, définir le prix du tuple de la ligne et le type de prix correspondant. Dans la logique décrite ci-dessus, il vous suffit d'ajouter quelques paramètres supplémentaires à certaines propriétés.
À l'aide de plusieurs lignes de code supplémentaires, il est possible de dénormaliser tous les enregistrements de modification dans une seule table sur laquelle construire l'index correspondant. Ensuite, la sélection de n'importe quelle valeur pour n'importe quelle date sera effectuée dans un temps logarithmique. Une telle optimisation est nécessaire lorsqu'il y a plusieurs centaines de millions d'enregistrements dans ce tableau.
Vous pouvez essayer l'exemple construit en ligne sur la
page correspondante
du site (section Plateforme). Voici tout le code source que vous devez coller dans le champ souhaité: