Pas een promotie toe op basis van de totale verkoop of een andere voorwaarde

0000003171     -      05-07-2023

De instellingen die hier worden uitgelegd laten zien hoe je een promotie kunt toepassen op basis van het verkooptotaal. Als het verkooptotaal een bepaald bedrag bereikt, wordt de promotie toegepast. Deze promotie verwijst naar de mogelijkheden die worden aangeboden door "Tools / Kortingen". Vanaf dat moment zijn alle functionaliteiten op dit scherm beschikbaar en worden ze afhankelijk van het totaalbedrag van de verkoop:

  • korting volgens artikel, rayon, familie, enz.
  • korting in % of korting in EUR
  • korting beperkt tot bepaalde klanten
  • ...

Om deze functionaliteit te gebruiken, moet deze kolom eerst worden toegevoegd aan de tabel BAREMESV_PIEDS :

alter table BAREMESV_PIEDS add FILTER_KEY char(50) not null default ''
create index FILTER_KEY on BAREMESV_PIEDS(FILTER_KEY)

De kolom BAREMESV_PIEDS.FILTER_KEY is een optionele kolom die door Mercator wordt herkend. Ze werkt samen met de eigenschap BaremesVFilterKey van de BillingEngine.

  • Als BaremesVFilterKey leeg is, wat overeenkomt met de "normale" situatie, dan wordt er alleen rekening gehouden met kortingen die overeenkomen met een lege BAREMESV_PIEDS.FILTER_KEY;
  • als BaremesVFilterKey niet leeg is, wat overeenkomt met de situatie "voorwaardelijke promotie toegepast", dan wordt alleen rekening gehouden met kortingen waarbij BAREMESV_PIEDS.FILTER_KEY = BaremesVFilterKey.

Het basismechanisme bestaat daarom uit het tijdelijk instellen van een BillingEngine.BaremesVFilterKey waarde in de gebeurtenis BeforeBeforePaymentOrSave als het verkooptotaal is bereikt, en het herberekenen van de prijzen op dat moment. Dit mechanisme kan worden toegepast op elke andere voorwaarde dan een minimumtotaal van de verkoop :

  • aanwezigheid van bepaalde items,
  • teruggave van een kortingsbon door de klant,
  • aantal gekochte artikelen
  • ...

In deze eerste versie van de customiser worden de prijzen gewoon herberekend, rekening houdend met de promotie waarvoor de filter is ingesteld (in ons voorbeeld is deze filter geplaatst aan de voet van de korting MIN100).

Zoom
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Windows.Forms;
using MercatorApi;
using MercatorUi;
using MercatorExtensions;

namespace Billing
{
    public class Customizer : MercatorUi.ICustomizers.IBillingEngineCreated, MercatorUi.ICustomizers.IBillingEngineClosed
    {
        public void BillingEngineCreated(MercatorUi.Engine.Gescom.BillingEngine BillingEngine)
        {
            BillingEngine.BeforeBeforePaymentOrSave += BillingEngine_BeforeBeforePaymentOrSave;
        }

        public void BillingEngineClosed(MercatorUi.Engine.Gescom.BillingEngine BillingEngine)
        {
            BillingEngine.BeforeBeforePaymentOrSave -= BillingEngine_BeforeBeforePaymentOrSave;
        }

        private const double minTot = 100;

        private void BillingEngine_BeforeBeforePaymentOrSave(object sender, MercatorUi.Engine.Gescom.BillingEngine.BeforeBeforePaymentOrSaveEventArgs e)
        {
            MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
            billingEngine.ChangeAllPrices();
            if (billingEngine.PiedsVRecord.TOT_TTC_DV.CompareTo(minTot, billingEngine.NDec) > 0)
            {
                bool PRIO_PV_changed = false;
                string PRIO_PV = MercatorUi.Globals.Params["PRIO_PV"];

                try
                {
                    if (PRIO_PV != "PROMOSOLDE")
                    {
                        MercatorUi.Globals.Params["PRIO_PV"] = "PROMOSOLDE";
                        PRIO_PV_changed = true;
                    }
                    billingEngine.BaremesVFilterKey = "MIN100";
                    billingEngine.ChangeAllPrices();
                }
                finally
                {
                    billingEngine.BaremesVFilterKey = null;
                    if (PRIO_PV_changed)
                        MercatorUi.Globals.Params["PRIO_PV"] = PRIO_PV;
                }
            }
        }
    }
}

Merk op dat deze code alle prijzen opnieuw berekent voordat ze worden opgeslagen, met behulp van de methode ChangeAllPrices. De eerste aanroep van deze methode reset de prijzen zonder deze speciale promotie, in het geval dat dit document is gewijzigd en de drempel niet meer wordt bereikt.

De methode ChangeAllPrices kan worden toegepast om items uit te sluiten. Om bijvoorbeeld een "verzendkosten" item uit te sluiten waarvan de S_ID ID_PORT is :

Zoom
billingEngine.ChangeAllPrices("id_article<>'ID_PORT'");

Het is ook mogelijk om bepaalde items uit te sluiten van ChangeAllPrices door LIGNES_V.NON_MOD_PRIX (bit) op true te zetten op de corresponderende regels.

Als je wilt dat het mechanisme sneller wordt toegepast, bijvoorbeeld nadat elk item is ingevoegd, gebruik je gewoon de gebeurtenis AfterInsertItem in plaats van BeforeBeforePaymentOrSave.


In deze tweede versie van de customizer worden artikelprijzen niet herberekend, maar wordt een actiesimulatie uitgevoerd. Als deze simulatie resulteert in een totale verkoop die lager is dan de effectieve totale verkoop, wordt een regel met een "promotie"-item toegevoegd. Het bedrag van deze regel is het negatieve verschil.

Zoom
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Windows.Forms;
using MercatorApi;
using MercatorUi;
using MercatorExtensions;
using System.Linq;

namespace Billing
{
    public class Customizer : MercatorUi.ICustomizers.IBillingEngineCreated, MercatorUi.ICustomizers.IBillingEngineClosed
    {
        public void BillingEngineCreated(MercatorUi.Engine.Gescom.BillingEngine BillingEngine)
        {
            BillingEngine.BeforeBeforePaymentOrSave += BillingEngine_BeforeBeforePaymentOrSave;
        }

        public void BillingEngineClosed(MercatorUi.Engine.Gescom.BillingEngine BillingEngine)
        {
            BillingEngine.BeforeBeforePaymentOrSave -= BillingEngine_BeforeBeforePaymentOrSave;
        }

        private const double minTot = 100;
        private const string id_art_promo = "PROM51JNJT";

        private void BillingEngine_BeforeBeforePaymentOrSave(object sender, MercatorUi.Engine.Gescom.BillingEngine.BeforeBeforePaymentOrSaveEventArgs e)
        {
            MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
            foreach (DataRow drLigne in billingEngine.LIGNES.RowsEnumerable(dr => dr["id_article"].Equals(id_art_promo)).ToArray())
            {
                billingEngine.LIGNES.Rows.Remove(drLigne);
            }
            billingEngine.UpdateAmounts();
            double totInitial = billingEngine.PiedsVRecord.TOT_TTC_DV;
            
            if (totInitial.CompareTo(minTot, billingEngine.NDec) > 0)
            {
                DataTable dtLignesCopy = billingEngine.LIGNES.Copy();
                bool PRIO_PV_changed = false;
                string PRIO_PV = MercatorUi.Globals.Params["PRIO_PV"];
                try
                {
                    if (PRIO_PV != "PROMOSOLDE")
                    {
                        MercatorUi.Globals.Params["PRIO_PV"] = "PROMOSOLDE";
                        PRIO_PV_changed = true;
                    }
                    billingEngine.BaremesVFilterKey = "MIN100";
                    billingEngine.ChangeAllPrices();
                }
                finally
                {
                    billingEngine.BaremesVFilterKey = null;
                    if (PRIO_PV_changed)
                        MercatorUi.Globals.Params["PRIO_PV"] = PRIO_PV;
                }

                double totPromo = Math.Round(totInitial - billingEngine.PiedsVRecord.TOT_TTC_DV, billingEngine.NDec);

                // reset de beginwaarden in billingEngine.LIGNES
                foreach (DataRow dr in dtLignesCopy.Rows)
                {
                    DataRow drLigne = billingEngine.LIGNES.RowsEnumerable(p => p["dl_id"].Equals(dr["dl_id"])).First();
                    Api.DataRowMerge(drLigne, dr, false, new string[4] {"ID", "JOURNAL", "PIECE", "DL_ID" });
                }
                // voeg indien nodig een promotieregel toe
                if (totPromo.CompareTo(0d, billingEngine.NDec) > 0)
                {
                    int n = billingEngine.AppendLine();
                    if (!billingEngine.InsertItem(id_art_promo, billingEngine.LIGNES.Rows[n]))
                    {
                        MercatorUi.Dialogs.Stop(string.Format("Impossible d'ajouter l'article \"{0}\" !", id_art_promo));
                        e.Cancel = true;
                    }
                    billingEngine.LignesVRecords[n].DESIGNATIO = "Promo panier min. € " + minTot;
                    billingEngine.LignesVRecords[n].REMISE = 0;
                    billingEngine.LignesVRecords[n].REMISE2 = 0;
                    billingEngine.LignesVRecords[n].REMISE3 = 0;
                    billingEngine.LignesVRecords[n].REMISE4 = 0;
                    if (billingEngine.PiedsVRecord.REGIME == MercatorDatabase.RegimesEnum.Normal)
                        billingEngine.LignesVRecords[n].PU = -totPromo / (1 + (billingEngine.LignesVRecords[n].TAUX_TVA / 100));
                    else
                        billingEngine.LignesVRecords[n].PU = -totPromo;
                }
                billingEngine.UpdateAmounts();
            }
        }
    }
}

De regel id_art_promo = "PROM51JNJT" moet worden aangepast met de S_ID van een "promotie"-item.

Lijnen met REMISE2, REMISE3 en REMISE4 moeten worden verwijderd als deze kolommen niet bestaan in tabel LIGNES_V.

De promotie wordt berekend op basis van het bedrag inclusief btw. Als het regime Normaal is, dan wordt het bedrag exclusief btw van de promotie berekend aan de hand van het btw-tarief van het gebruikte "promotie"-item. Indien nodig kan het nodig zijn om meerdere "promotie"-regels toe te voegen met verschillende btw-tarieven in verhouding tot de btw-grondslagen van het document.


Opmerkingen over mogelijke uitsluitingen of cumulatie van kortingen

De hier beschreven parameterisatie moet worden gezien als een hulpmiddel. Het startpunt voor dit mechanisme is een customizer, waarmee je de berekening van de voorwaardelijke promotie kunt aanpassen.

Het is bijvoorbeeld mogelijk om items die al een promotie hebben van dit mechanisme uit te sluiten. In dit geval moet niet het totaal van het document in aanmerking worden genomen, maar het totaal van de items die niet in promotie zijn. Vervang hiervoor

billingEngine.PiedsVRecord.TOT_TTC_DV
door
Math.Round(billingEngine.LignesVRecords.Where(l => !l.PROMOSOLDE).Sum(l => l.TOTAL), billingEngine.NDec) 

En deze regels

billingEngine.ChangeAllPrices();
worden vervangen door
billingEngine.ChangeAllPrices("promosolde=0");

Als u "normale" kortingen en voorwaardelijke kortingen op basis van het totaal wilt combineren, moet u mogelijk de "normale" promoties dupliceren in "Tools / Kortingen" en de extra korting voor minimale winkelmandgrootte toevoegen.

Bijvoorbeeld:

  • in rayon A: 10% korting
  • als de waarde van de verkoop 100 euro bedraagt, een extra korting van 5%.

In dit geval worden twee schaalvoeten gecreëerd:

  • de eerste, zonder filter, met de 10% korting op rayon A
  • de tweede, met filter, en
    • korting 1: 10% (de eerste schaal wordt hier herhaald)
    • korting 2: 5% korting