Pas un autre langage de programmation. Partie 2: Logique de représentation



La deuxième partie de la trilogie du langage et de la plateforme lsFusion. La première partie se trouve ici .

Il se concentrera sur la logique des représentations, à savoir sur tout ce qui concerne la combinaison des données et leur affichage à l'utilisateur ou à d'autres systèmes d'information.

Il est clair que beaucoup ne sont peut-être pas aussi intéressés à regarder la présentation du combattant, et ils aimeraient voir un vrai combat, si possible avec du sang (et ce le sera, car la discussion des articles précédents a aidé à mieux comprendre les endroits non protégés des concurrents potentiels et où les battre) . Mais il y a deux choses à considérer:

a) c'est Habr. C'est-à-dire, une ressource technique, ils n'aiment pas les belles images et les slogans publicitaires ici - pour dire quelque chose, vous avez besoin de détails sur la façon dont vous allez y parvenir.
b) c'est un marché pour le développement de systèmes d'information, et il est très similaire au marché des produits amaigrissants. Ici, tout le monde dit que nous avons un service rapide et facile. Mais en ce qui concerne les détails, dans lesquels, comme vous le savez, le diable se trouve, soit les CRUD les plus simples sont utilisés comme exemples, soit ils ont recours à diverses astuces: ils affichent quelques extraits de code et masquent la partie principale avec les mots «cela n'a pas d'importance "," Fait en quelques minutes "et tout le reste.

En fait, c’est pourquoi nous avions deux options: soit commencer par les avantages et les risques d’obtenir des reproches pour le marketing de conneries, soit commencer par une description technique et les questions «pourquoi avons-nous besoin d’une autre langue». Théoriquement, bien sûr, tout cela pourrait être fait en un seul article, mais un tel article serait difficile non seulement à lire, mais même juste à faire défiler. En conséquence, nous avons choisi la deuxième option, bien que s'il est toujours important pour quelqu'un de se renseigner sur les raisons de l'apparence et des avantages de la langue en ce moment (et non dans de futurs articles), bienvenue sur le site . Il ne comprend que trois pages: quoi, comment et pourquoi pas, et donne, à mon avis, suffisamment d'informations pour répondre à toutes ces questions. De plus, vous pouvez également essayer la plate-forme en ligne là - bas , y compris pour vous assurer qu'il n'y a pas de «piano dans les buissons», et le code des exemples de cet article est vraiment tout le code nécessaire pour exécuter l'application.

Mais assez digressions lyriques, nous revenons à la présentation de la description combattante de la logique des représentations.

Comme la logique de domaine (premier article), tous les concepts de logique de présentation dans lsFusion forment une pile:



et c'est dans l'ordre de cette pile que je vais en parler.


Formulaires


Un formulaire est le concept le plus important (et en fait pratiquement le seul) dans la logique de présentation, qui est responsable de tout - à la fois pour l'interaction avec l'utilisateur et pour l'impression, l'exportation et l'importation de données.

Le formulaire peut logiquement être divisé en deux parties:

  • La structure du formulaire détermine quelles données le formulaire affiche.
  • La présentation du formulaire détermine comment il affiche ces données.

Structure de forme


Nous commençons, naturellement, par la structure de la forme.

Les objets


Lors de la création d'un formulaire, vous devez d'abord déterminer les objets qu'il affichera. Il convient de noter qu'il peut y avoir une légère confusion terminologique entre les objets du formulaire et les objets affichés dans ces objets de formulaire. Par conséquent, à l'avenir, si cela n'est pas évident dans le contexte, nous utiliserons les termes «objets de formulaire» (pour le premier cas) et «objets dans la base de données» (pour le second).

Pour chaque objet de formulaire, vous devez définir sa classe. Cette classe peut être primitive (intégrée) ou objet (personnalisée).
FORM currentBalances ' '
OBJECTS s = Stock, i = Item // ,
;
Conformément à l'ordre d'ajout d'objets au formulaire, une liste ordonnée d'objets est formée. Par conséquent, le dernier objet d'un certain ensemble d'objets sera appelé un objet de cet ensemble avec le numéro de série maximal dans cette liste (c'est-à-dire le plus récent).

Chaque objet du formulaire à un moment donné a une valeur actuelle. Son changement se produit en fonction de la présentation, soit du fait des actions correspondantes de l'utilisateur dans la présentation interactive, soit "virtuellement" dans le processus de lecture des données dans une représentation statique.

Propriétés et actions


Après avoir défini les objets sur le formulaire, vous pouvez ajouter des propriétés et des actions, en les remplaçant par les objets décrits ci-dessus comme entrée dans les arguments.

Notez que l'ajout d'actions n'est pertinent que pour la présentation interactive; elles sont ignorées dans les rapports et les exportations. De plus, étant donné que le comportement des propriétés et des actions du point de vue de leur affichage sur le formulaire est exactement le même, à l'avenir, nous n'utiliserons que le terme propriété (pour les actions, le comportement est absolument similaire).

Objet d'affichage


Chaque propriété est affichée dans exactement un objet sur le formulaire (nous l'appellerons l'objet d'affichage de cette propriété). Par défaut, l'objet d'affichage est un objet, ce dernier pour l'ensemble d'objets qui sont passés à l'entrée de cette propriété. Par exemple, si nous avons une forme de soldes courants avec deux objets - un entrepôt et des marchandises et trois propriétés - les noms de l'entrepôt et des marchandises et le solde des marchandises dans l'entrepôt:
FORM currentBalances ' '
OBJECTS s = Stock, i = Item // ,
PROPERTIES name(s), name(i), currentBalance(s, i)
;
Ensuite, pour le nom de l'entrepôt, l'objet d'affichage sera s (entrepôt), et pour le nom des marchandises et le solde, i (marchandises).

Cependant, si nécessaire, le développeur peut spécifier explicitement l'objet d'affichage (c'est-à-dire, par exemple, dans une vue interactive, afficher la propriété avec le reste dans le tableau des entrepôts, pas des marchandises).

Filtres et tri


Pour chaque formulaire, le développeur peut définir des filtres et des commandes qui limiteront la liste des objets disponibles pour la visualisation / sélection sur le formulaire, ainsi que l'ordre dans lequel ils sont affichés.

Pour définir un filtre, vous devez spécifier une propriété qui sera utilisée comme critère de filtre. Le filtre sera appliqué à la table de cet objet, qui est le dernier pour l'ensemble d'objets transmis à l'entrée de cette propriété (c'est-à-dire, de la même manière qu'avec la définition de l'objet d'affichage de propriété). Dans ce cas, seuls les ensembles d'objets (séries) seront affichés pour lesquels les valeurs de propriété ne sont pas NULL. Par exemple, si nous ajoutons le filtre currentBalance (s, i) OU isActive (i) au formulaire ci-dessus:
FORM currentBalances ' '
OBJECTS s = Stock, i = Item // ,
PROPERTIES name(s), name(i), currentBalance(s, i)
FILTERS currentBalance(s, i) OR isActive(i)
;
Capture d'écran du formulaire


lors de l'affichage des produits, seuls les produits en équilibre ou marqués comme actifs seront affichés.

Les tris sont définis comme une liste de propriétés sur le formulaire dans lequel les objets doivent être affichés. Sinon, tout est similaire aux filtres.

Groupes d'objets


La plateforme a également la possibilité de combiner des objets en un groupe d'objets . Dans ce cas, le «produit cartésien» de ces objets sera affiché dans les tableaux / listes (c'est-à-dire pour deux objets - toutes les paires, trois objets - les triplets, etc.).
FORM currentBalances ' '
OBJECTS (s = Stock, i = Item) //
PROPERTIES name(s), name(i), currentBalance(s, i)
FILTERS currentBalance(s, i) OR isActive(i)
;
Capture d'écran du formulaire


Par conséquent, presque partout, avant et après, au lieu d'objets uniques du formulaire, vous pouvez utiliser des groupes d'objets.

En fait, c'est ce qui a été fait dans la documentation: le terme plus général «groupe d'objets» est utilisé partout, mais pour ne pas compliquer les choses dans cet article (et les groupes d'objets composés de plusieurs objets sont utilisés beaucoup moins souvent), il a été décidé d'oublier et de compter sur les groupes d'objets, qu'un groupe d'objets se compose toujours d'un seul objet et, par conséquent, utilise partout le terme «objet» au lieu du «groupe d'objets» et «ensemble d'objets» plus complexe.

Groupes de propriétés


Les propriétés du formulaire, comme les objets, peuvent également être combinées en groupes, qui, à leur tour, sont utilisés dans les représentations interactives (conception par défaut) et hiérarchiques du formulaire (à leur sujet un peu plus tard). Par défaut, la liaison d'une propriété à un groupe est globale (c'est-à-dire qu'elle est définie pour une propriété pour tous les formulaires à la fois), mais si nécessaire, pour les formulaires individuels, cette liaison peut être redéfinie.

Objets de colonne


Par défaut, une propriété est affichée exactement une fois dans son objet d'affichage. Dans ce cas, comme les valeurs des objets autres que l'objet d'affichage de cette propriété (nous les appelons supérieur), leurs valeurs actuelles sont utilisées. Cependant, la plate-forme a également la possibilité d'afficher une propriété plusieurs fois afin que les valeurs de certains objets supérieurs ne soient pas utilisées par leurs valeurs actuelles, mais tous les objets de la base de données qui conviennent aux filtres. Avec ce mappage de propriétés, une sorte de «matrice» est formée - (objet d'affichage) x (objets supérieurs). En conséquence, afin de créer une telle matrice, lors de l'ajout d'une propriété au formulaire, il est nécessaire d'indiquer quels objets supérieurs doivent être utilisés pour créer des colonnes (nous appellerons ces objets objets-en-colonnes).
FORM currentBalances ' '
// ,
//
OBJECTS s = Stock, i = Item
//
PROPERTIES name(i), currentBalance(s, i) COLUMNS (s) HEADER name(s)
FILTERS isActive(i), isActive(s)
;
Capture d'écran du formulaire


Donc, avec ce que le formulaire affiche, plus ou moins compris, passons à la façon dont il peut le faire.

Soumissions de formulaires


Il y a trois soumissions de formulaires:



Voir les captures d'écran
Interactif:



Imprimé:



Structuré:



  • Interactif. Une vue avec laquelle l'utilisateur peut interagir consiste à modifier les données et les objets actuels en déclenchant divers événements. En fait, cette représentation est généralement appelée un formulaire.
  • Imprimé. Il est généralement appelé un rapport - en téléchargeant toutes les données du formulaire et en les présentant sous forme graphique. Y compris avec la possibilité de les imprimer (d'où il tire son nom).
  • Structuré - représentation du formulaire dans différents formats structurés (JSON, XML, DBF, etc.). Généralement utilisé pour une intégration plus poussée avec d'autres systèmes.

Les représentations interactives et imprimées sont graphiques, c'est-à-dire qu'elles affichent les données reçues dans un espace bidimensionnel: le papier ou l'écran de l'appareil. En conséquence, chacune de ces représentations a une conception qui, en fonction de la représentation particulière, peut être définie à l'aide de mécanismes appropriés (à leur sujet un peu plus tard).

La présentation imprimée et structurée est statique, c'est-à-dire qu'ils lisent toutes les données au moment de l'ouverture du formulaire (par opposition à la présentation interactive, qui lit les données si nécessaire).

La description des performances commencera peut-être par la plus difficile - la présentation interactive.

Présentation interactive


Dans une vue interactive, les objets de formulaire sont affichés sous forme de tableau. Les lignes de ce tableau correspondent aux objets de la base de données qui satisfont aux filtres spécifiés, les colonnes, à leur tour, correspondent aux propriétés.

Toutefois, si nécessaire, la propriété peut être affichée non pas comme une colonne de tableau, c'est-à-dire pour toutes ses lignes, mais comme un champ distinct sur le formulaire, c'est-à-dire uniquement pour la valeur actuelle de l'objet de formulaire. Par exemple:
currentBalance ' ' (Stock s) = GROUP SUM currentBalance(s, Item i);
FORM currentBalances ' '
OBJECTS s = Stock, i = Item
// currentBalance(s) ,
PROPERTIES name(s), currentBalance(s) PANEL ,
name(i), currentBalance(s, i)
FILTERS currentBalance(s, i)
;
Capture d'écran du formulaire


La modification de la valeur actuelle d'un objet de formulaire se produit soit à la suite d'un changement de ligne actuelle de la table par un utilisateur, soit à la suite de l'exécution d'une action créée à l'aide d'un opérateur de recherche spécial (SEEK).

Notez que la façon dont une propriété est affichée dans un panneau ou une table, en règle générale, n'est pas définie séparément pour chaque propriété, mais dans son ensemble pour un objet de formulaire. Par conséquent, si l'objet de formulaire est marqué comme PANEL, toutes ses propriétés sont affichées dans le panneau (c'est-à-dire pour la valeur actuelle), sinon (par défaut) toutes ses propriétés sont affichées dans le tableau. Les propriétés sans paramètres et actions par défaut sont affichées dans le panneau.

Toutes les tables de la vue interactive sont dynamiques par défaut, c'est-à-dire que seul un nombre limité d'objets dans la base de données sont lus et les autres sont lus lorsque l'objet actuel dans la table change. Dans ce cas, le nombre d'objets affichés peut être déterminé automatiquement en fonction de la hauteur de la partie visible de la table ou défini explicitement par le développeur lors de la création du formulaire.

En outre, le formulaire dans la présentation interactive est complètement réactif, c'est-à-dire qu'il met automatiquement à jour toutes les données du formulaire lorsque des données qui les affectent changent (telles que React, uniquement dans le cas général). De plus, tout cela ne se fait pas par recalcul complet (comme dans le même React), mais de manière incrémentielle, de plus, sur le serveur SQL.

En général, c'est drôle quand, en comparant avec d'autres technologies, vous essayez d'inclure les trois principales exigences dans la tâche, les gens se font souvent des yeux ronds, comme si on leur demandait de lancer une personne dans l'espace. Bien que le non-respect de la deuxième exigence par tout utilisateur normal soit classé comme un bug, et la première et la troisième exigence sont que le formulaire développé fonctionne normalement lorsqu'au moins un peu de données apparaissent dans la base de données (plusieurs dizaines de milliers d'enregistrements, par exemple).

La présentation interactive est prise en charge à la fois en mode client Web (c'est-à-dire les applications Web dans un navigateur) et en mode client de bureau (applications Java). Le client de bureau, comme tout client natif, a une réactivité légèrement meilleure de l'interface, mais surtout, il vous permet de travailler avec du matériel et d'effectuer d'autres opérations qui ne sont pas disponibles dans le navigateur (principalement en raison de problèmes de sécurité).

Arbres d'objets


En plus des tableaux, la plateforme vous permet également d'organiser l'affichage des objets sous forme d'arbres, à la fois plats ("imbriqués" dans les autres tableaux) et récursifs (par exemple, les objets "imbriqués" dans la base de données).

Les arbres plats, en fait, sont une généralisation des tables, lorsque plusieurs tables sont "combinées" en une seule table à la fois:
FORM currentBalances ' '
TREE tree s = Stock, i = Item
//
//
PROPERTIES name(s), currentBalance(s),
name(i), currentBalance(s, i)
;
Capture d'écran du formulaire


Il s'agit d'un mécanisme relativement complexe et rarement utilisé dans la pratique, nous n'y reviendrons donc pas en détail.

Mais les arbres récursifs, au contraire, sont utilisés assez souvent (par exemple, pour implémenter des classificateurs). Pour afficher un objet de formulaire sous la forme d'un tel arbre, il est nécessaire de définir un filtre supplémentaire pour lui - une propriété dont la valeur pour les objets inférieurs doit être égale à l'objet supérieur. Initialement, l'objet supérieur est considéré comme NULL.
parent = DATA ItemGroup (ItemGroup) IN base;
group = DATA ItemGroup (Item) IN base;
//
level '' (ItemGroup child, ItemGroup parent) = RECURSION 1 AND child IS ItemGroup AND parent = child STEP 1 IF parent = parent($parent);
currentBalance ' ' (ItemGroup ig, Stock s) = GROUP SUM currentBalance(s, Item i) IF level(ig, group(i));

FORM currentBalances ' '
OBJECTS s=Stock PANEL //
PROPERTIES (s) name
TREE tree ig = ItemGroup PARENT parent, i = Item // /
PROPERTIES name(ig), currentBalance(ig, s)
PROPERTIES name(i), currentBalance(s, i)
FILTERS group(i) = ig
;
Capture d'écran du formulaire


Gestion du formulaire utilisateur


Pour assurer une meilleure ergonomie du système (notamment pour ne pas créer de formulaires pour tout le monde), une partie des opérations de mise en place de la présentation interactive du formulaire peut être effectuée par les utilisateurs eux-mêmes. Par exemple, ces opérations sont les suivantes:

  • mise en place de tableaux (colonnes visibles, ordre, polices, etc.),
  • créer des filtres et des tris personnalisés,
  • regrouper les données par valeurs de colonne,
  • imprimez un tableau et téléchargez-le dans Excel.

En outre, le développeur peut créer les soi-disant groupes de filtres, que l'utilisateur peut activer / désactiver indépendamment. Par exemple:
EXTEND FORM currentBalances //
FILTERGROUP stockActive // , , /
FILTER '' active(st) 'F11' // , F11
FILTERGROUP bal
FILTER ' ' currentBalance(st, sk) > 0 'F10'
FILTER ' ' currentBalance(st, sk) < 0 'F9'
FILTER ' ' currentBalance(st, sk) 'F8' DEFAULT
FILTER ' ' NOT currentBalance(st, sk) 'F7'
;
Ce ne sont pas toutes les possibilités de personnalisation du système par l'utilisateur, mais nous reviendrons sur les autres possibilités dans le troisième article, car la plupart d'entre elles n'ont toujours pas de relation directe avec la logique de présentation.

Notez que la fonctionnalité décrite ci-dessus se réfère plus probablement à la fonctionnalité des plates-formes ERP, qui est déjà complètement discordante avec le titre de l'article. D'autre part, comme mentionné dans le premier article, à l'avenir, le langage / la plate-forme prétend remplacer, y compris cette classe de plates-formes, il serait donc faux de ne pas mentionner du tout ces fonctionnalités.

Opérateurs d'objets


L'un des scénarios les plus courants d'utilisation d'un formulaire consiste à ajouter / supprimer un objet, ainsi qu'à le modifier dans un nouveau formulaire. Pour implémenter de tels scénarios, la plateforme dispose d'un ensemble prédéfini d'opérateurs qui vous permettent de créer les actions nécessaires en un mot directement dans l'opérateur de création de formulaire:

  • NOUVEAU - créer un objet
  • EDIT - modification d'un objet
  • NEWEDIT - création et modification d'un objet
  • DELETE - supprimer un objet

De plus, comme il est très souvent nécessaire d'effectuer ces actions dans une nouvelle session (si vous devez séparer les actions de création d'objets des actions sur le formulaire à partir duquel ces objets sont créés), la plateforme prend en charge le sucre syntaxique correspondant - les options NEWSESSION et NESTEDSESSION, qui fonctionnent de manière similaire aux opérateurs du même nom créer des actions, mais, comme les opérateurs eux-mêmes travaillant avec des objets, il n'est pas nécessaire que le développeur crée et nomme de nouvelles actions. Par exemple:
FORM teams
OBJECTS t=Team
// /
PROPERTIES (t) NEWSESSION NEW , EDIT , DELETE
OBJECTS p=Player
FILTERS team(p)=t
//
PROPERTIES (p) NEW , DELETE
;
Par défaut, lors de la modification d'un objet, le formulaire de modification est appelé, qui est automatiquement généré pour la classe de l'objet de formulaire transmis. Cependant, il est souvent nécessaire de redéfinir ce formulaire (par exemple, ajouter des informations supplémentaires, changer la conception, etc.) Pour ce faire, il suffit de créer le formulaire d'édition nécessaire et d'indiquer qu'il s'agit du formulaire par défaut pour éditer les objets d'une classe donnée:
FORM order ''
OBJECTS o = Order PANEL
PROPERTIES (o) date, number

OBJECTS d = OrderDetail
PROPERTIES (d) nameBook, quantity, price, NEW , DELETE
FILTERS order(d) = o

EDIT Order OBJECT o
;
De même, les formulaires de sélection des objets d'une classe donnée sont redéfinis.

Conception de forme


Comme avec la plupart des interfaces graphiques existantes, la conception d'une présentation interactive d'un formulaire est une hiérarchie dont les nœuds sont des composants. À leur tour, les composants peuvent être:

  • conteneurs - composants qui contiennent d'autres composants.
  • composants de base - représentations graphiques des éléments de base: tableaux, panneaux de propriétés, groupes de filtres, etc.

Le mécanisme d'organisation des composants à l'intérieur des conteneurs répète essentiellement la disposition de boîte flexible CSS (et est implémenté dans le client Web avec lui), nous ne nous attarderons donc pas sur ce mécanisme en détail.

Notez que la conception du formulaire n'est généralement pas créée à partir de zéro (car elle prend beaucoup de temps). En règle générale, une conception de formulaire est automatiquement créée en fonction de la structure du formulaire, puis le développeur ne le modifie que légèrement: par exemple, ajoute un nouveau conteneur et lui transfère des composants existants:

Exemple de conception de formulaire par défaut
FORM myForm 'myForm'
OBJECTS myObject = myClass
PROPERTIES (myObject) myProperty1, myProperty2 PANEL
FILTERGROUP myFilter
FILTER 'myFilter' myProperty1(myObject)
;
La hiérarchie des conteneurs et des composants dans la conception par défaut ressemblera à ceci:


FORM myForm ' '
OBJECTS u = CustomUser
PROPERTIES (u) name, NEW , DELETE

OBJECTS c = Chat
PROPERTIES (c) message, NEW , DELETE
FILTERS user(c) = u
;

DESIGN myForm {
NEW middle FIRST {
type = CONTAINERH ;
fill = 1 ; //
MOVE BOX (u);
MOVE BOX (c);
}
}
Capture d'écran du formulaire


Form Design 2.0 (React)


En regardant les représentations interactives des formulaires dans les captures d'écran ci-dessus (ou, par exemple, dans une démo en ligne), vous pouvez voir que la conception actuelle du formulaire dans l'interface utilisateur est, disons, assez ascétique. Bien entendu, cela n'a jamais été un problème particulier pour les systèmes d'information, où les principaux utilisateurs sont des employés ou des partenaires de l'entreprise propriétaire de ces systèmes d'information. De plus, malgré l'ascétisme, le mécanisme actuel de conception de formulaire vous permet d'implémenter des cas très difficiles, par exemple POS:

Capture d'écran du formulaire


Mais s'il s'agit, par exemple, du SaaS B2B ou même du B2C (par exemple, certains services bancaires par Internet), des questions commencent immédiatement à apparaître sur la façon de rendre le design plus ergonomique.

Au stade actuel, pour résoudre ce problème, une bibliothèque javascript spéciale a été développée, dont la tâche principale est de créer et de mettre à jour un objet js spécial contenant des données de formulaire. En conséquence, cet objet peut être utilisé comme état pour le composant React et ainsi créer toute conception et toute interactivité supplémentaire de la forme en cours de développement. Par exemple:

Exemple de formulaire React (sur codesandbox)

Ou un exemple plus complexe - avec des listes déroulantes et en utilisant l'API REST (ou plutôt sans état) pour cela:

Exemple de formulaire React avec listes déroulantes (sur codesandbox)

Certes, le problème avec cette approche est que les formulaires des exemples ci-dessus ne seront pas intégrés dans l'interface générale de la plateforme ou dans le flux général de contrôle des actions. Par conséquent, l'une des tâches les plus immédiates dans le développement de la plateforme est la traduction du mécanisme de conception de formulaire dans le schéma utilisé dans la conception du rapport (présentation imprimée):

  • La plate-forme génère automatiquement une conception React basée sur la structure du formulaire comme dans l'exemple ci-dessus.
  • Si nécessaire, le développeur peut l'enregistrer et le modifier à sa guise. Par conséquent, la plate-forme utilisera cette conception modifiée lors de l'ouverture du formulaire.

De plus, cette approche permettra de générer des formulaires React Native, et ainsi de créer des applications mobiles natives. Bien que ce problème (avec les applications mobiles natives), nous n'ayons pas encore travaillé très profondément.

Certes, nous notons que l'ancien mécanisme de conception sera également pris en charge, car, pour les mêmes applications métier, il remplit parfaitement sa fonction.

Événements de formulaire


Les événements de formulaire sont le deuxième mécanisme clé après les événements de domaine, qui est chargé de déterminer quand effectuer des actions.

La plate-forme a tout un ensemble de divers événements de formulaire qui surviennent à la suite de certaines actions de l'utilisateur, mais dans cet article, nous n'envisagerons qu'un seul, le plus souvent utilisé - l'événement CHANGE. Cet événement se produit lorsque l'utilisateur a lancé un appel de modification / action de propriété, par exemple, en appuyant sur une touche non système du clavier, en se trouvant dans le champ de la propriété en cours de modification ou en cliquant sur ce champ avec la souris.

En ce qui concerne les événements du domaine, pour les événements du formulaire, vous pouvez spécifier le traitement - l'action qui sera effectuée lorsque l'événement spécifié se produit. Notez que la plupart des événements du formulaire ont déjà un traitement par défaut qui implémente le comportement le plus attendu par l'utilisateur (par exemple, avec l'événement CHANGE susmentionné, demandez l'entrée de l'utilisateur et modifiez la propriété à la valeur entrée). Cependant, dans la pratique, des situations surviennent parfois lorsque, pour un événement de formulaire, il est nécessaire de spécifier un traitement spécifique, par exemple:
changeQuantity (Order o, Book b) {
INPUT q = INTEGER DO { //
IF lastOrderDetail(o, b) THEN { // ,
IF q THEN //
quantity(OrderDetail d) <- q IF d = lastOrderDetail(o, b) WHERE order(d) = o AND book(d) = b; //
ELSE // -
DELETE OrderDetail d WHERE order(d) == o AND book(d) == b;
} ELSE
IF q THEN
NEW d = OrderDetail { //
order(d) <- o;
book(d) <- b;
quantity(d) <- q;
}
}
}

EXTEND FORM order
OBJECTS b = Book
PROPERTIES name(b) READONLY , quantity(o, b) ON CHANGE changeQuantity(o, b)
;
, – , ( , , , paste , ). . .


, , , , , , , , INPUT.

, , , , , , . Par exemple:
FORM order
OBJECTS o = Order
PROPERTIES (o) customer ON CHANGE {
INPUT s = STRING DO {
customer(o) <- s;
IF s THEN
MESSAGE 'Customer changed to ' + s;
ELSE
MESSAGE 'Customer dropped' ;
}
}
;
, , :

  • ,
  • ( ),
  • ,
  • .

, INPUT – . (ASK):
DELETE Order o WHERE selected(o);
ASK ' ' + ( GROUP SUM 1 IF DROPPED (Order o)) + ' . ?' DO {
APPLY ;
}
(DIALOG) , , , , .

.


, . , , . , «» . , A B, A B, A A, B (A, B) B, A B ( «» ).

, , :

  • :
  • , , A B, A ( ).

Par exemple:
FORM myForm 'myForm'
OBJECTS A, B SUBREPORT , C, D, E
PROPERTIES f(B, C), g(A, C)
FILTERS c(E) = C, h(B, D)
;


, , , , .


LGPL – JasperReports.

, JasperReports , . , :

  • «» ( , O1, O2, O3,… On, O2 – O1, O3 – O2 ..) ;
  • , .

«» , SUBREPORT ( , -):



:
FORM shipment
OBJECTS s=Shipment //
PROPERTIES (s) date, customer = nameCustomer, stock = nameStock // , ( customer) ( stock)
PROPERTIES total = ( GROUP SUM quantity(ShipmentDetail d)*price(d) IF shipment(d)=s) //
OBJECTS sd=ShipmentDetail //
FILTERS shipment(sd) = s //
PROPERTIES (sd) index, item = nameItem // , ( item)
PROPERTIES (sd) price, quantity // ,
PROPERTIES sum '' = (quantity(sd) * price(sd)) // - * ( sum)
;

run() {
// 12345
PRINT shipment OBJECTS s = ( GROUP MAX Shipment s IF number(s) = '12345' )
XLSX TO exportFile;
}



JasperReports (, lsFusion). , . , JasperSoft Studio.

, lsFusion- IDEA, Eclipse, ( Eclipse JasperReports ). IDEA , language injection, jrxml-, , , , , , . , , Eclipse GrammarKit autocomplete (, ), stub-, lazy chameleon- ( ), , . .


() :

  • (XML, JSON) – , () -.
  • (DBF, CSV, XLS) – - . parent, «» -.

, (- ), , , ( ). . , .


– XML, JSON. , , JSON ( XML ).

/ JSON , : JSON-, – , – . :

JSON
/ , / :

  • / : , .
  • X:
    • X , , . Dans ce cas:
      • X ,
      • ( ) X.

, / :

 JSON  ::= { JSON  ,   /    } JSON  ,   /  ::= JSON  1 | JSON   1 | JSON   1 JSON  2 | JSON   2 | JSON   2 ... JSON  M | JSON   M | JSON   M JSON  ::= "   " :   JSON   ::= "  " : { JSON   ,   /  } JSON   ::= "  " : [ { JSON   ,   /  1 }, { JSON   ,   /  2 }, ... { JSON   ,   /  N }, ] 


:
GROUP money;

FORM shipment
OBJECTS dFrom= DATE , dTo= DATE
OBJECTS s=Shipment //
PROPERTIES (s) date, customer = nameCustomer, stock = nameStock // , ( customer) ( stock)
FILTERS dFrom <= date(s) AND date(s) <= dTo //
OBJECTS sd=ShipmentDetail //
FILTERS shipment(sd) = s //
PROPERTIES (sd) IN money index, item = nameItem, price, quantity // , ( item), , money
;

run() {
EXPORT shipment OBJECTS dFrom = 2019_02_20 , dTo = 2019_04_28 ; //
}
 { "s": [ { "date": "21.02.19", "sd": [ { "money": { "item": " 3", "quantity": 1, "price": 5, "index": 1 } } ], "stock": " 2", "customer": " 2" }, { "date": "15.03.19", "sd": [ { "money": { "item": " 1", "quantity": 1, "price": 5, "index": 1 } }, { "money": { "item": " 2", "quantity": 1, "price": 10, "index": 2 } }, { "money": { "item": " 3", "quantity": 1, "price": 15, "index": 3 } }, { "money": { "item": " 4", "quantity": 1, "price": 20, "index": 4 } }, { "money": { "item": "Milk", "quantity": 1, "price": 50, "index": 5 } } ], "stock": " 1", "customer": " 3" }, { "date": "04.03.19", "sd": [ { "money": { "item": " 1", "quantity": 2, "price": 4, "index": 1 } }, { "money": { "item": " 2", "quantity": 3, "price": 4, "index": 2 } }, { "money": { "item": " 1", "quantity": 2, "price": 5, "index": 3 } } ], "stock": " 1", "customer": " 2" }, { "date": "04.03.19", "sd": [ { "money": { "item": " 1", "quantity": 3, "price": 1, "index": 1 } }, { "money": { "item": " 2", "quantity": 2, "price": 1, "index": 2 } } ], "stock": " 1", "customer": " 2" }, { "date": "14.03.19", "sd": [ { "money": { "item": " 2", "quantity": 1, "price": 2, "index": 1 } } ], "stock": " 1", "customer": " 2" }, { "date": "17.04.19", "sd": [ { "money": { "item": " 2", "quantity": 5, "price": 6, "index": 1 } }, { "money": { "item": " 1", "quantity": 2, "price": 6, "index": 2 } } ], "stock": " 1", "customer": " 1" }, { "date": "21.02.19", "sd": [ { "money": { "item": " 3", "quantity": 1, "price": 22, "index": 1 } } ], "stock": " 2", "customer": " 1" }, { "date": "21.02.19", "sd": [ { "money": { "item": " 3", "quantity": 1, "price": 22, "index": 1 } } ], "stock": " 2", "customer": " 1" }, { "date": "20.02.19", "sd": [ { "money": { "item": " 3", "quantity": 1, "price": 22, "index": 1 } } ], "stock": " 2", "customer": " 1" } ] } 


, JSON JSON-. , , IDE JSON , – JSON. , ( , , JSON- ), . / JSON .


, :

  • .
  • – , .

, , , ( ). , , , / :
run() {
EXPORT XLSX FROM item = upper(name(Item i)), currentBalance(i, Stock s),
stock = name(s), barcode(i), salePrice(i)
WHERE (name(i) LIKE '%%' OR salePrice(i) > 10 ) AND currentBalance(i, s);
}
, SELECT SQL. , , , ( , ).

, , , – .


, , , :

  • – .
  • – : .
run(Genre g) {
SHOW booksByGenre OBJECTS g=g;
PRINT booksByGenre OBJECTS g=g;
EXPORT booksByGenre OBJECTS g=g;
}
– ( ) . .

(SHOW, DIALOG)


:

  • (WAIT) – , , , , .
  • (NOWAIT) – .

.

, :

  • (FLOAT) – .
  • (DOCKED) – System.forms.

, – .

, , (DIALOG). (, , ), , , .

, , (INPUT), ( ), , , ( ), , , ( ).
FORM booksByGenre
OBJECTS g = Genre PANEL
PROPERTIES (g) name
OBJECTS b = Book
PROPERTIES (b) name
FILTERS genre(b) = g
;

EXTEND FORM ordersByGenre
PROPERTIES (o) nameBook
ON CHANGE {
DIALOG booksByGenre OBJECTS g = g, b = book(o) INPUT DO
book(o) <- b;
}
;

(PRINT)


( ) , JasperReports : DOC, DOCX, XLS, XLSX, PDF, HTML, RTF , JasperReports. , , , , ( , ).

, - (PREVIEW), , / . , , .

(EXPORT, IMPORT)


, , , : XML, JSON, DBF, CSV, XLS, XLSX. .

, , , – . () , () , .

– , , « », :
  • .
  • ( , , , , , TRUE f(a) = b – f(a) b)

TRUE ( , , 0 , .., , , ).
// ,
inn = DATA LOCAL BPSTRING [ 9 ] (Shipment);
barcode = DATA LOCAL BPSTRING [ 13 ] (ShipmentDetail);

FORM shipments
OBJECTS s=Shipment EXTID 'shipments' // EXTID s, shipments
PROPERTIES (s) number, date, inn
OBJECTS sd=ShipmentDetail EXTID 'detail' // EXTID sd, detail
FILTERS shipment(sd) = s // shipment detail
PROPERTIES (sd) barcode, price, quantity
;

run() {
FOR jsonFile = JSONFILE ( '\{ shipments : [ ' + // jsonFile / run, {} escape'
' \{number : "13423", date : "01.01.2019", inn : "2", detail : [\{ barcode : "141", quantity : 5, price : 10 \}, \{ barcode : "545", quantity : 2, price : 11 \}] \},' +
' \{number : "12445", date : "01.02.2019", inn : "1", detail : [\{ barcode : "13", quantity : 1, price : 22 \}] \} ]\}' )
DO {
IMPORT shipments FROM jsonFile; //
FOR BPSTRING [ 9 ] inn = inn(Shipment s) DO { // inn
customer(s) <- legalEntityINN(inn); // INN
stock(s) <- GROUP MAX st AS Stock; // - ( id)
}
FOR barcode(Item item) = barcode(ShipmentDetail sd) DO //
item(sd) <- item;

APPLY ;
exportString() <- IF canceled() THEN applyMessage() ELSE ' ' ;
}
}

Navigateur


, , . , , , . . ( ), .

- , ( ). , , ( ). .


. , — .

– , . , , . , , .



.



100x100 . , , . , «» . , ( ). , . , .

. , - , , .

:
  • , – , , .
  • – forms, log, status, root, toolbar, tree, (, root, , )
FORM items;
FORM stocks;
FORM legalEntities;
FORM shipments;
hello() { MESSAGE 'Hello world' ; }
hi() { MESSAGE 'Hi' ; }

NAVIGATOR {
NEW FOLDER catalogs '' WINDOW toolbar { // ,
NEW items; // - items,
}
catalogs { //
NEW FORM stocksNavigator '' = stocks; // - stocksNavigator stocls catalogs
NEW legalEntities AFTER items; // - legalEntities catalogs items
NEW shipments;
}
NEW FOLDER documents '' WINDOW toolbar { // ,
// root,
//
NEW ACTION hi; // -
NEW ACTION h=hello; // -
MOVE shipments BEFORE h; // shipments catalogs document hello
}
}


, – , , , , , ERP-. , , « », : ? Vraiment? ? , -, lsFusion language-based ( SQL ABAP), library-based ( Java 1C) / . , , – domain-specific , . -, , , , . : , , , . , , , ( ).

. – , :

  • : , , , , , ;
  • : , .

, :

  • . «» : , , / . , , , , , , .
  • . control flow . , – .
  • SQL ( ORM). , , , .

Conclusion


, , (, , , , tutorial, , , ). , , ( ) «», , lsFusion.

, , – . , . , , « . .», , ( - ).

. : « ». , . , , lsFusion SQL-. , , , – , - , . , SQL- ( ). , . , , ( , ), , , . «?», « ...?» «?».

UPD: .

Source: https://habr.com/ru/post/fr460141/


All Articles