MercatorPenguin 2.8 permet la création et la modification de documents de la gestion commerciale avec lignes et pour tous les types de documents (devis, commandes, livraisons, factures, ... ventes-achats). Contrairement à la modification de documents sans ligne, ceci requiert une licence spécifique par appareil mobile utilisant cette fonctionnalité (PENGUINBILLING=...) ainsi qu'une version 10.7 ou ultérieure de Mercator. Toutefois, il faut noter qu'un paramétrage "sans ligne" est compatible avec un paramétrage "avec ligne". Seule la présence d'un paramétrage XAML ListViewCell fait la différence.
La gestion des licences contient un système d'activation et de désactivation des appareils mobiles pouvant utiliser cette fonctionnalité. Cette désactivation peut être effectuée, soit via la page de maintenance de MercatorPenguinServer, soit via le bundle de maintenance. Dans tous les cas, MercatorPenguinServer doit pouvoir se connecter à un web service hébergé chez Mercator (host = restapi.mercator.eu) sur le port https (TCP 443). La fonctionnalité décrite dans cette page n'est pas disponible si MercatorPenguinServer ne peut communiquer avec ce serveur.
Note concernant le RGPD à ce propos : dans le cadre de cette gestion des licences par appareil mobile, seules ces informations sont transmises :
- l'identifiant unique de l'appareil, généré aléatoirement : par exemple 3f2504e0-4f89-11d3-9a0c-0305e82c3301
- le nom du host hébergeant MercatorPenguinServer : par exemple penguin.mercator.eu
- le nom de la configuration (nom du fichier ini de MercatorPenguinServer).
Aucune donnée personnelle de l'utilisateur, ni aucune donnée contenue dans Mercator ou MercatorPenguin ne sont communiquées.
L'accès à un document se fait toujours au départ d'un bundle de type "Liste", via un bouton BillingEditButton, dont les propriétés suivantes doivent être spécifiées :
- BillingTypeVA = V pour une vente, A pour un achat
- BillingId = id du document dans la table PIEDS_V/A
- BillingJournal = le journal du document
- BillingPiece = le n° du document
- BillingType = la valeur de TYPE dans PIEDS_V/A.
- BillingTargetSequence (optionnelle) = journal de destination pour effectuer une transformation vers cette séquence
<m:BillingEditButton Text="Modifier" Font="Small"
BillingTypeVA="V" BillingId="@ID" BillingJournal="@JOURNAL" BillingPiece="@PIECE" BillingType="@TYPE" />
Exemple de bundle liste : Brouillons_edition_lignes.pngb
Le layout d'une fenêtre de modification dans MercatorPenguin doit être créé en XAML, au départ de la fiche de la séquence correspondante "Outils > Séquences > Ventes ou Achats" de Mercator et en utilisant cet élément de ce bouton déroulant :
Un écran de document de la gestion commerciale avec lignes requiert deux paramétrages XAML :
- la saisie proprement dite : les informations du pied et de la ligne en cours d'édition
- le format de chaque cellule de la ListView affichant les lignes du document en cours.
Exemple de paramétrage XAML Tab 1
<StackLayout Margin="10, 10, 10, 10">
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<m:EditEntry Placeholder="Client" Source="id_cli" Margin="0,0,25,0" WidthRequest="120" />
<m:TargetSigLabel Origin="id_cli" Font="Medium" VerticalOptions="Center" HorizontalOptions="FillAndExpand" />
</StackLayout>
<m:EditDatePicker Source="date" HorizontalOptions="FillAndExpand" />
<m:EditPicker Source="id_depot" HorizontalOptions="FillAndExpand" />
<m:EditPicker Source="tarif" HorizontalOptions="FillAndExpand" />
<m:EditEntry Placeholder="Reference" Source="reference" HorizontalOptions="FillAndExpand" />
<BoxView HorizontalOptions="FillAndExpand" HeightRequest="2" Margin="0,25,0,25" Color="%ThemeColor%"/>
<Grid RowSpacing="10" ColumnSpacing="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="25" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<m:EditEntry Placeholder="Article" Source="LINE|ID_ARTICLE" HorizontalOptions="FillAndExpand" Grid.Column="0" Grid.Row="0" />
<m:EditScanButton TargetSource="LINE|ID_ARTICLE" Grid.Column="1" Grid.Row="0" />
<m:TargetSigLabel Origin="LINE|ID_ARTICLE" Font="Medium" VerticalOptions="Center" HorizontalOptions="FillAndExpand" Margin="10,0,0,0" Grid.Column="2" Grid.Row="0" />
<m:EditEntry Placeholder="Désignation" Source="LINE|DESIGNATIO" HorizontalOptions="FillAndExpand" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3" />
<Label Text="Q" FontSize="12" VerticalOptions="Center" HorizontalOptions="End" Margin="0,0,5,0" Grid.Column="0" Grid.Row="2" />
<m:EditEntry Source="LINE|Q" WidthRequest="120" Decimals="2" Grid.Column="2" Grid.Row="2" />
<Label Text="PU" FontSize="12" VerticalOptions="Center" HorizontalOptions="End" Margin="0,0,5,0" Grid.Column="0" Grid.Row="3" />
<m:EditEntry Source="LINE|PU" WidthRequest="120" Decimals="2" Grid.Column="2" Grid.Row="3" />
<Label Text="Remise" FontSize="12" VerticalOptions="Center" HorizontalOptions="End" Margin="0,0,5,0" Grid.Column="0" Grid.Row="4" />
<m:EditEntry Source="LINE|REMISE" WidthRequest="120" Decimals="2" Grid.Column="2" Grid.Row="4" />
</Grid>
<m:EditAddLineButton CheckDispo="true" Margin="25,25,25,0" HorizontalOptions="FillAndExpand" />
<m:EditTransformButton Margin="25,25,25,0" HorizontalOptions="FillAndExpand" Text="Transformer Document">
<m:EditTransformButton.TargetSequences>
<x:Array Type="{x:Type m:ItemString}">
<m:ItemString Id="1Fact" Lib="Facture" />
<m:ItemString Id="1Bliv" Lib="Livraison" />
<m:ItemString Id="1Devi" Lib="Devis" />
</x:Array>
</m:EditTransformButton.TargetSequences>
</m:EditTransformButton>
<BoxView HorizontalOptions="FillAndExpand" HeightRequest="2" Margin="0,25,0,25" Color="%ThemeColor%"/>
<StackLayout Orientation="Horizontal">
<Label Text="Remise gén. (" Margin="0,0,0,0" />
<Label Text="@RED_GEN_PC:##0.00" />
<Label Text=" %)" Margin="0,0,0,0" />
<Label Text="@RED_GEN_DV:### ##0.00" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Total HT = " Margin="0,0,10,0" />
<Label Text="@TOT_BAS_DV:### ##0.00" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Total TVA = " Margin="0,0,10,0" />
<Label Text="@TOT_TVA_DV:### ##0.00" />
</StackLayout>
<StackLayout Orientation="Horizontal" Margin="0,0,0,25">
<Label Text="Total TTC = " Margin="0,0,10,0" />
<Label Text="@TOT_TTC_DV:### ##0.00" />
</StackLayout>
</StackLayout>
Exemple de paramétrage XAML ListViewCell
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label Text="Q=" FontSize="12" Margin="0,0,10,0" />
<Label Text="@Q:### ##0.00" FontSize="12" Margin="0,0,25,0" />
<Label Text="PU=" FontSize="12" Margin="0,0,10,0" />
<Label Text="@PU:### ##0.00" FontSize="12" Margin="0,0,25,0" />
<Label Text="%=" FontSize="12" Margin="0,0,10,0" />
<Label Text="@REMISE:##0.00" FontSize="12" Margin="0,0,25,0" HorizontalOptions="FillAndExpand" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="@ID_ARTICLE" FontSize="12" Margin="0,0,10,0" />
<Label Text="@DESIGNATIO" FontSize="12" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Emplacement" FontSize="12" Margin="0,0,10,0" />
<Label Text="@ID_MPLACE" FontSize="12" />
</StackLayout>
Ce code XAML produit ces écrans :
Principes de paramétrage
Dans le premier code XAML, il convient d'utiliser des contrôles de type Edit qui permettent la modification des champs du pied (au-dessus de la première ligne orange) ainsi que de la ligne en cours d'édition (en dessous de la ligne orange). Ces champs de ligne ont une source qui est constituée de LINE| suivi du nom de la colonne dans la table LIGNES_V/A. Les totaux et autres informations non modifiables sont repris sous la forme de Labels (voir sous la seconde ligne orange)
La saisie de l'article en ligne se fait toujours dans un EditEntry, dont la source est LINE|ID_ARTICLE. Cette source sera reconnue comme étant une liaison vers le signalétique des articles, ce qui rend possible l'affichage de la désignation dans un TargetSigLabel. (Voir la fonction SQL LIBTARGETSIG_STOCK qui est modifiable).
Le code XAML doit contenir un EditAddLineButton qui permet l'ajout de la ligne en cours dans la liste des lignes déjà encodées.
Dans le second code XAML (ListViewCell), les variables à afficher doivent être précédées d'un @ et en majuscules. Il est possible de fixer un masque d'affichage sous la forme @COLONNE:masque. Ce masque fonctionne de façon identique à celui de string.Format() du Framework .net.
Utilisation
Si le dossier contient la gestion des emplacements, l'utilisateur peut saisir l'emplacement. Dans le cas contraire, la première row de la grille peut être retirée et tous les Grid.Row décrémentés de 1. Notez que la valeur de l'emplacement saisi n'est jamais réinitialisée, ce qui permet de saisir une seule fois l'emplacement et d'encoder ensuite de multiples articles se trouvant dans cet emplacement.
L'utilisateur saisit ensuite un code article. Le cas échéant, MercatorPenguin présente une liste d'articles afin d'en permettre la sélection. Il peut ensuite saisir les autres informations telles que la quantité, le prix, la remise, ...
Enfin, l'utilisateur doit ajouter effectivement la ligne en cours de saisie à la liste du second onglet en utilisant le bouton "Ajouter". Ceci remettra à blanc les données de l'article et le focus dans la zone "Article", afin de permettre la saisie du prochain article.
Les saisies d'article et d'emplacement peuvent être effectuées via l'EditScanButton afin de scanner un code-barres.
Le second onglet permet de visualiser la liste des lignes encodées. Il permet aussi de supprimer une ligne encodée au préalable :
- sous iOS : en tirant la ligne vers la gauche et en utilisant ensuite le bouton "Supprimer"
- sous Android : en maintenant appuyé sur la ligne et en utilisant ensuite le bouton "Supprimer".
Il n'est pas possible de remettre en édition une ligne présente dans la liste pour la modifier. Le cas échéant, il convient donc de la supprimer et la recréer.
La finalisation d'un document se fait par ces boutons situés dans le haut de l'écran, qui offrent respectivement ces fonctions :
- Annuler les modifications en attente et fermer l'écran du document
- Enregistrer les modifications en attente et fermer l'écran du document
- Enregistrer les modifications en attente tout en maintenant ouvert l'écran du document en cours.
Une fois un document sauvegardé, un troisième onglet apparaît : il permet la prévisualisation du document en format PDF. Le modèle d'impression n° 1 défini dans la séquence est utilisé.
Durée de vie des BillingEngines
Contrairement aux inventaires gérés depuis MercatorPenguin, un BillingEngine ouvert par MercatorPenguinServer afin de permettre l'édition du document associé dans MercatorPenguin, n'est pas immédiatement refermé. En effet, MercatorPenguinServer aura besoin de ce BillingEngine, notamment pour déterminer le prix de chaque article qui sera appelé dans l'application mobile. Seule la sauvegarde "Enregistrer les modifications en attente et fermer l'écran du document" ferme réellement le BillingEngine. Il est rappelé qu'un BillingEngine ouvert dans MercatorPenguinServer a pour conséquence que le document correspondant n'est pas modifiable, ni dans Mercator Desktop, ni sur un autre appareil mobile. Il est aussi rappelé qu'en tant qu'application web, MercatorPenguinServer peut redémarrer à tout instant (redémarrage manuel forcé ou redémarrage planifié par IIS). Dans tels cas, MercatorPenguinServer utilise sa capacité à rouvrir un BillingEngine fermé dont l'accès serait à nouveau nécessaire.
Afin d'éviter que des BillingEngines ne restent indéfiniment ouverts, MercatorPenguinServer dispose d'une temporisation qui fait qu'un BillingEngine inactif pendant plus de 15 minutes est automatiquement refermé. Il est possible de changer cette valeur en ajoutant/modifiant cette ligne dans le fichier ini :
AutoCloseBillingEngine = 15
Limitations
Il est nécessaire de bien comprendre que les sauvegardes sont effectuées exclusivement par MercatorPenguinServer et que celui-ci est stateless. Cela veut dire que durant toute la durée de vie du document dans MercatorPenguin, il n'est pas nécessairement maintenu ouvert par MercatorPenguinServer. Dès lors, il est possible qu'un autre utilisateur modifie en parallèle celui-ci. Cette pratique doit être évitée. Lors de l'enregistrement des modifications, les règles suivantes sont d'application :
- les lignes supprimées dans MercatorPenguin sont supprimées par MercatorPenguinServer si elles existent encore. Aucun message d'erreur n'est produit dans le cas contraire.
- les lignes ajoutées dans MercatorPenguin sont toujours ajoutées par MercatorPenguinServer.
Circulation des données
Voir cette page.
Programmation
Les customizers exploitant les événements du BillingEngine sont bien utilisés par MercatorPenguinServer. Il est possible de savoir si le document est géré par MercatorPenguin en utilisant cette propriété : MercatorUi.Globals.IsMercatorPenguinServer.
Pour rappel, il est impossible d'exécuter du code sur mesure dans l'appareil mobile. Il n'y a donc pas de customizer qui "tourne" dans l'application MercatorPenguin en tant que tel. Les plateformes Android et iOS ne l'autorisent pas. Dès lors, ce point ne sera jamais modifié.
Astuces
Voir comment peaufiner le paramétrage des documents de la gestion commerciale avec lignes.
Considérations à propos de la transformation de documents
Il est possible d'effectuer des transformations de documents (changement de séquence) via un bouton de type EditTransformButton. (Voir dans l'exemple "paramétrage XAML Tab 1" ci-dessus). Le code de ce bouton doit être comme celui-ci :
<m:EditTransformButton Margin="25,25,25,0" HorizontalOptions="FillAndExpand" Text="Transformer Document">
<m:EditTransformButton.TargetSequences>
<x:Array Type="{x:Type m:ItemString}">
<m:ItemString Id="1Fact" Lib="Facture" />
<m:ItemString Id="1Bliv" Lib="Livraison" />
<m:ItemString Id="1Devi" Lib="Devis" />
</x:Array>
</m:EditTransformButton.TargetSequences>
</m:EditTransformButton>
Dans l'array de la propriété TargetSequences, il faut lister les séquences vers lesquelles l'utilisateur pourra transformer son document. Il n'y a pas de distinction à effectuer entre un changement horizontal, une transformation vers un stade ultérieur ou antérieur. Cet aspect sera reconnu automatiquement par MercatorPenguinServer.
Lors d'un clic sur ce bouton, une boîte de dialogue sera présentée à l'utilisateur lui permettant de choisir la séquence de destination.
La transformation sera effectuée par MercatorPenguinServer lors de la prochaine sauvegarde. Notez qu'une fois la transformation demandée mais pas encore réalisée (sauvegarde non effectuée), le libellé de ce bouton devient "Annuler changer vers ...". Ceci permet d'annuler la demande de transformation. Les contrôles relatifs à la validité de la transformation demandée sont effectués exclusivement par MercatorPenguinServer, donc au moment de la sauvegarde. De même, tous les customizers liés à cet événement seront exécutés.
Note : une fois la transformation demandée, il n'est plus possible de sauvegarder le document sans le fermer.
Considération à propos des numéros de série
Cette fonctionnalité est compatible avec la gestion des numéros de série. Les contrôles ne sont effectués que pour le niveau 3 (gestion in/out) et uniquement lors de la sauvegarde. A aucun moment, MercatorPenguin ne présente une liste de sélection de numéros de série, l'environnement mobile privilégiant le scan direct d'un numéro de série.
Pour qu'un numéro de série soit éditable, il faut ajouter un EditEntry lié à cette source : LINE|NUM_SERIE.
Pour qu'un numéro de série soit visible dans l'onglet des lignes, il faut ajouter dans le code XAML de la ListViewCell un code semblable à celui-ci :
<StackLayout Orientation="Horizontal">
<Label Text="N° Série" FontSize="12" Margin="0,0,10,0" />
<Label Text="@NUM_SERIE" FontSize="12" />
</StackLayout>
Lorsqu'un article à numéro de série est scanné et que Mercator sait qu'il attendra un numéro de série pour le type de séquence en cours, l'EditEntry mentionné ci-dessus verra sa couleur de fond modifiée afin d'indiquer que cette zone est obligatoire. De plus, il ne sera pas possible d'ajouter une ligne dans ces conditions avec un numéro de série vide.
Contrairement à l'utilisation de la gestion "in/out" des numéros de série (niveau 3) dans Mercator Desktop, MercatorPenguin n'effectue pas de contrôle quant à l'utilisation simultanée d'un même numéro de série sur deux dispositifs différents, lors de la saisie d'une ligne. Ce contrôle est uniquement effectué lors de la sauvegarde du document en cours.
Si la gestion "in/out" des numéros de série est activée (niveau 3), alors MercatorPenguin prend en compte l'option "Encodage / Recherche n° série" (SEARCH_SER). Si cette option est à OUI, MercatorPenguin effectuera aussi une recherche dans les numéros de série disponibles sur base de l'information qui lui est passée et le numéro de série sera automatiquement saisi. Si l'utilisateur emploie exclusivement cette méthode (en vente, scanner directement un numéro de série), alors on peut retirer l'EditEntry lié à LINE|NUM_SERIE. Ainsi, on est certain que l'utilisateur ne modifiera pas le numéro de série qu'il vient de scanner.
Considérations relatives à la gestion des lots
MercatorPenguin est compatible avec la gestion des lots sous certaines réserves imposées notamment par l'ergonomie disponible dans un appareil mobile.
L'affichage des informations de lot dans le second onglet peut se faire via ce type de code XAML de la ListViewCell :
<StackLayout Orientation="Horizontal">
<Label Text="Lot" FontSize="12" Margin="0,0,10,0" />
<Label Text="@LIB_LOT" FontSize="12" />
</StackLayout>
Par défaut, cet affichage reprend le n° du lot suivi de son libellé, séparé par un double point. Ceci peut-être modifié en adaptant le contenu de la fonction scalaire SQL dbo.LIBARTLOT. Elle est installée automatiquement par MercatorPenguinServer mais n'est jamais mise à jour par la suite. En conséquence, il est permis de modifier son code.
La façon la plus ergonomique et directe de mouvementer un lot en sortie est de mettre à OUI l'option "Encodage / Recherche Lots" (SEARCH_LOT). Ainsi, le scan d'un libellé de lot existant permet la saisie automatique, à la fois de l'article et du lot à utiliser.
Il est aussi possible d'ajouter un EditEntry associé à LINE|ID_LOT et d'y saisir l'identifiant d'un lot existant et associé à l'article saisi au préalable :
<m:EditEntry Source="LINE|ID_LOT" Placeholder="Lot" />
Si on veut généraliser l'usage des lots, il faut utiliser les capacités de Mercator en ce qui concerne la gestion implicite des lots, ainsi que la capacité qu'à MercatorPenguin d'échanger avec Mercator n'importe quelle colonne de la table des lignes. Nous montrons ci-dessous un exemple de paramétrage permettant de créer ou de mouvementer des lots en entrée depuis les achats. La clé de dédoublonnent est le libellé du lot : si un lot avec ce libellé n'existe pas, alors il est créé. S'il existe, Mercator mouvemente ce lot. Cette programmation est basée sur l'ajout dans LIGNES_A de cette colonne, qui va simplement être utilisée pour transporter le libellé du lot depuis MercatorPenguin vers Mercator :
alter table LIGNES_A add LOT_INFO char(25) not null default ''
Dans le code XAML Tab 1 de cette séquence d'achat, il faut ajouter cet EditEntry :
<m:EditEntry Source="LINE|LOT_INFO" Placeholder="Lot" />
Dans cette même séquence, il faut ajouter ce customizer qui va effectuer la gestion implicite des lots :
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Windows.Forms;
using MercatorApi;
using MercatorUi;
using MercatorExtensions;
using MercatorDatabase;
namespace Billing
{
public class Customizer : MercatorUi.ICustomizers.IBillingEngineCreated, MercatorUi.ICustomizers.IBillingEngineClosed
{
public void BillingEngineCreated(MercatorUi.Engine.Gescom.BillingEngine billingEngine)
{
billingEngine.BeginningSave += billingEngine_BeginningSave;
}
public void BillingEngineClosed(MercatorUi.Engine.Gescom.BillingEngine billingEngine)
{
billingEngine.BeginningSave -= billingEngine_BeginningSave;
}
void billingEngine_BeginningSave(object sender, MercatorUi.Engine.Gescom.BillingEngine.BeginningSaveEventArgs e)
{
MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
if (MercatorUi.Globals.IsMercatorPenguinServer && billingEngine.LotsRequired)
{
//MercatorUi.Globals.ApiLogDelegate("..."); // écrire dans le log de MercatorPenguinServer
using (SqlCommand cmd = new SqlCommand("", e.Connection.Connection, e.Transaction))
{
cmd.CommandText = @"
if not exists(select * from STOCK where (s_id=@id_art) and (s_lot=1))
begin
select '' as id_lot
return
end
if not exists(select * from ARTLOT where (id_art=@id_art) and (lib=@lib))
begin
declare @num bigint , @id_lot char(10)
select @num=isnull(num,0)+1 from ARTLOT where (id_art=@id_art)
set @id_lot=right(newid(),10)
insert into ARTLOT (id_lot,id_art,id_depot,num,creation,lib) values (@id_lot,@id_art,@id_depot,@num,getdate(),@lib)
select @id_lot as id_lot
end
else
select id_lot from ARTLOT where (id_art=@id_art) and (lib=@lib)";
foreach (var l in billingEngine.LignesARecords.Where(p => !string.IsNullOrWhiteSpace(p.LOT_INFO)))
{
cmd.Parameters.Clear();
cmd.Parameters.AddWithValue("@id_art", l.ID_ARTICLE).SqlDbType = SqlDbType.Char;
cmd.Parameters.AddWithValue("@lib", l.LOT_INFO).SqlDbType = SqlDbType.Char;
cmd.Parameters.AddWithValue("@id_depot", billingEngine.PiedsARecord.ID_DEPOT).SqlDbType = SqlDbType.Char;
object ox = Api.ZselectDirect(cmd);
if (ox == null)
{
billingEngine.LastError = Api.LastError;
e.CancelSave = true;
return;
}
l.ID_LOT = ox.ToString();
}
}
}
}
}
}
Le fonctionnement est le suivant : dans MercatorPenguin, l'utilisateur scanne l'article entrant et scanne ensuite dans la zone "Lot" le libellé du lot. Lors de la sauvegarde, MercatorPenguinServer va parcourir les différentes lignes du document et associer les "lot_info" à un lot existant. Si aucun lot n'est trouvé, il sera automatiquement créé et numéroté.
Considérations à propos des articles liés, mariés et pacsés
MercatorPenguin est compatible avec les fonctionnalités suivantes :
et ce, de façon récursive, jusque 9 niveaux, comme dans Mercator Desktop. Toutefois, l'utilisation est quelque peu différente. En effet, l'écran de saisie des articles mariés et celui des articles pacsés est présenté dès la sélection de l'article parent. Toutefois, les lignes correspondant aux articles liés, mariés et pacsés ne sont créées qu'après avoir cliqué sur le bouton "Ajouter" (ajouter ligne). Ceci permet donc la saisie au préalable de la quantité et donc sa prise en compte lors de la création de ces lignes.
L'écran de saisie des articles mariés permet de choisir une quantité pour chacun des articles enfants, tandis que pour les articles pacsés, un switch permet de sélectionner un article enfant ou pas.
Considérations à propos de la différenciation des unités
A ce stade du développement, cette fonctionnalité n'est pas compatible avec la différenciation des unités. L'application fonctionnera toutefois sans produire d'erreur mais en ignorant les coefficients de conversion entre les différentes unités.