Table of Contents

Module CAP Logistics - WMS

The solution that connects your warehouses and your business application.

Properties

Name Value
Version 26.202601.41333.0
Publisher CAP Vision
Brief The solution that connects your warehouses and your business application.

Namespace

Name Summary
CapVision

Concept

Le concept de développement repose sur l'usage du client web de Business Central en mode téléphone ou tablette. Chaque page comprend un unique composant nommé Liquid Page CWCAP qui se charge de l'affichage des données.

La mise en forme des données se fait en Liquid.

Modèle d'une page

L'exemple ci-dessous présente une page typique de l'application.

page 50000 "MyFirstLiquidPage"
{
    PageType = Card;
    ApplicationArea = All;
    UsageCategory = None;
    Caption = 'MyFirstLiquidPage';

    layout
    {
        area(Content)
        {
            usercontrol(PageCtrl; "Liquid Page CWCAP")
            {
                trigger OnControlReady()
                begin
                    InitializeComponents();
                    LoadPage();
                end;

                trigger OnAction(Sender: JsonObject; SenderId: Text; EventArgs: JsonObject)
                var
                    IsHandled: Boolean;
                begin
                    OnBeforeOnAction(Sender, SenderId, EventArgs, IsHandled);

                    if IsHandled then
                        exit;

                    case SenderId of
                        'close':
                            CurrPage.Close();
                        'ok': begin
                            RecallLabelCaption := '***' + Liquid.GetTextboxText(EventArgs, 'inputValue') + '***';
                            LoadPage();
                        end;
                    end;
                end;
            }
        }
    }

    local procedure InitializeComponents()
    var
        Template: Text;
    begin
        Template := '{% render "title", id: "title", caption: title %}'
                    + '{% render "textBox", id: "inputValue", caption: inputValueCaption, value: inputValueText, placeholder: inputValuePlaceHolder %}'
                    + '{% render "label", caption: recallLabelCaption %}'
                    + '{% render "actionBar", buttons: actionBarButtons %}';

        OnInitializeComponents(Template);

        CurrPage.PageCtrl.Create(Template);
    end;

    protected procedure LoadPage()
    var
        PageTitleLbl: Label 'Hello World';
        TypeSomethingLbl:Label 'Type something';
    begin
        Clear(Payload);
        Liquid.SetPayload(Payload);

        Liquid.Set('title', PageTitleLbl);
        Liquid.Set('inputValueCaption', TypeSomethingLbl);
        Liquid.Set('inputValueText', '');
        Liquid.Set('inputValuePlaceHolder', TypeSomethingLbl);
        Liquid.Set('recallLabelCaption', RecallLabelCaption);

        Liquid.SetActionBarButtons('actionBarButtons', 'close', 'Close', 'ok', 'OK');

        OnLoadPage(Payload);

        CurrPage.PageCtrl.Update(Payload);
    end;

    protected var
        Payload: JsonObject;

    var
        Symbology: Codeunit "Symbology CWCAP";
        Liquid: Codeunit "Liquid Helper CWCAP";
        RecallLabelCaption:Text;


    [IntegrationEvent(false, false)]
    local procedure OnInitializeComponents(var Template: Text)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnLoadPage(var Payload: JsonObject)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeOnAction(Sender: JsonObject; SenderId: Text; EventArgs: JsonObject; var IsHandled: Boolean)
    begin
    end;
}

Une fois affichée en mode téléphone, la page ressemble à ceci :

Aperçu de la page

Bibliothèques

Codeunits

Nom de l'objet Description
Data Search Fournit un ensemble de fonctionnalités de recherche de données.
Liquid Helper Fournit des fonctions utilitaires pour manipuler les templates et données Liquid.
Session Settings Utilisé pour stocker les informations de session de l'utilisateur. Notamment le magasin en cours.
Symbology Contient des symboles à réutiliser dans vos applications.

Pages

Nom de l'objet Description
WMS Bin Content Affiche le contenu de l'emplacement spécifié.
WMS Pick Location Permet de sélectionner un magasin parmi ceux pour lequel l'utilisateur est magasinier.

Templates disponibles

Lorsque vous invoquez la méthode Create du composant Liquid Page CWCAP, vous devez lui passer un template Liquid. Ce template est une chaîne de caractères qui contient des instructions Liquid et du code HTML.

L'application fournit un certain nombre de templates que vous pouvez utiliser dans vos développements.

actionBar

Alt text

Propriétés

Propriété Optionnel Description
id oui
buttons oui Il s'agit du JsonArry qui doit être saisit avec liquid.Set(...), les 2 propriétés suivantes sont renseignées via la fonction Liquid.SetActionBarButtons(...)
button.Id oui Permet de s'abonner à l'évènement du click
button.Text oui

Exemples d'utilisation

InitializeComponents
{% render "actionBar", buttons: ButtonsList %}
LoadPage
Liquid.SetActionBarButtons('buttonsList', Liquid.CloseId(), Liquid.CloseCaption(), Liquid.MenuId(), Liquid.MenuCaption(), Liquid.PostId(), Liquid.SaveCaption());

button

Propriétés

Propriété Optionnel Description
id oui Permet de s'abonner à l'évènement du click
buttonValue oui

Exemples d'utilisation

InitializeComponents
{% render "button", id: buttonMinusId, buttonValue: buttonMinusValue %}
LoadPage
Liquid.Set('buttonMinusId', ButtonMinusLbl);
Liquid.Set('buttonMinusValue', '-');
OnAction

Pour récupérer l'évènement du clic sur le bouton, il faut utiliser l'évènement OnAction du ControlAddIn.

trigger OnAction(Sender: JsonObject; SenderId: Text; EventArgs: JsonObject)
begin
    case SenderId of
        ButtonMinusLbl:
            ButtonMinus_OnAction();
    end;
end;

input

L'input est l'équivalent d'une textBox mais sans étiquette (caption).

Propriétés

Propriété Optionnel Description
id oui Définit l'identifiant du composant. Vous pouvez réutiliser cet identifiant dans votre code pour faire référence au composant.
caption oui Permet de renseigner un placeholder

Exemples d'utilisation

InitializeComponents
{% render "input", id: InputId, caption: InputCaption %}
LoadPage
Liquid.Set('InputId', 'aed6505c-05c6-45b4-8cf4-7aae25898c88');
Liquid.Set('InputCaption', 'Write something...');
OnAction

L'exemple suivant permet de lire la valeur saisie dans le composnant input.

trigger OnAction(Sender: JsonObject; SenderId: Text; EventArgs: JsonObject)
begin
    case focusedControlId of
        InputIdLbl:
            InputValue := Liquid.GetTextboxText(EventArgs, InputIdLbl);
    end;

    LoadPage();
end;

label

Alt text

Propriétés

Générales
Propriété Optionnel Valeur défaut Description
id oui Définit l'identifiant du composant. Vous pouvez réutiliser cet identifiant dans votre code pour faire référence au composant.
caption oui Si cette valeur est vide aucun code HTML ne sera généré sur la page
linkLabel oui Si ce tag est ajouté le label devient cliquable
Style
Propriété Optionnel Valeur défaut Description
fontSize oui 1em
fontColor oui #000000
backgroundColor oui #f6f6f6
textAlign oui left Voir les valeurs possibles
padding oui 1em Voir les valeurs possibles
margin oui 1em 0 Voir les valeurs possibles

Exemples d'utilisation

InitializeComponents
{% render "label",
    id: LabelId,
    caption: LabelCaption,
    fontSize: LabelFontSize,
    fontColor: LabelFontColor,
    padding: LabelPadding,
    margin: LabelPadding,
    linkLabel
%}
LoadPage
Liquid.Set('LabelId', 'aed6505c-05c6-45b4-8cf4-7aae25898c88');
Liquid.Set('LabelCaption', 'Write something...');
Liquid.Set('LabelFontColor', '#ffffff');
Liquid.Set('LabelFontSize', '1.5em');
Liquid.Set('LabelPadding', '1px 2px 3px 4px');
Liquid.Set('LabelMargin', '0');

listLabel

Ce composant permet d'afficher une liste d'éléments dans la page.

Alt text

Propriétés

Propriété Optionnel Description
id oui Définit l'identifiant du composant. Vous pouvez réutiliser cet identifiant dans votre code pour faire référence au composant.
labelValues oui Spécifie un tableau d'éléments de type LabelValue à afficher dans la liste.
swipe oui Permet d'activer un menu d'actions sur chaque élement de la liste.
swipeNumber Si swipe = false Le nombre d'actions souhaitées. Les valeurs supportées sont one et two.
swipeRight Si swipe = false Nom du bouton de droite.
swipeLeft Si swipe = false Nom du bouton de gauche.
selectionMode oui Permet de spécifier le mode de sélection des éléments de la liste. Les valeurs supportées sont single (sélection unique) et multiple (sélection multiple).

Propriétés d'un LabelValue

Propriété Optionnel Description
listItemId oui Spécifie l'identifiant de l'élément de la liste. Par exemple, si cet élément fait référence à un enregistrement de Business Central, utilisez le System ID de l'enregistrement comme référence.
title oui Spécifie le texte affiché en haut à gauche de l'élément.
description oui Spécifie le texte affiché en bas à gauche de l'élément.
value oui Spécifie le texte affiché en haut à droite de l'élément.
subtitle oui Spécifie le texte affiché en bas à droite de l'élément.
backgroundColor oui Spécifie la couleur de fond de l'élément.

Exemples d'utilisation

InitializeComponents
{% render "listLabel", id: LabelArrayId, labelValues: LabelArrayValue %}
LoadPage
Liquid.Set('LabelArrayId', ListIdLbl);
Liquid.Set('LabelArrayValue', CreateBinContents());

local procedure CreateBinContents() Payload: JsonArray
var
    BinContent: Record "Bin Content";
begin
    BinContent.SetAutoCalcFields(Quantity);

    if BinContent.FindSet() then
        repeat
            Liquid.AddListItem(
                Payload,
                BinContent."Bin Code",
                BinContent."Item No.",
                '',
                BinContent.Quantity,
                BinContent.SystemId,
                '#ff000040');
        until BinContent.Next() = 0;
end;
OnAction

L'exemple suivant présente comment récupérer l'élément sélectionné dans la liste quand l'utilisateur clique dessus.

trigger OnAction(Sender: JsonObject; SenderId: Text; EventArgs: JsonObject)
begin
    case SenderId of
        LabelArrayId:
            begin
                BinContent.GetBySystemId(Liquid.GetSelectedListItemId(EventArgs));
                CurrBinCode := BinContent."Bin Code";
            end;
end;

textBox

Une textBox est un composant permettant à l'utilisateur de saisir une valeur. Elle peut être utilisée avec un étiquette (caption) ou sans. Elle est typiquement utilisée pour avoir une zone de scannage dans la page :

TextBox en tant que zone de scannage

Elle peut également être utilisée pour afficher une information, telle que le numéro d'article scanné dans l'exemple ci-dessous :

Zone d'affichage d'un numéro d'article

Vous noterez que dans l'exemple ci-dessus, la textBox est en lecture seule.

Propriétés

Propriété Optionnel Description
id oui Définit l'identifiant du composant. Vous pouvez réutiliser cet identifiant dans votre code pour faire référence au composant.
caption oui Spécifie l'étiquette du composant. L'étiquette est affichée à gauche ou au-dessus de la zone de saisie.
value oui Spécifie la valeur du composant. Si readOnly = false, c'est cette valeur que l'utilisateur peut modifier.
placeholder oui Spécifie le texte affiché à la place de la valeur lorsque la valeur est vide.
hidden oui Spécifie que le composant ne doit pas être affiché dans la page.
captionHidden oui Spécifie que le caption doit être masqué.
readOnly oui Spécifie que le composant est en lecture seule.
inputMode oui Le type de clavier virtuel affiché. none par défaut. Valeurs possibles

Exemples d'utilisation

InitializeComponents
{% render "textBox", id: TextboxItemDescriptionId, value: TextboxItemDescriptionValue, hidden: TextboxItemDescriptionHidden, readOnly, captionHidden %}
LoadPage
Liquid.Set('TextboxItemDescriptionHidden', true);

if Item.Get(ItemNo) then begin
    Liquid.Set('TextboxItemDescriptionId', TextBoxItemDescriptionIdLbl);
    Liquid.Set('TextboxItemDescriptionValue', Item.Description + ' ' + Item."Description 2");
    Liquid.Set('TextboxItemDescriptionHidden', false);
end;

title

Le template title permet d'afficher un titre en haut de la page. Son usage est normalement réservé pour être le premier élément d'une page.

Template title

Propriétés

Générales
Propriété Optionnel Valeur défaut Description
id oui
caption oui
actionId oui Si défini, alors un bouton est créé à gauche du titre ayant pour action l'identifiant associé.
actionText oui &#xE72B actionId doit être également défini. Par défaut, il s'agit d'une flèche pointant vers la gauche.
Style
Propriété Optionnel Valeur défaut
fontSize oui 1.5em
fontColor oui #ffffff

Exemples d'utilisation

InitializeComponents
{% render "title", id: titleId, Caption: titleCaption, fontSize: titleFontSize, fontColor: titleFontColor, actionId: titleActionId %}
LoadPage
Liquid.Set('titleId', 'aed6505c-05c6-45b4-8cf4-7aae25898c88');
Liquid.Set('titleCaption', 'Write something...');
Liquid.Set('titleFontSize', '2em');
Liquid.Set('titleFontColor', '#dddddd');
Liquid.Set('titleActionId, Liquid.CloseId());

Boîtes de dialogue

Alt text

Exemples d'utilisation

Les boites de dialogue fonctionnent différement des autres templates, elles ne sont pas intégrées directement dans le template Liquid avec le reste de la page. Elles permettent d'afficher du texte et des boutons.

Pour invoquer une boîte de dialogue, utilisez les fonctions ShowDialog et HideDialog.

Liquid.ShowDialog(CurrPage.PageCtrl, DialogIdLbl, 'Dialog title', DetailsContent.ToText(), ButtonResetDetailsIdLbl, ButtonResetDetailsLbl, ButtonCloseDetailsIdLbl, Liquid.CloseCaption());

CurrPage.PageCtrl.HideDialog(DialogIdLbl);

Donner le focus à un élément

Pour mettre le focus sur une zone particulière utilisez la fonction SetFocus avec l'id de l'élément auquel donner le focus.

Exemples d'utilisation

SetFocus peut être utilisé dans l'évènement OnUpdateCompleted pour définir une zone par défaut à focus, sinon elle peut être utilisée dans le trigger OnAction.

trigger OnUpdateCompleted()
begin
    CurrPage.PageCtrl.SetFocus(TextboxScanIdLbl);
end;

trigger OnAction(Sender: JsonObject; SenderId: Text; EventArgs: JsonObject)
begin
    case SenderId of
        LabelArrayIdLbl:
            ItemList_OnClick(EventArgs);
    end;
end;

local procedure ItemList_OnClick(EventArgs: JsonObject)
begin
    //Do Something

    CurrPage.PageCtrl.SetFocus(TextboxScanIdLbl);
end;

Extensibilité

Etendre les pages existantes

Utiliser les événements OnInitializeComponents pour ajouter un élément au template.

Par exemple, dans la page contenant le template suivant :

{% render "label",  id: "LabelHello", caption: "Hello" %}
{% render "listLabel", id: "ListLabel1", labelValues: LabelArrayValue %}

En vous abonnant à l'événement OnInitializeComponents

[EventSubscriber(ObjectType::Page, Page::"Base Page", OnInitializeComponents, '', false, false)]
local procedure BasePage_OnInitializeComponents(var Template: Text)
begin
    Liquid.AddComponentsAfter(Template, 'LabelHello', '{% render "label", caption: "World" %}')
end;

Les fonctions suivantes sont disponibles :

  • AddComponentsAfter(var LiquidTemplate: Text; ControlId: Text; TemplateToAdd: Text)
  • AddComponentsBefore(var LiquidTemplate: Text; ControlId: Text; TemplateToAdd: Text)
  • RemoveComponent(var LiquidTemplate: Text; ControlId: Text): Text
Argument Description
LiquidTemplate
ControlId Il s'agit du nom de l'Id du render.
TemplateToAdd Il peut s'agir d'un ou plusieurs nouveaux render ou même de code HTML.

Utilisez ensuite les événements OnLoadPage pour fournir les variables de votre template. Et enfin utilisez les évenements OnBeforeOnAction pour capter les actions, implémenter les vôtres ou surcharger les actions prédéfinies dans la page.

Ajouter du code HTML

Il est possible d'ajouter du code HTML dans le template créé en AL.

Par exemple, en partant de la page affichant ces éléments :

Avant ajout du code HTML

Vous souhaitez ajouter un champ Quantité (PCS) avec ses deux boutons +/- pour incrémenter ou décrémenter la valeur.

Template +=
    '{% unless TextboxQuantityHidden %}'
        + '<div class="container">'
            + '<div class="row">'
                + '<div class="col">'
                    + '{% render "textBox", id: TextboxQuantityId, caption: TextboxQuantityCaption, value: TextboxQuantityValue, hidden: TextboxQuantityHidden, readOnly: QuantityReadOnly %}'
                + '</div>'
                + '<div class="col-md-auto">'
                    + '{% render "button", id: buttonMinusId, buttonValue: buttonMinusValue %}'
                + '</div>'
                + '<div class="col-md-auto">'
                    + '{% render "button", id: buttonPlusId, buttonValue: buttonPlusValue %}'
                + '</div>'
            + '</div>'
        + '</div>'
    + '{% endunless %}';

Après ajout du code HTML

Le template {% unless TextboxQuantityHidden %}...{% endunless %} permet de cacher ou d'afficher tout le code HTML en fonction de la variable TextboxQuantityHidden qui est attendue dans le Payload.

Il existe aussi {% if TextboxQuantityHidden %}...{% endif %}.

Dans cet exemple, le code complet est caché mais on peut aussi l'utiliser pour afficher ou masquer des propriétés dans les balises HTML.

Etendre la recherche article

Lorsqu'un code-barres est scanné on utilise la fonction TrySearchItem du codeunit Data Search CWCAP pour trouver des résultats. Par défaut, elle cherche dans les tables Item, Item Reference et Item Ledger Entry sur les champs Lot No., Serial No. et Package No. et renvoie uniquement le premier résultat.

Il est possible de modifier certains paramètres de cette recherche.

Contexte

var
    Context: JsonObject;
begin
    Context.Add('itemNo', 'BT00002');
    DataSearch.TrySearchItem(CurrScanValue, SearchResult, Context);
end;
Article Lot
BT00001 LOT001
BT00001 LOT002
BT00002 LOT002

Le but du contexte est de spécifier l'article qui doit être trouvé. Dans cet exemple si on scanne le LOT002 le résultat retourné peut être BT00001 ou BT00002. Si on se trouve par exemple sur une ligne de prélèvement pour l'article BT00002, on peut préciser ce numéro article dans le contexte pour rechercher les lots de cet article uniquement.

Priorités

var
    SearchPriorities: List of [Enum "Searchable CWCAP"];
begin
    SearchPriorities.Add(Enum::"Searchable CWCAP"::"Item");
    SearchPriorities.Add(Enum::"Searchable CWCAP"::"Item Ledger Entry");
    SearchPriorities.Add(Enum::"Searchable CWCAP"::"Item Reference");
    DataSearch.TrySearchItem(CurrScanValue, SearchResult, SearchPriorities);
end;

Par défaut la recherche est faite sur les tables Item, Item Reference et Item Ledger Entry, SearchPriorities permet de choisir l'ordre de recherche ou alors de ne chercher que dans une seule table. Si on sait que l'utilisateur doit scanner un N° de lot, alors Enum::"Searchable CWCAP"::"Item Reference" sera suffisant.

Evénements

L'événement OnBeforeSearchItem permet de retraiter la chaîne envoyée par le terminal avant d'être envoyée à la fonction de recherche.

Etendre l'incrément automatique

Lorsqu'un code-barres est scanné deux options existent :

  • incrémenter la quantité de 1 OU
  • ne rien faire

Il est possible d'étendre ce fonctionnement pour ajouter une méthode supplémentaire qui pourra être choisie dans l'écran de paramétrage ou pour simplement forcer la valeur d'incrément via un événement. Les deux exemples ci-dessous illustrent comme surcharger le fonctionnement standard.

Pour vos pages spécifiques, si vous souhaitez exploiter la personnalisation de l'incrément automatique,

  1. Vous devez invoquer la fonction AddQuantity dans votre page, par exemple au moment où un article est scanné :
var
    IncrementQuantity: Codeunit "Increment Quantity CWCAP";
begin
    //Do something before...
    IncrementQuantity.AddQuantity(Quantity, '', Page::"MyPage 1 PTE");
    //Do something after...
end;
  1. Créez une tableextension de l'objet Solution Setup CWCAP et ajoutez un champ permettant à l'utilisateur de configurer ce qu'il préfère pour l'incrément automatique de votre page.
tableextension 70345287 "Solution Setup CWCAP"
{
    fields
    {
        field(11; "MyPage 1 Increment PTE"; Enum "Increment Quantity CWCAP")
        {
            Caption = 'MyPage 1 Increment';
        }
    }
}
  1. Abonnez-vous à l'événement OnBeforeAddQuantity du codeunit Increment Quantity CWCAP et implémentez un code de ce type :
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Increment Quantity CWCAP", OnBeforeAddQuantity, '', false, false)]
local procedure OnBeforeAddQuantity(var Quantity: Decimal; Rec: Variant; WMSPage: Integer; var IsHandled: Boolean);
begin

    case WMSPage of
        Page::"MyPage 1 PTE":
            Quantity += GetSolutionSetupValue(SolutionSetup."MyPage 1 Increment PTE", Rec);
    end;

end;

Forcer via un événement

Vous pouvez vous abonner à l'événement OnBeforeAddQuantity et modifier la valeur de la quantité dans votre code.

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Increment Quantity CWCAP", OnBeforeAddQuantity, '', false, false)]
local procedure OnBeforeAddQuantity(var Quantity: Decimal; Rec: Variant; WMSPage: Integer; var IsHandled: Boolean);
begin
end;

Nouvelle méthode d'incrément

Vous pouvez créer une nouvelle méthode d'incrément selon vos règles. Pour cela, suivez l'étapes ci-dessous :

  1. Etendez l'énumération Increment Quantity CWCAP
  2. Abonnez-vous à l'événement OnBeforeGetSolutionSetupValue et retournez la quantité à incrémenter dans la variable IncrementQuantity.