Filtre

Procédure de filtre sur MObjetListe

Introduction

Un filtre est appliqué directement sur une requête SELECT, dans un Charger() d’une classe de type MObjetListe.
Il permet de créer la partie de code de la clause WHERE.

Mise en place d’un filtre sur un MObjetListe

  • Créer un MObjetFiltre.
  • Il hérite de AFiltreListe.
  • Les membres sont les paramètres du filtre.

Déclaration

STCaracteristiqueReference est une Structure
	nRefCaracteristique	est un entier
	nRefValeur			est un entier
FIN

STCaracteristiqueNumerique est une Structure
	nRefCaracteristique	est un entier
	rMin				est un réel
	rMax				est un réel
FIN

MArticleFiltre est une Classe
	hérite de AFiltreListe

PUBLIC
	m_tabEtat				est un tableau d'entiers
	m_tabRefFamille			est un tableau d'entiers
	m_tabRefArticle			est un tableau d'entiers
	m_tabCodeArticle		est un tableau de chaînes
	m_tabArticleGenerique	est un tableau de chaînes

PRIVÉ
	m_tabCaracteristiqueReference	est un tableau de STCaracteristiqueReference
	m_tabCaracteristiqueNumerique	est un tableau de STCaracteristiqueNumerique
FIN

Constructeur

Ne pas oublier de définir si le filtre vide est autorisé.
Par défaut, le filtre vide n’est pas autorisé.
Pour l’autoriser, il faut : m_bFiltreVideAutorise = Vrai.

Redéfinition de p_sNomComplet (ABSTRAIT)

Liste des noms de paramètres compréhensibles pour l’utilisateur, séparés par un point-virgule.

PROCÉDURE p_sNomComplet() : chaîne

sNom est une chaîne

sNom += [RC] + "Nom : [%m_sNom%]"
sNom += [RC] + "Etat : [%m_tabEtat..Occurrence%]"
sNom += [RC] + "Famille : [%m_tabRefFamille..Occurrence%]"
sNom += [RC] + "Article : [%m_tabRefArticle..Occurrence%]"
sNom += [RC] + "Code article : [%m_tabCodeArticle..Occurrence%]"
sNom += [RC] + "Article générique : [%m_tabArticleGenerique..Occurrence%]"
sNom += [RC] + "Caractéristique (référence) : [%m_tabCaracteristiqueReference..Occurrence%]"
sNom += [RC] + "Caractéristique (numérique) : [%m_tabCaracteristiqueNumerique..Occurrence%]"

RENVOYER sNom

Redéfinition de p_bFiltreVide (ABSTRAIT)

  • RENVOYER Faux si aucun paramètre du filtre n’est utilisé.
  • RENVOYER Vrai si au moins un paramètre est utilisé.

Si l’état de l’objet (ACTIF, INACTIF, SUPPRIME) est un membre du filtre. Il ne faut pas en tenir compte dans p_bFiltreVide.

Autrement dit, même si m_tabRefEtat..Vide = faux, le filtre peut être considéré comme vide.

PROCÉDURE PUBLIQUE p_bFiltreVide() : booléen

SI PAS m_sNom ~= "" ALORS RENVOYER Faux
//si pas m_tabEtat..vide alors renvoyer faux
SI PAS m_tabRefFamille..Vide RENVOYER Faux
SI PAS m_tabRefArticle..Vide RENVOYER Faux
SI PAS m_tabCodeArticle..Vide ALORS RENVOYER Faux
SI PAS m_tabArticleGenerique..Vide ALORS RENVOYER Faux
SI PAS m_tabCaracteristiqueReference..Vide ALORS RENVOYER Faux
SI PAS m_tabCaracteristiqueNumerique..Vide ALORS RENVOYER Faux

RENVOYER Vrai

Procédure ResetFiltre()

Tous les paramètres sont vidés.
Ne pas prendre l’exemple ci-dessous pour argent comptant ! Il faut peut-être vider les paramètres un par un.

PROCÉDURE ResetFiltre()

VariableRAZ(m_sNom)
m_tabEtat = [CEtat.REF_ACTIF]
m_tabRefFamille.SupprimeTout()
m_tabRefArticle.SupprimeTout()
m_tabCodeArticle.SupprimeTout()
m_tabArticleGenerique.SupprimeTout()
m_tabCaracteristiqueReference.SupprimeTout()
m_tabCaracteristiqueNumerique.SupprimeTout()

Procédure GetFiltre()

Créer le code SQL à joindre à la clause WHERE.
RENVOYER sFiltre en fin de code.
Il n’y a pas de WHERE dans un GetFiltre() ; il est mentionné dans la requête principale.
Cela permet de commencer chaque élément de requête par un AND.
Si aucun WHERE n’est requis, il suffit d’en créer un avec une condition toujours vraie.

PROCÉDURE GetFiltre() : chaîne

sFiltre		est une chaîne
tabTMP		est un tableau de chaînes
sTMP		est une chaîne
nJointure	est un entier

SI PAS m_tabEtat..Vide ALORS
	sTMP = TableauEntierVersChaineAvecBalise(m_tabEtat,",","")
	sFiltre += [RC] + "AND ar.actif IN ([%sTMP%])"
FIN

SI PAS m_sNom ~= "" ALORS
	ChaîneVersTableau(m_sNom,tabTMP,RC)
	POUR TOUT sTMP DE tabTMP
		sFiltre += [RC] + "AND ar.nom LIKE '%[%SQLDoubleQuote(sTMP)%]%'"
	FIN
FIN

SI PAS m_tabRefFamille..Vide ALORS
	sTMP = TableauEntierVersChaineAvecBalise(m_tabRefFamille,",","")
	sFiltre += [RC] + "AND ar.ref_famille IN ([%sTMP%])"
FIN

SI PAS m_tabRefArticle..Vide ALORS
	sTMP = TableauEntierVersChaineAvecBalise(m_tabRefArticle,",","")
	sFiltre += [RC] + "AND ar.ref_article IN ([%sTMP%])"
FIN

SI PAS m_tabCodeArticle..Vide ALORS
	sTMP = TableauChaineVersChaineAvecBalise(m_tabCodeArticle,",","'")
	sFiltre += [RC] + "AND ar.code IN ([%sTMP%])"
FIN

SI PAS m_tabArticleGenerique..Vide ALORS
	sTMP = TableauChaineVersChaineAvecBalise(m_tabArticleGenerique,",","'")
	sFiltre += [RC] + "AND fa.article_generique IN ([%sTMP%])"
FIN

POUR TOUT M DE m_tabCaracteristiqueReference
	nJointure++
	sFiltre += [RC] + "AND lk[%nJointure%].ref_valeur = [%M.nRefValeur%]"
FIN

POUR TOUT M DE m_tabCaracteristiqueNumerique
	nJointure++
	SI M.rMin <> 0 _ET_ M.rMax <> 0 ALORS
		// BETWEEN CONSIDERE INCLUSIF
		sFiltre += [RC] + "AND CAST(lk[%nJointure%].valeur AS float) BETWEEN [%M.rMin%] AND [%M.rMax%]"
	SINON SI M.rMin <> 0 _ET_ M.rMax = 0 ALORS
		sFiltre += [RC] + "AND CAST(lk[%nJointure%].valeur AS float) >= [%M.rMin%]"
	SINON SI M.rMin = 0 _ET_ M.rMax <> 0 ALORS
		sFiltre += [RC] + "AND CAST(lk[%nJointure%].valeur AS float) <= [%M.rMax%]"
	FIN
FIN

RENVOYER sFiltre

Inclusion du filtre dans l’objet MObjetListe

  • m_pclFiltre est un MObjetFiltre dynamique, etc.
  • Créer une propriété en lecture/écriture p_pclFiltre.
  • Garder m_pclFiltre en PUBLIC.
  • Inclure le filtre dans la requête SELECT.

Modification des jointures par le filtre

Quand le filtre requiert une modification des jointures, il faut ajouter une méthode GetJointure() qui renvoie cette partie de requête.

PROCÉDURE GetJointure() : chaîne

sJointure	est une chaîne
nJointure	est un entier

POUR TOUT M DE m_tabCaracteristiqueReference
	nJointure++
	
	sJointure += [RC] + [
		INNER JOIN LATEArticle_LKArticleCaracteristique AS lk[%nJointure%] ON lk[%nJointure%].ref_article = ar.ref_article AND lk[%nJointure%].ref_caracteristique = [%M.nRefCaracteristique%]
	]
FIN

POUR TOUT M DE m_tabCaracteristiqueNumerique
	nJointure++	 
	
	sJointure += [RC] + [
		INNER JOIN LATEArticle_LKArticleCaracteristique AS lk[%nJointure%] ON lk[%nJointure%].ref_article = ar.ref_article
		AND lk[%nJointure%].ref_caracteristique = [%M.nRefCaracteristique%]
		AND ISNUMERIC(lk[%nJointure%].valeur) = 1
		AND lk[%nJointure%].valeur <> '-'
	]
FIN

RENVOYER sJointure
Modification (22/04/25) : m_bFiltreVideAutorise
Rapport de Changement - Filtre Vide Autorisé

Remplacement de m_bFiltreVideAutorise par m_bFiltreVideInterdit

Contexte

La classe AFiltreListe a été introduite pour centraliser la logique de validation des filtres dans l'application. À l'origine, un booléen nommé m_bFiltreVideAutorise contrôlait si un filtre vide était autorisé.

Cependant, la valeur par défaut était Faux, interdisant les filtres vides, ce qui allait à l'encontre de la logique métier utilisée dans 95% des cas, où les filtres vides sont en réalité permis.

Problème rencontré

Cette inversion logique a provoqué des erreurs de compréhension et des erreurs d'exécution, notamment dans le module LambiqueWelding où une opération a mis à jour l'intégralité d'une table en base de données, faute d'un filtre actif mais considéré valide par erreur.

Solution apportée

  • Remplacement du champ m_bFiltreVideAutorise par m_bFiltreVideInterdit.
  • Valeur par défaut : Faux (donc les filtres vides sont autorisés par défaut).
  • Réécriture de la méthode p_bValide() pour refléter cette logique :
SI p_bFiltreVide _ET_ m_bFiltreVideInterdit ALORS
    Erreur("Filtre vide non autorisé...")
    RENVOYER Faux
FIN
RENVOYER Vrai

Avantages du changement

  • La logique par défaut est maintenant en phase avec l'usage réel.
  • Réduction du risque d'erreurs silencieuses dans les modules métiers.
  • Amélioration de la lisibilité et de la compréhension du code.
  • Facilite l'intégration de nouveaux développeurs grâce à une logique plus naturelle.

Prochaines étapes recommandées

  • Audit du code pour remplacer toutes les anciennes références à m_bFiltreVideAutorise.
  • Ajout de vérifications systématiques avec p_bValide() avant toute opération en base.
  • Ajout de logs si p_bFiltreVide() = Vrai, à des fins de contrôle qualité.