2021-11-28 12:43:05 +01:00
# Tkinter
2021-11-28 12:06:41 +01:00
from tkinter import IntVar , Checkbutton , LabelFrame , PhotoImage , Scrollbar , Listbox , Entry , Button , Label , Frame , Tk , Toplevel
2021-11-29 13:02:52 +01:00
from tkinter . ttk import Combobox , Separator
2021-11-28 12:06:41 +01:00
from tkinter . messagebox import showerror , showinfo , showwarning , askyesno
2021-11-29 22:10:03 +01:00
from tkinter . filedialog import askopenfile , asksaveasfile
2021-11-28 12:43:05 +01:00
# Regex
2021-11-19 11:41:37 +01:00
from re import sub
2021-11-28 14:36:28 +01:00
# Date
from datetime import date
2021-11-19 11:41:37 +01:00
2021-11-29 13:52:24 +01:00
# Import des fichiers pour gérer la base de donnée et l'export en CSV
2021-11-28 12:43:05 +01:00
from users import Utilisateurs
2021-11-24 17:53:17 +01:00
from stock import Stock
2021-11-29 14:11:25 +01:00
from stats import Stats
2021-11-18 20:41:58 +01:00
class GesMag :
2021-11-19 18:52:58 +01:00
""" Programme de Gestion d ' une caise de magasin. """
2021-11-28 12:43:05 +01:00
def __init__ ( self , presentation : bool = False ) - > None :
2021-11-19 21:37:48 +01:00
""" Instancie quelques variables pour plus de clareté. """
2021-11-28 12:43:05 +01:00
Utilisateurs ( ) . creationTable ( presentation ) # on créer la table utilisateurs si elle n'existe pas déjà
Stock ( ) . creationTable ( presentation ) # on créer la table du stock si elle n'existe pas déjà
2021-11-29 20:24:27 +01:00
Stats ( ) . creationCSV ( ) # on créer le fichier CSV qui stockera les statistiques des utilisateurs
2021-11-28 12:43:05 +01:00
2021-11-29 09:12:51 +01:00
self . nomApp = " GesMag " # nom de l'application
2021-11-20 00:11:59 +01:00
self . parent = Tk ( ) # fenêtre affiché à l'utilisateur
self . parent . resizable ( False , False ) # empêche la fenêtre d'être redimensionnée
2021-11-29 13:02:52 +01:00
self . f = Frame ( self . parent ) # `Frame` "principale" affiché à l'écran
self . tableau = Frame ( ) # `Frame` qui va afficher le tableau des éléments présents dans le stock
2021-11-25 23:21:36 +01:00
self . imagesStock = [ ] # liste qui va contenir nos images pour l'affichage du stock
2021-11-28 12:28:03 +01:00
self . dossierImage = PhotoImage ( file = " img/dossier.gif " ) # image pour l'icone de selection
2021-11-29 13:02:52 +01:00
self . panierAffichage = Frame ( ) # `Frame` qui va afficher le panier
2021-11-28 14:36:28 +01:00
self . panier = [ ] # liste des éléments "dans le panier"
2021-11-19 21:37:48 +01:00
2021-11-18 20:41:58 +01:00
def demarrer ( self ) - > None :
""" Lance le programme GesMag. """
2021-11-19 11:41:37 +01:00
self . font = ( " Comfortaa " , 14 ) # police par défaut
self . _interfaceConnexion ( ) # on créer la variable `self.f` qui est la frame a affiché
self . f . grid ( ) # on affiche la frame
2021-11-19 00:47:01 +01:00
2021-11-19 11:41:37 +01:00
self . parent . mainloop ( ) # on affiche la fenêtre
2021-11-19 00:47:01 +01:00
2021-11-19 11:41:37 +01:00
def motDePasseCorrect ( self , motDPasse : str ) - > tuple :
""" Détermine si un mot de passe suit la politique du programme ou non. """
if len ( motDPasse ) == 0 : # si le champs est vide
return ( False , " Mot de passe incorrect. " )
if len ( motDPasse ) < 8 : # si le mot de passe est plus petit que 8 caractères
return ( False , " Un mot de passe doit faire 8 caractères minimum. " )
"""
2021-11-20 19:47:13 +01:00
- Pour le regex , la fonction ` sub ` élimine tout ce qui est donné en fonction
du pattern renseigné , alors si la fonction ` sub ` renvoie pas exactement
la même chaîne de charactère alors c ' est qu ' il y avait un charactère interdit .
- J ' utilises pas `match` parce que je suis plus à l ' aise avec ` sub ` .
2021-11-19 11:41:37 +01:00
"""
if not sub ( r " [A-Z] " , ' ' , motDPasse ) != motDPasse :
return ( False , " Un mot de passe doit au moins contenir une lettre majuscule. " )
if not sub ( r " [a-z] " , ' ' , motDPasse ) != motDPasse :
return ( False , " Un mot de passe doit au moins contenir une lettre minuscule. " )
if not sub ( r " *?[^ \ w \ s]+ " , ' ' , motDPasse ) != motDPasse :
return ( False , " Un mot de passe doit au moins contenir un caractère spécial. " )
return ( True , ) # si aucun des tests précédents n'est valide, alors le mot de passe est valide
2021-11-18 20:41:58 +01:00
2021-11-20 19:47:13 +01:00
def utilisateurCorrect ( self , utilisateur : str ) - > tuple :
""" Détermine si un nom d ' utilisateur suit la politique du programme ou non. """
"""
Pour le nom d ' utilisateur on vérifie si le champs n ' est pas vide
et si il y a bien que des lettres et des chiffres .
"""
if len ( utilisateur ) == 0 :
return ( False , " Utilisateur incorrect. " )
if sub ( r " *?[^ \ w \ s]+ " , ' ' , utilisateur ) != utilisateur :
return ( False , " Un nom d ' utilisateur ne doit pas contenir de caractère spécial. " )
return ( True , )
2021-11-21 02:29:24 +01:00
def nomCorrect ( self , nom : str ) - > bool :
""" Détermine si un nom suit la politique du programme ou non. """
if len ( nom ) == 0 :
return False
if sub ( r " *?[^ \ w \ s]+ " , ' ' , nom ) != nom : # pas de caractères spéciaux dans un nom
return False
return True
def prenomCorrect ( self , prenom : str ) - > bool :
""" Détermine si un prénom suit la politique du programme ou non. """
if len ( prenom ) == 0 :
return False
if sub ( r " *?[^ \ w \ s]+ " , ' ' , prenom ) != prenom : # pas de caractères spéciaux dans un prénom
return False
return True
def naissanceCorrect ( self , naissance : str ) - > bool :
""" Détermine si une date de naissance suit la politique du programme ou non. """
if len ( naissance ) == 0 :
return False
2021-11-21 12:40:03 +01:00
# lien pour mieux comprendre ce qui se passe : https://www.debuggex.com/r/hSD-6BfSqDD1It5Z
if sub ( r " [0-9] {4} \ /(0[1-9]|1[0-2]) \ /(0[1-9]|[1-2][0-9]|3[0-1]) " , ' ' , naissance ) != ' ' :
2021-11-21 02:29:24 +01:00
return False
return True
def adresseCorrect ( self , adresse : str ) - > bool :
""" Détermine si une adresse suit la politique du programme ou non. """
if len ( adresse ) == 0 :
return False
return True
def postalCorrect ( self , code : str ) - > bool :
""" Détermine si un code postal suit la politique du programme ou non. """
if len ( code ) == 0 :
return False
if sub ( r " \ d {5} " , ' ' , code ) != ' ' :
return False
return True
2021-11-18 20:41:58 +01:00
def connexion ( self , utilisateur : str , motDePasse : str ) :
2021-11-19 00:47:01 +01:00
""" Gère la connexion aux différentes interfaces de l ' application. """
2021-11-19 11:41:37 +01:00
"""
Vérification nom d ' utilisateur / mot de passe correctement entré
2021-11-20 19:47:13 +01:00
avec leurs fonctions respectives .
2021-11-19 11:41:37 +01:00
"""
2021-11-20 19:47:13 +01:00
pseudoOk = self . utilisateurCorrect ( utilisateur )
if not pseudoOk [ 0 ] :
2021-11-28 12:06:41 +01:00
showerror ( " Erreur " , pseudoOk [ 1 ] )
2021-11-19 11:41:37 +01:00
return
mdpOk = self . motDePasseCorrect ( motDePasse )
if not mdpOk [ 0 ] :
2021-11-28 12:06:41 +01:00
showerror ( " Erreur " , mdpOk [ 1 ] )
2021-11-18 20:41:58 +01:00
return
2021-11-19 00:47:01 +01:00
# Redirection vers la bonne interface
2021-11-21 02:29:24 +01:00
utilisateurBaseDeDonnee = Utilisateurs ( ) . verificationIdentifiants ( utilisateur , motDePasse )
2021-11-19 23:57:11 +01:00
if utilisateurBaseDeDonnee [ 0 ] > 0 :
if utilisateurBaseDeDonnee [ 1 ] == 0 : # si le métier est "Manager"
self . _interfaceManager ( utilisateurBaseDeDonnee [ 0 ] )
elif utilisateurBaseDeDonnee [ 1 ] == 1 : # si le métier est "Caissier"
self . _interfaceCaissier ( utilisateurBaseDeDonnee [ 0 ] )
else :
2021-11-28 12:06:41 +01:00
showerror ( " Erreur " , " Une erreur est survenue : métier inconnue. " )
2021-11-18 20:41:58 +01:00
else :
2021-11-28 12:06:41 +01:00
showerror ( " Erreur " , " Utilisateur ou mot de passe incorrect. " )
2021-11-18 20:41:58 +01:00
2021-11-20 11:39:16 +01:00
def dimensionsFenetre ( self , fenetre , nouveauX : int , nouveauY : int ) :
2021-11-19 20:14:41 +01:00
""" Permet de changer les dimensions de la fenêtre parent et la place au centre de l ' écran. """
2021-11-20 11:39:16 +01:00
largeur = fenetre . winfo_screenwidth ( )
hauteur = fenetre . winfo_screenheight ( )
2021-11-19 20:12:06 +01:00
2021-11-19 20:13:53 +01:00
x = ( largeur / / 2 ) - ( nouveauX / / 2 )
y = ( hauteur / / 2 ) - ( nouveauY / / 2 )
2021-11-19 20:12:06 +01:00
2021-11-20 11:39:16 +01:00
fenetre . geometry ( f " { nouveauX } x { nouveauY } + { x } + { y } " )
2021-11-19 20:12:06 +01:00
2021-11-19 00:47:01 +01:00
def _interfaceConnexion ( self ) :
2021-11-19 23:57:11 +01:00
""" Affiche l ' interface de connexion. """
2021-11-18 20:41:58 +01:00
# Paramètres de la fenêtre
2021-11-20 11:39:16 +01:00
self . dimensionsFenetre ( self . parent , 400 , 600 )
2021-11-29 09:12:51 +01:00
self . parent . title ( f " Fenêtre de connexion – { self . nomApp } " )
2021-11-19 00:47:01 +01:00
2021-11-19 23:57:11 +01:00
# Suppresssion de la dernière Frame
self . f . destroy ( )
# Instanciation d'une nouvelle Frame, on va donc ajouter tout nos widgets à cet Frame
2021-11-19 00:47:01 +01:00
self . f = Frame ( self . parent )
2021-11-20 02:40:02 +01:00
self . f . grid ( )
2021-11-18 20:41:58 +01:00
# Affichage des labels et boutons
2021-11-19 00:47:01 +01:00
tentativeDeConnexion = lambda _ = None : self . connexion ( utilisateur . get ( ) , motDpasse . get ( ) ) # lambda pour envoyer les informations entrés dans le formulaire
2021-11-19 11:41:37 +01:00
ecart = 80 # écart pour avoir un affichage centré
2021-11-19 00:47:01 +01:00
Label ( self . f ) . grid ( row = 0 , pady = 50 ) # utilisé pour du padding (meilleur affichage)
2021-11-18 20:41:58 +01:00
2021-11-20 02:40:02 +01:00
Label ( self . f , text = " Utilisateur " , font = self . font ) . grid ( column = 0 , row = 1 , columnspan = 2 , padx = ecart - 20 , pady = 20 , sticky = ' w ' )
2021-11-19 00:47:01 +01:00
utilisateur = Entry ( self . f , font = self . font , width = 18 )
2021-11-18 20:41:58 +01:00
utilisateur . grid ( column = 1 , row = 2 , columnspan = 2 , padx = ecart )
2021-11-20 02:40:02 +01:00
Label ( self . f , text = " Mot de passe " , font = self . font ) . grid ( column = 0 , row = 3 , columnspan = 2 , padx = ecart - 20 , pady = 20 , sticky = ' w ' )
2021-11-19 00:47:01 +01:00
motDpasse = Entry ( self . f , font = self . font , show = ' ⁎ ' , width = 18 )
motDpasse . grid ( column = 1 , row = 4 , columnspan = 2 , padx = ecart )
motDpasse . bind ( " <Return> " , tentativeDeConnexion )
2021-11-19 00:08:48 +01:00
def __afficherMDP ( self ) :
""" Permet de gérer l ' affichage du mot de passe dans le champs sur la page de connexion. """
if self . mdpVisible == False : # si mot de passe caché, alors on l'affiche
self . mdpVisible = True
motDpasse . config ( show = ' ' )
bouttonAffichageMDP . config ( font = ( " Arial " , 10 , " overstrike " ) )
else : # inversement
self . mdpVisible = False
motDpasse . config ( show = ' ⁎ ' )
bouttonAffichageMDP . config ( font = ( " Arial " , 10 ) )
2021-11-19 00:47:01 +01:00
bouttonAffichageMDP = Button ( self . f , text = ' 👁 ' , command = lambda : __afficherMDP ( self ) )
2021-11-19 00:08:48 +01:00
bouttonAffichageMDP . grid ( column = 2 , row = 4 , columnspan = 2 )
self . mdpVisible = False
2021-11-19 00:47:01 +01:00
bouton = Button ( self . f , text = " Se connecter " , font = self . font , command = tentativeDeConnexion )
2021-11-18 20:41:58 +01:00
bouton . grid ( column = 0 , row = 5 , columnspan = 3 , padx = ecart , pady = 20 )
2021-11-19 00:47:01 +01:00
bouton . bind ( " <Return> " , tentativeDeConnexion )
2021-11-18 20:41:58 +01:00
2021-11-19 17:14:15 +01:00
Button ( self . f , text = " Quitter " , font = self . font , command = quit ) . grid ( column = 0 , row = 6 , columnspan = 4 , pady = 20 )
2021-11-18 20:41:58 +01:00
2021-11-24 16:47:54 +01:00
self . _interfaceCaissier ( 1 ) # DEBUG: affiche directement l'interface du Caissier
2021-11-20 00:11:59 +01:00
2021-11-19 23:57:11 +01:00
def _interfaceCaissier ( self , id : int ) :
""" Affiche l ' interface du caissier. """
2021-11-24 16:12:35 +01:00
caissier = Utilisateurs ( ) . recuperationUtilisateur ( id = id )
2021-11-29 09:12:51 +01:00
self . parent . title ( f " Caissier { caissier [ ' nom ' ] } { caissier [ ' prenom ' ] } – { self . nomApp } " )
2021-11-29 13:23:45 +01:00
self . dimensionsFenetre ( self . parent , 1160 , 710 )
2021-11-19 23:57:11 +01:00
2021-11-29 13:25:44 +01:00
self . panier = [ ] # remet le panier à 0
2021-11-19 23:57:11 +01:00
# Suppresssion de la dernière Frame
self . f . destroy ( )
# Instanciation d'une nouvelle Frame, on va donc ajouter tout nos widgets à cet Frame
self . f = Frame ( self . parent )
2021-11-20 02:40:02 +01:00
self . f . grid ( )
2021-11-19 23:57:11 +01:00
2021-11-24 16:12:35 +01:00
Label ( self . f , text = " Interface Caissier " , font = ( self . font [ 0 ] , 20 ) ) . grid ( column = 0 , row = 0 ) # titre de l'interface
2021-11-29 13:52:24 +01:00
def __formatPrix ( prix : str ) - > str :
"""
Renvoie un string pour un meilleur affichage du prix :
- ` , ` au lieu de ` . `
- Symbole ` € `
- 2 chiffres après la virgule
"""
return f " { float ( prix ) : .2f } € " . replace ( ' . ' , ' , ' )
# -> Partie affichage du Stock
2021-11-25 18:01:05 +01:00
stock = LabelFrame ( self . f , text = " Stock " )
2021-11-25 23:21:36 +01:00
stock . grid ( column = 0 , row = 1 , sticky = ' n ' , padx = 5 )
2021-11-24 16:12:35 +01:00
2021-11-26 13:18:12 +01:00
# Variables pour les filtres du tableau
stockDisponibleVerif = IntVar ( stock ) # controle si on affiche que les éléments en stocks ou non
# Cache un certain type de produit
fruitsLegumesVerif = IntVar ( stock )
boulangerieVerif = IntVar ( stock )
boucheriePoissonnerieVerif = IntVar ( stock )
entretienVerif = IntVar ( stock )
2021-11-29 13:02:52 +01:00
def __affichageTableau ( page : int = 1 ) :
2021-11-26 13:18:12 +01:00
""" Fonction qui va actualiser le tableau avec une page donnée (par défaut affiche la première page). """
2021-11-25 18:01:05 +01:00
# On supprime et refais la frame qui va stocker notre tableau
2021-11-29 13:02:52 +01:00
self . tableau . destroy ( )
self . tableau = Frame ( stock )
self . tableau . grid ( column = 0 , row = 1 , columnspan = 7 )
2021-11-25 18:01:05 +01:00
2021-11-26 13:18:12 +01:00
# Filtre pour le tableau
filtres = Frame ( stock ) # Morceau qui va contenir nos checkbutton
ecartFiltre = 10 # écart entre les champs des filtres
Label ( filtres , text = " Filtre " , font = self . font ) . grid ( column = 0 , row = 0 ) # titre
2021-11-29 13:02:52 +01:00
Checkbutton ( filtres , text = " Stock disponible \n uniquement " , variable = stockDisponibleVerif , command = __affichageTableau ) . grid ( sticky = ' w ' , pady = ecartFiltre )
Checkbutton ( filtres , text = " Cacher les \n fruits & légumes " , variable = fruitsLegumesVerif , command = __affichageTableau ) . grid ( sticky = ' w ' , pady = ecartFiltre )
Checkbutton ( filtres , text = " Cacher les produits de \n la boulangerie " , variable = boulangerieVerif , command = __affichageTableau ) . grid ( sticky = ' w ' , pady = ecartFiltre )
Checkbutton ( filtres , text = " Cacher les produits de \n la boucherie \n et poissonnerie " , variable = boucheriePoissonnerieVerif , command = __affichageTableau ) . grid ( sticky = ' w ' )
Checkbutton ( filtres , text = " Cacher les produits \n d ' entretien " , variable = entretienVerif , command = __affichageTableau ) . grid ( sticky = ' w ' , pady = ecartFiltre )
2021-11-26 13:18:12 +01:00
filtres . grid ( column = 7 , row = 1 , sticky = ' w ' )
stockListe = Stock ( ) . listeStocks ( ) # stock récupéré de la base de données
2021-11-29 13:02:52 +01:00
def ___miseAJourPanier ( element : dict , action : bool ) :
"""
Permet d ' ajouter ou de retirer des éléments au panier
- > Action
- > Vrai : Ajout
- > Faux : Retire
"""
# On compte combien de fois l'élément est présent dans le panier
nombreDeFoisPresentDansLePanier = 0
index = None
for idx , elementDansLePanier in enumerate ( self . panier ) :
if elementDansLePanier [ 0 ] == element :
index = idx # On met à jour l'index
nombreDeFoisPresentDansLePanier = elementDansLePanier [ 1 ]
break # on peut quitter la boucle car on a trouvé notre élément
# On vérifie que on peut encore l'ajouter/retirer
if nombreDeFoisPresentDansLePanier == 0 and not action : # pop-up seulement si on veut retirer un élément pas présent
showerror ( " Erreur " , " Impossible de retirer cet élément au panier. \n Non présent dans le panier. " )
return
if nombreDeFoisPresentDansLePanier > = element [ " quantite " ] and action : # pop-up seulement si on veut en rajouter
showerror ( " Erreur " , " Impossible de rajouter cet élément au panier. \n Limite excédée. " )
return
if index != None : # on retire l'ancienne valeur du panier si déjà présente dans le panier
self . panier . pop ( index )
else : # sinon on définie un index pour pouvoir ajouté la nouvelle valeur à la fin de la liste
index = len ( self . panier )
# On change la valeur dans le panier
if action : # si on ajoute
nombreDeFoisPresentDansLePanier + = 1
else : # si on retire
nombreDeFoisPresentDansLePanier - = 1
# On rajoute l'élément avec sa nouvelle quantité seulement s'il y en a
if nombreDeFoisPresentDansLePanier > 0 :
self . panier . insert ( index , ( element , nombreDeFoisPresentDansLePanier ) )
2021-11-29 20:24:27 +01:00
__affichagePanier ( ) # met-à-jour le panier
2021-11-29 13:02:52 +01:00
2021-11-26 13:18:12 +01:00
for i in range ( 0 , len ( stockListe ) ) : # on retire les éléments plus présent dans la liste
if stockDisponibleVerif . get ( ) == 1 and stockListe [ i ] [ " quantite " ] < 1 :
stockListe [ i ] = None
elif fruitsLegumesVerif . get ( ) == 1 and stockListe [ i ] [ " type " ] == " fruits legumes " :
stockListe [ i ] = None
elif boulangerieVerif . get ( ) == 1 and stockListe [ i ] [ " type " ] == " boulangerie " :
stockListe [ i ] = None
elif boucheriePoissonnerieVerif . get ( ) == 1 and stockListe [ i ] [ " type " ] == " boucherie poissonnerie " :
stockListe [ i ] = None
elif entretienVerif . get ( ) == 1 and stockListe [ i ] [ " type " ] == " entretien " :
stockListe [ i ] = None
# Supprime toutes les valeurs `None` de la liste
stockListe = list ( filter ( None , stockListe ) )
2021-11-25 18:01:05 +01:00
ecart = 10 # écart entre les champs
elementsParPage = 10 # on définit combien d'élément une page peut afficher au maximum
pageMax = - ( - len ( stockListe ) / / elementsParPage ) # on définit combien de page il y au maximum
if pageMax < = 1 :
page = 1 # on force la page à être à 1 si il n'y a qu'une page, peut importe l'argument donnée à la fonction
limiteIndex = elementsParPage * page # on définit une limite pour ne pas afficher plus d'éléments qu'il n'en faut par page
if len ( stockListe ) > 0 : # si stock non vide
# Définition des colonnes
2021-11-29 13:02:52 +01:00
Label ( self . tableau , text = " ID " ) . grid ( column = 0 , row = 0 , padx = ecart )
Label ( self . tableau , text = " Image " ) . grid ( column = 1 , row = 0 , padx = ecart )
Label ( self . tableau , text = " Type " ) . grid ( column = 2 , row = 0 , padx = ecart )
Label ( self . tableau , text = " Nom " ) . grid ( column = 3 , row = 0 , padx = ecart )
Label ( self . tableau , text = " Quantité " ) . grid ( column = 4 , row = 0 , padx = ecart )
Label ( self . tableau , text = " Prix unité " ) . grid ( column = 5 , row = 0 , padx = ecart )
Label ( self . tableau , text = " Action " ) . grid ( column = 6 , row = 0 , padx = ecart )
Separator ( self . tableau ) . grid ( column = 0 , row = 0 , columnspan = 7 , sticky = " sew " )
Separator ( self . tableau ) . grid ( column = 0 , row = 0 , columnspan = 7 , sticky = " new " )
for j in range ( 0 , 8 ) :
Separator ( self . tableau , orient = ' vertical ' ) . grid ( column = j , row = 0 , columnspan = 2 , sticky = " nsw " )
2021-11-25 18:01:05 +01:00
curseur = limiteIndex - elementsParPage # on commence à partir du curseur
i = 1 # on commence à 1 car il y a déjà le nom des colonnes en position 0
2021-11-26 00:26:59 +01:00
self . imagesStock = [ ] # on vide la liste si elle contient déjà des images
2021-11-25 18:01:05 +01:00
for element in stockListe [ curseur : limiteIndex ] : # on ignore les éléments avant le curseur et après la limite
2021-11-29 13:02:52 +01:00
Label ( self . tableau , text = element [ " id " ] ) . grid ( column = 0 , row = i , padx = ecart )
2021-11-25 18:01:05 +01:00
"""
L ' idée est que on a une liste `images` qui permet de stocker toutes nos images
( c ' est une limitation de tkinter que de garder nos images en mémoire)
Une fois ajouté à la liste , on l ' affiche dans notre Label
"""
2021-11-28 11:48:31 +01:00
if Stock ( ) . fichierExiste ( element [ " image_url " ] ) : # si l'image existe, utilisation de la fonction de `db.py`
2021-11-25 23:21:36 +01:00
self . imagesStock . append ( PhotoImage ( file = element [ " image_url " ] ) )
2021-11-28 11:48:31 +01:00
else : # si l'image n'existe pas
2021-11-26 00:26:59 +01:00
self . imagesStock . append ( PhotoImage ( file = " img/defaut.gif " ) ) # image par défaut
2021-11-29 13:02:52 +01:00
Label ( self . tableau , image = self . imagesStock [ i - 1 ] ) . grid ( column = 1 , row = i , padx = ecart )
Label ( self . tableau , text = element [ " type " ] . capitalize ( ) ) . grid ( column = 2 , row = i , padx = ecart )
Label ( self . tableau , text = element [ " nom " ] . capitalize ( ) ) . grid ( column = 3 , row = i , padx = ecart )
Label ( self . tableau , text = element [ " quantite " ] ) . grid ( column = 4 , row = i , padx = ecart )
Label ( self . tableau , text = __formatPrix ( element [ " prix " ] ) ) . grid ( column = 5 , row = i , padx = ecart )
# boutons d'actions pour le panier
2021-11-29 13:07:59 +01:00
Button ( self . tableau , text = ' + ' , font = ( " Arial " , 7 , " bold " ) , command = lambda e = element : ___miseAJourPanier ( e , True ) ) . grid ( column = 6 , row = i , sticky = ' n ' , padx = ecart )
Button ( self . tableau , text = ' − ' , font = ( " Arial " , 7 , " bold " ) , command = lambda e = element : ___miseAJourPanier ( e , False ) ) . grid ( column = 6 , row = i , sticky = ' s ' , pady = 2 )
2021-11-29 13:02:52 +01:00
for j in range ( 0 , 8 ) :
Separator ( self . tableau , orient = ' vertical ' ) . grid ( column = j , row = i , columnspan = 2 , sticky = " nsw " )
Separator ( self . tableau ) . grid ( column = j , row = i , columnspan = 2 , sticky = " sew " )
2021-11-25 18:01:05 +01:00
curseur + = 1
i + = 1
# Information sur la page actuelle
2021-11-29 13:02:52 +01:00
Label ( self . tableau , text = f " Page { page } / { pageMax } " ) . grid ( column = 2 , row = i , columnspan = 3 )
2021-11-25 18:01:05 +01:00
# Boutons
2021-11-29 13:02:52 +01:00
precedent = Button ( self . tableau , text = " Page précédente " , command = lambda : __affichageTableau ( page - 1 ) )
precedent . grid ( column = 0 , row = i , columnspan = 2 , sticky = ' w ' , padx = ecart , pady = ecart )
suivant = Button ( self . tableau , text = " Page suivante " , command = lambda : __affichageTableau ( page + 1 ) )
suivant . grid ( column = 5 , row = i , columnspan = 2 , sticky = ' e ' , padx = ecart )
2021-11-25 18:01:05 +01:00
if page == 1 : # si on est a la première page on désactive le boutton précédent
precedent . config ( state = " disabled " )
if page == pageMax : # si on est a la dernière page on désactive le boutton suivant
suivant . config ( state = " disabled " )
else :
2021-11-29 13:02:52 +01:00
Label ( self . tableau , text = " Il n ' y a rien en stock \n Essayez de réduire les critères dans le filtre. " ) . grid ( column = 0 , row = 0 , columnspan = 7 )
2021-11-25 18:01:05 +01:00
2021-11-29 13:02:52 +01:00
__affichageTableau ( ) # affichage du tableau
2021-11-25 18:01:05 +01:00
2021-11-29 13:52:24 +01:00
# -> Partie affichage du ticket de caisse
2021-11-28 14:36:28 +01:00
ecart = 10
2021-11-25 18:01:05 +01:00
ticket = LabelFrame ( self . f , text = " Ticket de caisse " )
2021-11-25 23:21:36 +01:00
ticket . grid ( column = 1 , row = 1 , sticky = ' n ' , padx = 5 )
2021-11-28 14:36:28 +01:00
Label ( ticket , text = f " Date de vente : { date . today ( ) . strftime ( ' % Y/ % m/ %d ' ) } " ) . grid ( column = 0 , row = 0 , pady = ecart )
2021-11-29 20:24:27 +01:00
def __affichagePanier ( ) :
2021-11-29 13:52:24 +01:00
""" Affiche le panier actuel dans le ticket de caisse. """
2021-11-29 13:02:52 +01:00
self . panierAffichage . destroy ( )
self . panierAffichage = Frame ( ticket )
self . panierAffichage . grid ( column = 0 , row = 1 , pady = ecart )
elementsAchetes = Label ( self . panierAffichage )
2021-11-29 13:23:45 +01:00
elementsAchetes . grid ( column = 0 , columnspan = 2 )
2021-11-29 13:02:52 +01:00
prixTotal = 0
compteurElements = 0
2021-11-29 13:23:45 +01:00
for idx , element in enumerate ( self . panier ) :
Label ( self . panierAffichage , text = f " [ { element [ 0 ] [ ' id ' ] } ] - " ) . grid ( column = 0 , row = idx + 1 , sticky = ' e ' )
2021-11-29 13:52:24 +01:00
if element [ 1 ] > 1 :
2021-11-29 20:30:32 +01:00
message = f " { element [ 1 ] } x { element [ 0 ] [ ' nom ' ] . capitalize ( ) } ( { __formatPrix ( element [ 0 ] [ ' prix ' ] ) } | total: { __formatPrix ( element [ 0 ] [ ' prix ' ] * element [ 1 ] ) } ) "
2021-11-29 13:52:24 +01:00
else :
message = f " { element [ 1 ] } x { element [ 0 ] [ ' nom ' ] . capitalize ( ) } ( { __formatPrix ( element [ 0 ] [ ' prix ' ] ) } ) "
Label ( self . panierAffichage , text = message ) . grid ( column = 1 , row = idx + 1 , sticky = ' w ' )
2021-11-29 13:02:52 +01:00
prixTotal + = ( element [ 0 ] [ " prix " ] * element [ 1 ] ) # ajout du prix
compteurElements + = element [ 1 ]
2021-11-29 13:23:45 +01:00
elementsAchetes . config ( text = f " Élément { ' s ' if compteurElements > 1 else ' ' } acheté { ' s ' if compteurElements > 1 else ' ' } ( { compteurElements } ) : " )
2021-11-29 13:02:52 +01:00
2021-11-29 20:24:27 +01:00
try : # désactive le bouton si rien n'est dans le panier
if len ( self . panier ) < = 0 :
validationTicketDeCaisseBouton . config ( state = " disabled " )
else :
validationTicketDeCaisseBouton . config ( state = " active " )
except NameError : # si pas renseigné, alors = panier vide, déjà désactiver
pass
2021-11-29 13:23:45 +01:00
Label ( self . panierAffichage , text = f " Prix total : { __formatPrix ( prixTotal ) } " ) . grid ( column = 0 , pady = ecart , columnspan = 2 )
2021-11-28 14:36:28 +01:00
2021-11-29 20:24:27 +01:00
__affichagePanier ( )
def __validationTicketDeCaisse ( ) :
""" Lance plusieurs méthodes pour valider le ticket de caisse. """
2021-11-30 10:46:12 +01:00
# Met à jour la valeur dans le fichier `CSV` (statistiques)
2021-11-29 20:24:27 +01:00
Stats ( ) . miseAJourStatsUtilisateur ( id , sum ( [ element [ 0 ] [ " prix " ] * element [ 1 ] for element in self . panier ] ) )
# Informe l'utilisateur que tout est validé
showinfo ( " Validation " , " Ticket de caisse validé ! " )
# Retire les éléments renseigné dans le panier du stock
for element in self . panier :
2021-11-29 20:28:23 +01:00
Stock ( ) . reduitQuantiteStock ( element [ 0 ] [ " id " ] , element [ 1 ] )
2021-11-29 20:24:27 +01:00
# Remet le panier à 0
self . panier = [ ]
# Met-à-jour le panier et le tableau du stock
__affichagePanier ( )
__affichageTableau ( )
2021-11-28 14:36:28 +01:00
2021-11-29 20:24:27 +01:00
validationTicketDeCaisseBouton = Button ( ticket , text = " Valider le \n ticket de caisse " , font = self . font , command = __validationTicketDeCaisse , state = " disabled " )
validationTicketDeCaisseBouton . grid ( column = 0 , pady = ecart )
2021-11-28 14:36:28 +01:00
2021-11-29 13:52:24 +01:00
# -> Partie ajout élément au stock
2021-11-28 11:48:31 +01:00
def __ajouterElementStock ( ) :
""" Ouvre une fenêtre qui permet d ' ajouter un nouvel élément à la base de donnée. """
"""
L ' enfant (`TopLevel`) dépend de la `Frame` et non du parent (`Tk`)
pour éviter de resté ouverte meme lorsque le caissier se déconnecte .
"""
enfant = Toplevel ( self . f )
2021-11-29 09:12:51 +01:00
enfant . title ( f " Ajouter un élément au stock – { self . nomApp } " )
2021-11-28 11:48:31 +01:00
def ___verification ( ) :
""" Vérifie si les champs renseignées sont valides. """
"""
La variable ` ok ` sert à savoir si la vérification est passée
si elle vaut ` True ` alors tout est bon ,
Par contre si elle vaut ` False ` alors il y a eu une erreur .
Les valeurs ` Entry ` qui ne sont pas passés seront dans
la liste ` mauvaisChamps ` .
"""
ok = True
mauvaisChamps = [ ]
# vérification pour l'image, on utilise la fonction du fichier `db.py`
if Stock ( ) . fichierExiste ( image . get ( ) ) == False :
ok = False
mauvaisChamps . append ( image )
# vérification pour le type
2021-11-28 12:33:57 +01:00
if typeElement . get ( ) not in Stock ( ) . listeTypes ( ) :
2021-11-28 11:48:31 +01:00
ok = False
# Pas de coloration orange si le type est mauvais parce que on ne peut pas changé la couleur de fond d'une ComboBox
# vérification pour le nom
2021-11-28 12:03:15 +01:00
def ___nomValide ( nom : str ) - > bool :
if len ( nom ) < = 0 :
return False
if Stock ( ) . stockExistant ( nom ) == True :
return False
return True
if ___nomValide ( nom . get ( ) ) == False :
2021-11-28 11:48:31 +01:00
ok = False
mauvaisChamps . append ( nom )
# vérification pour la quantité
try :
int ( quantite . get ( ) ) # conversion en int
2021-11-29 20:33:30 +01:00
except ValueError : # si la conversion a échoué
2021-11-28 11:48:31 +01:00
ok = False
mauvaisChamps . append ( quantite )
# vérification pour le prix
try :
float ( prix . get ( ) ) # conversion en float
2021-11-29 20:33:30 +01:00
except ValueError : # si la conversion a échoué
2021-11-28 11:48:31 +01:00
ok = False
mauvaisChamps . append ( prix )
if ok == False :
"""
Tous les champs qui n ' ont pas réunies les conditions nécéssaires
sont mis en orange pendant 3 secondes pour bien comprendre quelles champs
sont à modifié .
La fonction lambda ` remettreCouleur ` permet de remettre la couleur initial
après les 3 secondes .
"""
remettreCouleur = lambda widget , ancienneCouleur : widget . configure ( bg = ancienneCouleur )
for champs in mauvaisChamps :
couleur = champs [ " background " ] # couleur d'avant changement
champs . configure ( bg = " orange " ) # on change la couleur du champs en orange
# dans 3 secondes on fait : `remettreCouleur(champs, couleur)`
champs . after ( 3000 , remettreCouleur , champs , couleur )
else :
"""
Tous les tests sont passés , on peut ajouter l ' utilisateur à la base de donnée
Pas besoin de gérer les erreurs lors des casts car on a déjà vérifié que c ' était bien les bons types avant
"""
Stock ( ) . ajoutStock (
2021-11-28 12:33:57 +01:00
typeElement . get ( ) ,
2021-11-28 11:48:31 +01:00
nom . get ( ) ,
int ( quantite . get ( ) ) ,
float ( prix . get ( ) ) ,
image . get ( )
)
2021-11-29 13:02:52 +01:00
__affichageTableau ( ) # met à jour le tableau
2021-11-28 11:48:31 +01:00
# Champs de saisie
# Image
Label ( enfant , text = " Image : " ) . grid ( column = 0 , row = 0 , sticky = ' e ' )
image = Entry ( enfant )
image . grid ( column = 1 , row = 0 , sticky = ' w ' )
2021-11-28 12:28:03 +01:00
def ___selectionImage ( ) :
""" Fonction qui permet de choisir une image dans l ' arborescence de fichiers de l ' utilisateur. """
try :
chemin = askopenfile ( title = " Choisir une image " , filetypes = [ ( " Image GIF " , " .gif " ) ] )
image . delete ( 0 , " end " )
image . insert ( 0 , chemin . name )
2021-11-29 20:33:30 +01:00
except AttributeError : # si l'utilisateur n'a pas choisit d'image
2021-11-28 12:28:03 +01:00
pass
Button ( enfant , image = self . dossierImage , command = ___selectionImage ) . grid ( column = 1 , row = 0 , sticky = ' e ' )
2021-11-28 11:48:31 +01:00
# Type (ComboBox)
Label ( enfant , text = " Type : " ) . grid ( column = 0 , row = 1 , sticky = ' e ' )
2021-11-28 12:33:57 +01:00
typeElement = Combobox ( enfant , values = Stock ( ) . listeTypes ( ) )
# typeElement.current(0) # valeur 0 par défaut
typeElement . grid ( column = 1 , row = 1 , sticky = ' w ' )
2021-11-28 11:48:31 +01:00
# Nom
Label ( enfant , text = " Nom : " ) . grid ( column = 0 , row = 2 , sticky = ' e ' )
nom = Entry ( enfant )
nom . grid ( column = 1 , row = 2 , sticky = ' w ' )
# Quantité
Label ( enfant , text = " Quantité : " ) . grid ( column = 0 , row = 3 , sticky = ' e ' )
quantite = Entry ( enfant )
quantite . grid ( column = 1 , row = 3 , sticky = ' w ' )
# Prix à l'unité
Label ( enfant , text = " Prix à l ' unité : " ) . grid ( column = 0 , row = 4 , sticky = ' e ' )
prix = Entry ( enfant )
prix . grid ( column = 1 , row = 4 , sticky = ' w ' )
def ___viderChamps ( ) :
""" Vide tout les champs de leur contenu """
# On récupère toutes les `Entry` de la fenêtre et on change leur contenu
2021-11-28 12:33:57 +01:00
for champ in [ widget for typeElement , widget in enfant . children . items ( ) if " entry " in typeElement ] :
2021-11-28 11:48:31 +01:00
champ . delete ( 0 , " end " )
champ . update ( )
# Boutons
Button ( enfant , text = " Valider " , command = ___verification ) . grid ( column = 0 , row = 8 , columnspan = 3 , sticky = ' w ' )
Button ( enfant , text = " Vider les champs " , command = ___viderChamps ) . grid ( column = 0 , row = 8 , columnspan = 3 )
Button ( enfant , text = " Quitter " , command = enfant . destroy ) . grid ( column = 0 , row = 8 , columnspan = 3 , sticky = ' e ' )
2021-11-28 14:36:28 +01:00
Button ( self . f , text = " Ajouter un élément \n au stock " , font = self . font , command = __ajouterElementStock ) . grid ( column = 1 , row = 2 )
2021-11-28 11:48:31 +01:00
2021-11-29 13:52:24 +01:00
# -> Partie export des statistiques
2021-11-29 22:10:03 +01:00
def __exportation ( ) :
""" Exporte dans un fichier choisie par l ' utilisateur ses statistiques de la journée. """
chemin = asksaveasfile ( title = f " Exportation des statistiques de { caissier [ ' nom ' ] } { caissier [ ' prenom ' ] } " , filetypes = [ ( " Fichier CSV " , " .csv " ) ] )
if chemin == None : # si rien n'a été spécifie on arrête l'exportation
return
Stats ( ) . exporteCSV ( chemin . name , id )
Button ( self . f , text = " Exporter les statistiques " , font = self . font , command = __exportation ) . grid ( column = 0 , row = 2 , sticky = ' e ' , padx = ecart )
2021-11-29 13:52:24 +01:00
# -> Boutton pour passer en mode manager si la personne est un manager
2021-11-24 16:47:54 +01:00
if caissier [ " metier " ] == 0 :
2021-11-29 13:52:24 +01:00
Button ( self . f , text = " Passer en mode Manager " , font = self . font , command = lambda : self . _interfaceManager ( id ) ) . grid ( column = 0 , row = 2 , sticky = ' w ' , padx = ecart )
2021-11-24 16:47:54 +01:00
2021-11-24 16:12:35 +01:00
2021-11-19 23:57:11 +01:00
def _interfaceManager ( self , id : int ) :
""" Affiche l ' interface du manager. """
2021-11-20 02:40:02 +01:00
manager = Utilisateurs ( ) . recuperationUtilisateur ( id = id )
2021-11-23 14:41:11 +01:00
# Dans le cas où un utilisateur réussi à trouvé cette interface alors qu'il n'a pas le droit, il sera bloqué
if manager [ " metier " ] != 0 :
2021-11-28 12:06:41 +01:00
showerror ( " Erreur " , " Vous ne pouvez pas accéder à cette interface. " )
2021-11-23 14:41:11 +01:00
return
2021-11-29 09:12:51 +01:00
self . parent . title ( f " Manager { manager [ ' nom ' ] } { manager [ ' prenom ' ] } – { self . nomApp } " )
2021-11-21 12:40:03 +01:00
self . dimensionsFenetre ( self . parent , 580 , 310 )
2021-11-19 23:57:11 +01:00
# Suppresssion de la dernière Frame
self . f . destroy ( )
# Instanciation d'une nouvelle Frame, on va donc ajouter tout nos widgets à cet Frame
self . f = Frame ( self . parent )
2021-11-20 02:40:02 +01:00
self . f . grid ( )
Label ( self . f , text = " Interface Manager " , font = ( self . font [ 0 ] , 20 ) ) . grid ( column = 0 , row = 0 )
Button ( self . f , text = " Se déconnecter " , font = self . font , command = self . _interfaceConnexion ) . grid ( column = 1 , row = 0 , padx = 50 )
Label ( self . f ) . grid ( row = 1 , pady = 10 ) # séparateur
def __ajouterUtilisateur ( metier : int ) :
""" Permet de créer un nouvel utilisateur, manager (`metier = 0`) et caissier (`metier = 1`). """
2021-11-20 19:47:13 +01:00
"""
L ' enfant (`TopLevel`) dépend de la `Frame` et non du parent (`Tk`)
pour éviter de resté ouverte meme lorsque le manager se déconnecte .
"""
enfant = Toplevel ( self . f )
2021-11-29 09:12:51 +01:00
enfant . title ( f " Ajouter un { ' manager ' if metier == 0 else ' caissier ' } – { self . nomApp } " )
2021-11-20 19:47:13 +01:00
def ___verification ( ) :
""" Vérifie si les champs renseignées sont valides. """
2021-11-21 02:29:24 +01:00
"""
Les valeurs ` Entry ` qui ne sont pas passés seront dans
la liste ` mauvaisChamps ` .
2021-11-28 11:48:31 +01:00
Si la liste ` mauvaisChamps ` contient un élément alors un test n ' est pas ok.
2021-11-21 02:29:24 +01:00
"""
mauvaisChamps = [ ]
2021-11-20 19:47:13 +01:00
# vérification pour le nom d'utilisateur
2021-11-21 02:29:24 +01:00
if self . utilisateurCorrect ( pseudo . get ( ) ) [ 0 ] == False or Utilisateurs ( ) . utilisateurExistant ( pseudo . get ( ) ) == True :
mauvaisChamps . append ( pseudo )
2021-11-20 19:47:13 +01:00
# vérification pour le mot de passe
2021-11-21 02:29:24 +01:00
if self . motDePasseCorrect ( passe . get ( ) ) [ 0 ] == False :
mauvaisChamps . append ( passe )
2021-11-20 19:47:13 +01:00
# vérification pour le nom
2021-11-21 02:29:24 +01:00
if self . nomCorrect ( nom . get ( ) ) == False :
mauvaisChamps . append ( nom )
2021-11-20 19:47:13 +01:00
# vérification pour le prénom
2021-11-21 02:29:24 +01:00
if self . prenomCorrect ( prenom . get ( ) ) == False :
mauvaisChamps . append ( prenom )
2021-11-20 19:47:13 +01:00
# vérification pour la date de naissance
2021-11-21 02:29:24 +01:00
if self . naissanceCorrect ( naissance . get ( ) ) == False :
mauvaisChamps . append ( naissance )
2021-11-20 19:47:13 +01:00
# vérification pour l'adresse
2021-11-21 02:29:24 +01:00
if self . adresseCorrect ( adresse . get ( ) ) == False :
mauvaisChamps . append ( adresse )
2021-11-20 19:47:13 +01:00
# vérification pour le code postal
2021-11-21 02:29:24 +01:00
if self . postalCorrect ( postal . get ( ) ) == False :
mauvaisChamps . append ( postal )
2021-11-28 11:48:31 +01:00
if len ( mauvaisChamps ) != 0 :
2021-11-21 02:29:24 +01:00
"""
Tous les champs qui n ' ont pas réunies les conditions nécéssaires
sont mis en orange pendant 3 secondes pour bien comprendre quelles champs
sont à modifié .
La fonction lambda ` remettreCouleur ` permet de remettre la couleur initial
après les 3 secondes .
"""
remettreCouleur = lambda widget , ancienneCouleur : widget . configure ( bg = ancienneCouleur )
for champs in mauvaisChamps :
couleur = champs [ " background " ] # couleur d'avant changement
champs . configure ( bg = " orange " ) # on change la couleur du champs en orange
# dans 3 secondes on fait : `remettreCouleur(champs, couleur)`
champs . after ( 3000 , remettreCouleur , champs , couleur )
else :
# Tous les tests sont passés, on peut ajouter l'utilisateur à la base de donnée
Utilisateurs ( ) . ajoutUtilisateur (
pseudo . get ( ) ,
passe . get ( ) . strip ( ) ,
metier ,
nom . get ( ) ,
prenom . get ( ) ,
naissance . get ( ) ,
adresse . get ( ) ,
int ( postal . get ( ) ) , # pas besoin de gérer d'erreur lors du cast car on a vérifié avant que c'était bien une suite de chiffre
)
2021-11-21 11:52:35 +01:00
__ajouterUtilisateursListe ( listeUtilisateurs ) # met à jour la liste
2021-11-21 02:29:24 +01:00
# Champs de saisie
# Nom d'utilisateurs
2021-11-20 19:47:13 +01:00
Label ( enfant , text = " Nom d ' utilisateur : " ) . grid ( column = 0 , row = 0 , sticky = ' e ' )
2021-11-21 02:29:24 +01:00
Label ( enfant , text = " Pas de caractères spéciaux " , font = ( " Arial " , 10 , " italic " ) ) . grid ( column = 2 , row = 0 , sticky = ' w ' )
2021-11-20 19:47:13 +01:00
pseudo = Entry ( enfant )
pseudo . grid ( column = 1 , row = 0 , sticky = ' w ' )
2021-11-21 02:29:24 +01:00
# Mot de passe
2021-11-20 19:47:13 +01:00
Label ( enfant , text = " Mot de passe : " ) . grid ( column = 0 , row = 1 , sticky = ' e ' )
2021-11-21 02:29:24 +01:00
Label ( enfant , text = " 1 majuscule, miniscule et caractère spécial minimum " , font = ( " Arial " , 10 , " italic " ) ) . grid ( column = 2 , row = 1 , sticky = ' w ' )
2021-11-20 19:47:13 +01:00
passe = Entry ( enfant )
passe . grid ( column = 1 , row = 1 , sticky = ' w ' )
2021-11-21 02:29:24 +01:00
# Nom
Label ( enfant , text = " Nom : " ) . grid ( column = 0 , row = 2 , sticky = ' e ' )
Label ( enfant , text = " Pas de caractères spéciaux " , font = ( " Arial " , 10 , " italic " ) ) . grid ( column = 2 , row = 2 , sticky = ' w ' )
2021-11-20 19:47:13 +01:00
nom = Entry ( enfant )
2021-11-21 02:29:24 +01:00
nom . grid ( column = 1 , row = 2 , sticky = ' w ' )
# Prénom
Label ( enfant , text = " Prénom : " ) . grid ( column = 0 , row = 3 , sticky = ' e ' )
Label ( enfant , text = " Pas de caractères spéciaux " , font = ( " Arial " , 10 , " italic " ) ) . grid ( column = 2 , row = 3 , sticky = ' w ' )
2021-11-20 19:47:13 +01:00
prenom = Entry ( enfant )
2021-11-21 02:29:24 +01:00
prenom . grid ( column = 1 , row = 3 , sticky = ' w ' )
# Date de naissance
Label ( enfant , text = " Date de naissance : " ) . grid ( column = 0 , row = 4 , sticky = ' e ' )
Label ( enfant , text = " Format : AAAA/MM/JJ " , font = ( " Arial " , 10 , " italic " ) ) . grid ( column = 2 , row = 4 , sticky = ' w ' )
2021-11-20 19:47:13 +01:00
naissance = Entry ( enfant )
2021-11-21 02:29:24 +01:00
naissance . grid ( column = 1 , row = 4 , sticky = ' w ' )
# Adresse
Label ( enfant , text = " Adresse " ) . grid ( column = 0 , row = 5 , sticky = ' e ' )
2021-11-20 19:47:13 +01:00
adresse = Entry ( enfant )
2021-11-21 02:29:24 +01:00
adresse . grid ( column = 1 , row = 5 , sticky = ' w ' )
# Code postal
Label ( enfant , text = " Code postal : " ) . grid ( column = 0 , row = 6 , sticky = ' e ' )
Label ( enfant , text = " 5 chiffres " , font = ( " Arial " , 10 , " italic " ) ) . grid ( column = 2 , row = 6 , sticky = ' w ' )
2021-11-20 19:47:13 +01:00
postal = Entry ( enfant )
2021-11-21 02:29:24 +01:00
postal . grid ( column = 1 , row = 6 , sticky = ' w ' )
2021-11-20 19:47:13 +01:00
def ___viderChamps ( ) :
""" Vide tout les champs de leur contenu """
# On récupère toutes les `Entry` de la fenêtre et on change leur contenu
2021-11-28 12:33:57 +01:00
for champ in [ widget for typeElement , widget in enfant . children . items ( ) if " entry " in typeElement ] :
2021-11-20 19:47:13 +01:00
champ . delete ( 0 , " end " )
champ . update ( )
# Boutons
Button ( enfant , text = " Valider " , command = ___verification ) . grid ( column = 0 , row = 8 , columnspan = 3 , sticky = ' w ' )
Button ( enfant , text = " Vider les champs " , command = ___viderChamps ) . grid ( column = 0 , row = 8 , columnspan = 3 )
Button ( enfant , text = " Quitter " , command = enfant . destroy ) . grid ( column = 0 , row = 8 , columnspan = 3 , sticky = ' e ' )
2021-11-20 02:40:02 +01:00
def __retirerUtilisateur ( metier : int ) :
""" Permet de supprimer un utilisateur existant, manager (`metier = 0`) et caissier (`metier = 1`). """
2021-11-20 19:47:13 +01:00
enfant = Toplevel ( self . f ) # cf. l'explication dans `__ajouterUtilisateur`
2021-11-29 09:12:51 +01:00
enfant . title ( f " Retirer un { ' manager ' if metier == 0 else ' caissier ' } – { self . nomApp } " )
2021-11-21 11:52:35 +01:00
# Liste des utilisateurs
Label ( enfant , text = f " Liste des { ' manager ' if metier == 0 else ' caissier ' } " , font = self . font ) . grid ( column = 0 , row = 0 ) # titre
# On définit une barre pour pouvoir scroller dans la liste
2021-11-25 18:01:05 +01:00
scroll_retirer = Scrollbar ( enfant )
2021-11-21 11:52:35 +01:00
scroll_retirer . grid ( column = 1 , row = 1 , sticky = " nse " )
# On définit notre liste et on la lie à notre `Scrollbar`
listeUtilisateurs_retirer = Listbox ( enfant , width = 25 , height = 4 , yscrollcommand = scroll_retirer . set )
2021-11-25 18:01:05 +01:00
scroll_retirer . config ( command = listeUtilisateurs_retirer . yview ) # scroll à la verticale dans notre liste
2021-11-21 11:52:35 +01:00
# On ajoute nos utilisateurs à notre liste
__ajouterUtilisateursListe ( listeUtilisateurs_retirer , metier )
listeUtilisateurs_retirer . grid ( column = 0 , row = 1 )
# On affiche l'utilisateur quand on double-clique dessus
def ___suppressionUtilisateur ( ) :
""" Supprime l ' utilisateur actuellement sélectionné dans la liste """
element = listeUtilisateurs_retirer . curselection ( )
if len ( element ) == 0 : # si aucun élément n'est selectionné
2021-11-28 12:06:41 +01:00
showwarning ( " Attention " , " Aucun utilisateur n ' a été selectionné. " )
2021-11-21 11:52:35 +01:00
else :
utilisateur = listeUtilisateurs_retirer . get ( listeUtilisateurs_retirer . curselection ( ) [ 0 ] ) . split ( ' ( ' ) [ 0 ] [ : - 1 ]
2021-11-28 12:06:41 +01:00
reponse = askyesno ( " Confirmation " , f " Voulez vous supprimer { utilisateur } ? " )
2021-11-21 11:52:35 +01:00
if reponse == True :
Utilisateurs ( ) . suppressionUtilisateurs ( utilisateur )
__ajouterUtilisateursListe ( listeUtilisateurs_retirer ) # met à jour la liste dans la fenêtre de suppression
__ajouterUtilisateursListe ( listeUtilisateurs ) # met à jour la liste dans l'interface principale
2021-11-28 12:06:41 +01:00
showinfo ( " Information " , f " Utilisateur { utilisateur } supprimé. " )
2021-11-21 11:52:35 +01:00
# Boutons
Button ( enfant , text = " Supprimer " , command = ___suppressionUtilisateur ) . grid ( column = 0 , row = 8 , columnspan = 3 , sticky = ' w ' )
Button ( enfant , text = " Quitter " , command = enfant . destroy ) . grid ( column = 0 , row = 8 , columnspan = 3 , sticky = ' e ' )
2021-11-20 02:40:02 +01:00
def __afficherInformationsUtilisateur ( _ ) :
""" Permet d ' afficher les informations d ' un utilisateur """
element = listeUtilisateurs . curselection ( )
if len ( element ) == 0 : # si aucun élément n'est selectionné
return
2021-11-20 18:22:04 +01:00
"""
On split le champs car dans la liste on affiche le métier entre
parenthèses et on doit donner que le nom d ' utilisateur à
la fonction ` recuperationUtilisateur ` , aussi on retire le dernièr
charactère avec [ : - 1 ] car c ' est un espace.
"""
utilisateur = Utilisateurs ( ) . recuperationUtilisateur ( pseudo = listeUtilisateurs . get ( element [ 0 ] ) . split ( ' ( ' ) [ 0 ] [ : - 1 ] )
2021-11-20 19:47:13 +01:00
enfant = Toplevel ( self . f ) # cf. l'explication dans `__ajouterUtilisateur`
2021-11-29 09:12:51 +01:00
enfant . title ( f " { utilisateur [ ' nom ' ] } { utilisateur [ ' prenom ' ] } – { self . nomApp } " )
2021-11-21 12:40:03 +01:00
# Informations sur l'utilisateur
frameInfos = LabelFrame ( enfant , text = " Informations utilisateur " , font = self . font )
frameInfos . grid ( column = 0 , row = 0 , sticky = ' n ' , padx = 5 )
2021-11-20 11:39:16 +01:00
utilisateur [ " metier " ] = " Manager " if utilisateur [ " metier " ] == 0 else " Caissier "
2021-11-20 12:02:48 +01:00
del utilisateur [ " passe " ] # le manager ne doit pas connaître le mot de passe de l'utilisateur
2021-11-20 11:39:16 +01:00
for idx , cle in enumerate ( utilisateur ) :
2021-11-30 10:54:07 +01:00
# TODO ne pas utiliser la base de donnée mais le fichier CSV
2021-11-28 14:36:28 +01:00
if cle == " stats_journaliere " :
cleAffichage = cle . replace ( ' _ ' , ' ' ) . title ( ) # remplace le `_` en espace et met une majuscule à tous les mots
else :
cleAffichage = cle . capitalize ( )
2021-11-30 10:54:07 +01:00
# TODO ne pas utiliser la base de donnée mais le fichier CSV
2021-11-28 14:36:28 +01:00
if cle == " date " :
continue
Label ( frameInfos , text = f " { cleAffichage } : " ) . grid ( column = 0 , row = idx + 1 , sticky = ' e ' )
2021-11-21 12:40:03 +01:00
Label ( frameInfos , text = utilisateur [ cle ] ) . grid ( column = 1 , row = idx + 1 , sticky = ' w ' )
frameSuivi = LabelFrame ( enfant , text = " Suivi des ventes " , font = self . font )
frameSuivi . grid ( column = 1 , row = 0 , sticky = ' n ' , padx = 5 )
Label ( frameSuivi , text = " Aucun résultat récemment enrengistré " ) . grid ( )
2021-11-30 00:02:57 +01:00
# TODO
"""
Probablement un canvas ?
Attention , après une actualisation , les dates dans le fichier CSV ne seront plus rangés dans l ' ordre.
"""
2021-11-21 12:40:03 +01:00
Button ( enfant , text = " Quitter " , command = enfant . destroy ) . grid ( column = 0 , row = 1 , columnspan = 2 )
2021-11-20 02:40:02 +01:00
Button ( self . f , text = " Ajouter un caissier " , font = self . font , command = lambda : __ajouterUtilisateur ( 1 ) ) . grid ( column = 0 , row = 2 )
Button ( self . f , text = " Retirer un caissier " , font = self . font , command = lambda : __retirerUtilisateur ( 1 ) ) . grid ( column = 1 , row = 2 )
Label ( self . f ) . grid ( row = 3 , pady = 10 ) # séparateur
# Liste des utilisateurs
2021-11-20 18:09:34 +01:00
managerVerif = IntVar ( self . f ) # filtre pour afficher ou non les managers dans la liste
caissierVerif = IntVar ( self . f ) # filtre pour afficher ou non les caissiers ou non dans la liste
caissierVerif . set ( 1 ) # par défaut on affiche que les caissiers
2021-11-21 11:52:35 +01:00
def __ajouterUtilisateursListe ( liste : Listbox , force : int = None ) :
2021-11-20 18:09:34 +01:00
"""
Ajoute des utilisateurs à la liste du Manager .
- > metier = 0 : manager uniquement
- > metier = 1 : caissier uniquement
- > metier = 2 : manager et caissier
"""
2021-11-21 11:52:35 +01:00
liste . delete ( 0 , " end " ) # vidé la liste des utilisateurs
if force : # si `force` n'est pas `None`, alors on force l'utilisation d'un métier
metier = force
else : # sinon on fait une vérification normale en fonction des filtres de l'interface manager
if managerVerif . get ( ) == 1 :
if caissierVerif . get ( ) == 1 :
metier = None # on affiche les 2
else :
metier = 0 # on affiche seulement les managers
2021-11-20 18:09:34 +01:00
else :
2021-11-21 11:52:35 +01:00
metier = 1 # on affiche les caissiers
if caissierVerif . get ( ) == 0 : # rien est coché, on revient à la configuration par défaut (caissiers uniquement)
metier = 1
caissierVerif . set ( 1 )
2021-11-21 02:38:01 +01:00
if metier == None : # on ajoute tous les utilisateurs
2021-11-20 18:09:34 +01:00
for idx , utilisateur in enumerate ( Utilisateurs ( ) . listUtilisateurs ( ) ) :
2021-11-21 11:52:35 +01:00
liste . insert ( idx , f " { utilisateur [ 0 ] } ( { ' manager ' if utilisateur [ 1 ] == 0 else ' caissier ' } ) " )
2021-11-20 18:09:34 +01:00
elif metier == 0 : # on ajoute que les managers
for idx , utilisateur in enumerate ( Utilisateurs ( ) . listUtilisateurs ( ) ) :
if utilisateur [ 1 ] == metier :
2021-11-21 11:52:35 +01:00
liste . insert ( idx , f " { utilisateur [ 0 ] } ( { ' manager ' if utilisateur [ 1 ] == 0 else ' caissier ' } ) " )
2021-11-20 18:09:34 +01:00
elif metier == 1 : # on ajoute que les caissiers
for idx , utilisateur in enumerate ( Utilisateurs ( ) . listUtilisateurs ( ) ) :
if utilisateur [ 1 ] == metier :
2021-11-21 11:52:35 +01:00
liste . insert ( idx , f " { utilisateur [ 0 ] } ( { ' manager ' if utilisateur [ 1 ] == 0 else ' caissier ' } ) " )
2021-11-20 18:09:34 +01:00
else : # ce cas est là au cas où mais n'est pas sensé être appellé
raise NameError ( " Métier inconnu. " )
2021-11-21 12:40:03 +01:00
# Label d'information
Label ( self . f , text = """
Double - cliquez sur un
utilisateur de la liste
pour obtenir des informations
2021-11-24 16:47:54 +01:00
supplémentaire à son sujet .
2021-11-21 12:40:03 +01:00
""" , justify= " right " ).grid(column=1, row=4, rowspan=2, sticky= " e " )
2021-11-20 02:40:02 +01:00
Label ( self . f , text = " Liste des utilisateurs " , font = self . font ) . grid ( column = 0 , row = 4 ) # titre
# On définit une barre pour pouvoir scroller dans la liste
2021-11-25 18:01:05 +01:00
scroll = Scrollbar ( self . f )
2021-11-20 02:40:02 +01:00
scroll . grid ( column = 0 , row = 5 , sticky = " nse " )
# On définit notre liste et on la lie à notre `Scrollbar`
listeUtilisateurs = Listbox ( self . f , width = 25 , height = 4 , yscrollcommand = scroll . set )
2021-11-25 18:01:05 +01:00
scroll . config ( command = listeUtilisateurs . yview ) # scroll à la verticale dans notre liste
2021-11-20 02:40:02 +01:00
# On ajoute nos utilisateurs à notre liste
2021-11-21 11:52:35 +01:00
__ajouterUtilisateursListe ( listeUtilisateurs )
2021-11-20 02:40:02 +01:00
listeUtilisateurs . grid ( column = 0 , row = 5 )
listeUtilisateurs . bind ( ' <Double-Button> ' , __afficherInformationsUtilisateur ) # on affiche l'utilisateur quand on double-clique dessus
2021-11-20 00:11:59 +01:00
2021-11-20 18:09:34 +01:00
# Filtre pour la liste
Label ( self . f , text = " Filtre " , font = self . font ) . grid ( column = 1 , row = 4 , sticky = ' w ' , padx = 10 ) # titre
filtres = Frame ( self . f ) # Morceau qui va contenir nos checkbutton
filtres . grid ( column = 1 , row = 4 , rowspan = 2 , sticky = ' w ' )
2021-11-21 11:52:35 +01:00
Checkbutton ( filtres , text = " Manager " , variable = managerVerif , command = lambda : __ajouterUtilisateursListe ( listeUtilisateurs ) ) . grid ( sticky = ' w ' )
Checkbutton ( filtres , text = " Caissier " , variable = caissierVerif , command = lambda : __ajouterUtilisateursListe ( listeUtilisateurs ) ) . grid ( sticky = ' w ' )
2021-11-20 18:09:34 +01:00
2021-11-24 16:47:54 +01:00
Button ( self . f , text = " Passer en mode caissier " , font = self . font , command = lambda : self . _interfaceCaissier ( id ) ) . grid ( column = 0 , row = 6 , columnspan = 3 , pady = 10 )
2021-11-21 12:40:03 +01:00
2021-11-18 20:41:58 +01:00
if __name__ == " __main__ " :
2021-11-20 00:11:59 +01:00
""" Application " GesMag " pour le module de Programmation d ' interfaces (2021-2022) """
2021-11-28 12:43:05 +01:00
"""
2021-11-28 12:46:57 +01:00
Si presentation = True alors une base de donnée par défaut sera généré .
Si presentation = False ou n ' est même pas mentionné, alors aucune base de donnée par défaut ne sera généré.
2021-11-28 12:43:05 +01:00
"""
GesMag ( presentation = True ) . demarrer ( )