Nous construisons une interface pour saisir des documents par sélection

image

Dans diverses applications commerciales, la tâche de saisie de documents se pose souvent. En règle générale, un document se compose d'un en-tête et de quelques lignes, chacune faisant référence à un objet (par exemple, un produit). Le plus souvent, un tableau standard est utilisé pour saisir des enregistrements dans un document, dans lequel l'utilisateur peut ajouter et supprimer des lignes, ainsi que modifier leur contenu.

Cependant, dans certains cas, un tel schéma n'est pas toujours pratique pour les utilisateurs. Dans cet article, je vais vous expliquer comment créer une interface utilisateur pratique en utilisant la technique de sélection des produits.

Défi


En règle générale, lors de la saisie de documents pour l'utilisateur, il existe une restriction sur l'ensemble d'objets qui peuvent y être ajoutés. Dans ce cas, il est logique pour l'utilisateur d'afficher une liste de ces objets avec la possibilité de les inclure (et de les exclure) rapidement dans le document. Dans les colonnes, pour chacun des objets, il est également pratique d'afficher les données nécessaires pour prendre une décision sur la nécessité de l'inclure dans le document. Et enfin, avec l'objet, il est souvent nécessaire d'entrer sa quantité.

Prenons trois types de documents que l'on retrouve souvent dans les applications métier:

  1. Bon de commande . Dans ces documents, il est logique pour l'utilisateur d'afficher une liste de toutes les marchandises disponibles à la commande auprès du fournisseur. Dans les colonnes, il est pratique d'afficher le solde actuel, les ventes pour un certain intervalle, la quantité commandée pour l'achat et la vente.
  2. Commande client . Ici, le plus souvent, une liste des marchandises qui sont sur le solde de l'entrepôt sélectionné et disponibles à la vente pour le client sélectionné est affichée. Les prix actuels doivent également être indiqués.
  3. Variation des soldes . Ce document est utilisé pour ajuster les soldes courants en cas de divergence avec le montant réel. Tous les produits sont généralement affichés dans la sélection, avec la possibilité d'entrer le solde réel pour chacun d'entre eux. Dans le même temps, un produit est ajouté au document avec un montant égal à la différence entre le solde réel et le solde actuel.

Solution


Ensuite, je montrerai comment implémenter rapidement et facilement cette logique basée sur la plate-forme lsFusion ouverte et gratuite. Par exemple, créons une logique pour saisir un bon de commande.

Pour commencer, ajoutez un guide produit via l'interface CRUD standard:
CLASS Product '' ;
name '' = DATA STRING [ 50 ] (Product);

FORM product ''
OBJECTS p = Product PANEL
PROPERTIES (p) name

EDIT Product OBJECT p //
;

FORM products ''
OBJECTS p = Product
PROPERTIES (p) READONLY name
PROPERTIES (p) NEWSESSION NEW , EDIT , DELETE

LIST Product OBJECT p // ,
;

NAVIGATOR {
NEW products;
}

Nous créons le concept de fournisseur, et sous forme d'édition nous donnons la possibilité de choisir les produits avec lesquels il travaille:
CLASS Supplier '' ;
name '' = DATA STRING [ 50 ] (Supplier);

in '' = DATA BOOLEAN (Supplier, Product); // TRUE,

FORM supplier ''
OBJECTS s = Supplier PANEL
PROPERTIES (s) name

OBJECTS p = Product //
PROPERTIES in(s, p), name(p) READONLY //

EDIT Supplier OBJECT s
;

FORM suppliers ''
OBJECTS s = Supplier
PROPERTIES (s) READONLY name
PROPERTIES (s) NEWSESSION NEW , EDIT , DELETE

LIST Supplier OBJECT s
;

NAVIGATOR {
NEW suppliers;
}

Déclarez la logique de commande avec les lignes:
CLASS Order '' ;
date '' = DATA DATE (Order);
number '' = DATA INTEGER (Order);

supplier '' = DATA Supplier (Order);
nameSupplier '' (Order o) = name(supplier(o));

CLASS OrderDetail ' ' ;
order '' = DATA Order (OrderDetail) NONULL DELETE ;

Ajoutez des produits et des quantités aux lignes:
product '' = DATA Product (OrderDetail);
nameProduct '' (OrderDetail d) = name(product(d));

quantity '-' = DATA NUMERIC [ 14 , 3 ] (OrderDetail);

Nous procédons directement à la construction du formulaire d'édition de commande dont nous avons besoin. Tout d'abord, ajoutez-y la commande elle-même et ses lignes:
FORM order ''
OBJECTS o = Order PANEL
PROPERTIES (o) date, number, nameSupplier

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

EDIT Order OBJECT o
;

Ainsi, nous obtenons une interface standard pour travailler avec les lignes de commande en ajoutant et en supprimant.
Ensuite, nous ajoutons la fonctionnalité de sélection dont nous avons besoin à ce formulaire. Pour ce faire, créez d'abord un objet sur le formulaire avec une liste de toutes les marchandises, dans lesquelles nous filtrons uniquement celles qui sont autorisées à commander auprès du fournisseur sélectionné:
EXTEND FORM order
OBJECTS p = Product
PROPERTIES name(p) READONLY
FILTERS in(supplier(o), p) //
;

Nous configurons le design de sorte que les lignes de commande et la sélection soient affichées sous forme d'onglets d'un conteneur:
DESIGN order {
OBJECTS {
NEW pane { //
fill = 1 ; //
type = TABBED ; // ,
MOVE BOX (d); //
MOVE BOX (p) { //
caption = '' ;
}
}
}
}

Nous construisons des propriétés auxiliaires pour afficher et saisir la quantité par l'utilisateur dans la colonne correspondante:
quantity '-' (Order o, Product p) =
GROUP SUM quantity(OrderDetail d) BY order(d), product(d);
lastOrderDetail ' ' (Order o, Product p) =
GROUP LAST OrderDetail d ORDER d BY order(d), product(d);

La première propriété considère la quantité pour cette commande et ce produit dans ce document. Le second trouve la dernière ligne (l'utilisateur peut entrer plusieurs lignes avec un seul produit).
Ensuite, nous créons une action qui traitera l'utilisateur saisissant la valeur dans la colonne correspondante dans l'onglet de sélection:
changeQuantity ' -' (Order o, Product p) {
INPUT q = NUMERIC [ 14 , 3 ] DO { //
IF lastOrderDetail(o, p) THEN { // ,
IF q THEN //
quantity(OrderDetail d) <- q IF d = lastOrderDetail(o, p)
WHERE order(d) = o AND product(d) = p; //
ELSE // -
DELETE OrderDetail d WHERE order(d) = o AND product(d) == p;
} ELSE
IF q THEN
NEW d = OrderDetail { //
order(d) <- o;
product(d) <- p;
quantity(d) <- q;
}
}
}

Enfin, nous ajoutons une colonne au formulaire, indiquant l'action qui doit être effectuée lorsque l'utilisateur essaie de changer sa valeur:
EXTEND FORM order
PROPERTIES (o, p) quantity ON CHANGE changeQuantity(o, p)
;

Il ne reste plus qu'à dessiner un formulaire avec une liste de commandes et à l'ajouter au navigateur:
FORM orders ''
OBJECTS o = Order
PROPERTIES (o) READONLY date, number
PROPERTIES (o) NEWSESSION NEW , EDIT , DELETE
;

NAVIGATOR {
NEW orders;
}

Le formulaire résultant ressemblera à ceci:

image

Toutes les modifications apportées à l'un des onglets seront automatiquement répercutées sur l'autre.

Dans les systèmes ERP réels, beaucoup plus de colonnes et d'autres éléments sont ajoutés au formulaire. Par exemple, dans l'une de ces implémentations, cela ressemble à ceci:

image


Voyons comment cette fonctionnalité est implémentée dans d'autres applications métier.

1C


Dans différentes configurations de 1C, la logique de sélection présente certaines différences, mais le principe est plus ou moins le même. Il y a un bouton Sélectionner sur le formulaire d'édition de document, qui ouvre une boîte de dialogue avec le choix des marchandises:

image

Dans cette boîte de dialogue, vous pouvez sélectionner les marchandises comme suit:

image

Il y a au moins deux inconvénients dans ce mécanisme.

Tout d'abord, pour ajouter la quantité du produit au document (et le plus souvent l'utilisateur connaît déjà la quantité qu'il souhaite ajouter), vous devez d'abord double-cliquer sur le produit, l'ajouter aux lignes, puis modifier la quantité dans les lignes. De plus, l'utilisateur ne voit pas dans la liste des produits si ce produit a déjà été ajouté et avec quelle quantité. En outre, un tel schéma «mange» de l'espace supplémentaire, car vous devez afficher deux tableaux sur un écran au lieu d'un.

Deuxièmement, une fois que les lignes ont été ajoutées directement au document, si vous cliquez à nouveau sur le bouton Sélectionner, la sélection commencera «à partir de zéro». Il n'apparaîtra aucune ligne dans le tableau du bas, et au moment de la resélection, il ne sera pas clair ce qui est déjà dans le document et ce qui ne l'est pas. Il viole également l'une des règles importantes lors de la construction de l'interface: si l'utilisateur se trompe, il doit avoir la possibilité de revenir en arrière et de corriger l'erreur. Ici, il s'avère qu'il ne pourra pas revenir à la sélection de marchandises ayant les mêmes données qu'avant d'appuyer sur le bouton ajouter au document.

Cependant, il est possible dans 1C que vous puissiez implémenter le schéma ci-dessus, mais pour une raison quelconque, dans les configurations typiques disponibles sur le site avec des démos, une autre est utilisée.

Microsoft Dynamics 365


En tant que solution, j'ai pris Retail (seulement en elle, j'ai trouvé des fonctionnalités similaires disponibles sur https://trials.dynamics.com ). Dans ce document, vous pouvez appeler la sélection sur le formulaire Commandes d'achat de deux manières:

image

Le premier bouton est implémenté selon le schéma 1C (bien qu'il y ait beaucoup moins de colonnes), donc je ne m'y attarderai pas.

Le deuxième bouton fait apparaître une boîte de dialogue avec une liste de produits où la quantité peut déjà être entrée dans des colonnes. Mais cela se fait d'une manière très particulière.

Tout d'abord, par défaut, seuls les codes produits y sont affichés (même sans noms). Bien sûr, je comprends que vous pouvez créer des extensions et y inclure d'autres colonnes, mais voir ce comportement dans la version de base est un peu étrange.

Deuxièmement, Microsoft était apparemment tellement emporté par la conception que, pour une raison quelconque, ils ont marqué le traitement normal des données de défilement, les astuces de traitement et les événements. Cela ressemble à ceci:

image

Pendant le fonctionnement normal du clavier, les séries actuelles sont constamment commutées lors de la relecture des données. Dans le même temps, vous ne pouvez commencer à entrer qu'après avoir attendu tout le traitement de l'événement, sinon la quantité n'est pas du tout entrée ou est entrée dans la mauvaise série. Comment les vrais utilisateurs travaillent avec cela reste un mystère pour moi. Apparemment, peu de gens utilisent cette fonctionnalité spécifiquement. Eh bien, ou ce sont les limites de la démo, mais en fonctionnement, tout fonctionne bien.

Conclusion


Le schéma de saisie des documents décrit via un onglet de sélection séparé a été bien apprécié par nos utilisateurs.

En pratique, ce concept peut être compliqué de diverses manières. Par exemple, au lieu d'une liste de produits, affichez une liste d'articles avec la possibilité de saisir différentes quantités pour des caractéristiques individuelles. Ou dans l'un des documents, il y avait des entrepôts dans les rangées, et il était nécessaire de définir les quantités pour chacun de ces entrepôts dans les colonnes.

Un tel schéma peut être implémenté, par exemple, dans le même React, en utilisant les lignes actuelles du document comme état et en l'affichant avec deux composants dans deux onglets différents. Mais il ne faut pas oublier qu'il peut y avoir beaucoup d'entrées dans le tableau avec des marchandises, et vous devrez implémenter le soi-disant «défilement infini». De plus, il est conseillé d'ajouter à l'utilisateur la possibilité de trier et filtrer les produits de cette liste (et cela doit être fait sur le serveur, et non sur le client, afin de ne pas y glisser de données supplémentaires). Tout cela devient une tâche plutôt banale pour le développeur.

Dans la plate-forme lsFusion, cette fonctionnalité est implémentée prête à l'emploi et ne nécessite que le code décrit ci-dessus. Vous pouvez essayer comment cela fonctionne, et si vous le souhaitez, vous pouvez modifier le code en ligne sur la page correspondante. Voici le code source complet, qui peut être inséré dans l'onglet Plateforme puis en cliquant sur Lecture:

Code source
CLASS Product '' ;
name '' = DATA STRING [ 50 ] (Product);

FORM product ''
OBJECTS p = Product PANEL
PROPERTIES (p) name

EDIT Product OBJECT p //
;

FORM products ''
OBJECTS p = Product
PROPERTIES (p) READONLY name
PROPERTIES (p) NEWSESSION NEW , EDIT , DELETE

LIST Product OBJECT p // ,
;

NAVIGATOR {
NEW products;
}

CLASS Supplier '' ;
name '' = DATA STRING [ 50 ] (Supplier);

in '' = DATA BOOLEAN (Supplier, Product); // TRUE,

FORM supplier ''
OBJECTS s = Supplier PANEL
PROPERTIES (s) name

OBJECTS p = Product //
PROPERTIES in(s, p), name(p) READONLY //

EDIT Supplier OBJECT s
;

FORM suppliers ''
OBJECTS s = Supplier
PROPERTIES (s) READONLY name
PROPERTIES (s) NEWSESSION NEW , EDIT , DELETE

LIST Supplier OBJECT s
;

NAVIGATOR {
NEW suppliers;
}

CLASS Order '' ;
date '' = DATA DATE (Order);
number '' = DATA INTEGER (Order);

supplier '' = DATA Supplier (Order);
nameSupplier '' (Order o) = name(supplier(o));

CLASS OrderDetail ' ' ;
order '' = DATA Order (OrderDetail) NONULL DELETE ;

product '' = DATA Product (OrderDetail);
nameProduct '' (OrderDetail d) = name(product(d));

quantity '-' = DATA NUMERIC [ 14 , 3 ] (OrderDetail);

FORM order ''
OBJECTS o = Order PANEL
PROPERTIES (o) date, number, nameSupplier

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

EDIT Order OBJECT o
;

EXTEND FORM order
OBJECTS p = Product
PROPERTIES name(p) READONLY
FILTERS in(supplier(o), p) //
;

DESIGN order {
OBJECTS {
NEW pane { //
fill = 1 ; //
type = TABBED ; // ,
MOVE BOX (d); //
MOVE BOX (p) { //
caption = '' ;
}
}
}
}

quantity '-' (Order o, Product p) =
GROUP SUM quantity(OrderDetail d) BY order(d), product(d);
lastOrderDetail ' ' (Order o, Product p) =
GROUP LAST OrderDetail d ORDER d BY order(d), product(d);

changeQuantity ' -' (Order o, Product p) {
INPUT q = NUMERIC [ 14 , 3 ] DO { //
IF lastOrderDetail(o, p) THEN { // ,
IF q THEN //
quantity(OrderDetail d) <- q IF d = lastOrderDetail(o, p)
WHERE order(d) = o AND product(d) = p; //
ELSE // -
DELETE OrderDetail d WHERE order(d) = o AND product(d) == p;
} ELSE
IF q THEN
NEW d = OrderDetail { //
order(d) <- o;
product(d) <- p;
quantity(d) <- q;
}
}
}

EXTEND FORM order
PROPERTIES (o, p) quantity ON CHANGE changeQuantity(o, p)
;

FORM orders ''
OBJECTS o = Order
PROPERTIES (o) READONLY date, number
PROPERTIES (o) NEWSESSION NEW , EDIT , DELETE
;

NAVIGATOR {
NEW orders;
}

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


All Articles