MercatorSelfScan (NL)

0000003167     -      07-08-2023

MercatorSelfScan is een applicatie die zelfstandige kassafunctionaliteit biedt voor het invoeren van verkopen en het innen van betalingen door de klant (zelfafrekening). Het is een zelfstandig programma dat verbinding maakt met Mercator ERP. Het is gebouwd op basis van een hybride Blazor-applicatie, waardoor het instellen en aanpassen van de interface gemakkelijk wordt gemaakt.

Installatie

MercatorSelfScan vereist de optie SELFSCAN in de voucher, gevolgd door één of meerdere gebruikers ID's uit de tabel USERS. Bijvoorbeeld:

SELFSCAN=A12345678X+Z87654321T

Het is compatibel met versie 11.0 of hoger

In eerste instantie is het noodzakelijk om een functionele en up-to-date Mercator Core op de computer te hebben geïnstalleerd. (MercatorSelfScan is niet compatibel met de klassieke versie van Mercator).

Vervolgens is het noodzakelijk WebView2 Runtime.

Tot slot moet de inhoud van het bestand MercatorSelfScan.zip worden uitgepakt in een lege map. Het bestand MercatorSelfScan.appsettings.json moet worden aangepast zoals hieronder aangegeven, vooral om het pad naar de Mercator Core in de MainDir setting over te nemen. De assembly MercatorDatabase.dll van de gekoppelde Mercator moet worden gekopieerd naar de map van MercatorSelfScan. De applicatie wordt gestart via MercatorSelfScan.exe.

Gebruik

Bij het opstarten van de applicatie bevindt deze zich in de "niet-actief"-modus. Het is noodzakelijk dat een personeelslid de kassa activeert. Net als alle inlogmogelijkheden die het programma biedt, kan dit op de volgende manieren worden gedaan:

  • door het invoeren van een geldig wachtwoord in Mercator.
  • door het scannen van de inlog QR-code vanuit MercatorPenguin.
  • door het scannen van een barcode (op een persoonlijke kaart) die het wachtwoord bevat.

De self-checkout kan worden gestart

Als het programma is geconfigureerd om meerdere talen te ondersteunen, kan de gebruiker een taal kiezen uit de beschikbare opties. De gebruiker start het scannen door op deze knop te tikken:

MercatorSelfScan_ButtonStart

Als het programma is geconfigureerd om te vragen om een klantenkaart, kan de gebruiker ervoor kiezen om deze kaart te scannen of om door te gaan als anonieme klant.

De klant scant vervolgens alle items in zijn winkelmandje en de inhoud ervan wordt weergegeven in een raster. Een knop met de tekst "Artikel zonder barcode" stelt de klant in staat om artikelen te selecteren die niet kunnen worden gescand (zie hieronder). Wanneer de gebruiker alle artikelen heeft gescand, moet hij op deze knop tikken:

De klant krijgt een scherm te zien waarop hij wordt uitgenodigd om een betaalmethode te kiezen (bijvoorbeeld contant geld via een muntwisselaar en een kaartbetalingsterminal).

Afhankelijk van de configuratie:

  • de klant krijgt een scherm te zien waarin wordt gevraagd of hij een bon wil.
  • de klant, geïdentificeerd met zijn klantenkaart en op basis van zijn klantgegevens, wordt geïnformeerd dat de bon beschikbaar is in een elektronische vorm, in een app, op een website, enz.
  • de bon wordt automatisch afgedrukt.
  • de bon wordt nooit afgedrukt.

Het verkoopproces van de self-checkout eindigt met een scherm waarop de klant bedankt wordt voor zijn aankoop.

Bovenaan elk scherm zijn deze knoppen beschikbaar:

MercatorSelfScan_ButtonHelp

Ze stellen je in staat om de taal van de interface te veranderen en hulp van het personeel in te roepen. Een medewerker ontvangt hiervan een melding via MercatorSelfScanMonitor.

Alles wat er in de kassa gebeurt, kan op afstand worden gemonitord via de zelfstandige applicatie MercatorSelfScanMonitor.

Instellingen

De configuratie wordt uitgevoerd door de waarde van de sleutels in het bestand MercatorSelfScan.appsettings.json te wijzigen. Dit bestand wordt gelezen bij het opstarten van de applicatie. Dit betekent dat na elke wijziging MercatorSelfScan opnieuw moet worden opgestart om de wijzigingen van kracht te laten gaan.

Hier zijn de instellingen die kunnen worden aangepast door de waarden van de sleutels in het bestand MercatorSelfScan.appsettings.json te wijzigen:

  • MainDir: de map waarin Mercator staat is gekoppeld aan MercatorSelfScan. Deze map moet een lokale en niet-gemapte locatie zijn. Let op: de backslashes moeten verdubbeld zijn.
  • IdDossier: voor multi-dossierconfiguraties, het ID van het dossier. Anders leeg.
  • UserLogin: de Mercator-gebruiker waaronder SelfScan zal werken. Het is aanbevolen om een supergebruiker te gebruiken om ervoor te zorgen dat er altijd een sessie beschikbaar is.
  • Password: het wachtwoord in Mercator voor deze gebruiker.
  • DeveloperTools: Geef 1 op om ontwikkelaarstools te activeren waarmee u toegang hebt tot de console en HTML-elementen kunt inspecteren (via CTRL-SHIFT-I). In productie moet deze waarde 0 zijn.
  • DefaultLanguage: Taal voor het opstarten van de applicatie. Mogelijke keuzes: F N E D.
  • AvailableLanguages: Lijst van beschikbare talen voor gebruikers. Als deze waarde slechts één taal bevat, wordt de taalselector nooit weergegeven.
  • CustomerField: Kolom in de tabel CLI voor het zoeken van klantenkaarten. Als deze waarde leeg is, wordt de stap voor het scannen van klantenkaarten niet weergegeven.
  • DefaultCustomer: ID van de standaardklant als de klant zich niet identificeert met zijn klantenkaart.
  • Sequence: de gebruikte verkoopsequentie
  • PriceUpdatable: Geef aan of de verkoopprijs van eenheid aanpasbaar is. Deze wijziging vereist altijd een assistent-login. Mogelijke waarden: 0 en 1.
  • MaxRowsCart: het maximale aantal regels dat wordt weergegeven in de lijst met gescande artikelen. Eenmaal overschreden, wordt paginering weergegeven.
  • MaxLengthCart: Het maximale aantal tekens voor de weergave van de omschrijving in de lijst met gescande artikelen.
  • FreeRemoveInCart: Is het verwijderen van een gescande regel toegestaan of vereist het de goedkeuring van een assistent? Mogelijke waarden: 0 en 1.
  • PayTerms : Beschikbare betalingsterminals. Lijst gescheiden door komma. Mogelijke waarden: 
    • Czam
    • DevTest (testterminal voor ontwikkeling of debugging)
  • PayTypes: Nummers van de betalingsmethoden die zijn gekoppeld aan de hierboven genoemde terminals. Het aantal elementen in deze lijst gescheiden door een komma moet gelijk zijn aan het aantal elementen in PayTerms. (Het nummer is te vinden in "Tools / Instellingen / Betaalmethoden".)
  • PrintTicket: moet de laatste stap van het verkoopproces een bon afdrukken en een bericht weergeven dat de bon is afgedrukt? Mogelijke waarden:
    • 0: geen bon afdrukken
    • 1: altijd bon afdrukken
    • de naam van een bitkolom in de tabel CLI (bijv. C_PRINT_TICKET) waarvan de waarde het gewenste gedrag aangeeft
  • SecondsFinish: het aantal seconden waarna de interface automatisch de bedanktpagina weergeeft nadat het betalings- en bonafdrukproces is voltooid.
  • SecondsThankYou: het aantal seconden waarna de interface wordt gereset (terug naar stap 1) nadat het verkoopproces is voltooid.
  • Name: de naam van deze kassa. Het zal verschijnen in MercatorSelfScanMonitor. Als deze waarde leeg is, wordt de naam van de machine gebruikt.
  • MonitorHost: De TCP-coördinaten van MercatorSelfScanMonitor in de vorm van "ip-adres:poort". Als MercatorSelfScanMonitor niet wordt gebruikt, moet deze waarde leeg zijn.

 

Het uiterlijk van de applicatie kan aanzienlijk worden aangepast door de volgende bestanden te bewerken die zich in de submap wwwroot bevinden:

  • index.html
  • css/style.css
  • css/mercator.css
  • de afbeeldingen

Artikelen zonder barcode

 

MercatorSelfScan kan artikelen zonder barcode beheren. Hiervoor maakt het gebruik van een scherm genaamd "ItemSelector", waarmee de gebruiker een artikel kan selecteren op basis van zijn foto en naam, en indien nodig de hoeveelheid kan specificeren.

Om dit scherm zichtbaar te maken, moet de optie "ItemSelectorRecordsPerPage" groter zijn dan nul. Deze pagina houdt rekening met de volgende opties:

  • ItemSelectorColumns : aantal kolommen, mogelijke waarden: 1, 2, 3, 4, 6, 12
  • ItemSelectorRecordsPerPage :aantal elementen per pagina
  • ItemSelectorImageSize : de grootte van de afbeelding, in pixels
  • ItemSelectorMaxLength : het maximale aantal tekens waardoor de aanduiding altijd op één regel wordt weergegeven.
  • rubriques : het selecteren van een categorie geeft toegang tot een "sublijst" met andere categorieën en/of artikelen.
  • Er zijn artikelen die direct kunnen worden geselecteerd.

Dit systeem maakt het mogelijk om artikelen te presenteren in elke gewenste hiërarchie, bijvoorbeeld afdelingen, categorieën en subcategorieën, of een andere aangepaste classificatie. Het is mogelijk om artikelen en categorieën te combineren. Hierdoor kunnen items in een optimale volgorde voor de gebruiker worden weergegeven. (Bijvoorbeeld, eerst worden categorieën weergegeven, maar ook enkele van de meest vaak gekochte artikelen.) Deze weergave wordt bepaald door een vrije SQL-opgeslagen procedure. Deze procedure moet SP_SELFSCAN_ITEMSELECTOR worden genoemd en accepteert de volgende parameters:

  • @langue char(1) : de taal in de vorm van F, N, E, D
  • @first int : het nummer van het eerste record dat moet worden geretourneerd volgens de paginering.
  • @last int : het nummer van het laatste record dat moet worden geretourneerd volgens de paginering.
  • @level char(10) :  een vrije code om de categorie te identificeren. Het hoogste niveau wordt herkend door @level = ''.
  • @id char(10) : de identificatie van de categorie of het artikel.

Het onderstaande codevoorbeeld geeft een voorbeeld van een dergelijke opgeslagen procedure. MercatorSelfScan verwacht de namen van de kolommen in de tabel @t en de kolom RowCount.

CREATE PROCEDURE [dbo].[SP_SELFSCAN_ITEMSELECTOR] 
@langue char(1),@first int,@last int,@level char(10),@id char(10)
AS
BEGIN
declare @t table (recno int identity(1,1), [level] char(1),id char(10),name varchar(250),img varbinary(MAX))

if @level = ''
begin
insert into @t ([level],id,name,img)
select 'R' as level,id,nom as name,img from RAYONS where (img is not null) and (DATALENGTH(img) > 0)
union
select null as level,s_id as id,s_modele as name,s_image1 as img from STOCK where s_id in ('100058','100059','100060')
end

if @level = 'R'
begin
insert into @t ([level],id,name,img)
select 'F' as level,id,nom as name,img from FAMILLES where (id_rayon = @id) and (IMG is not null)
union
select null as level,s_id as id,s_modele as name,s_image1 as img from STOCK where (s_id_rayon = @id) and (s_image1 is not null) and (DATALENGTH(s_image1) > 0)
end

if @level = 'F'
begin
insert into @t ([level],id,name,img)
select null as level,s_id as id,s_modele as name,s_image1 as img from STOCK where (s_id_famil = @id) and (s_image1 is not null) and (DATALENGTH(s_image1) > 0)
end

declare @rowcount int
select @rowcount = count(*) from @t
select *,@rowcount as [rowcount] from @t where recno between @first and @last

END

 

Personalisatie door middel van code

Het gedrag van MercatorSelfScan kan worden aangepast met behulp van een SelfScan-customizer die aanwezig is in de gekoppelde Mercator. Deze customizer moet de interface MercatorUi.ICustomizers.ISelfScanStarted implementeren, wat vereist dat de volgende methode wordt geïmplementeerd: 
Zoom
public void SelfScanStarted(MercatorUi.SelfScan.SelfScan selfScan)

 

Vanaf daar is het mogelijk om in te schrijven op de gedocumenteerde gebeurtenissen hieronder. Deze methode wordt opgeroepen bij het opstarten van MercatorSelfScan.

De klasse MercatorUi.SelfScan heeft een eigenschap ConfigurationManagerAppSettings waarmee toegang wordt verkregen tot de inhoud van het bestand MercatorSelfScan.appsettings.json, via de naam van de sleutel: :

Zoom
string s = selfScan.ConfigurationManagerAppSettings["CustomerField"];
int= selfScan.ConfigurationManagerAppSettings.GetValue<int>("CartMaxRows");

Deze toegang is alleen-lezen.

Het is toegestaan om extra sleutels toe te voegen aan het json-bestand om ze hier te gebruiken.

 

Tekenreeksen personaliseren

Alle getoonde tekens in de interface kunnen worden aangepast. Hiervoor moet het evenement 'ChangeStringResource' van het object 'MercatorUi.SelfScan' worden onderschept.

Zoom
namespace SelfScan
{
    public class Customizer : MercatorUi.ICustomizers.ISelfScanStarted
    {
        public void SelfScanStarted(MercatorUi.SelfScan.SelfScan selfScan)
        {
            selfScan.ChangeStringResource += SelfScan_ChangeStringResource;
        }

        private void SelfScan_ChangeStringResource(object sender, MercatorUi.SelfScan.SelfScan.ChangeStringResourceEventArgs e)
        {
            if ((e.ResourceName == "I don't have a loyalty card") && (MercatorUi.Globals.Langue == ApiLangues.F))
                e.ResourceValue = "Helaas heb ik niet het voordeel van zo'n gunstige klantenkaart!";
        }
    }
}

 

Als u de naam en waarde van de bronnen in MercatorSelfScan wilt kennen, plaatst u eenvoudigweg tijdelijk deze code, die een bestand zal genereren met de getoonde bronnen tijdens de sessie. Het bestand wordt geproduceerd bij het sluiten van MercatorSelfScan.

Zoom
namespace SelfScan
{
    public class Customizer : MercatorUi.ICustomizers.ISelfScanStarted
    {
        private Dictionary<string, string> dicoResources = new Dictionary<string, string>();

        public void SelfScanStarted(MercatorUi.SelfScan.SelfScan selfScan)
        {
            selfScan.ChangeStringResource += SelfScan_ChangeStringResource;    
            selfScan.Disposing += SelfScan_Disposing;
        }

        private void SelfScan_ChangeStringResource(object sender, MercatorUi.SelfScan.SelfScan.ChangeStringResourceEventArgs e)
        {
            if (!dicoResources.ContainsKey(Globals.Langue + "\t" + e.ResourceName))
                dicoResources.Add(Globals.Langue + "\t" + e.ResourceName, e.ResourceValue);
        }

        private void SelfScan_Disposing(object sender, EventArgs e)
        {
            StringBuilder sb = new StringBuilder();
            foreach (KeyValuePair<string, string> kvp in dicoResources)
                sb.AppendLine(kvp.Key + "\t" + kvp.Value);
            Api.StrToFile(sb.ToString(), @"C:\Test\resources.txt", System.Text.Encoding.UTF8);
        }
    }
}

 

Aanpassen van de kolommen in het raster van gescande artikelen.

In dit raster zijn alle gegevens uit LIGNES_V beschikbaar. MercatorSelfScan biedt een standaard lijst van kolommen aan. Deze lijst kan worden aangepast door kolommen toe te voegen, te wijzigen of te verwijderen via het evenement ChangeCartColumns. In het onderstaande voorbeeld wordt getoond hoe je:

  • een kolom met de barcode die is opgeslagen in S_CLE1 kunt toevoegen op de tweede positie.
  • de kolom met het kruisje om een regel te verwijderen kunt verwijderen
Zoom
namespace SelfScan
{
    public class Customizer : MercatorUi.ICustomizers.ISelfScanStarted
    {
        public void SelfScanStarted(MercatorUi.SelfScan.SelfScan selfScan)
        {
            selfScan.ChangeCartColumns += SelfScan_ChangeCartColumns;
        }

        private void SelfScan_ChangeCartColumns(object sender, MercatorUi.SelfScan.SelfScan.ChangeCartColumnsEventArgs e)
        {
            MercatorBlazor.GridColumn newCol = new MercatorBlazor.GridColumn
            {
                HeaderText = MercatorUi._Divers.Iif_langue(MercatorUi.Globals.Langue, "Barcode", "Barcode", "Code-barres"),
                DataMember = "s_cle1",
                Sortable = false,
                CssClass = "order-status"
            };
            e.Columns.Insert(1, newCol);

            e.Columns.Remove(e.Columns.Last());
        }
    }
}

 

De klasse MercatorBlazor.GridColumn heeft een methode Clone() die het gemakkelijk maakt om een bestaande kolom te dupliceren.

 

SQL-queries wijzigen

Alle SQL-query's kunnen worden onderschept en aangepast als de customizer de IStringUpdater-interface implementeert. Elke query is identificeerbaar aan de hand van een ID.

In het onderstaande voorbeeld laten we zien hoe we de zoekquery voor het vinden van een klant op basis van zijn klantenkaart kunnen aanpassen, om slapende klanten uit te sluiten.

Zoom
namespace SelfScan
{
    public class Customizer : MercatorUi.ICustomizers.IStringUpdater
    {
        public string StringUpdate(string stringToModify)
        {
            string id = Api.StrExtract(stringToModify, "<ID>", "</ID>");
            if (id == "SELECT_CLI")
                stringToModify = stringToModify.Replace("=@barcode)", "=@barcode) and (c_sommeil=0)");
            return stringToModify;
        }
    }
}

 

Aanpassing aan de specificaties van de barcodelezer

De klasse MercatorUi.SelfScan.SelfScan, die wordt doorgegeven aan de methode SelfScanStarted, bevat een eigenschap BarcodeReader van het type MercatorUi.SelfScan.BarcodeReader. Deze eigenschap moet worden gevuld met een instantie van een concrete klasse die is afgeleid van deze abstracte klasse. 

Zoom
public abstract class BarcodeReader
{
    public abstract void SetEnabled(bool enabled);
 
    public void OnScanSuccessful(string barcode)
    { ... }
}

 

Het is noodzakelijk om in de concrete klasse de methode "SetEnabled" te overschrijven, die het mogelijk maakt om de scanner in- en uit te schakelen. (Het is essentieel dat de scanner wordt uitgeschakeld wanneer de gebruiker zich op een scherm bevindt dat geen scan verwacht).

Wanneer de concrete klasse een barcode ontvangt (barcode gelezen evenement), moet de methode "OnScanSuccessful" worden aangeroepen met de zojuist gelezen barcode als parameter.

Opmerking: Als deze configuratie niet wordt uitgevoerd, presenteert MercatorSelfScan een emulatie van de barcodelezer, waarmee het programma in de "demo"-modus kan worden gebruikt.

Advies : deze customizer is gecentraliseerd in de Mercator-database en is daarom van toepassing op alle kassa's. We raden aan om een sleutel toe te voegen in MercatorSelfScan.appsettings.json die de verbindingsinformatie bevat met betrekking tot de barcodelezer die op deze computer is geïnstalleerd. (Bijvoorbeeld, de COM-poort of USB-poort die wordt gebruikt). Deze sleutel wordt gelezen zoals hierboven beschreven.

Hieronder staat de code voor een Datalogic MAGELLAN™ 3410VSi-barcodelezer geconfigureerd om een COM-poort te emuleren:
Zoom
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Linq;
using MercatorApi;
using MercatorExtensions;
using MercatorUi;
using MercatorDatabase;
using System.IO.Ports;
using MercatorUi.SelfScan;

// <ReferenceInclude >"System.IO.Ports.dll"</ReferenceInclude>

namespace SelfScan
{
    public class Customizer : MercatorUi.ICustomizers.ISelfScanStarted
    {
        public void SelfScanStarted(MercatorUi.SelfScan.SelfScan selfScan)
        {
            selfScan.Disposing += SelfScan_Disposing;
            string barCodePort = selfScan.ConfigurationManagerAppSettings["BarCodePort"];
            if (!string.IsNullOrEmpty(barCodePort))
                selfScan.BarcodeReader = new DataLogicScan(selfScan, barCodePort);
        }

        private void SelfScan_Disposing(object sender, EventArgs e)
        {
            MercatorUi.SelfScan.SelfScan selfScan = (MercatorUi.SelfScan.SelfScan)sender;
            if (selfScan.BarcodeReader is DataLogicScan dataLogicScan)
                dataLogicScan.Close();
        }
    }

    public class DataLogicScan : BarcodeReader
    {
        private SerialPort serialPort = null;

        public DataLogicScan(MercatorUi.SelfScan.SelfScan selfScan, string barCodePort)
            : base(selfScan)
        {
            try
            {
                serialPort = new SerialPort(barCodePort)
                {
                    BaudRate = 9600,
                    Parity = Parity.None,
                    StopBits = StopBits.One,
                    DataBits = 8
                };
                serialPort.Open();
                if (!serialPort.IsOpen)
                    return;
                if (barCodePort.StartsWith("COM", StringComparison.InvariantCultureIgnoreCase))
                {
                    serialPort.ReadTimeout = 1000;
                    serialPort.WriteTimeout = 1000;
                }
                serialPort.DataReceived += serialPort_DataReceived;
            }
            catch (Exception ex)
            {
                MercatorUi.Globals.ApiLogDelegate("DataLogicScan Init : " + ex.ToString());
            }
            return;
        }

        public void Close()
        {
            if (serialPort != null)
            {
                SetEnabled(false);
                serialPort.Dispose();
                serialPort = null;
            }
        }

        public override void SetEnabled(bool enabled)
        {
            if (serialPort != null)
            {
                byte[] buffer = Api.StringToBytes(enabled ? "E" : "D");
                try { serialPort.Write(buffer, 0, buffer.Length); }
                catch (Exception ex)
                {
                    MercatorUi.Globals.ApiLogDelegate("DataLogicScan SetEnabled : " + ex.ToString());
                }
            }
        }

        private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            string result = "";
            try
            {
                byte[] bb = new Byte[serialPort.BytesToRead];
                int n = serialPort.Read(bb, 0, serialPort.BytesToRead);
                Array.Resize(ref bb, n);
                result = Api.BytesToString(bb);
                OnScanSuccessful(result.Substring(1).TrimEnd());
            }
            catch (Exception ex)
            {
                MercatorUi.Globals.ApiLogDelegate("DataLogicScan DataReceived : " + ex.ToString());
            }
        }
    }
}

 

Aanpassing van een eventuele lichtindicator

Sommige scanstations zijn uitgerust met een lichtindicator (led) waarmee de status van de zelfscankassa kan worden weergegeven op basis van de kleur. In het algemeen is dit apparaat via een USB-poort of COM-poort verbonden. Deze poort kan worden toegevoegd in het bestand MercatorSelfScan.appsettings.json zoals hierboven aangegeven. Een eenvoudige manier is om het evenement SendingToMonitor te gebruiken, dat wordt opgeheven onder verschillende omstandigheden om statusberichten naar de MercatorSelfScanMonitor-applicatie te sturen.

Hieronder een voorbeeldcode die compatibel is met een Toshiba-kassa waarmee de kleur van de led kan worden gespecificeerd. De led zal de volgende kleuren weergeven:

  • Groen: actieve kassa
  • Oranje: waarschuwing op de kassa
  • Rood: kassa vereist assistentie van het personeel
  • Uit: kassa is niet actief

Zoom
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Linq;
using MercatorApi;
using MercatorExtensions;
using MercatorUi;
using MercatorDatabase;
using System.IO.Ports;
using MercatorUi.SelfScan;

// <ReferenceInclude >"System.IO.Ports.dll"</ReferenceInclude>

namespace SelfScan
{
    public class Customizer : MercatorUi.ICustomizers.ISelfScanStarted
    {
        private ManageLed manageLed;

        public void SelfScanStarted(MercatorUi.SelfScan.SelfScan selfScan)
        {
            selfScan.Disposing += SelfScan_Disposing;
            string ledPort = selfScan.ConfigurationManagerAppSettings["LedPort"];
            if (!string.IsNullOrWhiteSpace(ledPort))
                manageLed = new ManageLed(ledPort);
        }

        private void SelfScan_Disposing(object sender, EventArgs e)
        {
            manageLed?.Close();
        }

        private void SelfScan_SendingToMonitor(object sender, MercatorUi.SelfScan.SelfScan.SendingToMonitorEventArgs e)
        {
            MercatorUi.SelfScan.SelfScan selfScan = (MercatorUi.SelfScan.SelfScan)sender;
            if (manageLed != null)
            {
                switch (e.Level)
                {
                    case MercatorUi.SelfScan.SelfScan.SendingToMonitorEventArgs.LevelEnum.Inactive:
                        MercatorPos.Pos.SendToPort(manageLed.LedPort, MercatorPos.Pos.Evaluate("chr(204)+chr(218)+chr(0)+chr(0)+chr(0)+chr(0)+chr(6)+chr(0)"), "1"); //Off
                        break;
                    case MercatorUi.SelfScan.SelfScan.SendingToMonitorEventArgs.LevelEnum.Normal:
                        MercatorPos.Pos.SendToPort(manageLed.LedPort, MercatorPos.Pos.Evaluate("chr(204)+chr(218)+chr(0)+chr(255)+chr(0)+chr(0)+chr(6)+chr(0)"), "1"); //Green
                        break;
                    case MercatorUi.SelfScan.SelfScan.SendingToMonitorEventArgs.LevelEnum.Closed:
                        MercatorPos.Pos.SendToPort(manageLed.LedPort, MercatorPos.Pos.Evaluate("chr(204)+chr(218)+chr(0)+chr(0)+chr(0)+chr(0)+chr(6)+chr(0)"), "1"); //Off
                        break;
                    case MercatorUi.SelfScan.SelfScan.SendingToMonitorEventArgs.LevelEnum.Alert:
                        MercatorPos.Pos.SendToPort(manageLed.LedPort, MercatorPos.Pos.Evaluate("chr(204)+chr(218)+chr(255)+chr(0)+chr(0)+chr(0)+chr(6)+chr(0)"), "1"); //Red
                        break;
                    case MercatorUi.SelfScan.SelfScan.SendingToMonitorEventArgs.LevelEnum.Warning:
                        MercatorPos.Pos.SendToPort(manageLed.LedPort, MercatorPos.Pos.Evaluate("chr(204)+chr(218)+chr(255)+chr(128)+chr(0)+chr(0)+chr(6)+chr(0)"), "1");//Orange
                        break;
                }
            }
        }
    }

    public class ManageLed
    {
        public string LedPort { get; private set; }
        private SerialPort serialPort = null;
        
        public ManageLed(string ledPort)
        {
            LedPort = ledPort;
            try
            {
                serialPort = new SerialPort(ledPort)
                {
                    BaudRate = 9600,
                    Parity = Parity.None,
                    StopBits = StopBits.One,
                    DataBits = 8
                };
                serialPort.Open();
                if (!serialPort.IsOpen)
                    MercatorUi.Globals.ApiLogDelegate("ManageLed Init : not open !");
                else if (ledPort.StartsWith("COM", StringComparison.InvariantCultureIgnoreCase))
                    serialPort.WriteTimeout = serialPort.ReadTimeout = 1000;
            }
            catch (Exception ex)
            {
                MercatorUi.Globals.ApiLogDelegate("ManageLed Init : " + ex.ToString());
            }
            return;
        }

        public void Close()
        {
            if (serialPort != null)
            {
                serialPort.Dispose();
                serialPort = null;
            }
        }
    }
}


Hieronder een voorbeeld van code die compatibel is met een kassa waarmee alleen de led kan worden aan- en uitgezet.

Zoom
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Linq;
using MercatorApi;
using MercatorExtensions;
using MercatorUi;
using MercatorDatabase;
using System.IO.Ports;
using MercatorUi.SelfScan;

// <ReferenceInclude >"System.IO.Ports.dll"</ReferenceInclude>

namespace SelfScan
{
    public class Customizer : MercatorUi.ICustomizers.ISelfScanStarted
    {
        private ManageLed manageLed;

        public void SelfScanStarted(MercatorUi.SelfScan.SelfScan selfScan)
        {
            selfScan.Disposing += SelfScan_Disposing;
            string ledPort = selfScan.ConfigurationManagerAppSettings["LedPort"];
            if (!string.IsNullOrWhiteSpace(ledPort))
                manageLed = new ManageLed(ledPort);
        }

        private void SelfScan_Disposing(object sender, EventArgs e)
        {
            manageLed?.Close();
        }

        private void SelfScan_SendingToMonitor(object sender, MercatorUi.SelfScan.SelfScan.SendingToMonitorEventArgs e)
        {
            MercatorUi.SelfScan.SelfScan selfScan = (MercatorUi.SelfScan.SelfScan)sender;
            if (manageLed != null)
            {
                switch (e.Level)
                {
                    case MercatorUi.SelfScan.SelfScan.SendingToMonitorEventArgs.LevelEnum.Inactive:
                        manageLed.SetEnabled(false);
                        break;
                    case MercatorUi.SelfScan.SelfScan.SendingToMonitorEventArgs.LevelEnum.Normal:
                        manageLed.SetEnabled(true);
                        break;
                    case MercatorUi.SelfScan.SelfScan.SendingToMonitorEventArgs.LevelEnum.Closed:
                        manageLed.SetEnabled(false);
                        break;
                    case MercatorUi.SelfScan.SelfScan.SendingToMonitorEventArgs.LevelEnum.Alert:
                        manageLed.SetEnabled(true);
                        break;
                    case MercatorUi.SelfScan.SelfScan.SendingToMonitorEventArgs.LevelEnum.Warning:
                        manageLed.SetEnabled(true);
                        break;
                }
            }
        }
    }

    public class ManageLed
    {
        public string LedPort { get; private set; }
        private SerialPort serialPort = null;
        
        public ManageLed(string ledPort)
        {
            LedPort = ledPort;
            try
            {
                serialPort = new SerialPort(ledPort)
                {
                    BaudRate = 9600,
                    Parity = Parity.None,
                    StopBits = StopBits.One,
                    DataBits = 8
                };
                serialPort.Open();
                if (!serialPort.IsOpen)
                    MercatorUi.Globals.ApiLogDelegate("ManageLed Init : not open !");
                else if (ledPort.StartsWith("COM", StringComparison.InvariantCultureIgnoreCase))
                    serialPort.WriteTimeout = serialPort.ReadTimeout = 1000;
            }
            catch (Exception ex)
            {
                MercatorUi.Globals.ApiLogDelegate("ManageLed Init : " + ex.ToString());
            }
            return;
        }

        public void SetEnabled(bool enabled)
        {
            if (serialPort != null)
            {
                byte[] buffer = Api.StringToBytes(enabled ? "d" : "n");
                try { serialPort.Write(buffer, 0, buffer.Length); }
                catch (Exception ex)
                {
                    MercatorUi.Globals.ApiLogDelegate("ManageLed SetEnabled : " + ex.ToString());
                }
            }
        }

        public void Close()
        {
            if (serialPort != null)
            {
                SetEnabled(false);
                serialPort.Dispose();
                serialPort = null;
            }
        }
    }
}

 

Het uitsluiten van bepaalde artikelen van de verkoop

Het is vaak nodig of verplicht om bepaalde artikelen uit de zelfscan verkoop te halen (bijvoorbeeld alcohol). Deze uitsluiting kan worden gedaan via een customizer die is geplaatst op de verkoopssequentie en die reageert op het evenement BeforeInsertItem. Door e.CancelInsertItem = true te gebruiken, kunt u voorkomen dat dit artikel wordt ingevoegd. De reden voor deze uitsluiting kan worden geplaatst in de eigenschap billingEngine.TagString en zal worden weergegeven aan zowel de klant als via de MercatorSelfscanMonitor-applicatie.

Ci-dessous un exemple de code. Il faut y noter le test sur cette condition 

if (Globals.IsMercatorSelfScan)

qui permet de déterminer si l'encodage a lieu dans MercatorSelfScan ou pas.

Zoom
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
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.BeforeInsertItem += billingEngine_BeforeInsertItem;
        }

        public void BillingEngineClosed(MercatorUi.Engine.Gescom.BillingEngine billingEngine)
        {
            billingEngine.BeforeInsertItem -= billingEngine_BeforeInsertItem;
        }

        private void billingEngine_BeforeInsertItem(object sender, MercatorUi.Engine.Gescom.BillingEngine.BeforeInsertItemEventArgs e)
        {
            MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
            if (Globals.IsMercatorSelfScan && (e.StockRecord.S_ID_RAYON == "id_rayon_alcool"))
            {
                billingEngine.TagString = "Cet article ne peut être vendu via cette caisse autonome !";
                e.CancelInsertItem = true;
            }
        }
    }
}