Vous consultez une page technique concernant le logiciel de gestion Mercator. Celle-ci contient des informations spécifiques destinées aux professionnels de Mercator. Souhaitez-vous être redirigés vers des informations plus générales ?


   Ne plus poser cette question

Soldes et promotions par quantité

0000003027     -      11/04/2022

Mercator 10.10 ou ultérieur permet de gérer les soldes et promotions (Outils > Remises) selon des seuils de quantité. Pour activer cette fonctionnalité dans les ventes, il faut 

  • s'assurer que l'option "Prix > Tarifs par quantités sur ventes" (id = UTIL_TARQ) est à OUI
  • ajouter la colonne Q_MIN de type float dans la table BAREMES
alter table BAREMES add Q_MIN float not null default 0

De même, si on souhaite activer cette fonctionnalité dans les achats, il faut 

  • s'assurer que l'option "Prix > Tarifs par quantités sur achats" (id = UTIL_TARQA) est à OUI
  • ajouter la colonne Q_MIN de type float dans la table BAREMESA
alter table BAREMESA add Q_MIN float not null default 0

 

Une fois ces modifications effectuées, l'écran disponible via "Outils > Remises" présente une colonne supplémentaire "Qté min.", qui permet de fixer une quantité minimale pour que la ligne soit prise en compte. Comme l'ordre des lignes détermine l'ordre de priorité des règles, il est nécessaire de commencer par la plus haute valeur de "Qté min." et de mettre en place ces valeurs selon un ordre décroissant.

bareme_qmin


Rappel : l'option "Prix > Tarif par quantité sur quantités négatives" (id = TARQ_ABS) permet de ne tenir compte que de la valeur absolue de la quantité pour déterminer le seuil à utiliser.


Le BillingEngine dispose d'un événement levé lors de la recherche d'une ligne de remise selon un seuil de quantité : GettingBareme. L'eventArgs de cet événement permet de changer la quantité à prendre en compte pour la sélection du seuil de quantité :

  • Qart : pour une règle sélectionnée sur base de l'article
  • Qrayon : pour une règle sélectionnée sur base du rayon
  • Qfamille : pour une règle sélectionnée sur base de la famille
  • Qssfam : pour une règle sélectionnée sur base de la sous-famille
  • Qcat1 : pour une règle sélectionnée sur base de la catégorie article 1
  • Qcat2 : pour une règle sélectionnée sur base de la catégorie article2
  • Qcat3 : pour une règle sélectionnée sur base de la catégorie article3
  • Qother : pour une règle sélectionnée sans critère particulier
ainsi que la Date pour une modification de la date (ex : tenir compte de la date de création de la commande alors qu'on est au niveau du BL).

Dans l'exemple ci-dessous, nous montrons comment changer ce seuil en sommant toutes les quantités pour l'article en cours. Ensuite, une fois le prix obtenu, celui-ci est réappliqué par code (pour PU et REMISE) dans les autres lignes comportant aussi cet article. Ceci permet d'appliquer une remise même si la quantité du seuil n'est atteinte qu'en sommant plusieurs lignes.

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

// <CompileWithRoslyn />

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

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

        private void billingEngine_GettingBareme(object sender, MercatorUi.Engine.Gescom.BillingEngine.GettingBaremeEventArgs e)
        {
            MercatorUi.Engine.Gescom.BillingEngine billingEngine = (MercatorUi.Engine.Gescom.BillingEngine)sender;
            double totQ = billingEngine.LignesVRecords.Where(l => l.ID_ARTICLE == e.LigneVRecord.ID_ARTICLE).Sum(l => l.Q);
            double totQRayon = billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE) && billingEngine.StockRecords[l.ID_ARTICLE].S_ID_RAYON == billingEngine.StockRecords[e.LigneVRecord.ID_ARTICLE].S_ID_RAYON).Sum(l => l.Q);
            double totQFamille = billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE) && billingEngine.StockRecords[l.ID_ARTICLE].S_ID_FAMIL == billingEngine.StockRecords[e.LigneVRecord.ID_ARTICLE].S_ID_FAMIL).Sum(l => l.Q);
            double totQSsFam = billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE) && billingEngine.StockRecords[l.ID_ARTICLE].S_ID_SSFAM == billingEngine.StockRecords[e.LigneVRecord.ID_ARTICLE].S_ID_SSFAM).Sum(l => l.Q);
            double totQCat1 = billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE) && billingEngine.StockRecords[l.ID_ARTICLE].S_CAT1 == billingEngine.StockRecords[e.LigneVRecord.ID_ARTICLE].S_CAT1).Sum(l => l.Q);
            double totQCat2 = billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE) && billingEngine.StockRecords[l.ID_ARTICLE].S_CAT2 == billingEngine.StockRecords[e.LigneVRecord.ID_ARTICLE].S_CAT2).Sum(l => l.Q);
            double totQCat3 = billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE) && billingEngine.StockRecords[l.ID_ARTICLE].S_CAT3 == billingEngine.StockRecords[e.LigneVRecord.ID_ARTICLE].S_CAT3).Sum(l => l.Q);
            double totQOther = billingEngine.LignesVRecords.Where(l => !string.IsNullOrEmpty(l.ID_ARTICLE)).Sum(l => l.Q);

            if ((totQ.CompareTo(e.Qart, Globals.N_DEC_Q) != 0) || (totQRayon.CompareTo(e.Qrayon, Globals.N_DEC_Q) != 0) || (totQFamille.CompareTo(e.Qfamille, Globals.N_DEC_Q) != 0) || (totQSsFam.CompareTo(e.Qssfam, Globals.N_DEC_Q) != 0) || (totQCat1.CompareTo(e.Qcat1, Globals.N_DEC_Q) != 0)
                || (totQCat2.CompareTo(e.Qcat2, Globals.N_DEC_Q) != 0) || (totQCat3.CompareTo(e.Qcat3, Globals.N_DEC_Q) != 0) || (totQOther.CompareTo(e.Qother, Globals.N_DEC_Q) != 0))
            {
                e.Qart = totQ;
                e.Qcat1 = totQCat1;
                e.Qcat2 = totQCat2;
                e.Qcat3 = totQCat3;
                e.Qrayon = totQRayon;
                e.Qfamille = totQFamille;
                e.Qssfam = totQSsFam;
                e.Qother = totQOther;

                MercatorUi.Engine.Gescom.BillingEngine.AfterApplyPricingInfoEventHandler afterApplyPricingInfoEventHandler = null;
                afterApplyPricingInfoEventHandler = (s, e2) =>
                {
                    billingEngine.AfterApplyPricingInfo -= afterApplyPricingInfoEventHandler;
                    if ((e.DataRowLignes == e2.DataRowLignes) && (e2.PricingInfosV.Bareme != null) && (Convert.ToDouble(e2.PricingInfosV.Bareme["q_min"]).CompareTo(0 ,Globals.N_DEC_Q) > 0)) // est-on toujours sur la même ligne ?
                    {
                        if (!string.IsNullOrWhiteSpace(e2.PricingInfosV.Bareme["ar_ref"].ToString()))
                        {
                            // mettre à jour les autres lignes avec ce même article
                            foreach (var l in billingEngine.LignesVRecords.Where(l => (l.ID_ARTICLE == e.LigneVRecord.ID_ARTICLE)))
                            {
                                l.PU = e.LigneVRecord.PU;
                                l.REMISE = e.LigneVRecord.REMISE;
                                l.PROMOSOLDE = true;
                            }
                        }
                        else if (!string.IsNullOrWhiteSpace(e2.PricingInfosV.Bareme["id_fam"].ToString()))
                        {
                            MercatorDatabase.STOCK stockRecord = (e2.StockRecord ?? billingEngine.StockRecords[e2.LignesVRecord.ID_ARTICLE]);
                            switch (e2.PricingInfosV.Bareme["tag"].ToString())
                            {
                                case "4":
                                case "12":
                                case "20":
                                    billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE) && billingEngine.StockRecords[l.ID_ARTICLE].S_ID_RAYON == stockRecord.S_ID_RAYON).ToList()
                                    .ForEach(l =>
                                    {
                                        l.REMISE = e.LigneVRecord.REMISE;
                                        l.PROMOSOLDE = true;
                                    }
                                            );
                                    break;
                                case "3":
                                case "11":
                                case "19":
                                    billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE) && billingEngine.StockRecords[l.ID_ARTICLE].S_ID_FAMIL == stockRecord.S_ID_FAMIL).ToList()
                                    .ForEach(l =>
                                    {
                                        l.REMISE = e.LigneVRecord.REMISE;
                                        l.PROMOSOLDE = true;
                                    }
                                            );
                                    break;
                                case "2":
                                case "10":
                                case "18":
                                    billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE) && billingEngine.StockRecords[l.ID_ARTICLE].S_ID_SSFAM == stockRecord.S_ID_SSFAM).ToList()
                                    .ForEach(l =>
                                    {
                                        l.REMISE = e.LigneVRecord.REMISE;
                                        l.PROMOSOLDE = true;
                                    }
                                            );
                                    break;
                                case "5":
                                case "13":
                                case "21":
                                    billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE) && billingEngine.StockRecords[l.ID_ARTICLE].S_CAT1 == stockRecord.S_CAT1).ToList()
                                    .ForEach(l =>
                                    {
                                        l.REMISE = e.LigneVRecord.REMISE;
                                        l.PROMOSOLDE = true;
                                    }
                                            );
                                    break;
                                case "6":
                                case "14":
                                case "22":
                                    billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE) && billingEngine.StockRecords[l.ID_ARTICLE].S_CAT2 == stockRecord.S_CAT2).ToList()
                                    .ForEach(l =>
                                    {
                                        l.REMISE = e.LigneVRecord.REMISE;
                                        l.PROMOSOLDE = true;
                                    }
                                            );
                                    break;
                                case "7":
                                case "15":
                                case "23":
                                    billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE) && billingEngine.StockRecords[l.ID_ARTICLE].S_CAT3 == stockRecord.S_CAT3).ToList()
                                    .ForEach(l =>
                                    {
                                        l.REMISE = e.LigneVRecord.REMISE;
                                        l.PROMOSOLDE = true;
                                    }
                                            );
                                    break;
                            }
                        }
                        else
                        {
                            billingEngine.LignesVRecords.Where(l => !string.IsNullOrWhiteSpace(l.ID_ARTICLE)).ToList()
                                    .ForEach(l =>
                                    {
                                        l.REMISE = e.LigneVRecord.REMISE;
                                        l.PROMOSOLDE = true;
                                    }
                                            );
                        }

                    }
                };
                billingEngine.AfterApplyPricingInfo += afterApplyPricingInfoEventHandler;
            }
        }
    }
}

 

Le principe de ce customizer est identique à celui illustré sur cette page : Appliquer un tarif par quantité en fonction de la quantité totale par article dans le document.

Note : le code illustré ici ne gère pas

  • le changement de prix en cas de suppression de ligne
  • le fait de redescendre en dessous de la quantité minimum par l'ajout d'une nouvelle ligne avec une quantité négative.
Ceci doit donc être géré via un appel de ChangeAllPrices dans un événement BeforeBeforePaymentOrSave.

 


Informations complémentaires : Fonctionnalités de "Outils > Remises"

 

Mots clés : minimum